/* ==========================================================================
   Precision Audio & Video — style.css
   Single stylesheet driving every page. Values are pulled from tokens.css
   custom properties — never hardcode a color/font/spacing value here that
   isn't a var() reference to tokens.css.
   ========================================================================== */

/* ---- Reset ---- */
*, *::before, *::after { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
  margin: 0;
  font: var(--text-body);
  color: var(--color-text);
  background: var(--color-background);
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
ul, ol { margin: 0; padding: 0; list-style: none; }
h1, h2, h3, h4, p { margin: 0; }
button { font-family: inherit; cursor: pointer; }
input, textarea, select { font-family: inherit; font-size: 16px; }

h1 { font: var(--text-h1); }
h2 { font: var(--text-h2); }
h3 { font: var(--text-h3); }
small, .text-small { font: var(--text-small); }
.label {
  font: var(--text-label);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--color-primary);
}

/* ---- Focus visibility (accessibility floor) ---- */
a:focus-visible, button:focus-visible, input:focus-visible,
textarea:focus-visible, select:focus-visible, summary:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

/* ---- Layout helpers ---- */
.container {
  max-width: var(--content-max-width);
  margin: 0 auto;
  padding: 0 var(--space-5);
}
.section {
  padding: clamp(3rem, 6vw, var(--section-rhythm)) 0;
}
.section-head {
  max-width: 720px;
  margin: 0 0 var(--space-7);
}
.section-head.center { margin-left: auto; margin-right: auto; text-align: center; }
.section-head .label { display: block; margin-bottom: var(--space-3); }
.section-head h2 { margin-bottom: var(--space-4); }
.section-head p { color: var(--color-muted); }

.bg-background { background: var(--color-background); color: var(--color-text); }
.bg-surface { background: var(--color-surface); color: var(--color-text); }
.bg-primary { background: var(--color-primary); color: var(--color-primary-foreground); }
.bg-primary .section-head p { color: rgba(255,255,255,0.85); }
.bg-surface-dark { background: var(--color-surface-dark); color: #fff; }
.bg-surface-dark .section-head p { color: rgba(255,255,255,0.7); }
.bg-primary .muted, .bg-surface-dark .muted { color: rgba(255,255,255,0.75); }
.muted { color: var(--color-muted); }

/* ---- Scroll reveal ----
   Fail-safe by design: .reveal is fully visible by default. JS adds
   .reveal-pending (the hidden start state) right before it starts observing,
   then removes it when the element scrolls into view. If JS never runs or
   IntersectionObserver never fires for any reason, content simply stays
   visible instead of being silently stuck invisible. */
.reveal { opacity: 1; transform: none; }
.reveal.reveal-pending {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity var(--motion-duration) var(--motion-easing),
              transform var(--motion-duration) var(--motion-easing);
}
.reveal.reveal-pending.is-visible { opacity: 1; transform: translateY(0); }

/* ---- Buttons ---- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: 14px 28px;
  border-radius: var(--radius-full);
  font-weight: 700;
  font-size: 15px;
  border: 2px solid transparent;
  transition: transform var(--motion-duration) var(--motion-easing),
              background var(--motion-duration) var(--motion-easing),
              box-shadow var(--motion-duration) var(--motion-easing);
  white-space: nowrap;
}
.btn-primary { background: var(--color-primary); color: #fff; }
.btn-primary:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.btn-outline { background: transparent; border-color: currentColor; color: inherit; }
.btn-outline:hover { transform: translateY(-2px); }
.btn-white { background: #fff; color: var(--color-primary); }
.btn-white:hover { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.cta-row { display: flex; flex-wrap: wrap; gap: var(--space-4); align-items: center; }

/* ---- Top promo banner ---- */
.promo-banner {
  background: var(--color-surface-dark);
  color: #fff;
  overflow: hidden;
  white-space: nowrap;
  position: relative;
}
.promo-banner .promo-track {
  display: inline-flex;
  gap: var(--space-8);
  padding: 10px 0;
  animation: promo-scroll 26s linear infinite;
}
.promo-banner .promo-track span { display: inline-flex; gap: var(--space-8); }
.promo-banner .promo-track small { font-size: 13px; letter-spacing: 0.02em; }
@keyframes promo-scroll {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ---- Header / nav ---- */
.site-header {
  position: sticky;
  top: 0;
  z-index: 100;
  /* Small negative overlap into the promo banner above — guards against a
     sub-pixel compositing seam (the banner's animated marquee child forces
     it onto its own GPU layer, which can leave a hairline gap at certain
     zoom/DPR levels even when the layout math lines up exactly). */
  margin-top: -2px;
  background: #fff;
  border-bottom: 1px solid var(--color-border);
  box-shadow: 0 2px 24px rgba(24,22,23,0.10);
  transition: background-color var(--motion-duration) var(--motion-easing),
              border-color var(--motion-duration) var(--motion-easing),
              box-shadow var(--motion-duration) var(--motion-easing),
              margin-bottom var(--motion-duration) var(--motion-easing);
}
/* Only engaged once JS confirms it can drive the scroll-based color swap below —
   without it the header stays in normal, fully-opaque flow (safe default). */
.js-hero-overlap .site-header {
  margin-bottom: calc(-1 * var(--header-height, 84px));
}
.site-header.is-top {
  background: transparent;
  border-bottom-color: transparent;
  box-shadow: none;
}
.site-header .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
  transition: padding var(--motion-duration) var(--motion-easing);
}
/* Airier and slightly taller while floating transparently over the hero —
   condenses down once solid, echoing the shrink-on-scroll motion of the
   J. Great Roofing reference site's nav. */
.site-header.is-top .container {
  padding-top: var(--space-4);
  padding-bottom: var(--space-4);
}
.site-logo { flex-shrink: 0; display: inline-flex; align-items: center; }
.site-logo img { height: 38px; width: auto; max-width: none; transition: height var(--motion-duration) var(--motion-easing); }
.site-header.is-top .site-logo img { height: 44px; }
.site-logo .logo-negative { display: none; }
.site-header.is-top .site-logo .logo-color { display: none; }
.site-header.is-top .site-logo .logo-negative { display: inline-block; }
.site-header.is-top .main-nav > ul > li > a { color: #fff; }
.site-header.is-top .header-phone { color: #fff; }
.site-header.is-top .nav-toggle span,
.site-header.is-top .nav-toggle span::before,
.site-header.is-top .nav-toggle span::after { background: #fff; }
/* The mobile menu panel is always its own opaque white surface, regardless of
   scroll position — its links must stay dark even while the header is transparent. */
.site-header.is-top .main-nav.is-open > ul > li > a { color: var(--color-text); }
.main-nav { display: flex; align-items: center; gap: var(--space-6); }
.main-nav > ul { display: flex; align-items: center; gap: var(--space-6); }
.main-nav > ul > li { position: relative; }
.main-nav > ul > li > a {
  font-weight: 600;
  font-size: 15px;
  padding: var(--space-2) 0;
}
.main-nav > ul > li > a[aria-current="page"] { color: var(--color-primary); }
.has-dropdown > a::after { content: " \25BE"; font-size: var(--text-label-size); }
.dropdown {
  position: absolute;
  top: 100%;
  left: 0;
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  min-width: 260px;
  padding: var(--space-3);
  display: none;
}
.has-dropdown:hover .dropdown,
.has-dropdown:focus-within .dropdown { display: block; }
.dropdown ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.dropdown a {
  display: block;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  font-size: 14px;
  font-weight: 500;
}
.dropdown a:hover { background: var(--color-surface); color: var(--color-primary); }
/* Services dropdown: many items, laid out in two columns. */

.header-actions { display: flex; align-items: center; gap: var(--space-5); }
.header-phone { display: flex; align-items: center; gap: 8px; font-weight: 700; white-space: nowrap; }
.nav-toggle {
  display: none;
  background: none;
  border: none;
  padding: 8px;
}
.nav-toggle span, .nav-toggle span::before, .nav-toggle span::after {
  display: block;
  width: 24px;
  height: 2px;
  background: var(--color-text);
  position: relative;
  transition: transform var(--motion-duration) var(--motion-easing);
}
.nav-toggle span::before, .nav-toggle span::after { content: ""; position: absolute; }
.nav-toggle span::before { top: -8px; }
.nav-toggle span::after { top: 8px; }

/* ---- Trust marquee (hero-badges variant: static, wrapped) ---- */

/* ---- Trust marquee band (standalone section: single-line, scrolling) ---- */

@keyframes trust-marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* ---- Services marquee: one auto-scrolling row of image-backed cards (right→left) ---- */
.services-marquee {
  overflow: hidden;
  /* 72px of internal room top/bottom so a hover-scaled slide isn't clipped;
     negative margins keep the section's visual spacing unchanged. */
  margin-top: calc(var(--space-7) - 72px);
  margin-bottom: -72px;
  padding: 72px 0;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 5%, #000 95%, transparent);
}
.services-marquee-track {
  display: flex;
  align-items: center;      /* scale grows evenly from the middle */
  width: max-content;
  animation: services-marquee-scroll 55s linear infinite;
}
.services-marquee:hover .services-marquee-track { animation-play-state: paused; }
@keyframes services-marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}
.service-slide {
  position: relative;
  flex: 0 0 300px;
  height: 400px;
  margin-right: var(--space-5);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-size: cover;
  background-position: center;
  display: flex;
  align-items: flex-end;
  color: #fff;
  box-shadow: var(--shadow-md);
  transition: transform 0.3s var(--motion-easing), box-shadow 0.3s var(--motion-easing);
}
.service-slide:hover, .service-slide:focus-visible {
  transform: scale(1.35);
  box-shadow: 0 30px 60px rgba(24,22,23,0.35);
  z-index: 3;
}
.service-slide-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(20,18,19,0.92) 0%,
    rgba(20,18,19,0.45) 55%,
    rgba(228,37,40,0.22) 100%);
}
.service-slide-content {
  position: relative;
  z-index: 1;
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.service-slide-title { font-size: 1.3rem; font-weight: 700; line-height: 1.2; color: #fff; }
.service-slide-desc { color: rgba(255,255,255,0.82); font-size: 0.95rem; line-height: 1.45; }
.service-slide .card-link { color: #fff; font-weight: 700; margin-top: var(--space-1); }

/* ---- Hero / page banner (layered background, never flat) ---- */
.hero, .page-banner {
  position: relative;
  overflow: hidden;
  color: #fff;
}
.hero-bg, .page-banner-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}
.hero-overlay, .page-banner-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(135deg, rgba(228,37,40,0.92) 0%, rgba(34,31,32,0.92) 100%);
}
/* Lighter tint when a real photo sits underneath — enough contrast for white
   text, but the photo itself should still read clearly instead of being
   buried under an opaque color. */

/* Hero only: lighter tint so the homepage background video reads through. */

.hero-content, .page-banner-content {
  position: relative;
  z-index: 2;
}
.hero {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: clamp(4rem, 10vw, 7.5rem) 0 clamp(3rem, 7vw, 5rem);
}
/* Homepage hero only: content sits at the BOTTOM-left over the footage
   (matching the reference the client approved) instead of vertically
   centered like the inner-page banners. */
.hero[data-hero-slider] {
  align-items: flex-end;
  padding-bottom: clamp(3.5rem, 7vw, 6rem);
}
/* Inner-page hero (service pages): shorter, so the content below is visible
   without scrolling. Only the homepage hero fills the viewport. */
.hero.hero-compact {
  min-height: 0;
  padding: clamp(2.75rem, 6vw, 4.5rem) 0 clamp(2.25rem, 5vw, 3.5rem);
}
.hero-content { max-width: 760px; width: 100%; }
.hero-content .label { color: #fff; opacity: 0.9; }
.hero-content h1 { margin: var(--space-3) 0 var(--space-4); }
.hero-content .lede { font-size: 19px; line-height: 30px; opacity: 0.92; max-width: 640px; }
.hero-content .cta-row { margin-top: var(--space-6); justify-content: center; }

/* Homepage hero content: bottom-left, left-aligned, with a brand accent rule
   down the side. It intentionally breaks out of the centered .container
   (max-width + auto margins) that every other section uses — on wide
   viewports that centering would leave several hundred px of empty gutter
   before the text ever started, unlike the ~50px edge margin the reference
   layout uses. This is the one hero where the text hugs the real edge. */
.hero[data-hero-slider] .hero-content {
  max-width: 760px;
  width: auto;
  /* margin-left is the gap between the viewport edge and the accent rule;
     padding-left is the gap between the rule and the text — kept as two
     separate values so the rule never sits flush against the edge. */
  margin: 0 0 0 40px;
  padding: 0 var(--space-5) 0 60px;
  border-left: 3px solid var(--color-primary);
}
/* No CTA row anymore in this hero — "Watch with sound" sits alone under the
   lede, and the primary button is a separate floating element (see
   .hero-cta-float) pinned to the opposite corner. */
.hero[data-hero-slider] .hero-content .hero-play { margin-top: var(--space-6); }
/* Hero headline runs tighter and larger than the shared h1 token — at this
   size the token's 1.15 leading opens visible gaps between the lines. */
.hero[data-hero-slider] .hero-content h1 {
  /* No max-width of its own — it fills the same content-box width as the
     lede below it (the parent's width, minus padding), so both wrap at the
     same right edge instead of the headline stopping short. */
  font-size: clamp(1.55rem, 2.9vw, 2.4rem);
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin: var(--space-3) 0 var(--space-4);
}
.hero[data-hero-slider] .hero-content .lede { max-width: none; }

/* Primary CTA: pinned bottom-right, independent of the text block — mirrors
   the reference layout's opposite-corner button instead of stacking it
   inline with the copy. Mobile drops this (see breakpoint below) since the
   sitewide floating call button already owns that corner there. */

/* ---- Home hero: 3-video slider, no full-bleed color wash ----
   The reference hero (ghtgroup.com) buries its footage under a flat grey
   overlay. Here the video stays the focus: no tint over the frame, just a
   bottom-anchored scrim sized to the text block. */
.hero-slides { position: absolute; inset: 0; }
.hero-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1000ms var(--motion-easing);
}
.hero-slide.is-active { opacity: 1; }
.hero-slide-bg, .hero-slide-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background-size: cover;
  background-position: center;
  /* The 1.06 scale gives the parallax translate room to move without
     exposing an edge; --hero-parallax-y is set by script.js on scroll
     (skipped entirely under prefers-reduced-motion, where it stays 0). */
  transform: scale(1.06) translateY(var(--hero-parallax-y, 0px));
  will-change: transform;
}
/* Videos are gated by script.js to tablet+/no-data-saver/no-reduced-motion.
   They start hidden and fade in once playable, so mobile (which never gets a
   <source>) shows the static poster underneath, and desktop never flashes an
   empty black box while buffering. */
.hero-slide-video { opacity: 0; transition: opacity 700ms var(--motion-easing); }
.hero-slide-video.is-playing { opacity: 1; }

/* Bottom scrim only — the top 45% of the footage is left completely clean.
   Depth is stronger at the very bottom where the lede and CTAs sit. */

/* Dots run VERTICALLY down the right edge, vertically centered. The bottom-
   right corner is already taken by the floating call button, and the bottom-
   left by the hero text — this is the one edge left free. */
.hero-dots {
  position: absolute;
  right: var(--space-4);
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: 0;
  align-items: center;
}
.hero-dot {
  /* 12px visual dot inside a 44px tap target (padding, not size) so the
     control meets the touch-target floor without a chunky-looking dot. */
  width: 44px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  display: grid;
  place-items: center;
}
.hero-dot::before {
  content: '';
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  background: rgba(255,255,255,0.45);
  transition: background var(--motion-duration) var(--motion-easing),
              transform var(--motion-duration) var(--motion-easing);
}
.hero-dot:hover::before { background: rgba(255,255,255,0.8); }
.hero-dot.is-active::before { background: var(--color-primary); transform: scale(1.35); }

/* ---- "Watch with sound" trigger ---- */
.hero-play {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  min-height: 44px;
  padding: 0 var(--space-5) 0 var(--space-2);
  border: 1px solid rgba(255,255,255,0.45);
  border-radius: var(--radius-full);
  background: rgba(255,255,255,0.06);
  backdrop-filter: blur(6px);
  color: #fff;
  font: var(--text-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  transition: background var(--motion-duration) var(--motion-easing),
              border-color var(--motion-duration) var(--motion-easing);
}
.hero-play:hover { background: rgba(255,255,255,0.14); border-color: #fff; }
.hero-play:active { transform: scale(0.97); }
.hero-play-icon {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  position: relative;
  flex: none;
}
.hero-play-icon::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 52%;
  transform: translate(-50%, -50%);
  border-left: 9px solid #fff;
  border-top: 6px solid transparent;
  border-bottom: 6px solid transparent;
}

/* ---- Sound-on lightbox ---- */
.hero-modal {
  position: fixed;
  inset: 0;
  z-index: 1000;
  background: rgba(12,11,11,0.92);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
  opacity: 0;
  transition: opacity var(--motion-duration) var(--motion-easing);
}
.hero-modal[hidden] { display: none; }
.hero-modal.is-open { opacity: 1; }
.hero-modal-frame { width: min(1100px, 100%); aspect-ratio: 16/9; }
.hero-modal-video { width: 100%; height: 100%; display: block; background: #000; }
.hero-modal-close {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
  width: 48px;
  height: 48px;
  border: 0;
  border-radius: var(--radius-full);
  background: rgba(255,255,255,0.12);
  color: #fff;
  font-size: 28px;
  line-height: 1;
}
.hero-modal-close:hover { background: rgba(255,255,255,0.24); }

/* Staggered entrance for the hero's own text — reuses the site's .reveal
   mechanism (already visible-by-default/fail-safe), just adding a per-child
   delay so the eyebrow/headline/lede/CTA cascade in instead of arriving as
   one flat block. */
.hero-content > .label { --reveal-order: 0; }
.hero-content > h1 { --reveal-order: 1; }
.hero-content > .lede { --reveal-order: 2; }
.hero-content > .cta-row { --reveal-order: 3; }
.hero-content > .reveal.reveal-pending { transition-delay: calc(var(--reveal-order, 0) * 100ms); }
@keyframes heroScrollCue {
  0% { opacity: 0; transform: translateY(0); }
  30% { opacity: 1; }
  80% { opacity: 0; transform: translateY(14px); }
  100% { opacity: 0; transform: translateY(14px); }
}

.page-banner { padding: clamp(3rem, 6vw, 4.5rem) 0; }
.page-banner-content .breadcrumbs { margin-bottom: var(--space-3); }
.page-banner-content h1 { margin-top: var(--space-2); }
.page-banner-content p {
  opacity: 0.9;
  max-width: 640px;
  margin-top: var(--space-3);
  font-size: 15px;
  line-height: 25px;
}

.breadcrumbs { font-size: 13px; opacity: 0.85; }
.breadcrumbs a { text-decoration: underline; text-underline-offset: 3px; }
.breadcrumbs span[aria-hidden] { margin: 0 6px; }

/* ---- Quick estimate / lead form card (hero) ---- */

/* ---- Generic cards ---- */
.card {
  background: #fff;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  transition: transform var(--motion-duration) var(--motion-easing),
              box-shadow var(--motion-duration) var(--motion-easing);
}
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); }
.bg-surface-dark .card, .bg-primary .card {
  background: rgba(255,255,255,0.06);
  border-color: rgba(255,255,255,0.15);
  color: #fff;
}
.card h3 { margin-bottom: var(--space-3); }
.card p { color: var(--color-muted); }
.bg-surface-dark .card p, .bg-primary .card p { color: rgba(255,255,255,0.75); }
.card .card-link { display: inline-block; margin-top: var(--space-4); font-weight: 700; color: var(--color-primary); }
.bg-surface-dark .card .card-link, .bg-primary .card .card-link { color: #fff; text-decoration: underline; }

/* ---- Team cards (About Us) ---- */
.team-card { padding: 0; overflow: hidden; }
/* Placeholder portrait — no photo asset yet, so this is a generic silhouette
   on a surface tint, not a stock photo of a real person. Swap for a real
   headshot by replacing this whole .team-portrait div with an <img>. */
.team-portrait {
  aspect-ratio: 4 / 5;
  background: var(--color-surface);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-muted);
}
.team-portrait svg { width: 96px; height: 96px; opacity: 0.35; }
.team-card-body { padding: var(--space-5) var(--space-6) var(--space-6); }
.team-name { margin: 0 0 var(--space-1); }
.team-role {
  display: block;
  margin-bottom: var(--space-3);
  font: var(--text-label);
  letter-spacing: 0.08em;
  color: var(--color-primary);
}
.team-card p:last-child { margin: 0; }

/* ---- Grids (fixed columns, no auto-fit — see build notes on the 13-item prime count) ---- */
.grid { display: grid; gap: var(--space-6); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-6 { grid-template-columns: repeat(3, 1fr); }

/* Services grid: 13 real services, a prime count that can't divide evenly.
   Fixed 4-col desktop / 2-col tablet grid; the 13th (last) card spans the
   full row instead of leaving two orphaned empty cells, styled as a
   featured closing card (Racing Simulators — the differentiator service). */

/* ---- Process stepper (click a number, its description expands and the
   visual panel swaps — modeled on fiscal.ai's "Modern Data Pipeline" stepper) ---- */
.process-stepper {
  display: grid;
  grid-template-columns: 1.1fr 1fr;
  gap: var(--space-8);
  align-items: center;
  margin: var(--space-7) 0;
}
.process-stepper-list { position: relative; display: flex; flex-direction: column; padding-left: 2.9rem; }
/* Vertical rail: a grey track behind the numbers, with a brand-red fill that
   grows from the top down to the active step's dot (positions set in JS so it
   stays exact regardless of the variable step heights). */
.process-rail-track, .process-rail-fill {
  position: absolute;
  width: 2px;
  border-radius: 999px;
  left: 0; top: 0; height: 0;
  pointer-events: none;
}
.process-rail-track { background: var(--color-border); z-index: 0; }
.process-rail-fill { background: var(--color-primary); z-index: 1; transition: height 0.45s var(--motion-easing), top 0.45s var(--motion-easing); }
.process-step {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-4) 0;
  background: none;
  border: none;
  border-bottom: 1px solid var(--color-border);
  text-align: left;
  cursor: pointer;
  font-family: inherit;
}
.process-step:last-child { border-bottom: none; }
.process-step-num {
  position: relative;
  flex-shrink: 0;
  width: 2.2ch;
  font: var(--text-display);
  font-size: 1.9rem;
  line-height: 1;
  color: var(--color-muted);
  opacity: 0.55;
  font-variant-numeric: tabular-nums;
  transition: font-size var(--motion-duration) var(--motion-easing),
              color var(--motion-duration) var(--motion-easing),
              opacity var(--motion-duration) var(--motion-easing);
}
.process-step-digit { display: inline-block; }
.process-step:hover .process-step-num { opacity: 0.9; }
.process-step.is-active .process-step-num { font-size: 3rem; color: var(--color-primary); opacity: 1; }
.process-step.is-active .process-step-digit { animation: process-num-pop 0.42s var(--motion-easing); }
@keyframes process-num-pop {
  0%   { transform: scale(0.72); }
  55%  { transform: scale(1.08); }
  100% { transform: scale(1); }
}
/* The dot sits on the rail, vertically centered on its number so it tracks the
   number as it grows/shrinks. */
.process-step-dot {
  position: absolute;
  top: 50%;
  left: -1.95rem;
  transform: translateY(-50%);
  width: 13px; height: 13px;
  border-radius: 50%;
  background: var(--color-surface);
  border: 2px solid var(--color-border);
  transition: background 0.3s ease, border-color 0.3s ease,
              transform 0.3s ease, box-shadow 0.3s ease;
  z-index: 2;
}
.process-step.is-filled .process-step-dot { background: var(--color-primary); border-color: var(--color-primary); }
.process-step.is-active .process-step-dot {
  transform: translateY(-50%) scale(1.2);
  box-shadow: 0 0 0 5px rgba(228,37,40,0.15);
}
/* Step text: ~25% larger at rest, ~75% larger (with a grow-and-pop) when active. */
.process-step-title {
  display: block;
  font-weight: 700;
  padding-top: var(--space-2);
  font-size: 1.05rem;
  transform-origin: left center;
  transition: font-size var(--motion-duration) var(--motion-easing);
}
.process-step.is-active .process-step-title {
  font-size: 1.45rem;
  animation: process-text-pop 0.42s var(--motion-easing);
}
@keyframes process-text-pop {
  0%   { transform: scale(0.88); }
  60%  { transform: scale(1.04); }
  100% { transform: scale(1); }
}
/* Always visible — most visitors never click through all 7 steps, so hiding
   the description behind a click meant most people only ever saw the title.
   The .is-active state below still grows it slightly for emphasis, but
   every step's copy is readable at rest. */
.process-step-desc {
  display: block;
  max-height: 220px;
  opacity: 1;
  overflow: hidden;
  color: var(--color-text);
  margin-top: var(--space-2);
  font-size: 1rem;
  line-height: 1.5;
  transition: font-size var(--motion-duration) var(--motion-easing);
}
.process-step.is-active .process-step-desc { font-size: 1.05rem; }
.process-stepper-visual {
  position: relative;
  align-self: stretch;
  min-height: 600px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--color-surface-dark);
}
.process-visual-panel {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-end;
  gap: var(--space-4);
  color: #fff;
  text-align: left;
  padding: var(--space-6);
  background-size: cover;
  background-position: center;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity 0.5s var(--motion-easing), transform 0.6s var(--motion-easing);
  pointer-events: none;
}
/* B&W photo underneath, tinted with a brand-red → charcoal scrim (heavier at the
   bottom) so it reads as on-brand and keeps the white number/title legible. */
.process-visual-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(20,18,19,0.90) 0%,
    rgba(20,18,19,0.45) 42%,
    rgba(228,37,40,0.30) 100%);
}
.process-visual-panel > * { position: relative; z-index: 1; }
.process-visual-panel.is-active { opacity: 1; transform: scale(1); pointer-events: auto; }
.process-visual-icon { width: 46px; height: 46px; opacity: 0.9; margin-bottom: auto; }
.process-visual-icon svg { width: 100%; height: 100%; }
.process-visual-copy { display: flex; flex-direction: column; gap: var(--space-1); }
.process-visual-number { font: var(--text-display); font-size: 4rem; opacity: 0.85; line-height: 1; }
.process-visual-title { font-size: 22px; font-weight: 700; max-width: 320px; }

.why-grid { grid-template-columns: repeat(3, 1fr); }
/* ---- "Why Precision" cards: ghosted number + icon, pop up on hover ---- */
.why-card {
  position: relative;
  overflow: hidden;
  padding: var(--space-6);
  background: var(--color-background);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition: transform 0.35s var(--motion-easing),
              box-shadow 0.35s var(--motion-easing),
              border-color 0.35s var(--motion-easing);
}
/* Decorative brand glow that fades in on hover (echoes the reference's art). */
.why-card::before {
  content: "";
  position: absolute;
  top: -40%;
  right: -30%;
  width: 70%;
  height: 80%;
  background: radial-gradient(circle, rgba(228,37,40,0.16), transparent 70%);
  opacity: 0;
  transition: opacity 0.4s var(--motion-easing);
  pointer-events: none;
}
.why-card-num {
  position: absolute;
  top: var(--space-3);
  right: var(--space-5);
  font: var(--text-display);
  font-size: 3.4rem;
  line-height: 1;
  color: var(--color-border);
  opacity: 0.6;
  pointer-events: none;
  transition: color 0.35s var(--motion-easing), opacity 0.35s var(--motion-easing);
}
.why-card-icon {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: var(--radius-md);
  background: rgba(228,37,40,0.09);
  color: var(--color-primary);
  margin: 0 auto var(--space-4);   /* centered in the card */
  transition: background 0.35s var(--motion-easing), color 0.35s var(--motion-easing);
}
.why-card-icon svg { width: 24px; height: 24px; }
.why-card h3 { position: relative; margin-bottom: var(--space-2); }
.why-card p { position: relative; color: var(--color-muted); }
.why-card:hover {
  transform: scale(1.35);
  box-shadow: 0 30px 60px rgba(24,22,23,0.22);
  border-color: var(--color-primary);
  z-index: 3;
}
.why-card:hover::before { opacity: 1; }
.why-card:hover .why-card-num { color: var(--color-primary); opacity: 0.9; }
.why-card:hover .why-card-icon { background: var(--color-primary); color: #fff; }

.stats-grid { grid-template-columns: repeat(4, 1fr); }
.gallery-grid { grid-template-columns: repeat(4, 1fr); }

/* ---- Services hub overview ---- */
.services-overview-grid {
  display: grid; grid-template-columns: 1.15fr 1fr;
  gap: var(--space-8); align-items: center;
}
.services-overview-grid .checklist li { font-weight: 600; }
/* CTA spans both columns and sits centered under the section. */
.services-overview-cta {
  grid-column: 1 / -1;
  text-align: center;
  margin-top: var(--space-6);
}

/* ---- Two-column split (replaces inline grid styles so it can collapse) ---- */
.split-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-8);
  align-items: start;
}
.split-grid.is-centered { align-items: center; }
.split-grid.is-wide-right { grid-template-columns: 1fr 1.2fr; }

/* ---- About page "Who We Are" (copy left, photo right) ---- */
.who-grid {
  display: grid;
  grid-template-columns: 1.05fr 1fr;
  gap: var(--space-8);
  align-items: center;
}
.who-grid .section-head { margin-bottom: var(--space-5); }
.who-grid p + p { margin-top: var(--space-4); }
.who-photo img {
  width: 100%;
  /* Sin `height: auto` los atributos width/height del HTML fijan el alto real
     y la foto se estira. Hace falta en cuanto la imagen declara dimensiones. */
  height: auto;
  display: block;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

/* ---- Photo-backed service cards (About page "What We Do") ---- */
.service-photo-card {
  position: relative;
  display: flex;
  align-items: flex-end;
  min-height: 290px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background-size: cover;
  background-position: center;
  color: #fff;
  box-shadow: var(--shadow-md);
  transition: transform var(--motion-duration) var(--motion-easing),
              box-shadow var(--motion-duration) var(--motion-easing);
}
.service-photo-card:hover, .service-photo-card:focus-visible {
  transform: translateY(-6px);
  box-shadow: 0 24px 48px rgba(24,22,23,0.28);
}
.service-photo-overlay {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top,
    rgba(20,18,19,0.92) 0%,
    rgba(20,18,19,0.45) 58%,
    rgba(228,37,40,0.22) 100%);
}
.service-photo-content {
  position: relative;
  z-index: 1;
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.service-photo-title { font-size: 1.25rem; font-weight: 700; line-height: 1.2; color: #fff; }
.service-photo-desc { font-size: 0.95rem; line-height: 1.45; color: rgba(255,255,255,0.85); }
.service-photo-card .card-link { color: #fff; font-weight: 700; margin-top: var(--space-1); }

/* ---- Equipment / Warranty band (brand-red, icon cards) ---- */
.equip-band { position: relative; overflow: hidden; }
/* Soft depth so the flat red reads less like a plain block. */
.equip-band::before {
  content: "";
  position: absolute;
  top: -35%; right: -10%;
  width: 55%; height: 170%;
  background: radial-gradient(circle, rgba(255,255,255,0.13), transparent 68%);
  pointer-events: none;
}
.equip-grid {
  position: relative;
  display: grid; grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
}
.equip-card {
  background: rgba(255,255,255,0.10);
  border: 1px solid rgba(255,255,255,0.24);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  transition: background var(--motion-duration) var(--motion-easing),
              transform var(--motion-duration) var(--motion-easing);
}
.equip-card:hover { background: rgba(255,255,255,0.16); transform: translateY(-4px); }
.equip-icon {
  display: flex; align-items: center; justify-content: center;
  width: 54px; height: 54px;
  border-radius: var(--radius-md);
  background: #fff;
  color: var(--color-primary);
  margin: 0 auto var(--space-4);   /* centered in the card */
}
.equip-card h3 { color: #fff; margin-bottom: var(--space-3); }
.equip-card p { color: rgba(255,255,255,0.9); }

/* Recent-projects preview (5 photos) on service pages. Flex rather than grid so
   the trailing photo centers instead of stranding at the left edge. 4 per row
   renders each photo ~35% larger than the previous 5-across strip. */
.grid.recent-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-5);
}
.grid.recent-grid > .gallery-item {
  flex: 0 1 calc((100% - 3 * var(--space-5)) / 4);
}

/* ---- Gallery lightbox ---- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  padding: var(--space-5);
  background: rgba(16,14,15,0.92);
}
.lightbox.is-open { display: flex; }
.lightbox-figure {
  margin: 0;
  max-width: min(1100px, 92vw);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.lightbox-img {
  max-width: 100%;
  max-height: 78vh;
  width: auto;
  border-radius: var(--radius-md);
  box-shadow: 0 30px 70px rgba(0,0,0,0.5);
}
.lightbox-caption {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  color: #fff;
}
.lightbox-title { font-weight: 700; font-size: 17px; }
.lightbox-service { color: #fff; font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.lightbox-service:hover { color: var(--color-primary); }
.lightbox-close {
  position: absolute;
  top: var(--space-4); right: var(--space-5);
  width: 44px; height: 44px;
  border: 0; border-radius: 50%;
  background: rgba(255,255,255,0.12);
  color: #fff; font-size: 28px; line-height: 1;
}
.lightbox-nav {
  flex: none;
  width: 48px; height: 48px;
  border: 0; border-radius: 50%;
  background: rgba(255,255,255,0.12);
  color: #fff; font-size: 30px; line-height: 1;
}
.lightbox-close:hover, .lightbox-nav:hover { background: var(--color-primary); }

/* ---- Gallery photo tiles ---- */
.gallery-item {
  position: relative; display: block; overflow: hidden;
  border-radius: var(--radius-lg); box-shadow: var(--shadow-sm);
  background: var(--color-surface-dark);
}
.gallery-item img {
  aspect-ratio: 4/3; width: 100%; height: auto; object-fit: cover; display: block;
  transition: transform 420ms var(--motion-easing);
}
.gallery-item:hover img, .gallery-item:focus-visible img { transform: scale(1.06); }
.gallery-caption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 30px 16px 12px;
  background: linear-gradient(to top, rgba(24,22,23,0.88), rgba(24,22,23,0));
  color: #fff; font-weight: 700; font-size: 14px;
}

.area-list-grid a { display: block; padding: 10px 0; border-bottom: 1px dashed var(--color-border); font-weight: 600; }
.bg-surface-dark .area-list-grid a, .bg-primary .area-list-grid a { border-color: rgba(255,255,255,0.2); }

/* ---- Stat band ---- */
/* ~15% shorter than a standard section (trimmed vertical padding). */
.stats-band { padding-top: clamp(2.25rem, 4.6vw, 3.6rem); padding-bottom: clamp(2.25rem, 4.6vw, 3.6rem); }
.stat { text-align: center; }
.stat .stat-number { font: var(--text-display); font-size: 44px; color: var(--color-primary); display: block; }
.bg-primary .stat .stat-number { color: #fff; }
.stat .stat-label { margin-top: var(--space-2); font-size: 14px; opacity: 0.85; }

/* ---- Checklist (unordered — never numbered, per ui-copy-conventions) ---- */
.checklist li {
  position: relative;
  padding-left: 30px;
  margin-bottom: var(--space-3);
}
.checklist li::before {
  content: "\2713";
  position: absolute;
  left: 0; top: 1px;
  width: 20px; height: 20px;
  border-radius: 50%;
  background: var(--color-primary);
  color: #fff;
  font-size: 12px;
  display: flex; align-items: center; justify-content: center;
}

/* ---- Bulleted feature list (service highlights) ---- */

/* ---- Process steps (the ONE section allowed numbered markers) ---- */

.process-step {
  display: grid;
  grid-template-columns: 72px 1fr;
  gap: var(--space-5);
  padding: var(--space-6) 0;
  border-bottom: 1px solid var(--color-border);
}
.process-step:last-child { border-bottom: none; }
.process-step h3 { margin-bottom: var(--space-2); }
.process-step p { color: var(--color-muted); }

/* ---- Testimonials ---- */
.testimonial-card { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-4); }
.testimonial-card .quote-mark { font: var(--text-display); font-size: 28px; color: var(--color-primary); opacity: 0.3; line-height: 1; }
.testimonial-card p.quote { font-size: 14px; line-height: 1.5; }
.testimonial-card .attribution { font-weight: 700; font-size: 13px; color: var(--color-muted); }
.testimonial-card.placeholder { border-style: dashed; }
.testimonial-card.placeholder .attribution { font-style: italic; color: var(--color-muted); font-weight: 500; }
/* Push attribution to the bottom so uneven quote lengths still line up. */
.testimonial-card p.quote { flex: 1 0 auto; }

/* ---- Testimonial carousel (arrow-scrolled) ---- */
.testimonial-carousel {
  display: flex; align-items: center; gap: var(--space-4);
  margin-top: var(--space-6);
}
.testimonial-track {
  flex: 1; min-width: 0;
  display: flex; gap: var(--space-5);
  overflow-x: auto; scroll-snap-type: x mandatory; scroll-behavior: smooth;
  padding: 6px 2px var(--space-3);          /* room for card shadow + focus ring */
  scrollbar-width: none; -ms-overflow-style: none;
}
.testimonial-track::-webkit-scrollbar { display: none; }
.testimonial-track > .testimonial-card {
  flex: 0 0 calc((100% - 2 * var(--space-5)) / 3);   /* 3 per view */
  scroll-snap-align: start;
  min-height: 232px;                        /* equal visual height across quotes */
}
.tcar-arrow {
  flex: none; width: 46px; height: 46px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: #fff; border: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm); color: var(--color-text);
  transition: background var(--motion-duration) var(--motion-easing),
              color var(--motion-duration) var(--motion-easing),
              border-color var(--motion-duration) var(--motion-easing),
              opacity var(--motion-duration) var(--motion-easing);
}
.tcar-arrow:hover { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.tcar-arrow[disabled] { opacity: 0.3; cursor: default; }
.tcar-arrow[disabled]:hover { background: #fff; color: var(--color-text); border-color: var(--color-border); }

/* ---- FAQ accordion ---- */
.faq-group { margin-bottom: var(--space-7); }
.faq-group h2 { margin-bottom: var(--space-5); }
.faq-item {
  border-bottom: 1px solid var(--color-border);
}
.faq-item summary {
  cursor: pointer;
  padding: var(--space-5) 0;
  font-weight: 700;
  font-size: 17px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--space-4);
  list-style: none;
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary::after {
  content: "+";
  font-size: 22px;
  font-weight: 400;
  color: var(--color-primary);
  flex-shrink: 0;
  transition: transform var(--motion-duration) var(--motion-easing);
}
.faq-item[open] summary::after { transform: rotate(45deg); }
.faq-item .faq-answer { padding: 0 0 var(--space-5); color: var(--color-muted); max-width: 760px; }
.bg-surface-dark .faq-item, .bg-surface-dark .faq-item summary { border-color: rgba(255,255,255,0.15); }

/* ---- Pre-footer stack ---- */
.lead-block {
  background: #fff;
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
}

/* ---- Service Areas section (charcoal, county-tabbed town grid) ---- */
.area-tabs {
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--space-3); margin-top: var(--space-6);
}
.area-tab {
  padding: 12px 26px; border-radius: var(--radius-md);
  border: 1px solid rgba(255,255,255,0.18); background: rgba(255,255,255,0.05);
  color: #fff; font-weight: 700; font-size: 15px;
  transition: background var(--motion-duration) var(--motion-easing),
              border-color var(--motion-duration) var(--motion-easing);
}
.area-tab:hover { border-color: rgba(255,255,255,0.45); }
.area-tab.is-active { background: var(--color-primary); border-color: var(--color-primary); }
.area-panels { margin-top: var(--space-7); }
.area-panel { display: none; }
.area-panel.is-active { display: block; }
.area-pin-grid { grid-template-columns: repeat(4, 1fr); gap: 0 var(--space-7); }
.area-pin-grid a {
  display: flex; align-items: center; gap: 12px;
  padding: 16px 4px; border-bottom: 1px solid rgba(255,255,255,0.12);
  font-weight: 600; color: #fff;
  transition: color var(--motion-duration) var(--motion-easing);
}
.area-pin-grid a:hover { color: var(--color-primary); }
.area-pin { color: var(--color-primary); display: inline-flex; flex: none; }
.area-cta { text-align: center; margin-top: var(--space-7); color: rgba(255,255,255,0.72); }
.area-cta a { color: #fff; font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }

/* ---- Schedule / lead-capture section ---- */
.schedule-grid {
  display: grid; grid-template-columns: 1fr 1.05fr;
  gap: var(--space-8); align-items: center;
}
.schedule-intro h2 { margin: var(--space-3) 0; }
.schedule-facts { margin-top: var(--space-6); display: flex; flex-direction: column; gap: var(--space-4); }
.schedule-facts li { display: flex; align-items: center; gap: var(--space-4); }
.schedule-facts li > span:last-child { font-weight: 700; font-size: 17px; }
.schedule-fact-icon {
  flex: none; width: 48px; height: 48px; border-radius: var(--radius-md);
  display: flex; align-items: center; justify-content: center;
  background: rgba(228,37,40,0.10); color: var(--color-primary);
}
.schedule-fact-label {
  display: block; font-size: 12px; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--color-muted); margin-bottom: 2px;
}

/* ---- Forms ---- */
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); }
.form-grid .full { grid-column: 1 / -1; }
.field { display: flex; flex-direction: column; gap: 6px; margin-bottom: var(--space-4); }
.field label { font-weight: 600; font-size: 14px; }
.field input, .field select, .field textarea {
  padding: 12px 14px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: #fff;
  color: var(--color-text);
}
.field textarea { resize: vertical; min-height: 100px; }
/* Grid/flex children default to min-width:auto, so a wide intrinsic child (a
   <select> sized by its longest option, a long email) can force a column wider
   than the viewport. Let them shrink instead. */
.field input, .field select, .field textarea { width: 100%; min-width: 0; max-width: 100%; }
.schedule-grid > *, .split-grid > *, .who-grid > *,
.services-overview-grid > *, .equip-grid > *, .form-grid > * { min-width: 0; }
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.form-status { margin-top: var(--space-4); font-weight: 600; padding: 12px 16px; border-radius: var(--radius-md); display: none; }
.form-status.success { display: block; background: #E9F7EE; color: #14532D; }
.form-status.error { display: block; background: #FDECEC; color: #7A1414; }

/* ---- Footer ---- */
.site-footer { background: var(--color-surface-dark); color: #fff; }
/* Sin `padding` aca: el atajo ponia el eje horizontal en 0 y le ganaba a
   `.container`, asi que entre 1000 y 1240px el texto del pie tocaba el borde
   de la pantalla. El vertical vive ahora en `.site-footer .footer-main`
   (padding-block) y el horizontal queda a cargo de `.container`, que ya lo
   ajusta por breakpoint. */
.footer-main {
  display: grid;
  grid-template-columns: 1fr 1.3fr 1fr;
  gap: var(--space-7);
}
.footer-main .footer-logo img { height: 40px; width: auto; margin-bottom: var(--space-4); }
.footer-main p { color: rgba(255,255,255,0.72); font-size: 15px; line-height: 1.6; }
.footer-col h4 { font: var(--text-label); color: #fff; margin-bottom: var(--space-4); opacity: 0.9; }
/* El tamano y el color viven aca y no solo en `.footer-col a` porque en la
   columna de contacto hay un `li` que no es enlace ("Myrtle Beach, SC..."):
   sin esto heredaba los 17px del body y salia mas grande y mas blanco que
   sus vecinos. Los enlaces ya traen estos mismos valores, asi que no cambian. */
.footer-col ul li {
  margin-bottom: 10px;
  font-size: 14px;
  color: rgba(255,255,255,0.78);
}
.footer-col a { color: rgba(255,255,255,0.78); font-size: 14px; }
.footer-col a:hover { color: #fff; }
.footer-services-list {
  column-count: 1;
}
.footer-services-list li { break-inside: avoid; }
.footer-services-more { font-weight: 700; color: var(--color-primary); }
.footer-services-more:hover { text-decoration: underline; text-underline-offset: 3px; }
.footer-social { display: flex; gap: var(--space-3); margin-top: var(--space-4); }
.footer-social a {
  width: 38px; height: 38px; border-radius: 50%;
  background: rgba(255,255,255,0.1);
  display: flex; align-items: center; justify-content: center;
}
.footer-social a { color: #fff; transition: background var(--motion-duration) var(--motion-easing); }
.footer-social a svg { display: block; }
.footer-social a:hover { background: var(--color-primary); }
/* Mismo caso que `.footer-main`: el vertical pasa a `.site-footer
   .footer-bottom` y el horizontal lo maneja `.container`. */
.footer-bottom {
  border-top: 1px solid rgba(255,255,255,0.15);
  display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: var(--space-3);
  font-size: 13px; color: rgba(255,255,255,0.6);
}
.footer-credit a { color: rgba(255,255,255,0.85); font-weight: 700; text-decoration: underline; }

/* ---- Floating call button (mobile) ---- */
.floating-call {
  display: none;
  position: fixed;
  bottom: 20px; right: 20px;
  z-index: 90;
  background: var(--color-primary);
  color: #fff;
  width: 56px; height: 56px;
  border-radius: 50%;
  align-items: center; justify-content: center;
  box-shadow: var(--shadow-md);
  font-size: 22px;
}

/* ==========================================================================
   LUXURY LAYER — reposicionamiento 2026-08-18
   --------------------------------------------------------------------------
   Capa de override + componentes nuevos. Va al final a proposito: pisa por
   cascada los componentes del sistema anterior ("Command Center") sin tener
   que reescribir las 1700 lineas de arriba, que siguen sirviendo a las 29
   paginas interiores mientras se migran.

   Principio rector del brief: "Technology that disappears into the
   architecture." La casa es la protagonista; el sitio nunca debe verse como
   un catalogo de electronica. En la practica: peso liviano, mucho vacio,
   hairlines en vez de sombras, esquina viva, y rojo racionado.
   ========================================================================== */

/* ---- Tipografia: tracking que el shorthand `font:` no puede llevar ---- */
.lux-hero h1 { letter-spacing: var(--tracking-display); }
h1 { letter-spacing: var(--tracking-display); }
h2 { letter-spacing: var(--tracking-h2); }
h3 { letter-spacing: -0.005em; }

/* El label es la firma tipografica del sistema nuevo: 11px, muy abierto, y
   en tinta —no en rojo— salvo que marque algo accionable. Esto ademas
   resuelve el fallo de contraste AA que arrastraba el eyebrow rojo. */
.label {
  font: var(--text-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-meta);
}
.bg-surface-dark .label, .bg-ink .label { color: rgba(255,255,255,0.55); }

/* Medida de lectura. Sin esto, el peso 300 en parrafos largos a ancho
   completo se vuelve ilegible. */
.section-head p, .lux-measure { max-width: var(--measure); }
.section-head.center p { margin-left: auto; margin-right: auto; }
.lede { font: var(--text-lede); max-width: var(--measure); }

/* ---- Ritmo ---- */
.section { padding: var(--section-rhythm) 0; }
.section-tight { padding: var(--section-rhythm-tight) 0; }
.section-head { max-width: 780px; margin-bottom: clamp(2.5rem, 5vw, 4.5rem); }
.section-head .label { display: block; margin-bottom: var(--space-4); }
.section-head h2 { margin-bottom: var(--space-5); }

/* ---- Botones: pill -> rectangulo arquitectonico ----
   El primario deja de ser rojo y pasa a tinta. El rojo aparece en hover
   (sigue significando "act now") y en .btn-accent, reservado a UN CTA de
   maxima intencion por pagina. */
.btn {
  padding: 19px 38px;
  border-radius: 0;
  font-weight: 500;
  font-size: var(--text-label-size);
  letter-spacing: 0.2em;
  text-transform: uppercase;
  border: 1px solid transparent;
  transition: background var(--motion-duration) var(--motion-easing),
              color var(--motion-duration) var(--motion-easing),
              border-color var(--motion-duration) var(--motion-easing);
}
.btn:hover { transform: none; box-shadow: none; }
.btn-primary { background: var(--color-ink); color: #fff; border-color: var(--color-ink); }
.btn-primary:hover { background: var(--color-primary); border-color: var(--color-primary); }
.btn-accent { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.btn-accent:hover { background: var(--color-ink); border-color: var(--color-ink); }
.btn-outline { background: transparent; border-color: currentColor; color: inherit; }
.btn-white { background: #fff; color: var(--color-ink); border-color: #fff; }
.btn-white:hover { background: transparent; color: #fff; }
.cta-row { gap: var(--space-4) var(--space-5); }

/* Enlace-regla: el CTA terciario del sistema. Subraya con hairline, y el
   subrayado se pinta de rojo al hover. */
.link-rule {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-label-size);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-hairline);
  transition: border-color var(--motion-duration) var(--motion-easing),
              color var(--motion-duration) var(--motion-easing);
}
.link-rule::after { content: "\2192"; transition: transform var(--motion-duration) var(--motion-easing); }
.link-rule:hover { border-bottom-color: var(--color-primary); color: var(--color-primary); }
.link-rule:hover::after { transform: translateX(5px); }
.bg-surface-dark .link-rule, .bg-ink .link-rule { border-bottom-color: rgba(255,255,255,0.25); }
.bg-surface-dark .link-rule:hover, .bg-ink .link-rule:hover { color: #fff; border-bottom-color: #fff; }

/* ---- Superficies ---- */
.bg-ink { background: var(--color-ink); color: #fff; }
.bg-ink .section-head p, .bg-ink .muted { color: rgba(255,255,255,0.72); }
.bg-ink h2, .bg-ink h3 { color: #fff; }

/* ---- Cards: de caja con sombra a hairline plano ---- */
.card {
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  box-shadow: none;
  background: var(--color-background);
}
.card:hover { transform: none; box-shadow: none; border-color: var(--color-ink); }

/* ==========================================================================
   COMPONENTES NUEVOS
   ========================================================================== */

/* ---- Hero luxury ----
   Foto full-bleed de residencia terminada. Sin scrim de color completo: solo
   un degrade anclado abajo, dimensionado al bloque de texto, para que la
   arquitectura siga siendo lo que se ve. */
.lux-hero {
  position: relative;
  min-height: clamp(34rem, 88vh, 54rem);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--color-ink);
}
.lux-hero-media { position: absolute; inset: 0; }
.lux-hero-media img,
.lux-hero-media video { width: 100%; height: 100%; object-fit: cover; }
.lux-hero-scrim {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(26,24,24,0.42) 0%, rgba(26,24,24,0.10) 32%, rgba(26,24,24,0.72) 100%);
}
.lux-hero .container { position: relative; padding-bottom: clamp(3.5rem, 7vw, 6.5rem); padding-top: clamp(6rem, 12vw, 9rem); }
.lux-hero-body { max-width: 54rem; }
.lux-hero h1 {
  color: #fff;
  font: var(--text-display);
  letter-spacing: var(--tracking-display);
  margin-bottom: var(--space-6);
}
/* "Home Technology." y "Precisely." en dos lineas: la marca es el remate. */
.lux-hero h1 .hero-line { display: block; }
.lux-hero h1 .hero-line-2 { font-weight: 400; }
.lux-hero .lede { color: rgba(255,255,255,0.88); max-width: 48ch; margin-bottom: var(--space-4); }
.lux-hero .cta-row .btn-white:hover { border-color: #fff; }

/* ---- Tira de audiencia ----
   "FOR HOMEOWNERS / BUILDERS / ARCHITECTS / DESIGNERS". Va inmediatamente
   bajo el hero: declara a quien le habla el sitio antes de vender nada. */
.audience-strip {
  border-bottom: 1px solid var(--color-hairline);
  padding: var(--space-6) 0;
}
.audience-strip .container {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: var(--space-3) var(--space-6);
}
.audience-strip span {
  font-size: var(--text-label-size);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-meta);
}
.audience-strip .sep { color: var(--color-primary); }

/* ---- Pilares (4 servicios core) ----
   Foto grande arriba, tipografia chica abajo. La imagen manda; el texto
   apenas la nombra. Es el opuesto de la card de servicio anterior, que
   apilaba icono rojo + titulo 700 + parrafo. */
.pillar-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: clamp(2.5rem, 5vw, 5rem) clamp(1.5rem, 3vw, 3rem); }
.pillar { display: block; }
.pillar-media {
  position: relative;
  overflow: hidden;
  aspect-ratio: 4/3;
  background: var(--color-surface);
  margin-bottom: var(--space-5);
}
.pillar-media img {
  width: 100%; height: 100%; object-fit: cover;
  transition: transform 900ms var(--motion-easing);
}
.pillar:hover .pillar-media img { transform: scale(1.04); }
.pillar-index {
  font-size: var(--text-label-size); font-weight: 500; letter-spacing: var(--tracking-label);
  color: var(--color-meta); display: block; margin-bottom: var(--space-3);
}
.pillar h3 { margin-bottom: var(--space-3); font-weight: 400; font-size: 1.5rem; }
.pillar p { max-width: 44ch; margin-bottom: var(--space-5); }
.pillar-sub {
  font-size: 0.8125rem; color: var(--color-meta); line-height: 1.8;
  padding-top: var(--space-4); border-top: 1px solid var(--color-hairline);
}

/* ---- Split editorial (imagen + texto con aire) ---- */
.editorial-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 6vw, 6rem);
  align-items: center;
}
.editorial-split.is-reversed .editorial-media { order: 2; }
/* `height: auto` es obligatorio desde que las <img> declaran width/height en
   el HTML: sin el, el atributo fija el alto real y la foto se estira. Con el,
   manda el aspect-ratio y el atributo solo sirve para reservar el espacio
   antes de que cargue (que es justamente para lo que se agrego). */
.editorial-media img { width: 100%; height: auto; aspect-ratio: 4/5; object-fit: cover; }
.editorial-split.is-wide { grid-template-columns: 1.15fr 1fr; }
.editorial-body h2 { margin-bottom: var(--space-5); }
.editorial-body p { max-width: var(--measure); }
.editorial-body p + p { margin-top: var(--space-4); }
.editorial-body .label { display: block; margin-bottom: var(--space-4); }
.editorial-body .cta-row { margin-top: var(--space-7); }

/* ---- Lista de valor (Why Precision) ----
   Numerada, separada por hairline, sin cards. Lee como indice de proyecto
   de arquitectura, no como grilla de beneficios. */
.value-list { border-top: 1px solid var(--color-hairline); }
.value-item {
  display: grid;
  grid-template-columns: 4rem 1fr;
  gap: var(--space-5);
  padding: clamp(1.75rem, 3vw, 2.75rem) 0;
  border-bottom: 1px solid var(--color-hairline);
  align-items: start;
}
.value-num {
  font-size: var(--text-label-size); font-weight: 500; letter-spacing: var(--tracking-label);
  color: var(--color-primary); padding-top: 0.55rem;
}
.value-item h3 { margin-bottom: var(--space-3); font-weight: 400; font-size: 1.375rem; }
.value-item p { max-width: var(--measure); }

/* ---- Proyecto / case study ---- */
.project-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: clamp(1.5rem, 3vw, 2.5rem); }
.project-card { display: block; }
.project-media { position: relative; overflow: hidden; aspect-ratio: 3/4; background: var(--color-surface); margin-bottom: var(--space-5); }
.project-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 900ms var(--motion-easing); }
.project-card:hover .project-media img { transform: scale(1.04); }
.project-meta { font-size: var(--text-label-size); font-weight: 500; letter-spacing: var(--tracking-label); color: var(--color-meta); display: block; margin-bottom: var(--space-3); }
.project-card h3 { font-weight: 400; font-size: 1.375rem; margin-bottom: var(--space-3); }
.project-card p { max-width: 40ch; }

/* Ficha de case study: objetivo / desafio / solucion / resultado */
.case-facts { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-6); border-top: 1px solid var(--color-hairline); padding-top: var(--space-7); }
.case-fact dt { font-size: var(--text-label-size); font-weight: 500; letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--color-meta); margin-bottom: var(--space-3); }
.case-fact dd { margin: 0; font-size: 0.9375rem; line-height: 1.75; }

/* ---- Banda de partners (Builders & Designers) ---- */
.partner-band { position: relative; overflow: hidden; }
.partner-band-media { position: absolute; inset: 0; }
.partner-band-media img { width: 100%; height: 100%; object-fit: cover; }
.partner-band-scrim { position: absolute; inset: 0; background: linear-gradient(90deg, rgba(26,24,24,0.95) 0%, rgba(26,24,24,0.82) 45%, rgba(26,24,24,0.38) 100%); }
.partner-band .container { position: relative; }
.partner-band-body { max-width: 46rem; }
.partner-band-body h2 { color: #fff; margin-bottom: var(--space-5); }
.partner-band-body p { color: rgba(255,255,255,0.80); max-width: var(--measure); }
.partner-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-4) var(--space-6); margin: var(--space-7) 0; }
.partner-list li { display: flex; gap: var(--space-3); align-items: baseline; color: rgba(255,255,255,0.86); font-size: 0.9375rem; line-height: 1.6; }
.partner-list li::before { content: ""; flex: none; width: 6px; height: 6px; background: var(--color-primary); transform: translateY(-0.15em); }

/* ---- Marcas soportadas ---- */
.brand-strip { display: flex; flex-wrap: wrap; justify-content: center; align-items: center; gap: clamp(1.5rem, 4vw, 3.5rem); }
.brand-strip span {
  font-size: 0.9375rem; font-weight: 300; letter-spacing: 0.06em;
  color: var(--color-meta);
}

/* ---- Testimonios ---- */

/* ---- Stats refinados ---- */
/* Los numeros salian a 44px con peso 200: elegante pero ilegible de lejos, y
   son el dato duro de la home. Suben de tamano y de peso. El rojo sobre el
   fondo oscuro da 3.87:1, que a este cuerpo entra como texto grande (AA pide
   3:1); con mas peso se lee todavia mejor. */
/* La banda declaraba su padding reducido en la linea 1354, pero `.section`
   lo redefine en la 1750 con la misma especificidad: al venir despues, ganaba
   y la banda quedaba con el ritmo completo de seccion (152px arriba y abajo
   contra 131px de contenido, o sea mas aire que datos). Se repone aca, ya
   despues de `.section`, para que la intencion original valga. */
.stats-band {
  padding-top: clamp(2.25rem, 4.6vw, 3.6rem);
  padding-bottom: clamp(2.25rem, 4.6vw, 3.6rem);
}

.stats-band .stat-number {
  font-size: clamp(3.25rem, 5vw, 4.25rem);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.05;
}
.stats-band .stat-label {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  opacity: 1;
  /* "Years Serving the Grand Strand" es el unico que parte en dos lineas;
     balance reparte las palabras en vez de dejar una sola colgando. */
  text-wrap: balance;
}

/* ---- CTA de consulta ---- */
.consult-cta { text-align: center; }
.consult-cta h2 { max-width: 22ch; margin: 0 auto var(--space-5); }
.consult-cta p { max-width: var(--measure); margin: 0 auto var(--space-8); }
.consult-cta .cta-row { justify-content: center; }

/* ---- Hero luxury + slider de video ----
   El slider de 3 videos vuelve a pedido del cliente y ahora vive dentro de
   .lux-hero-media. La lógica de script.js no cambió; esto sólo resuelve el
   apilado (video < scrim < texto/dots) y adapta el botón de sonido al
   sistema nuevo. */
.lux-hero-media .hero-slides { z-index: 0; }
.lux-hero-scrim { z-index: 1; }
.lux-hero .container { z-index: 2; }
.lux-hero .hero-dots { z-index: 3; }

/* El botón "Watch with Sound" convivía con un CTA rojo pill; ahora comparte
   fila con los botones rectangulares, así que se alinea a esa altura y pasa a
   hairline en vez de sólido. */
.lux-hero .hero-play {
  background: transparent;
  border: 1px solid rgba(255,255,255,0.45);
  border-radius: 0;
  color: #fff;
  padding: 18px 28px;
  font-size: var(--text-label-size);
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  transition: background var(--motion-duration) var(--motion-easing),
              border-color var(--motion-duration) var(--motion-easing);
}
.lux-hero .hero-play:hover { background: rgba(255,255,255,0.12); border-color: #fff; }
.lux-hero .hero-play-text { font: inherit; letter-spacing: inherit; }

/* ---- Header: alineación del nav ----
   El nav pasó de 4 items a 6 (se sumaron Projects y Builders & Designers) y
   con la tipografía anterior (15px/600, gap 32px) dejó de entrar. Al no tener
   nowrap, "Builders & Designers" partía en dos líneas y arrastraba la línea
   base del resto: cada item quedaba a una altura distinta.

   Tres causas, tres arreglos:
   1. nowrap en los links, para que ningún item parta.
   2. El caret del dropdown tenía un espacio normal delante (content: " ▾"),
      que es un punto de corte válido — por eso caía solo a la línea de abajo.
      Ahora se separa con margin, no con un espacio rompible.
   3. Tamaño y gap bajan al registro del sistema nuevo (13px/500), que además
      hace que el header lea más liviano y menos "menú de retail".

   El breakpoint del hamburguesa sube de 980px a 1150px (más arriba en este
   archivo): con 6 items + teléfono + CTA, por debajo de ~1150px el header
   queda apretado aunque no rompa. */
.main-nav { align-items: center; }
.main-nav > ul {
  align-items: center;
  gap: clamp(var(--space-4), 1.6vw, var(--space-6));
}
.main-nav > ul > li { display: flex; align-items: center; }
.main-nav > ul > li > a {
  display: inline-flex;
  align-items: center;
  font-size: 13px;
  font-weight: 500;
  letter-spacing: 0.04em;
  white-space: nowrap;
  line-height: 1;
  padding: var(--space-2) 0;
}
.has-dropdown > a::after {
  content: "\25BE";
  font-size: 8px;
  margin-left: 7px;
  position: relative;
  top: 1px;
}

/* La fila de la derecha se alinea al mismo eje y baja de peso para no
   competir con el CTA. */
.header-actions { align-items: center; gap: var(--space-4); }
.header-phone { font-weight: 500; font-size: 13px; letter-spacing: 0.02em; line-height: 1; }
.site-header .header-actions .btn {
  padding: 14px 24px;
  font-size: 10px;
  letter-spacing: 0.18em;
  line-height: 1;
}

/* ---- Tira de audiencia: marquesina continua ----
   Antes era una fila estática centrada. Ahora desplaza en loop infinito.

   No frena con el cursor encima — a diferencia de .services-marquee, que sí
   pausa (ahí tiene sentido: son tarjetas clickeables y el usuario necesita
   poder apuntarle a una). Acá es texto no interactivo, así que el
   animation-play-state: running va explícito para que ninguna regla futura de
   hover la detenga por accidente. */
.audience-strip {
  overflow: hidden;
  padding: var(--space-6) 0;
  border-bottom: 1px solid var(--color-hairline);
}
.audience-scroll { overflow: hidden; white-space: nowrap; }
.audience-track {
  display: inline-flex;
  animation: audience-scroll 34s linear infinite;
  will-change: transform;
}
/* Explícito: la marquesina sigue corriendo con el cursor encima. */
.audience-strip:hover .audience-track,
.audience-track:hover { animation-play-state: running; }

.audience-group {
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  padding-right: var(--space-6);
}
.audience-group span {
  font-size: var(--text-label-size);
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-meta);
  white-space: nowrap;
}
.audience-group .sep { color: var(--color-primary); }

/* El track lleva 8 grupos identicos y la animacion avanza exactamente uno
   (1/8 = 12.5%), asi que el corte cae siempre en contenido igual y no se nota.

   Por que 8 y no 2: con 2 grupos el track medi­a 1375px contra una tira de
   1515px, o sea que nunca llegaba a llenar el ancho y quedaba un hueco fijo a
   la derecha. Para que no se vea, lo que queda despues de correr un grupo
   —(N-1) x 688px— tiene que cubrir la pantalla. Con 8 eso da 4816px, de sobra
   hasta en un monitor 4K. */
@keyframes audience-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-12.5%); }
}

/* ---- Split editorial: la imagen se ata a la altura del texto ----
   Con aspect-ratio 4/5 fijo, la foto crecia a lo alto independientemente del
   largo de la columna de texto y quedaba desproporcionada (mucho mas alta que
   el parrafo que acompana). Ahora las dos columnas se estiran a la misma
   altura y la imagen recorta por object-fit, asi que siempre terminan
   alineadas arriba y abajo. El min-height evita que colapse cuando el texto
   es muy corto. */
.editorial-split { align-items: stretch; }
.editorial-body { display: flex; flex-direction: column; justify-content: center; }
.editorial-media { display: flex; }
.editorial-media img {
  aspect-ratio: auto;
  width: 100%;
  height: 100%;
  min-height: 20rem;
  object-fit: cover;
}

/* ---- link-rule: el CTA terciario se perdia ----
   Heredaba el color del texto (tinta) con una regla al 12% de opacidad, asi
   que sobre --color-surface no se leia como accionable. Pasa al rojo de marca
   —coherente con "rojo = act now"— en la variante oscurecida que si pasa AA a
   11px, y la regla sube a 2px en color pleno. En hover invierte a tinta, que
   mantiene el feedback sin depender solo del color. */
.link-rule {
  color: var(--color-primary-text);
  border-bottom-width: 2px;
  border-bottom-color: var(--color-primary-text);
  font-weight: 600;
}
.link-rule:hover,
.link-rule:focus-visible {
  color: var(--color-ink);
  border-bottom-color: var(--color-ink);
}

/* ---- Value list: de lista plana a bloque editorial ----
   Antes: numerito de 11px en rojo, titulo de 1.375rem y parrafo, todo al
   mismo peso visual. Leia como un documento de Word con vinetas numeradas.

   Ahora el numero es el elemento grafico que ordena la seccion: grande, en
   peso 200, en un gris muy bajo que lo deja como marca de agua. Se enciende
   en rojo al hover, junto con una barra de acento que crece desde arriba y
   un desplazamiento del titulo. Toda la fila responde, no solo el link. */
.value-item {
  position: relative;
  grid-template-columns: minmax(4rem, 6rem) 1fr;
  gap: clamp(1.25rem, 3vw, 3rem);
  padding: clamp(2.25rem, 4vw, 3.5rem) clamp(0.75rem, 2vw, 1.75rem);
  align-items: start;
  transition: background var(--motion-duration) var(--motion-easing);
}

/* Barra de acento vertical: crece desde arriba al pasar el cursor. Se apoya
   en top/bottom -1px para tapar los hairlines vecinos y leer como una sola
   pieza continua. */
.value-item::before {
  content: "";
  position: absolute;
  left: 0; top: -1px; bottom: -1px;
  width: 2px;
  background: var(--color-primary);
  transform: scaleY(0);
  transform-origin: top;
  transition: transform var(--motion-duration) var(--motion-easing);
}
.value-item:hover::before { transform: scaleY(1); }
/* Sobre la banda --color-surface, el blanco lee como una elevacion sutil. */
.value-item:hover { background: var(--color-background); }

.value-num {
  font: 200 clamp(2rem, 3.2vw, 3rem)/1 var(--font-display);
  letter-spacing: -0.03em;
  color: rgba(26, 24, 24, 0.17);
  padding-top: 0.15em;   /* alinea opticamente con la mayuscula del h3 */
  transition: color var(--motion-duration) var(--motion-easing);
}
.value-item:hover .value-num { color: var(--color-primary); }

.value-item h3 {
  font-size: clamp(1.375rem, 1.9vw, 1.6875rem);
  font-weight: 300;
  letter-spacing: -0.012em;
  margin-bottom: var(--space-4);
  transition: transform var(--motion-duration) var(--motion-easing);
}
.value-item:hover h3 { transform: translateX(6px); }
.value-item p { color: var(--color-text); }

/* ---- Entrada al hacer scroll ----
   El .reveal generico sube 16px; para estos bloques el recorrido es mas
   largo y mas lento, asi que cada item "asciende" a medida que entra en
   pantalla. El escalonado por nth-child hace que, cuando varios entran
   juntos en una pantalla alta, caigan en cascada en vez de todos a la vez. */
.value-item.reveal-pending {
  opacity: 0;
  transform: translateY(52px);
  transition: opacity 850ms var(--motion-easing),
              transform 850ms var(--motion-easing);
}
.value-item.reveal-pending.is-visible { opacity: 1; transform: translateY(0); }
.value-item:nth-child(1).reveal-pending { transition-delay: 0ms; }
.value-item:nth-child(2).reveal-pending { transition-delay: 80ms; }
.value-item:nth-child(3).reveal-pending { transition-delay: 160ms; }
.value-item:nth-child(4).reveal-pending { transition-delay: 240ms; }
.value-item:nth-child(5).reveal-pending { transition-delay: 320ms; }

/* ---- Mega dropdown de Services ----
   El menu volvio a enlazar cada pagina de servicio (13 URLs propias), a
   pedido del cliente, pero agrupadas por las 4 categorias del brief en vez de
   una lista plana. La regla generica .has-dropdown:hover .dropdown pone
   display:block; esta la pisa con grid por ir despues en la cascada. */
.dropdown-mega {
  min-width: min(58rem, calc(100vw - 4rem));
  left: 50%;
  transform: translateX(-50%);
  padding: var(--space-6);
  gap: var(--space-6);
}
.has-dropdown:hover .dropdown-mega,
.has-dropdown:focus-within .dropdown-mega {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}
.dropdown-heading {
  display: block;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-meta);
  padding: 0 12px var(--space-3);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-hairline);
}
.dropdown-col ul { display: flex; flex-direction: column; gap: 2px; }

/* ---- Indicador visual de campo obligatorio ----
   El select de "How Did You Hear About Us?" ya validaba y bloqueaba el envio,
   pero visualmente era identico a un campo opcional: el visitante recien se
   enteraba al apretar Enviar. Ahora los obligatorios llevan asterisco.

   :has() permite marcar el label a partir del input hermano sin tocar las 29
   paginas. Donde no este soportado simplemente no se pinta el asterisco — el
   required del HTML sigue funcionando igual, asi que no hay regresion. */
.field:has(:required) > label::after,
.field:has(:required) > label > span:first-child::after {
  content: " *";
  color: var(--color-primary-text);
  font-weight: 700;
}
/* El label de "Plans or Blueprints" ya dice "(optional)" en texto, y ese
   campo no es required, asi que nunca entra en la regla de arriba. */

/* ---- Mega dropdown: correccion de ancho y anclaje ----
   La primera version usaba 4 columnas a 58rem centradas sobre el item
   ("left:50% + translateX(-50%)"). Como "Services" es el segundo item del
   nav, un panel de ~930px centrado ahi se salia por el borde izquierdo del
   viewport y quedaba ilegible.

   Ahora: 2 columnas (2x2 para las 4 categorias), ancho acotado, y anclado al
   borde izquierdo del item en vez de centrado — asi se abre hacia la derecha,
   que es donde hay lugar. Ademas baja la densidad: mas interlineado y menos
   peso en los encabezados. */
.dropdown-mega {
  min-width: min(33rem, calc(100vw - 3rem));
  left: 0;
  transform: none;
  padding: var(--space-6) var(--space-4);
  gap: var(--space-6) var(--space-5);
}
.has-dropdown:hover .dropdown-mega,
.has-dropdown:focus-within .dropdown-mega { grid-template-columns: repeat(2, 1fr); }
.dropdown-mega .dropdown-heading {
  font-size: 9px;
  letter-spacing: 0.18em;
  padding: 0 12px var(--space-2);
  margin-bottom: var(--space-1);
}
.dropdown-mega .dropdown-col ul { gap: 0; }
.dropdown-mega .dropdown-col a {
  padding: 7px 12px;
  font-size: 13px;
  line-height: 1.4;
  white-space: nowrap;
}

/* ==========================================================================
   BOTON FLOTANTE DE LLAMADA
   --------------------------------------------------------------------------
   Antes: un glifo de texto (&#9742;) en un circulo rojo, visible solo por
   debajo de 1150px y sin ninguna interaccion. Ahora es un icono SVG real,
   visible en todos los anchos, con animacion al hover y al reposo.

   Reposo   -> circulo con un anillo que late cada 3s (llama la atencion sin
               ser molesto; el latido no se repite infinitamente rapido).
   Hover    -> se expande a pastilla y revela el numero; el auricular hace un
               pequeño balanceo, como un telefono sonando.
   Click    -> href="tel:" — en desktop abre el marcador del sistema
               (Teams/Skype/FaceTime), en movil llama directo.
   ========================================================================== */
.floating-call {
  display: inline-flex;              /* antes: none, solo mobile */
  position: fixed;
  bottom: 24px; right: 24px;
  z-index: 90;
  align-items: center;
  gap: 0;
  height: 58px;
  padding: 0 18px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: #fff;
  box-shadow: 0 10px 30px rgba(228, 37, 40, 0.32);
  overflow: hidden;
  transition: gap 420ms var(--motion-easing),
              padding 420ms var(--motion-easing),
              background 420ms var(--motion-easing),
              box-shadow 420ms var(--motion-easing);
}
.floating-call-icon { display: inline-flex; flex: none; }

/* El texto arranca colapsado (ancho 0) y se despliega en hover. Se anima
   max-width y no width para no tener que fijar un ancho exacto. */
.floating-call-text {
  max-width: 0;
  opacity: 0;
  white-space: nowrap;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: 0.02em;
  transition: max-width 420ms var(--motion-easing),
              opacity 300ms var(--motion-easing);
}
.floating-call:hover,
.floating-call:focus-visible {
  gap: 10px;
  background: var(--color-ink);
  box-shadow: 0 14px 38px rgba(26, 24, 24, 0.34);
}
.floating-call:hover .floating-call-text,
.floating-call:focus-visible .floating-call-text {
  max-width: 12rem;
  opacity: 1;
}
/* Balanceo del auricular, como un telefono sonando. */
.floating-call:hover .floating-call-icon { animation: call-shake 850ms var(--motion-easing); }
@keyframes call-shake {
  0%, 100% { transform: rotate(0); }
  15% { transform: rotate(-16deg); }
  30% { transform: rotate(13deg); }
  45% { transform: rotate(-10deg); }
  60% { transform: rotate(7deg); }
  75% { transform: rotate(-4deg); }
}

/* Anillo de reposo: pulso lento que se detiene mientras el cursor esta encima
   (ahi ya manda la animacion de hover). */
.floating-call-ring {
  position: absolute;
  left: 12px; top: 50%;
  width: 34px; height: 34px;
  margin-top: -17px;
  border-radius: 50%;
  border: 2px solid #fff;
  opacity: 0;
  pointer-events: none;
  animation: call-pulse 3s var(--motion-easing) infinite;
}
.floating-call:hover .floating-call-ring { animation: none; opacity: 0; }
@keyframes call-pulse {
  0%   { transform: scale(0.75); opacity: 0.55; }
  70%  { transform: scale(1.9);  opacity: 0; }
  100% { transform: scale(1.9);  opacity: 0; }
}

/* La regla original fijaba width:56px/height:56px. Como el bloque de arriba
   no declaraba width, ese valor fijo seguia ganando por no haber conflicto y
   la pastilla no podia expandirse en hover. Se libera el ancho y se sostiene
   el circulo con min-width en reposo. */
.floating-call {
  width: auto;
  min-width: 58px;
  height: 58px;
  justify-content: center;
}

/* Animar el ancho INTRINSECO (max-width sobre el texto) no prosperaba: alguna
   regla previa seguia fijando la caja y la pastilla nunca crecia. Se pasa a
   un ancho explicito, que es determinista y ademas hace la transicion mas
   suave porque el navegador interpola un solo valor. */
.floating-call {
  width: 58px;
  min-width: 0;
  justify-content: flex-start;
  padding: 0;
  transition: width 420ms var(--motion-easing),
              background 420ms var(--motion-easing),
              box-shadow 420ms var(--motion-easing);
}
.floating-call .floating-call-icon {
  width: 58px;
  height: 58px;
  align-items: center;
  justify-content: center;
  flex: none;
}
.floating-call .floating-call-text {
  max-width: none;
  padding-right: 22px;
  transition: opacity 260ms var(--motion-easing);
}
.floating-call:hover,
.floating-call:focus-visible { width: 202px; gap: 0; }
.floating-call:hover .floating-call-text,
.floating-call:focus-visible .floating-call-text { opacity: 1; }

/* ---- Retratos reales del equipo ----
   El placeholder era un icono SVG sobre --color-surface. Ahora entra una foto
   real recortada sobre blanco, asi que el fondo de la caja pasa a blanco para
   que no se vea un marco gris alrededor del recorte. object-fit: cover con
   object-position arriba, para que al recortar a 4/5 nunca se corte la cabeza. */
.team-portrait { background: #fff; }
.team-portrait img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
}

/* ---- Checklist: el badge rojo leia como error ----
   Un circulo rojo relleno con un tilde blanco es el patron visual de
   "alerta/error" en casi toda interfaz, asi que en una lista de ventajas
   comunicaba lo contrario de lo que dice el texto.

   Ademas contradecia la regla del sistema: el rojo quedo reservado para lo
   accionable ("act now"). Un check es confirmacion, no accion.

   Pasa a tinta, que es el otro color de marca, y de circulo relleno a tilde
   sobre hairline — mas cerca del registro editorial del resto del sitio. */
.checklist li::before {
  content: "\2713";
  background: transparent;
  color: var(--color-ink);
  border: 1px solid var(--color-hairline);
  border-radius: 50%;
  width: 22px; height: 22px;
  font-size: var(--text-label-size);
  top: 2px;
}
/* Sobre fondos oscuros el tilde y el aro se invierten. */
.bg-ink .checklist li::before,
.bg-surface-dark .checklist li::before,
.bg-primary .checklist li::before {
  color: #fff;
  border-color: rgba(255,255,255,0.35);
}

/* ---- Grillas de zonas: nunca dejar una fila con un solo elemento ----
   Los paneles por condado tienen 5 ciudades (Horry) y 3 (Georgetown), pero la
   grilla estaba fija en 4 columnas: el quinto item de Horry caia solo a una
   segunda fila y leia como un error de maquetado.

   Flex lo resuelve porque cada item toma una fraccion igual del ancho, asi que
   5 o 3 entran en una sola fila y se reparten parejo. Sirve justamente porque
   estas listas son de una sola fila. */
.area-pin-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--space-6);
}
.area-pin-grid > * {
  flex: 1 1 clamp(8rem, 15%, 12rem);
  min-width: 8rem;
}

/* ==========================================================================
   LEGIBILIDAD DE TEXTO SOBRE IMAGEN
   --------------------------------------------------------------------------
   Medido, no estimado: se muestrearon los pixeles reales de la foto detras
   del bloque de texto del hero. Luminancia media 0.19, pero MAXIMA 1.0 — hay
   blanco puro ahi atras. Texto blanco sobre eso da 1:1.

   El scrim anterior iba 0.42 -> 0.10 (32%) -> 0.72 (100%). El bloque de texto
   arranca al 37.7%, o sea justo despues del punto mas debil: ahi el alfa era
   ~0.15 y el peor caso daba ~1.2:1.

   Alfa minimo para garantizar AA contra un pixel BLANCO detras, con scrim
   rgba(26,24,24,a) y texto blanco:
     texto grande (h1, >=24px)  -> 3:1   -> a >= 0.46
     texto normal (lede, label) -> 4.5:1 -> a >= 0.60
   La rampa nueva llega a 0.52 al 36% y a 0.72 al 50%, asi que el titular
   entra con margen y todo lo que va debajo queda por encima de 0.72.

   El tercio superior se mantiene casi limpio (0.10-0.38) para no tapar la
   arquitectura, que es el punto del brief. */
.lux-hero-scrim {
  background: linear-gradient(180deg,
    rgba(26,24,24,0.38) 0%,
    rgba(26,24,24,0.10) 20%,
    rgba(26,24,24,0.64) 36%,
    rgba(26,24,24,0.75) 50%,
    rgba(26,24,24,0.86) 100%);
}

/* La banda de partners ya pasaba (alfa 0.65 en el borde derecho del texto),
   pero queda con poco margen si mañana entra una foto mas clara. Se sube el
   piso sin perder el degrade hacia la derecha. */
.partner-band-scrim {
  background: linear-gradient(90deg,
    rgba(26,24,24,0.95) 0%,
    rgba(26,24,24,0.88) 45%,
    rgba(26,24,24,0.72) 70%,
    rgba(26,24,24,0.45) 100%);
}

/* Las tarjetas de servicio del marquee tambien llevan titulo blanco sobre
   foto. El overlay se refuerza por la misma razon. */
.service-slide-overlay {
  background: linear-gradient(180deg,
    rgba(26,24,24,0.15) 0%,
    rgba(26,24,24,0.55) 45%,
    rgba(26,24,24,0.88) 100%);
}

/* ---- Texto chico sobre imagen: siempre blanco pleno ----
   El label del hero llevaba color rgba(255,255,255,0.62). El scrim estaba
   bien dimensionado, pero el calculo de contraste asumia blanco puro: a 62%
   de opacidad el texto se mezcla con el fondo y el contraste real caia a
   3.20:1, por debajo del minimo AA de 4.5.

   Sobre foto, el texto chico va en blanco pleno. La jerarquia se sostiene
   con tamaño y tracking, que no cuestan contraste — bajar la opacidad si.
   (Sobre fondos solidos oscuros el blanco al 55% si es seguro: ahi no hay
   pixeles claros impredecibles debajo.) */
.lux-hero .label { color: #fff; }
.lux-hero .lede { color: rgba(255,255,255,0.94); }

/* ---- Hero: scrim en dos ejes ----
   El degrade solo vertical dejaba el texto peleando contra lo que hubiera en
   la foto a esa altura. En la de obra eso significaba una zona reventada de
   blanco justo detras del titular: el contraste daba bien en la medicion,
   pero visualmente el texto se apoyaba sobre ruido.

   Se suma un segundo degrade horizontal, el mismo recurso que ya usa
   .partner-band: campo oscuro y parejo a la izquierda —donde vive el texto—
   y la foto respirando a la derecha. Los dos gradientes se apilan, asi que
   el alfa efectivo a la izquierda es ~0.92 y a la derecha ~0.47.

   Ademas de resolver legibilidad, esto vuelve utilizable una foto que de por
   si es mediocre: pasa de competir con el texto a leerse como textura. Es un
   parche mientras no haya fotografia profesional (ver _docs/PHOTO-BRIEF.md),
   no un reemplazo de ella. */
.lux-hero-scrim {
  background:
    linear-gradient(90deg,
      rgba(26,24,24,0.90) 0%,
      rgba(26,24,24,0.78) 34%,
      rgba(26,24,24,0.42) 62%,
      rgba(26,24,24,0.22) 100%),
    linear-gradient(180deg,
      rgba(26,24,24,0.34) 0%,
      rgba(26,24,24,0.14) 28%,
      rgba(26,24,24,0.40) 62%,
      rgba(26,24,24,0.66) 100%);
}

/* Correccion: el bloque de texto llega al 88% del ancho del hero, asi que el
   eje horizontal por si solo no cubre el titular — a esa altura ya venia
   apagado. El vertical, que si cubre todo el ancho, pasa a llevar el peso y
   el horizontal queda solo para dar profundidad hacia la izquierda.
   Alfa combinado en el peor punto (texto mas a la derecha, gradiente mas
   debil): 0.64, por encima del 0.60 que exige el texto chico. */
/* A pedido del cliente el velo baja un 20%: cada alfa se multiplica por 0.8
   para que el video se vea mas. Alfa combinado en el peor punto del titular:
   0.64 -> 0.53. */
.lux-hero-scrim {
  background:
    linear-gradient(90deg,
      rgba(26,24,24,0.48) 0%,
      rgba(26,24,24,0.34) 45%,
      rgba(26,24,24,0.16) 80%,
      rgba(26,24,24,0.08) 100%),
    linear-gradient(180deg,
      rgba(26,24,24,0.48) 0%,
      rgba(26,24,24,0.44) 35%,
      rgba(26,24,24,0.56) 70%,
      rgba(26,24,24,0.68) 100%);
}

/* ---- Paginas de servicio: imagen en el split "About This Service" ----
   Esa grilla es de 2 columnas pero solo tenia la de texto cargada, asi que
   media pagina quedaba vacia y el bloque leia como un documento de texto.
   Ahora entra la foto del servicio en la segunda columna, atada a la altura
   del texto como en el resto del sitio. */
.split-grid { align-items: stretch; }
.split-grid > .editorial-media { display: flex; }
.split-grid > .editorial-media img {
  width: 100%; height: 100%;
  min-height: 18rem;
  object-fit: cover;
  aspect-ratio: auto;
}

/* El hero compacto de servicio ahora si tiene foto detras, asi que necesita
   el mismo scrim en dos ejes que el hero principal — antes era un panel
   plano y el overlay alcanzaba. */
.hero-compact .hero-overlay {
  background:
    linear-gradient(90deg, rgba(26,24,24,0.88) 0%, rgba(26,24,24,0.72) 45%, rgba(26,24,24,0.45) 100%),
    linear-gradient(180deg, rgba(26,24,24,0.45) 0%, rgba(26,24,24,0.70) 100%);
}

/* ---- Paginas de servicio: la grilla quedo con 3 hijos ----
   Al sumar la imagen, el split de 2 columnas paso a tener texto | imagen en
   la fila 1 y la tarjeta "Common Signs" sola en la fila 2, con la mitad
   derecha vacia. La imagen ahora ocupa las dos filas y queda sticky: acompana
   la lectura de la lista en vez de dejar un hueco. */
.split-grid:has(> .editorial-media):has(> .card) {
  grid-template-columns: 1fr 1fr;
  align-items: start;
}
.split-grid > .editorial-media {
  grid-column: 2;
  grid-row: 1 / span 2;
  position: sticky;
  top: calc(var(--header-height, 84px) + var(--space-6));
  align-self: start;
}
.split-grid > .editorial-media img { min-height: 26rem; }
.split-grid > .card { grid-column: 1; }

/* ---- Checklist: entrada escalonada + estado interactivo ----
   La lista entra item por item al hacer scroll, en vez de aparecer entera de
   golpe, y cada fila responde al cursor. Da algo que mirar mientras se lee, y
   el tilde se pinta de rojo —accionable— solo al interactuar. */
.card .checklist li {
  transition: transform var(--motion-duration) var(--motion-easing),
              color var(--motion-duration) var(--motion-easing);
}
.card .checklist li:hover { transform: translateX(5px); }
.card .checklist li::before {
  transition: border-color var(--motion-duration) var(--motion-easing),
              background var(--motion-duration) var(--motion-easing),
              color var(--motion-duration) var(--motion-easing),
              transform var(--motion-duration) var(--motion-easing);
}
.card .checklist li:hover::before {
  border-color: var(--color-ink);
  background: var(--color-ink);
  color: #fff;
  transform: scale(1.12);
}
.card.reveal-pending .checklist li {
  opacity: 0;
  transform: translateY(14px);
}
.card.reveal-pending.is-visible .checklist li {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 620ms var(--motion-easing), transform 620ms var(--motion-easing);
}
.card.reveal-pending.is-visible .checklist li:nth-child(1) { transition-delay: 60ms; }
.card.reveal-pending.is-visible .checklist li:nth-child(2) { transition-delay: 150ms; }
.card.reveal-pending.is-visible .checklist li:nth-child(3) { transition-delay: 240ms; }
.card.reveal-pending.is-visible .checklist li:nth-child(4) { transition-delay: 330ms; }
.card.reveal-pending.is-visible .checklist li:nth-child(5) { transition-delay: 420ms; }
.card.reveal-pending.is-visible .checklist li:nth-child(6) { transition-delay: 510ms; }
.card.reveal-pending.is-visible .checklist li:nth-child(n+7) { transition-delay: 600ms; }

/* ---- Mega dropdown: 3 columnas ----
   Se quito la columna "New Construction": sus tres items apuntaban a
   secciones de /builders-designers, que ya es un item de primer nivel del
   nav. El menu competia consigo mismo y repetia el mismo destino.

   Quedan las 3 disciplinas que si tienen paginas propias, y "All Services"
   —que vivia en esa columna— pasa a un pie que cruza el ancho completo. */
.has-dropdown:hover .dropdown-mega,
.has-dropdown:focus-within .dropdown-mega { grid-template-columns: repeat(3, 1fr); }
.dropdown-mega { min-width: min(38rem, calc(100vw - 3rem)); }
.dropdown-all {
  grid-column: 1 / -1;
  margin-top: var(--space-2);
  padding: var(--space-4) 12px 0;
  border-top: 1px solid var(--color-hairline);
  font-size: var(--text-label-size);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-primary-text);
}
.dropdown-all:hover { color: var(--color-ink); }

/* ---- Foto de equipo en la home ----
   La original quedaba mal por dos motivos: el fondo lavado del evento y un
   recorte muy cerrado, porque la imagen es apaisada y el slot se estira a la
   altura del texto, asi que object-fit: cover los cortaba a media altura.

   Ahora la foto lleva recorte de sujeto sobre --color-surface (el mismo tono
   de las bandas del sitio) y se muestra con `contain`: al coincidir el fondo
   de la imagen con el de la caja, no se ve ninguna caja ni franja, y no se
   recorta a ninguno de los dos. */
.editorial-media img[src*="team-nathan-chase"] {
  object-fit: contain;
  object-position: center bottom;
  background: var(--color-surface);
  min-height: 0;
}

/* ---- Placa de equipo: logo + nombres sobre la foto ----
   La foto se muestra con `contain`, asi que deja aire libre del mismo tono
   que su fondo. En vez de desperdiciarlo, ahi van el logo y los nombres: la
   imagen deja de ser una foto suelta y pasa a leerse como una placa
   compuesta. Los nombres son texto real, no estan quemados en el archivo. */
.team-plate { position: relative; background: var(--color-surface); }
.team-plate > img:not(.team-plate-logo) {
  object-position: center top;
  padding-top: var(--space-7);
}
.team-plate-logo {
  position: absolute;
  top: var(--space-5);
  left: 50%;
  transform: translateX(-50%);
  width: 148px; height: auto;
  z-index: 2;
  opacity: 0.9;
}
/* Los nombres se anclan abajo, en dos mitades: cada uno cae bajo su persona
   —Chase a la izquierda, Nathan a la derecha— sin depender de coordenadas
   fijas, asi que el encuadre puede cambiar sin desalinearlos. */
.team-plate-names {
  position: absolute;
  left: 0; right: 0; bottom: var(--space-5);
  display: grid;
  grid-template-columns: 1fr 1fr;
  z-index: 2;
  padding: 0 var(--space-4);
}
.team-plate-names span {
  display: flex;
  flex-direction: column;
  gap: 2px;
  text-align: center;
}
.team-plate-names strong {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--color-ink);
}
.team-plate-names small {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-meta);
}

/* Correcciones de la placa:
   - El logo se estiraba a todo el ancho: `.editorial-media img { width:100% }`
     tiene mas especificidad que `.team-plate-logo`. Se sube el selector.
   - Los nombres se superponian a la foto. Ahora la foto tiene una franja
     reservada arriba (logo) y abajo (nombres) via padding, en vez de que
     `contain` reparta el aire libre a su criterio. */
.team-plate img.team-plate-logo {
  width: 148px;
  height: auto;
  min-height: 0;
}
.team-plate > img:not(.team-plate-logo) {
  padding-top: var(--space-9);
  padding-bottom: var(--space-9);
}

/* ---- Pilares de "What We Do": interaccion sobre la imagen ----
   Se descarto el carrusel: en desktop las 4 caben a la vista y esconderlas
   detras de flechas agrega un clic para ver algo que ya estaba visible.

   En su lugar, la interaccion vive en cada tarjeta. Al pasar el cursor la
   foto hace zoom, entra un velo desde abajo y sube un indicador — el gesto
   habitual en portfolios de arquitectura. La tarjeta entera es el area
   activa, no solo el titulo. */
.pillar-media {
  position: relative;
  overflow: hidden;
  background: var(--color-ink);
}
.pillar-media img {
  transition: transform 1100ms var(--motion-easing),
              opacity 700ms var(--motion-easing);
}
.pillar:hover .pillar-media img,
.pillar:focus-visible .pillar-media img { transform: scale(1.06); }

/* Velo: sube desde abajo, no aparece de golpe. */
.pillar-media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(26,24,24,0) 35%, rgba(26,24,24,0.78) 100%);
  opacity: 0;
  transform: translateY(24%);
  transition: opacity 620ms var(--motion-easing), transform 620ms var(--motion-easing);
  pointer-events: none;
}
.pillar:hover .pillar-media::after,
.pillar:focus-visible .pillar-media::after { opacity: 1; transform: translateY(0); }

/* Indicador que sube desde el borde inferior de la foto. */
.pillar-media::before {
  content: "View the Service \2192";
  position: absolute;
  left: var(--space-5);
  bottom: var(--space-5);
  z-index: 2;
  font-size: var(--text-label-size);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: #fff;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 620ms var(--motion-easing), transform 620ms var(--motion-easing);
  transition-delay: 90ms;
  pointer-events: none;
}
.pillar:hover .pillar-media::before,
.pillar:focus-visible .pillar-media::before { opacity: 1; transform: translateY(0); }

/* El texto acompana: el titulo se desplaza y la regla inferior se tine. */
.pillar h3 { transition: transform 620ms var(--motion-easing); }
.pillar:hover h3 { transform: translateX(6px); }
.pillar-sub { transition: border-color 620ms var(--motion-easing), color 620ms var(--motion-easing); }
.pillar:hover .pillar-sub { border-top-color: var(--color-ink); color: var(--color-text); }
.pillar-index { transition: color 620ms var(--motion-easing); }
.pillar:hover .pillar-index { color: var(--color-ink); }

.pillar:focus-visible { outline: 2px solid var(--color-ink); outline-offset: 6px; }

/* ---- Marcas: tratamiento tipografico, no logos ----
   Se evaluo poner los logos originales de Control4, Lutron, Sonos y Nest
   recoloreados a la paleta del sitio, y se descarto: las guias de marca de
   las cuatro prohiben explicitamente alterar el color del logo. Ver la nota
   en _docs/PHOTO-BRIEF.md.

   Nombrar la marca en texto es uso nominativo y es seguro. Asi que en vez de
   logos, el bloque se resuelve tipograficamente: nombres mas grandes,
   separados por hairlines verticales, con el mismo peso liviano del resto
   del sistema. Lee como una placa de credenciales, no como una lista suelta. */
.brand-strip {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: 0;
  border-top: 1px solid var(--color-hairline);
  border-bottom: 1px solid var(--color-hairline);
}
.brand-strip span {
  display: flex;
  align-items: center;
  padding: var(--space-6) clamp(1.25rem, 4vw, 3rem);
  font-size: clamp(1.0625rem, 1.6vw, 1.375rem);
  font-weight: 300;
  letter-spacing: 0.02em;
  color: var(--color-text);
  border-right: 1px solid var(--color-hairline);
  transition: color var(--motion-duration) var(--motion-easing);
}
.brand-strip span:last-child { border-right: none; }
.brand-strip span:hover { color: var(--color-ink); }

/* ---- Placa de marcas con logos reales ----
   Los logos van SIN modificar: ni recoloreados, ni con filtros CSS, ni
   distorsionados. Se controla solo la ALTURA, que es lo unico que todas las
   guias de marca permiten ajustar, y se les da aire alrededor (la mayoria
   exige una zona libre minima alrededor del logo).

   La coherencia visual sale del contenedor —fondo de marca, hairlines,
   ritmo parejo— y no de tocar el logo. Si el cliente baja la variante
   monocroma del portal de dealer, el bloque queda homogeneo por si solo. */
.brand-strip img {
  height: clamp(22px, 2.2vw, 30px);
  width: auto;
  max-width: 150px;
  object-fit: contain;
  display: block;
}
.brand-strip span:has(img) { padding-inline: clamp(1.5rem, 4vw, 3.25rem); }
/* Nest y Sonos tienen marcas mucho mas anchas que altas; se compensa para
   que opticamente pesen parejo sin deformar ninguna. */
.brand-strip img[alt*="Sonos"] { height: clamp(18px, 1.8vw, 24px); }

/* ---- Placa de marcas sobre blanco ----
   El logo de Control4 viene con fondo blanco solido (no transparente), asi
   que sobre --color-surface mostraba un recuadro. Se probo recortarle el
   fondo automaticamente y el detector de sujeto CORTO DENTRO de la marca,
   dañando el logo — peor que el recuadro, y ademas es exactamente lo que las
   guias de marca prohiben.

   La solucion correcta es al reves: se adapta el contenedor, nunca el logo.
   La placa va sobre blanco, asi el fondo de Control4 se funde y los que si
   son transparentes (Lutron, Sonos) se ven igual de bien. Cero modificacion
   sobre los archivos originales. */
.brand-strip {
  background: var(--color-background);
  border: 1px solid var(--color-hairline);
}
.brand-strip span { border-right-color: var(--color-hairline); }

/* ---- Normalizacion optica de los logos ----
   Una altura comun NO los iguala, porque cada archivo trae distinta cantidad
   de aire interno. Medido sobre los pixeles reales (recortando el relleno):

     archivo     lienzo      tinta      % alto usado   ratio tinta
     control4    900x500     193x43     39%            4.49
     lutron      320x320     180x26     13%            6.92
     sonos       3840x2160   183x36     32%            5.08
     nest        1000x452    200x90     100%           2.22

   Lutron usaba el 13% de su alto y Nest el 100%: a la misma altura de caja,
   uno queda diminuto y el otro domina. Por eso cada uno lleva la altura que
   hace que su TINTA mida lo mismo, no su archivo.

   Nest es la excepcion deliberada: su marca es compacta y pesada (ratio 2.2
   contra 4.5-6.9 de los wordmarks), asi que se iguala por ALTO de tinta. Si
   se igualara por ancho quedaria tres veces mas alta que las demas.

   Esto es escalado uniforme, no distorsion: ninguna guia de marca lo prohibe. */
.brand-strip img { max-width: none; max-height: none; height: auto; width: auto; }
.brand-strip img[alt="Control4"] { height: 60px; }
.brand-strip img[alt="Lutron"]   { height: 117px; }
.brand-strip img[alt="Sonos"]    { height: 64px; }
.brand-strip img[alt="Nest"]     { height: 24px; }

/* La celda fija su alto para que el aire sobrante de cada archivo no estire
   la banda: los logos se centran dentro. */
.brand-strip span { align-items: center; justify-content: center; min-height: 96px; padding-block: var(--space-4); }

/* ==========================================================================
   PILARES APILADOS (opcion B del estudio de movimiento)
   --------------------------------------------------------------------------
   Cada pilar se fija arriba y el siguiente se apila encima, como cartas que
   se juntan. Solo en la home: la clase .pillar-stack se aplica ahi y NO en
   /services, donde los pilares son destino de anclas (#smart-home, etc.) y
   fijarlos romperia el salto.

   Tres cosas que lo hacen funcionar:
   - Las tarjetas pasan a HORIZONTALES. Verticales median ~560px cada una y
     cuatro apiladas daban una seccion de siete pantallas. Horizontales bajan
     a ~300px y la seccion queda en ~4, que es el costo que se acepto.
   - Fondo OPACO: sin el, la tarjeta de abajo se transparenta y el apilado no
     se lee.
   - El header del sitio es sticky con z-index 100, asi que el offset arranca
     debajo de el y las tarjetas van por debajo en z-index.
   ========================================================================== */
.pillar-stack {
  display: block;
  --stack-top: calc(var(--header-height, 84px) + var(--space-5));
}
.pillar-stack .pillar {
  position: sticky;
  top: var(--stack-top);
  display: grid;
  grid-template-columns: minmax(0, 42%) 1fr;
  gap: clamp(1.5rem, 3vw, 2.75rem);
  align-items: center;
  background: var(--color-background);
  border: 1px solid var(--color-hairline);
  padding: clamp(1.25rem, 2.5vw, 1.75rem);
  margin-bottom: clamp(6rem, 14vh, 11rem);
}
/* Cada una un poco mas abajo y por encima de la anterior: asi se ve el borde
   de las que quedaron atras y se entiende la pila. */
.pillar-stack .pillar:nth-child(1) { z-index: 1; }
.pillar-stack .pillar:nth-child(2) { z-index: 2; top: calc(var(--stack-top) + 14px); }
.pillar-stack .pillar:nth-child(3) { z-index: 3; top: calc(var(--stack-top) + 28px); }
.pillar-stack .pillar:nth-child(4) { z-index: 4; top: calc(var(--stack-top) + 42px); margin-bottom: 0; }

.pillar-stack .pillar-media { aspect-ratio: 4/3; margin-bottom: 0; }
.pillar-stack .pillar h3 { font-size: clamp(1.25rem, 1.9vw, 1.6rem); }
.pillar-stack .pillar p { max-width: 46ch; margin-bottom: var(--space-4); }
.pillar-stack .pillar-sub { margin-bottom: 0; }
/* El indicador sobre la foto se mantiene, pero mas chico: en horizontal la
   imagen ocupa menos y el texto anterior lo desbordaba. */
.pillar-stack .pillar-media::before { left: var(--space-4); bottom: var(--space-4); font-size: 10px; }

/* Ajuste de altura: la tarjeta daba 665px, que en una laptop de 768px de alto
   no entra debajo del header sticky (85px de offset + 665 = 750, sin aire).
   Se acorta la foto a 16/10 y se comprime el bloque de texto para que la
   tarjeta quepa entera en pantallas bajas. */
.pillar-stack .pillar-media { aspect-ratio: 16/10; }
.pillar-stack .pillar h3 { margin-bottom: var(--space-2); }
.pillar-stack .pillar p { margin-bottom: var(--space-3); font-size: 1rem; }
.pillar-stack .pillar-sub { padding-top: var(--space-3); font-size: 0.78rem; line-height: 1.6; }
.pillar-stack .pillar-index { margin-bottom: var(--space-2); }

/* ---- Correccion de la grilla del pilar apilado + foto a sangre ----
   BUG: la tarjeta tiene 5 hijos directos (media, index, h3, p, pillar-sub) y
   se le habia puesto una grilla de 2 columnas sin ubicarlos. El auto-placement
   los repartia en zigzag —foto, indice, titulo, texto, subtitulo— asi que el
   titulo caia debajo de la foto y el indice quedaba solo en el aire.

   Ahora la foto ocupa la columna 1 entera (grid-row 1/-1) y los cuatro
   elementos de texto se apilan en la columna 2. Ademas la foto pierde el
   padding y llega al borde: el contraste entre imagen llena y texto con aire
   es lo que separa una tarjeta de catalogo de una editorial. */
.pillar-stack .pillar {
  padding: 0;
  column-gap: 0;
  align-items: stretch;
  align-content: center;
  overflow: hidden;
}
.pillar-stack .pillar-media {
  grid-column: 1;
  grid-row: 1 / -1;
  height: 100%;
  aspect-ratio: auto;
  margin: 0;
  align-self: stretch;
}
.pillar-stack .pillar-media img { height: 100%; width: 100%; object-fit: cover; }

/* Todo el texto vive en la columna 2, con su propio aire interno. */
.pillar-stack .pillar-index,
.pillar-stack .pillar > h3,
.pillar-stack .pillar > p { grid-column: 2; }
.pillar-stack .pillar-index { padding: clamp(1.75rem,3.5vw,2.75rem) clamp(1.75rem,3.5vw,2.75rem) 0; }
.pillar-stack .pillar > h3,
.pillar-stack .pillar > p { padding-inline: clamp(1.75rem,3.5vw,2.75rem); }
.pillar-stack .pillar-sub { padding-bottom: clamp(1.75rem,3.5vw,2.75rem); margin-bottom: 0; }

/* Jerarquia dentro de la columna de texto: indice chico arriba, titulo
   grande, descripcion, y los subservicios separados por hairline al pie. */
.pillar-stack .pillar-index { margin-bottom: var(--space-4); }
.pillar-stack .pillar > h3 { font-size: clamp(1.35rem,2.1vw,1.75rem); margin-bottom: var(--space-3); }
.pillar-stack .pillar > p:not(.pillar-sub) { margin-bottom: var(--space-5); }
.pillar-stack .pillar-sub { padding-top: var(--space-4); }

/* Correccion: `grid-row: 1 / -1` colapsaba a una sola fila. Sin filas
   explicitas declaradas, la linea -1 apunta a la unica linea del grid
   implicito, asi que el tramo 1/-1 no abarcaba nada y la foto quedaba en
   79px. Con `span 4` cubre las cuatro filas de texto.
   align-content vuelve a stretch para que las filas repartan el alto. */
.pillar-stack .pillar { align-content: stretch; grid-template-rows: auto auto auto 1fr; }
.pillar-stack .pillar-media { grid-row: 1 / span 4; }
.pillar-stack .pillar-index { align-self: end; }
.pillar-stack .pillar-sub { align-self: start; }

/* ---- Pilar apilado: se abandona la grilla ----
   Con `grid-template-rows: auto auto auto 1fr` + stretch, el alto sobrante se
   repartia ENTRE las filas y abria huecos enormes entre indice, titulo y
   descripcion. Centrar el grupo con align-content chocaba con que la foto
   necesita el alto completo — dependencia circular entre el alto de la
   tarjeta y el de la imagen.

   Solucion: la foto sale del flujo (position absolute, columna izquierda) y
   el texto queda como una pila flex simple, centrada, con gaps que yo
   controlo. Sin grilla no hay reparto de espacio que corregir. */
.pillar-stack .pillar {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0;
  min-height: 26rem;
  padding: clamp(2rem,4vw,3rem) clamp(2rem,4vw,3.25rem) clamp(2rem,4vw,3rem)
           calc(42% + clamp(2rem,4vw,3.25rem));
  grid-template-rows: none;
}
.pillar-stack .pillar-media {
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 42%;
  height: auto;
}
/* Pila de texto con ritmo propio, no heredado del reparto del grid. */
.pillar-stack .pillar-index { padding: 0; margin: 0 0 var(--space-4); align-self: auto; }
.pillar-stack .pillar > h3 { padding: 0; margin: 0 0 var(--space-4); }
.pillar-stack .pillar > p:not(.pillar-sub) { padding: 0; margin: 0 0 var(--space-6); max-width: 42ch; }
.pillar-stack .pillar-sub {
  padding: var(--space-4) 0 0;
  margin: 0;
  align-self: auto;
  max-width: 46ch;
}

/* Correccion: el bloque anterior declaraba `position: relative` en la tarjeta
   para que la foto absoluta se anclara ahi, y con eso PISO el
   `position: sticky` que produce el apilado. La animacion desaparecio.

   No hacia falta: `sticky` ya es un valor posicionado, asi que establece el
   bloque contenedor para los descendientes absolutos igual que `relative`.
   Se restituye sticky con su offset y su z-index. */
.pillar-stack .pillar { position: sticky; top: var(--stack-top); }
.pillar-stack .pillar:nth-child(1) { z-index: 1; top: var(--stack-top); }
.pillar-stack .pillar:nth-child(2) { z-index: 2; top: calc(var(--stack-top) + 14px); }
.pillar-stack .pillar:nth-child(3) { z-index: 3; top: calc(var(--stack-top) + 28px); }
.pillar-stack .pillar:nth-child(4) { z-index: 4; top: calc(var(--stack-top) + 42px); }

/* Recorrido final: medido a lo largo del scroll, llegaban a apilarse 3 de las
   4 tarjetas y despues se soltaban, porque el contenedor se quedaba sin alto
   por debajo de la ultima. Un elemento sticky deja de pegarse cuando el borde
   inferior de su contenedor lo alcanza.

   Se agrega recorrido al pie del contenedor para que la cuarta alcance a
   pegarse con las otras tres todavia arriba, que es el momento en que la pila
   completa se ve. */
.pillar-stack { padding-bottom: 60vh; }

/* Se quita el recorrido extra al pie. Se habia agregado 60vh para intentar
   que las 4 tarjetas se apilaran a la vez; no lo logro —el limite lo pone la
   distancia entre tarjetas, no el alto del contenedor— y dejaba un hueco
   visible de media pantalla cuando la ultima se despegaba.

   La ultima tarjeta necesita algo de recorrido para leerse pegada, pero le
   alcanza con su propio margen inferior. */
.pillar-stack { padding-bottom: 0; }
.pillar-stack .pillar:nth-child(4) { margin-bottom: clamp(2rem, 5vh, 4rem); }

/* ---- Process stepper: el panel visual acompaña el scroll ----
   El panel de la derecha estaba centrado con align-items: center, asi que
   subia junto con la lista de pasos y la animacion quedaba a mitad de camino:
   para cuando el visitante llegaba al paso 4, el panel ya se habia ido.

   Ahora el panel queda fijo mientras la lista de pasos pasa por al lado, y
   recien se suelta cuando termina el ultimo. El bloque contenedor es el
   .process-stepper, asi que el panel se despega solo/ al llegar al final de
   la seccion — que es exactamente el comportamiento pedido. */
.process-stepper { align-items: start; }
.process-stepper-visual {
  position: sticky;
  top: calc(var(--header-height, 84px) + var(--space-5));
  align-self: start;
  min-height: 0;
  height: min(600px, calc(100vh - var(--header-height, 84px) - 4rem));
}

/* Recorrido del panel: un elemento sticky se queda fijo mientras el borde
   inferior de su contenedor no lo alcance, asi que el recorrido util es
   (alto del contenedor - alto del panel). Con el panel en 600px sobre una
   lista de 1152px, se soltaba a poco mas de la mitad.

   Bajando el panel a ~430px el recorrido pasa de 552 a ~720px, o sea que
   acompaña casi dos tercios de la lista en vez de la mitad. Es el maximo
   razonable sin achicar tanto la foto que deje de leerse. */
.process-stepper-visual { height: min(430px, calc(100vh - var(--header-height, 84px) - 6rem)); }

/* ---- Contacto: layout apilado ----
   Historia corta de por que quedo asi. Primero se corrigio la jerarquia entre
   las dos columnas: el `h2` de la izquierda resolvia a 40px display contra los
   20px del `h3` del formulario, o sea que la informacion de apoyo pesaba el
   doble que la accion. Se invirtio y la tarjeta —que era #fff sobre seccion
   #fff, con `--shadow-sm: none`, o sea invisible— paso a tono superficie.

   No alcanzo. Mientras hubiera dos objetos lado a lado, el borde de la tarjeta
   partia la pantalla al medio. El problema ya no era de tamaño de letra sino de
   cantidad de objetos: dos cosas en paralelo siempre compiten un poco.

   Apilado no hay con que competir. Franja de datos arriba, formulario solo y
   centrado, foto y pasos debajo. */

/* Franja de datos. Sin filete entre celdas: cuatro columnas ya se leen como
   cuatro columnas, y los filetes verticales agregarian ruido en una franja que
   tiene que pasar rapido. */
.contact-strip {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.5rem, 3vw, 2.5rem);
  border-top: 1px solid var(--color-hairline);
  border-bottom: 1px solid var(--color-hairline);
  padding: clamp(1.25rem, 2.5vw, 1.75rem) 0;
}
.contact-strip-item { display: flex; flex-direction: column; gap: 0.4rem; }
.contact-strip dt {
  font-size: var(--text-label-size);
  font-weight: 700;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--color-meta);
}
.contact-strip dd {
  margin: 0;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--color-text);
}
.contact-strip dd a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--color-hairline); }
.contact-strip dd a:hover,
.contact-strip dd a:focus-visible { color: var(--color-primary-text); border-color: var(--color-primary-text); }

/* El telefono no se deja caer a tamaño de dato. Al apilar, la franja pierde
   jerarquia frente al formulario, y en este negocio buena parte de la gente
   prefiere llamar antes que escribir. */
.contact-strip-item.is-lead dd {
  font: 300 clamp(1.25rem, 1.8vw, 1.5rem)/1.2 var(--font-display);
  letter-spacing: -0.015em;
}
.contact-strip-item.is-lead dd a { border-bottom-color: transparent; }
.contact-strip-item.is-lead dd a:hover,
.contact-strip-item.is-lead dd a:focus-visible { border-bottom-color: var(--color-primary-text); }

.contact-strip-fine {
  margin: var(--space-4) 0 0;
  font-size: 0.85rem;
  color: var(--color-meta);
}

/* El formulario, al ancho de medida y no del contenedor: a 1240px una fila de
   dos campos se estira tanto que deja de leerse como par. */
.contact-form-wrap {
  max-width: 46rem;
  margin: clamp(3rem, 6vw, 4.5rem) auto 0;
}
.contact-lead .lead-block {
  background: var(--color-surface);
  border: 1px solid var(--color-hairline);
  padding: clamp(1.75rem, 3vw, 2.75rem);
}
.contact-lead .lead-block h2 {
  font: 300 clamp(1.75rem, 2.6vw, 2.25rem)/1.16 var(--font-display);
  letter-spacing: var(--tracking-h2);
}

/* ---- Lista de zonas y de servicios: grilla, no flex ----
   Estas listas son de varias filas, no de una sola, y ahi flex hace lo
   contrario de lo que se necesita: los items de la ultima fila crecen para
   llenar el ancho, asi que quedan mas anchos que los de arriba y las columnas
   no alinean. Medido en la lista de 8 ciudades: fila 1 con 5 items de 213px,
   fila 2 con 3 items de 376px.

   Con grid las columnas son fijas y todo cae en su lugar. Lo unico que hay que
   elegir es la cantidad de columnas para que ninguna fila quede con un solo
   item: 8 ciudades entran justo en 4 (4/4). */
.area-list-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3) var(--space-6);
}
/* La lista de servicios de las paginas de zona tiene 13 items, y 13 solo se
   reparte sin dejar a uno solo en 5 o en 7 columnas. En 5 quedan 5/5/3. */
.area-list-grid.is-dense { grid-template-columns: repeat(5, 1fr); }

/* ---- Secciones consecutivas del mismo fondo ----
   Cuando dos secciones seguidas comparten color de fondo, sus paddings se
   suman y queda el doble de aire sin ninguna linea que lo justifique: en
   /about, entre "Who We Are" y "Meet the Team" daba 152 + 152 = 304px de
   blanco. Con distinto fondo el aire se lee como separacion entre bandas;
   con el mismo, se lee como error.

   Se anula el padding superior de la segunda para que quede un solo ritmo.
   Solo hay dos pares asi en todo el sitio (/about y /services). */
.section.bg-background + .section.bg-background,
.section.bg-surface + .section.bg-surface { padding-top: 0; }

/* =====================================================================
   MEDIA QUERIES

   Todas juntas y al final, en su orden original. Antes estaban repartidas
   por el archivo, y muchas quedaban antes de reglas sin media query con la
   misma especificidad: al venir despues, la regla general ganaba y la
   responsive no se aplicaba nunca. Asi estaban muertas la adaptacion del
   menu a tablet, la del boton flotante de telefono, la del stack de
   pilares en movil y varias mas.

   Regla para el futuro: toda media query va aca abajo.
   ===================================================================== */

/* ---- Reduced motion (accessibility floor) ---- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  .promo-banner .promo-track { animation: none; }
  .promo-banner .promo-track span:nth-child(n+2) { display: none; }
}

@media (prefers-reduced-motion: reduce) {
}

@media (prefers-reduced-motion: reduce) {
  .services-marquee { -webkit-mask-image: none; mask-image: none; overflow-x: auto; }
  .services-marquee-track { animation: none; }
  .service-slide { transition: box-shadow 0.2s ease; }
  .service-slide:hover, .service-slide:focus-visible { transform: none; }
}

@media (max-width: 640px) {
  .service-slide { flex-basis: 250px; height: 350px; }
}

@media (max-width: 640px) {
  .hero[data-hero-slider] .hero-content { border-left-width: 2px; }
}

/* ---- Phone + tablet layout for the home hero ----
   Everything collapses into ONE bottom-anchored column: copy, then the sound
   button, then the primary CTA, then the slide dots — all sharing the text's
   left edge so the block reads as a single unit.
   The cut-off is 900px, not a phone width: below that there isn't room for
   the copy AND a bottom-right floating CTA AND the vertical dot rail on the
   same line, so at 768px the float landed on top of the sound button and the
   headline ran underneath the dots.
   This block MUST stay after every desktop .hero-* rule above: media queries
   add no specificity, so an earlier-placed override would silently lose the
   cascade to the equally-specific desktop rules (that is exactly what left
   the dots floating over the copy on phones). */
@media (max-width: 900px) {
  .hero[data-hero-slider] {
    /* Switching the main axis to vertical moves the bottom anchor from
       align-items to justify-content. */
    flex-direction: column;
    justify-content: flex-end;
    align-items: stretch;
    gap: var(--space-4);
    padding-bottom: var(--space-7);
  }
  .hero[data-hero-slider] .hero-content {
    max-width: none;
    /* Scales the edge gap and the rule-to-text gap together, so the indent
       grows with the viewport instead of looking cramped on a tablet. */
    margin-left: clamp(16px, 4vw, 40px);
    padding-left: clamp(16px, 4vw, 40px);
    padding-right: clamp(16px, 4vw, 40px);
  }
  .hero-play { width: auto; justify-content: flex-start; }
  /* space-4 (edge gap) + 2px rule + space-4 (rule-to-text gap) = the exact x
     the copy starts at, so the CTA lines up with the headline.
     width:auto is required — the sitewide `.btn { width: 100% }` at ≤380px
     would otherwise force a full-viewport width that ignores these margins
     and pushes the button off-screen. */
  /* .btn.hero-cta-float, not .hero-cta-float alone: the sitewide
     `@media (max-width:380px) { .btn { width:100% } }` sits later in this
     file at equal specificity, so a single-class selector loses the cascade
     and the button renders a full viewport wide, overrunning its margins. */
  
  /* Dots join the bottom of the same column instead of floating over the
     copy. The -17px pulls the 44px tap target back so the 10px dot inside it
     aligns with the text edge rather than the target's edge. */
  .hero-dots {
    position: static;
    transform: none;
    flex-direction: row;
    justify-content: flex-start;
    margin-left: calc(clamp(16px, 4vw, 40px) * 2 + 2px - 17px);
  }
  .hero-dot { height: 32px; }
}

@media (max-width: 780px) {
}

@media (max-width: 860px) {
  .process-stepper { grid-template-columns: 1fr; }
  .process-stepper-visual { order: -1; min-height: 360px; }
}

@media (prefers-reduced-motion: reduce) {
  .why-card { transition: box-shadow 0.2s ease, border-color 0.2s ease; }
  .why-card:hover { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .service-photo-card:hover, .service-photo-card:focus-visible { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .equip-card:hover { transform: none; }
}

@media (max-width: 1100px) {
  .grid.recent-grid > .gallery-item { flex-basis: calc((100% - 2 * var(--space-5)) / 3); }
}

@media (max-width: 780px) {
  .grid.recent-grid > .gallery-item { flex-basis: calc((100% - var(--space-5)) / 2); }
}

@media (max-width: 480px) {
  .grid.recent-grid > .gallery-item { flex-basis: 100%; }
}

@media (max-width: 640px) {
  /* Side-by-side arrows squeeze the photo to ~215px on a phone. Overlay them on
     the image instead so the photo gets the full screen width. */
  .lightbox { padding: var(--space-3); gap: 0; }
  .lightbox-figure { width: 100%; max-width: 100%; }
  .lightbox-img { width: 100%; height: auto; max-height: 64vh; object-fit: contain; }
  .lightbox-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    width: 44px; height: 44px;
    font-size: 26px;
    background: rgba(20,18,19,0.6);
  }
  .lightbox-prev { left: 8px; }
  .lightbox-next { right: 8px; }
  .lightbox-caption { justify-content: center; text-align: center; }
}

@media (prefers-reduced-motion: reduce) {
  .gallery-item img { transition: none; }
  .gallery-item:hover img, .gallery-item:focus-visible img { transform: none; }
}

/* ==========================================================================
   Responsive
   ========================================================================== */
@media (max-width: 1150px) {
  .main-nav {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    background: #fff;
    border-bottom: 1px solid var(--color-border);
    box-shadow: var(--shadow-md);
    max-height: calc(100vh - 60px);
    overflow-y: auto;
  }
  .main-nav.is-open { display: block; }
  .main-nav > ul { flex-direction: column; align-items: stretch; gap: 0; }
  .main-nav > ul > li { width: 100%; }
  .main-nav > ul > li > a { display: block; padding: 14px var(--space-5); border-bottom: 1px solid var(--color-border); }
  .dropdown {
    position: static;
    display: none;
    border: none;
    box-shadow: none;
    padding: 0 0 0 var(--space-5);
    background: var(--color-surface);
  }
  .has-dropdown.is-open .dropdown { display: block; }   /* single column on mobile */
  .nav-toggle { display: block; flex-shrink: 0; }
  .header-actions { gap: var(--space-3); }
  .header-phone { flex-shrink: 0; }
  .header-phone span.phone-text { display: none; }
  /* Touch targets: with the number hidden, the phone link collapses to a ~17px
     icon, and the burger is only ~18px tall. Both need a real tap area. */
  .header-phone {
    min-width: 44px;
    min-height: 44px;
    justify-content: center;
    font-size: 20px;
  }
  .nav-toggle {
    min-width: 44px;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
  }.why-grid { grid-template-columns: repeat(2, 1fr); }
  .testimonial-track > .testimonial-card { flex-basis: calc((100% - var(--space-5)) / 2); }  /* 2 per view */
  
  .gallery-grid { grid-template-columns: repeat(2, 1fr); }
  .stats-grid { grid-template-columns: repeat(2, 1fr); }
  .area-pin-grid { grid-template-columns: repeat(2, 1fr); gap: 0 var(--space-5); }
  .schedule-grid { grid-template-columns: 1fr; gap: var(--space-6); }
  .services-overview-grid { grid-template-columns: 1fr; gap: var(--space-6); }
  .equip-grid { grid-template-columns: 1fr; gap: var(--space-5); }
  .who-grid { grid-template-columns: 1fr; gap: var(--space-6); }
  .split-grid, .split-grid.is-wide-right { grid-template-columns: 1fr; gap: var(--space-6); }
  .footer-main { grid-template-columns: 1fr 1fr; }
  .floating-call { display: flex; }
}

@media (max-width: 640px) {.grid-2, .grid-3, .grid-4, .grid-6, .why-grid, .gallery-grid, .stats-grid, .area-pin-grid {
    grid-template-columns: 1fr;
  }
  .testimonial-track > .testimonial-card { flex-basis: 100%; }  /* 1 per view */
  /* The header CTA competes with the logo, phone icon and menu toggle for very
     little space and pushed the header past the viewport on 381-640px phones
     (e.g. iPhone Plus/Pro Max). The floating call button and the mobile menu's
     own CTA cover the same actions. */
  .header-actions > a.btn-primary { display: none; }
  .site-logo img { max-width: 150px; height: auto; }
  .footer-main { grid-template-columns: 1fr; }
  .footer-services-list { column-count: 1; }
  .form-grid { grid-template-columns: 1fr; }
  .process-step { grid-template-columns: 56px 1fr; }
  .hero-content .lede { font-size: 17px; }
  .container { padding: 0 var(--space-4); }
}

@media (max-width: 380px) {
  .btn { width: 100%; }
  .cta-row { flex-direction: column; align-items: stretch; }
}

/* ---- Responsive ---- */
@media (max-width: 900px) {
  .pillar-grid { grid-template-columns: 1fr; }
  .editorial-split, .editorial-split.is-wide { grid-template-columns: 1fr; }
  .editorial-split.is-reversed .editorial-media { order: 0; }
  .editorial-media img { aspect-ratio: 4/3; }
  .project-grid { grid-template-columns: 1fr; }
  .case-facts { grid-template-columns: repeat(2, 1fr); }
  .partner-list { grid-template-columns: 1fr; }
  .partner-band-scrim { background: linear-gradient(180deg, rgba(26,24,24,0.80) 0%, rgba(26,24,24,0.95) 100%); }
}

@media (max-width: 640px) {
  .value-item { grid-template-columns: 1fr; gap: var(--space-3); }
  .value-num { padding-top: 0; }
  .case-facts { grid-template-columns: 1fr; }
  .btn { padding: 17px 28px; }
  .audience-strip .container { gap: var(--space-2) var(--space-4); }
}

/* En mobile el video nunca carga (script.js no le inyecta <source>), así que
   los dots no controlan nada visible salvo el cambio de poster — se mantienen,
   pero el botón de sonido se apila full-width con el resto de los CTAs. */
@media (max-width: 640px) {
  .lux-hero .hero-play { width: 100%; justify-content: center; }
}

/* En el nav colapsado los links vuelven a tamaño táctil y se alinean a la
   izquierda: acá el nowrap ya no aplica porque cada uno ocupa su propia fila. */
@media (max-width: 1150px) {
  .main-nav > ul > li { display: block; }
  .main-nav > ul > li > a {
    display: block;
    font-size: 15px;
    line-height: 1.4;
    white-space: normal;
  }
  .has-dropdown > a::after { float: right; top: 6px; }
}

/* prefers-reduced-motion es una preferencia del sistema operativo por
   accesibilidad (trastornos vestibulares), no el hover — se respeta: la tira
   queda quieta y centrada, y el grupo duplicado se oculta. */
@media (prefers-reduced-motion: reduce) {
  .audience-track { animation: none; display: flex; justify-content: center; width: 100%; }
  .audience-group:nth-child(n+2) { display: none; }
  .audience-group { flex-wrap: wrap; justify-content: center; padding-right: 0; }
}

@media (max-width: 900px) {
  .editorial-split { align-items: start; }
  .editorial-media img { aspect-ratio: 4/3; height: auto; min-height: 0; }
}

/* El transform se cancela bajo reduced-motion, pero el cambio de color y el
   fade se conservan: el feedback no depende del movimiento. */
@media (prefers-reduced-motion: reduce) {
  .value-item.reveal-pending { transform: none; transition-delay: 0ms; }
  .value-item:hover h3 { transform: none; }
}

@media (max-width: 640px) {
  .value-item { grid-template-columns: 1fr; gap: var(--space-2); padding-left: var(--space-4); }
  .value-num { padding-top: 0; font-size: 1.75rem; }
}

/* Colapsado: el mega vuelve a una sola columna apilada. */
@media (max-width: 1150px) {
  .dropdown-mega { min-width: 0; left: auto; transform: none; padding: 0 0 0 var(--space-5); }
  .has-dropdown.is-open .dropdown-mega { display: block; }
  .has-dropdown:hover .dropdown-mega,
  .has-dropdown:focus-within .dropdown-mega { display: none; }
  .has-dropdown.is-open .dropdown-mega { display: block; }
  .dropdown-heading { padding-top: var(--space-4); border-bottom: none; }
}

/* ---- Footer: la lista de servicios paso de 6 a 13 items ----
   En una sola columna quedaba una tira larguisima. Dos columnas la vuelven
   a una altura razonable sin achicar el texto. */
@media (min-width: 641px) { .footer-services-list { column-count: 2; column-gap: var(--space-6); } }

@media (max-width: 1150px) {
  .dropdown-mega { min-width: 0; padding: 0 0 0 var(--space-5); }
  .dropdown-mega .dropdown-col a { white-space: normal; font-size: 15px; padding: 12px var(--space-5); }
}

/* En pantallas chicas el boton se queda circular: la pastilla con el numero
   compite con el pulgar y con el CTA del formulario. */
@media (max-width: 640px) {
  .floating-call { bottom: 16px; right: 16px; height: 54px; padding: 0 16px; }
  .floating-call:hover { gap: 0; }
  .floating-call:hover .floating-call-text { max-width: 0; opacity: 0; }
}

/* Sin movimiento: se conservan el cambio de color y la aparicion del numero,
   se descartan el latido y el balanceo. */
@media (prefers-reduced-motion: reduce) {
  .floating-call-ring { animation: none; opacity: 0; }
  .floating-call:hover .floating-call-icon { animation: none; }
}

@media (max-width: 640px) {
  .floating-call { min-width: 54px; height: 54px; }
}

@media (max-width: 640px) {
  .floating-call { width: 54px; }
  .floating-call .floating-call-icon { width: 54px; height: 54px; }
  .floating-call:hover, .floating-call:focus-visible { width: 54px; }
  .floating-call:hover .floating-call-text { opacity: 0; }
}

@media (max-width: 860px) {
  .area-pin-grid { gap: 0 var(--space-5); }
  .area-pin-grid > * { flex: 1 1 40%; }
}

@media (max-width: 480px) {
  .area-pin-grid > * { flex: 1 1 100%; }
}

@media (max-width: 900px) {
  .partner-band-scrim {
    background: linear-gradient(180deg, rgba(26,24,24,0.82) 0%, rgba(26,24,24,0.92) 100%);
  }
}

/* En mobile el texto ocupa todo el ancho, asi que el eje horizontal deja de
   tener sentido y vuelve a mandar el vertical, mas cerrado. */
@media (max-width: 780px) {
  .lux-hero-scrim {
    background: linear-gradient(180deg,
      rgba(26,24,24,0.55) 0%,
      rgba(26,24,24,0.45) 25%,
      rgba(26,24,24,0.78) 60%,
      rgba(26,24,24,0.88) 100%);
  }
}

/* Esta regla existia mas arriba pero era codigo muerto: estaba antes de la de
   escritorio y con la misma especificidad, asi que perdia y el telefono usaba
   el velo de dos ejes. Va aca, ya despues, para que valga. En mobile el texto
   ocupa todo el ancho y el eje horizontal deja de tener sentido: manda el
   vertical. Tambien al 80%. */
@media (max-width: 780px) {
  .lux-hero-scrim {
    background: linear-gradient(180deg,
      rgba(26,24,24,0.44) 0%,
      rgba(26,24,24,0.36) 25%,
      rgba(26,24,24,0.62) 60%,
      rgba(26,24,24,0.70) 100%);
  }
}

@media (max-width: 900px) {
  .split-grid > .editorial-media img { height: auto; min-height: 0; aspect-ratio: 4/3; }
}

@media (max-width: 780px) {
  .hero-compact .hero-overlay {
    background: linear-gradient(180deg, rgba(26,24,24,0.72) 0%, rgba(26,24,24,0.88) 100%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .card.reveal-pending .checklist li { opacity: 1; transform: none; }
  .card .checklist li:hover { transform: none; }
}

@media (max-width: 900px) {
  /* La primera linea es la importante y falto durante 13 paginas de servicio.
     El selector de arriba lleva dos `:has()`, o sea especificidad (0,3,0); la
     regla generica de movil es `.split-grid` a secas, (0,1,0). Las media
     queries no suman especificidad, asi que la generica nunca gano y la grilla
     se quedaba en `1fr 1fr` en el telefono. Como ademas los tres hijos se
     mandan a `grid-column: 1`, todo se apilaba dentro de una columna de 156px
     con la mitad derecha vacia: los parrafos quedaban a 18 caracteres por
     linea. Hay que repetir el selector completo para poder ganarle. */
  .split-grid:has(> .editorial-media):has(> .card) { grid-template-columns: 1fr; }
  .split-grid > .editorial-media { position: static; grid-column: 1; grid-row: auto; }
  .split-grid > .card { grid-column: 1; }
  .split-grid > .editorial-media img { min-height: 0; }
}

@media (max-width: 1150px) {
  .dropdown-all { display: block; padding: 14px var(--space-5); border-top: none; }
}

@media (max-width: 900px) {
  .team-plate > img:not(.team-plate-logo) { padding-top: var(--space-8); min-height: 0; }
  /* Los nombres pasan de absolutos a estaticos, asi que entran al flujo del
     flex. La placa es una fila, y sin cambiarla los nombres se paraban AL LADO
     de la foto: 131px + 705px contra un contenedor de 705px, o sea 92px de
     desborde horizontal en toda la pagina. En columna se apilan, que es lo que
     la regla queria. El `order` los manda debajo de la foto, porque en el HTML
     vienen antes (ahi arriba tienen que estar para superponerse en escritorio). */
  .team-plate { flex-direction: column; padding-bottom: 0; }
  .team-plate-names { position: static; order: 2; margin: var(--space-4) 0 var(--space-5); }
}

@media (max-width: 900px) {
  /* El logo vive dentro de `.editorial-media`, asi que le caia el
     `aspect-ratio: 4/3` + `object-fit: cover` pensado para las fotos: un logo
     de 900x221 (4.07:1) metido en una caja 4:3 sale recortado por los lados,
     y se leia "ECIS / DIO & VI". En escritorio no pasaba porque otra regla
     devuelve `aspect-ratio: auto`; aca hay que repetirlo. */
  .team-plate img.team-plate-logo {
    width: 132px;
    height: auto;
    aspect-ratio: auto;
    object-fit: contain;
  }
  .team-plate > img:not(.team-plate-logo) { padding-bottom: var(--space-4); }
}

@media (prefers-reduced-motion: reduce) {
  .pillar:hover .pillar-media img { transform: none; }
  .pillar:hover h3 { transform: none; }
  .pillar-media::after, .pillar-media::before { transition: opacity 200ms linear; transform: none; }
}

@media (max-width: 640px) {
  .brand-strip span {
    flex: 1 1 50%;
    justify-content: center;
    padding: var(--space-5) var(--space-3);
    font-size: 1rem;
  }
  .brand-strip span:nth-child(2n) { border-right: none; }
  .brand-strip span:nth-child(-n+2) { border-bottom: 1px solid var(--color-hairline); }
}

@media (max-width: 640px) {
  .brand-strip img[alt="Control4"] { height: 48px; }
  .brand-strip img[alt="Lutron"]   { height: 94px; }
  .brand-strip img[alt="Sonos"]    { height: 51px; }
  .brand-strip img[alt="Nest"]     { height: 19px; }
  .brand-strip span { min-height: 78px; }
}

/* Mobile: el apilado sticky no aporta en pantalla chica —las tarjetas ocupan
   todo el alto y no se ve la pila— y complica el scroll. Vuelve a lista. */
@media (max-width: 780px) {
  .pillar-stack .pillar {
    position: static;
    grid-template-columns: 1fr;
    margin-bottom: var(--space-7);
    padding: 0;
    border: none;
    background: none;
  }
  .pillar-stack .pillar-media { margin-bottom: var(--space-5); }
}

/* Sin movimiento: se desactiva el apilado, que depende del scroll. */
@media (prefers-reduced-motion: reduce) {
  .pillar-stack .pillar { position: static; margin-bottom: var(--space-7); }
}

@media (min-width: 781px) {
  .pillar-stack .pillar { max-height: calc(100vh - var(--stack-top) - 3rem); }
  .pillar-stack .pillar-media { height: 100%; }
  .pillar-stack .pillar-media img { height: 100%; }
}

@media (max-width: 780px) {
  .pillar-stack .pillar { overflow: visible; }
  .pillar-stack .pillar-media { grid-column: 1; grid-row: auto; height: auto; aspect-ratio: 4/3; }
  .pillar-stack .pillar-index,
  .pillar-stack .pillar > h3,
  .pillar-stack .pillar > p { grid-column: 1; padding-inline: 0; }
  .pillar-stack .pillar-index { padding-top: var(--space-5); }
  .pillar-stack .pillar-sub { padding-bottom: 0; }
}

@media (max-width: 780px) {
  .pillar-stack .pillar { display: block; padding: 0; min-height: 0; }
  /* `relative`, no `static`: los overlays de la foto ("View the Service" y el
     degradado) son pseudo-elementos absolutos que se anclan al primer ancestro
     posicionado. Con `static` se anclaban a cualquier otra cosa y el rotulo
     aparecia flotando encima del texto de la tarjeta. `relative` lo saca del
     apilado sticky igual, pero le deja el punto de anclaje. */
  .pillar-stack .pillar-media { position: relative; width: 100%; aspect-ratio: 4/3; margin-bottom: var(--space-5); }
}

@media (max-width: 780px) {
  .pillar-stack .pillar { position: static; }
}

@media (prefers-reduced-motion: reduce) {
  .pillar-stack .pillar { position: static; }
}

@media (max-width: 780px) { .pillar-stack { padding-bottom: 0; } }

@media (prefers-reduced-motion: reduce) { .pillar-stack { padding-bottom: 0; } }

/* En mobile la grilla ya se apila en una columna: ahi el panel arriba y los
   pasos abajo, sin sticky (no hay lugar al costado por donde pasen). */
@media (max-width: 860px) {
  .process-stepper-visual { position: static; height: auto; min-height: 320px; }
}

@media (prefers-reduced-motion: reduce) {
  .process-stepper-visual { position: static; }
}

@media (max-width: 860px) { .process-stepper-visual { height: auto; } }

@media (max-width: 900px) {
  .contact-strip { grid-template-columns: 1fr 1fr; }
}

@media (max-width: 520px) {
  .contact-strip { grid-template-columns: 1fr; gap: var(--space-5); }
}

@media (max-width: 900px) {
  .area-list-grid,
  .area-list-grid.is-dense { grid-template-columns: repeat(2, 1fr); }
  /* Con cantidad impar el ultimo queda solo igual. Que ocupe el ancho completo
     lo hace leer como decision y no como fila rota. La regla no depende de la
     cantidad: si algun dia son 12, el ultimo es par y no se estira. */
  .area-list-grid > :nth-child(odd):last-child { grid-column: 1 / -1; }
}

@media (max-width: 560px) {
  .area-list-grid,
  .area-list-grid.is-dense { grid-template-columns: 1fr; }
}

/* En telefonos el rotulo sube a 12px. Es un solo token, asi que alcanza a las
   etiquetas, los metadatos y el texto de los botones de una sola vez. */
@media (max-width: 640px) {
  :root { --text-label-size: 0.75rem; }
}

/* El rotulo "View the Service" y su degradado son una pista de hover: aparecen
   al pasar el cursor sobre la foto del pilar. En un dispositivo tactil no hay
   cursor, asi que no tienen forma de activarse a proposito —solo por accidente,
   al tocar— y ahi se leen como un elemento suelto encima de la imagen. */
@media (hover: none) {
  .pillar-media::before,
  .pillar-media::after { display: none; }
}

/* El boton flotante de llamada no se muestra en /contact. Ahi no agrega nada
   —el telefono ya esta en la franja de datos y el formulario esta a la vista—
   y en telefono se paraba encima de los campos, tapando el borde derecho de
   cada input. Es una linea: si se lo quiere de vuelta, se borra esta regla. */
.page-contact .floating-call { display: none; }

/* ---- Colision entre `.container` y sus modificadores ----
   Varios bloques llevan `class="container algo"`, y `.container` define su
   padding con el atajo `padding: 0 X`. Ese atajo tambien pone el eje vertical
   en 0. Mientras la regla del modificador viniera despues, ganaba; al juntar
   las media queries al final, la version mobile de `.container` paso a ser la
   ultima y se llevo puesto el aire vertical del pie —el logo quedaba pegado al
   borde superior.

   Se separa por eje y se sube la especificidad: `padding-block` no toca el
   horizontal, asi que `.container` sigue mandando ahi (incluida su version
   reducida en mobile), y el (0,2,0) gana sin depender del orden. */
.site-footer .footer-main {
  padding-block: var(--space-8) var(--space-7);
}
/* Mismo caso: la barra de copyright declara `padding: var(--space-4) 0` y
   tambien se lo comia el atajo de `.container`. */
.site-footer .footer-bottom {
  padding-block: var(--space-4);
}

/* ---- Costura blanca entre la franja promocional y el header (iOS Safari) ----
   En iPhone aparecia una linea blanca justo debajo de la franja negra. En
   Chrome la geometria da exacta —el header se solapa 2px sobre la franja— asi
   que es un artefacto de compositing de Safari: la marquesina animada empuja a
   la franja a su propia capa de GPU y el redondeo de esa capa puede dejar un
   hueco por el que se ve el fondo del documento, que es blanco.

   En vez de perseguir la causa en un navegador que no puedo inspeccionar, se
   quita el blanco de la ecuacion: la franja extiende su propio color unos
   pixeles hacia abajo, por detras del header. Si hay hueco, se ve negro sobre
   negro y no se nota.

   No tiene efecto en el resto: el header (z-index 100) tapa esta banda cuando
   es opaco, y al scrollear la franja se va hacia arriba y se la lleva con
   ella. `pointer-events: none` para que no intercepte clics del header. */
.promo-banner::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 4px;
  background: var(--color-surface-dark);
  pointer-events: none;
}

/* ---- Segundo intento con la franja blanca de iOS ----
   El ::after de la franja no alcanzo, asi que el blanco no sale de un hueco
   entre elementos sino del lienzo del documento: iOS Safari pinta el area de
   la barra de estado, y cualquier rebote de scroll, con el fondo de <html>.
   Como <html> no tenia fondo declarado, Safari cae en blanco.

   Se le da el mismo negro de la franja. Es seguro: <body> ya declara su propio
   blanco, asi que sigue pintando todo el contenido igual; este color solo
   asoma donde el body no llega —la barra de estado arriba y el rebote— y en
   los dos extremos del sitio lo que hay es oscuro (la franja arriba, el pie
   abajo). */
html { background-color: var(--color-surface-dark); }

/* ---- Puntos del slider contra el boton flotante (telefono) ----
   `.lux-hero` es un flex en fila: el bloque de texto ocupa el ancho y los
   puntos, como ultimo hijo, caian a la derecha del todo. Ahi se montaban con
   el boton de llamada —medido: se solapan 97px en horizontal y 21 en
   vertical— y ademas el contenedor terminaba en 425px sobre una pantalla de
   413, o sea que el ultimo punto quedaba fuera.

   En telefono pasan a la esquina inferior IZQUIERDA, alineados con el borde
   del texto y de los botones. El boton flotante vive en la derecha, asi que
   dejan de cruzarse. */
@media (max-width: 780px) {
  .lux-hero .hero-dots {
    position: absolute;
    left: var(--space-4);
    right: auto;
    bottom: var(--space-4);
    top: auto;
    margin: 0;
    transform: none;
    flex-direction: row;
    justify-content: flex-start;
  }
}

/* ---- El icono del telefono salia corrido dentro del circulo ----
   En telefono el boton colapsa a circulo de 54px, pero conservaba el
   `padding: 0 16px` que necesita cuando es pastilla con el numero al lado. El
   icono tambien mide 54px, asi que adentro de una caja de 54 habia 54+32 = 86px
   de contenido: con `justify-content: flex-start` el icono arrancaba despues
   del padding y terminaba 16px pasado el borde derecho. Como el boton lleva
   `overflow: hidden` y borde redondeado, el auricular quedaba corrido y
   recortado contra el filo del circulo.

   Sin padding y centrado, el icono de 54 coincide exacto con el boton de 54. */
@media (max-width: 640px) {
  .floating-call {
    padding: 0;
    justify-content: center;
  }
  /* Y el numero se saca del flujo. Estaba en opacity 0 pero conservaba su
     ancho completo, asi que el contenido del boton era icono + numero dentro
     de una caja de 54px: con `flex-start` empujaba el icono 16px a la derecha
     y con `center` lo mandaba 65px a la izquierda. `display: none` lo saca de
     la ecuacion y el icono de 54 coincide exacto con el boton de 54. El enlace
     ya tiene aria-label con el numero, asi que no se pierde accesibilidad. */
  .floating-call .floating-call-text { display: none; }
}
