/* =========================================================================
   Casa Lavalle — styles.css
   Authored mobile-first. Base = 0–767px (no media query).
   Layer up with min-width only:  md = 768px,  xl = 1280px.
   ========================================================================= */

/* ---------- Fonts ----------
   Newsreader e Inter Tight viven en `fonts/` (2026-07-20). Antes se pedían a
   Google Fonts: si la red del cliente bloquea fonts.gstatic.com, o se presenta
   sin internet, la página caía a Georgia/Arial. Autohospedadas cargan siempre y
   desde el mismo origen. Son los archivos variables de Google (Inter Tight wght
   100–900; Newsreader opsz 6–72 + wght 200–800, roman e itálica), partidos por
   subset con `unicode-range`: el navegador solo descarga `latin` para el
   español; `latin-ext` queda de respaldo y no se pide nunca.
   Al actualizarlas, regenerar los seis .woff2 juntos.

   La sans es Inter Tight y no Inter (cliente 2026-07-21). Hasta el 2026-07-20 el
   sitio se veía en Mac con Helvetica Neue Light, porque encabezaba la pila; al
   poner Inter al frente para arreglar el Arial de Windows, el menú pasó a Inter
   Light y el cliente lo leyó como más pesado y menos elegante. Tenía razón, y es
   medible: el mismo texto a 100px mide 1046px en Helvetica Neue 300 contra
   1182px en Inter 300, un 13% más ancho. Helvetica Neue no se puede
   autohospedar (licencia Monotype, viene con macOS pero no cubre webfont), así
   que se buscó la libre más cercana en proporción y color: Inter Tight, que es
   la misma familia estrechada. Se comparó contra Inter 200, Hanken Grotesk 300 y
   Geist 300; Inter Tight fue la que más se acercó a la referencia. */
@font-face {
  font-family: "Inter Tight";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-tight-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Inter Tight";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("fonts/inter-tight-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("fonts/newsreader-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 200 800;
  font-display: swap;
  src: url("fonts/newsreader-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 200 800;
  font-display: swap;
  src: url("fonts/newsreader-italic-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Newsreader";
  font-style: italic;
  font-weight: 200 800;
  font-display: swap;
  src: url("fonts/newsreader-italic-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* Cronde (el logotipo) NO se declara aquí a propósito. Es la cara del logo y el
   brief prohíbe usarla en headlines, botones o copy, así que ninguna regla la
   invocaba: el @font-face solo servía para subir un .ttf de 54KB al servidor
   que ningún navegador llegaba a pedir. El logotipo es SVG con los trazos ya
   convertidos, no necesita la fuente. El archivo sigue en Casa Lavalle Brand
   Assets/ como activo de marca, fuera del paquete de deploy. */

/* ---------- Design tokens ---------- */
:root {
  /* Palette */
  --azul-lavalle: #09394C;   /* weight bg: Hero, CTA, Footer; solid-button fill (white text) */
  --marfil:       #F7F4EF;   /* dominant bg for the rest of the site */
  --carbon:       #2C2C2C;   /* primary text on Marfil */
  --gris-piedra:  #A7A19A;   /* secondary text, hairlines */
  --bronce-anzures: #9C7A45; /* accent only: eyebrows, hover, live details. Never a button fill. */

  /* Typography
     - Editorial (Newsreader 300 Light): los TITULARES, y nada más que ellos
       (cliente 2026-07-21: los párrafos salieron de la serif y pasaron a la
       sans). Sobreviven dos usos fuera de los titulares, ambos a propósito:
       los links del panel del menú (excepción del 2026-07-21, son el texto más
       grande del sitio) y los acentos en <em> dentro de los párrafos, porque
       de Inter Tight solo se autohospeda la normal.
     - Sans (Inter Tight): párrafos, nav, botones, UI, card titles, labels.
     - Peso por defecto: delgado/light en toda línea. Bold solo para enfatizar
       una idea dentro de un párrafo, nunca un bloque completo.
     - Cronde: solo el logotipo (ver @font-face arriba). */
  --font-editorial: "Newsreader", Georgia, serif;
  /* La autohospedada PRIMERO, a propósito (2026-07-20). Antes iba después de
     Helvetica y en Windows el menú salía en Arial: Windows no trae ninguna
     Helvetica, pero sustituye "Helvetica" por Arial en su tabla del sistema, así
     que ese match ganaba y a la webfont nunca le llegaba el turno. Con ella al
     frente el sitio se ve igual en Mac, Windows, Android e iOS, que es lo que
     importa para una presentación. Las Helvetica quedan detrás solo como red de
     seguridad, y "Segoe UI" va antes que ellas porque es la única de la cadena
     que en Windows existe de verdad y tiene peso ligero: si la webfont llegara a
     fallar ahí, la caída es a Segoe UI Light y no al Arial 400 que se veía
     pesado. */
  --font-sans: "Inter Tight", "Helvetica Neue", "Segoe UI", Helvetica, Arial, sans-serif;

  /* Layout */
  --nav-h: 64px;
  --page-max: 1280px;
  --gutter: 20px;

  /* Ritmo vertical: padding uniforme por sección del cuerpo. El aire entre dos
     secciones = suma de ambos paddings alrededor de la hairline divisoria, de
     modo que cada separación queda simétrica.
     Apretado a propósito (cliente 2026-07-21, segunda pasada): el ritmo anterior
     (56–96px por lado, o sea 112–192px entre secciones) se leía como huecos, no
     como aire. Compacto lee más ordenado y más premium: el contenido manda y las
     hairlines siguen marcando la separación sin necesitar tanto vacío.
     Tercera pasada (2026-07-22): de 34–56 a 30–46, o sea 92px entre secciones en
     desktop en vez de 112. Va junto con quitar la hairline duplicada de Galería
     (ver .servicios__item:last-child): con una sola línea por frontera, el aire
     que la rodea es lo único que marca la separación y a 56 por lado volvía a
     leerse como hueco. */
  --section-py: clamp(30px, 3.4vw, 46px);
  /* Hairline divisoria entre secciones (tono gris-piedra muy tenue). */
  --hairline: rgba(167, 161, 154, 0.32);

  /* Motion (used site-wide; the Hero door is the one orchestrated exception) */
  --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
  --dur-reveal: 0.6s;
}

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

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

/* Lenis manages scroll; keep native as the fallback baseline. */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

/* El lienzo va en azul, no en marfil. En iOS, el área que queda por debajo del
   documento —la franja bajo la barra flotante de Safari y el rebote del
   overscroll— se pinta con el fondo del lienzo, y en marfil aparecía una tira
   clara justo debajo del footer azul: se leía como si el footer no llegara al
   borde. Al declararlo en <html>, el fondo del body deja de propagarse al
   lienzo y esa franja toma el color del footer. */
html { background-color: var(--azul-lavalle); }

/* Footer pegado al fondo: /proyectos con todos los desplegables cerrados no
   llena la pantalla, y el footer azul quedaba a media altura con una franja de
   marfil colgando debajo (se leía como si la página estuviera cortada). Con el
   body en columna y .page absorbiendo el sobrante, el footer siempre remata
   abajo. svh en vez de vh para no contar la barra del navegador móvil. */
body {
  margin: 0;
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100svh;
  background: var(--marfil);
  color: var(--carbon);
  font-family: var(--font-sans);
  font-size: 16px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

img, svg { display: block; max-width: 100%; height: auto; }

/* Headlines editoriales: Newsreader 300 Light (nunca Cronde, nunca bold). */
h1, h2, h3 {
  font-family: var(--font-editorial);
  font-weight: 300;
  font-optical-sizing: auto;
  line-height: 1.12;
  margin: 0;
  /* Reparte los titulares en líneas parejas en vez de dejar la última suelta con
     una palabra. Los titulares del sitio son cortos (2–4 líneas), que es
     justamente donde `balance` está pensado para actuar. */
  text-wrap: balance;
}

/* ---------- Corte de línea del cuerpo de texto ----------
   Cliente 2026-07-22: "que frases importantes o líneas importantes no se
   corten". `pretty` es la contraparte de `balance` para párrafos largos: no
   reparte todas las líneas (eso ahogaría un párrafo de seis renglones), pero
   evita la viuda —la última línea con una sola palabra— y penaliza los cortes
   feos al final de renglón. Se degrada solo en navegadores que no lo soportan.
   El resto del trabajo lo hace el `&nbsp;` en el HTML, que amarra las palabras
   de una y dos letras a la que les sigue (regla de composición en español: ni
   "e", ni "y", ni "en", ni "a la" se quedan colgando a final de línea). */
.casa__body,
.servicios__copy,
.cta__body,
.proyecto__body,
.relato__body,
.semblanza__body {
  text-wrap: pretty;
}

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

/* ---------- Accessibility ---------- */
/* Visible keyboard focus on every interactive element. */
:where(a, button, input, textarea, select, [tabindex]):focus-visible {
  outline: 2px solid var(--bronce-anzures);
  outline-offset: 3px;
}

/* Minimum 44x44 touch target for interactive controls on mobile. */
.nav__logo { display: inline-flex; min-height: 44px; align-items: center; }

/* ---------- Layout helpers ---------- */
.page { width: 100%; flex: 1 0 auto; }   /* empuja al footer contra el fondo */

/* Hairline divisoria entre secciones del cuerpo. Dentro del margen (no toca los
   bordes de la pantalla): se alinea al gutter, como en Maison Celeste. El aire
   a cada lado lo aportan los paddings de las secciones vecinas (--section-py). */
.rule {
  height: 1px;
  margin-inline: var(--gutter);
  background: var(--hairline);
}

/* ---------- Page Cover (Preloader) ---------- */
.page-cover {
  position: fixed;
  inset: 0;
  z-index: 200; /* above nav (100) */
  pointer-events: none;
  display: grid;
  grid-template-rows: 1fr 1fr;
}

.page-cover__row {
  background-color: var(--azul-lavalle);
  will-change: transform;
}

.page-cover__row--top { transform-origin: top; }
.page-cover__row--bottom { transform-origin: bottom; }

.page-cover__logo {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(60px, 11vw, 104px);
  height: auto;
  opacity: 0; /* animated by JS */
}

/* Oculta la capa una vez que termina la revelación */
.page-cover.is-done {
  display: none;
}

/* ---------- Navbar ---------- */
/* Bar: logo (left) · menu trigger (center) · CTA (right). Transparent over the
   Hero; solid marfil when scrolled or when the menu is open (with a light/dark
   logo + text swap so it stays legible on both). */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 120;                       /* bar above panel + scrim */
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: var(--nav-h);
  padding: 0 var(--gutter);
  background: transparent;
  transition: background var(--dur-reveal) var(--ease-out),
              box-shadow var(--dur-reveal) var(--ease-out);
}
.nav.is-scrolled {
  background: var(--marfil);
  box-shadow: 0 1px 0 rgba(44, 44, 44, 0.06), 0 8px 24px rgba(9, 57, 76, 0.06);
}
.nav.is-open { background: var(--marfil); }

/* Logo (left) with light/dark crossfade */
.nav__logo { position: relative; }
.nav__logo-img { height: 28px; width: auto; transition: opacity 0.3s var(--ease-out); }
.nav__logo-img--dark { position: absolute; left: 0; top: 50%; transform: translateY(-50%); opacity: 0; }
.nav.is-scrolled .nav__logo-img--light,
.nav.is-open .nav__logo-img--light { opacity: 0; }
.nav.is-scrolled .nav__logo-img--dark,
.nav.is-open .nav__logo-img--dark { opacity: 1; }

/* nav--iso (solo la landing): el estado transparente sobre el Hero muestra el
   isotipo. El logotipo azul del estado sólido sigue absoluto, así que quien
   define el ancho de la caja es la imagen en flujo — y el isotipo es cuadrado,
   ~97px más angosto que el logotipo. Sin reservar ese ancho, el logotipo se
   desbordaría al hacer scroll y en móvil chocaría con la hamburguesa. El ancho
   reservado es exactamente el del logotipo (viewBox 2115x616 → 3.434 × alto),
   así que la barra mide igual que antes de este cambio en los dos estados. */
.nav--iso .nav__logo { min-width: 97px; }

/* Trigger. En móvil la barra son dos piezas — logotipo y hamburguesa — así que
   el botón viaja al filo derecho con el `space-between` de la barra (cliente
   2026-07-21: fuera el botón de contacto de la barra). A partir de md, con el
   CTA de vuelta a la derecha, se centra en absoluto para quedar en el medio
   real sin depender de los anchos del logo y del CTA. */
.nav__trigger {
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  min-height: 44px;
  min-width: 44px;
  padding: 0 4px;
  margin-right: -4px;                 /* la hamburguesa cierra al ras del gutter */
  border: 0;
  background: none;
  cursor: pointer;
  color: var(--marfil);
  font-family: var(--font-sans);
  font-size: 0.82rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.nav.is-scrolled .nav__trigger,
.nav.is-open .nav__trigger { color: var(--carbon); }
/* Label hidden on mobile (burger only, aria-label carries the name); shown at md. */
.nav__trigger-label { display: none; min-width: 6ch; text-align: left; }

.nav__burger { position: relative; width: 22px; height: 12px; flex: none; }
.nav__burger span {
  position: absolute; left: 0; width: 100%; height: 1.5px;
  background: currentColor;
  transition: transform 0.28s var(--ease-out), opacity 0.18s var(--ease-out);
}
.nav__burger span:nth-child(1) { top: 0; }
.nav__burger span:nth-child(2) { top: 50%; transform: translateY(-50%); }
.nav__burger span:nth-child(3) { bottom: 0; }
.nav.is-open .nav__burger span:nth-child(1) { top: 50%; transform: translateY(-50%) rotate(45deg); }
.nav.is-open .nav__burger span:nth-child(2) { opacity: 0; }
.nav.is-open .nav__burger span:nth-child(3) { bottom: auto; top: 50%; transform: translateY(-50%) rotate(-45deg); }

/* CTA (right). Fuera de la barra en móvil (cliente 2026-07-21: "quitemos el
   botón de cotizar, dejemos el menú de hamburguesa"): ocupaba un tercio del
   ancho y aplastaba el logotipo. Contacto sigue a un toque de distancia — es el
   último enlace del panel, en bronce. Vuelve a la barra a partir de md, donde
   sí hay sitio. */
/* El selector lleva `.nav` a propósito: .nav__cta también es .btn, que se
   declara más abajo y con la misma especificidad ganaría por orden de fuente
   devolviéndole el `display: inline-flex`. */
.nav .nav__cta {
  display: none;
}
.nav__cta {
  min-height: 40px;
  padding: 0 16px;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
}
.nav__cta-long { display: none; }
.nav__cta-short { display: inline; }

/* Scrim over the page while open */
.nav__scrim {
  position: fixed;
  inset: 0;
  z-index: 100;
  background: rgba(9, 57, 76, 0.58);
  opacity: 0;
  visibility: hidden;
  will-change: opacity;
}

/* Roll-down panel */
.nav__panel {
  position: fixed;
  top: var(--nav-h);
  left: 0;
  right: 0;
  z-index: 110;
  background: var(--marfil);
  color: var(--carbon);
  padding: clamp(28px, 4vw, 56px) var(--gutter) clamp(20px, 3vw, 32px);
  clip-path: inset(0% 0% 100% 0%);    /* closed: rolled up */
  will-change: clip-path;
}
.nav__panel-inner {
  max-width: var(--page-max);
  margin: 0 auto;
  display: grid;
  grid-template-columns: 1fr;
  gap: 24px;
}
/* Móvil: columna centrada (cliente 2026-07-21). Cada enlace encoge a su texto
   —`align-items: center`— para que el área táctil no se extienda a lo ancho de
   la pantalla y el eje del menú coincida con el del resto de la página, que
   también va centrada. Vuelve al eje izquierdo a partir de md. */
.nav__links { display: flex; flex-direction: column; align-items: center; text-align: center; }
.nav__link {
  display: block;
  /* Newsreader, no la sans (cliente 2026-07-21). Excepción deliberada a la regla
     "nav = sans" de CLAUDE.md: los enlaces del panel son el texto más grande del
     sitio, y en grotesca eran lo único a esa escala que no hablaba en la voz
     editorial del resto. El cliente lo leyó como que "desentonaba". La regla
     sigue en pie para el resto de la UI: botones, labels, filtros, "MENÚ" y
     "CERRAR" siguen en sans. */
  font-family: var(--font-editorial);
  font-weight: 300;
  font-optical-sizing: auto;
  /* 1.14, no el 1.06 de la grotesca: Newsreader tiene descendentes largas y a
     1.06 la "y" de "Galería y espacios" se acercaba demasiado al renglón de
     abajo. */
  line-height: 1.14;
  /* Sin tracking negativo: el -0.005em compensaba la grotesca, en la serif
     cierra de más. */
  letter-spacing: normal;
  /* Un escalón más grande que en sans: a igual font-size la serif se lee más
     chica (altura de x menor). En móvil el panel es casi toda la pantalla. */
  font-size: clamp(1.9rem, 7.4vw, 2.4rem);
  color: var(--carbon);
  padding: clamp(4px, 0.9vw, 8px) 0;
  min-height: 44px;
  transition: color 0.3s var(--ease-out);
  will-change: transform, filter, opacity;
}
.nav__link:hover { color: var(--bronce-anzures); }
/* The CTA now lives inside the menu, as an accent among the links. */
.nav__link--cta { color: var(--bronce-anzures); }

.nav__panel-foot {
  max-width: var(--page-max);
  margin: clamp(22px, 4vw, 44px) auto 0;
  padding-top: 16px;
  border-top: 1px solid rgba(167, 161, 154, 0.4);
  text-align: center;                 /* mismo eje que los enlaces; izquierda en md */
}
.nav__addr {
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gris-piedra);
}

/* Scroll lock while the menu is open */
html.nav-open, html.nav-open body { overflow: hidden; }

/* ---------- Buttons (site-wide) ---------- */
/* Solid = azul-lavalle fill + marfil (reads as white) text, same as the Hero CTA.
   Bronce is never a button fill. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 26px;
  font-family: var(--font-sans);
  font-size: 0.95rem;
  font-weight: 400;                 /* peso light por defecto; nunca bold */
  letter-spacing: 0.02em;
  border: 1px solid transparent;
  border-radius: 6px;
  cursor: pointer;
  /* Sin `transform` en la transición: el efecto magnético (script.js) anima
     el transform por frame y una transición CSS lo haría sentirse pastoso. */
  transition: background-color var(--dur-reveal) var(--ease-out);
  will-change: transform;
}
.btn--solid { background-color: var(--azul-lavalle); color: var(--marfil); }
.btn--solid:hover { background-color: color-mix(in oklab, var(--azul-lavalle) 88%, white); }
.btn--solid:active { transform: translateY(1px); }

/* Flecha diagonal, SVG y no glifo: ↗ (U+2197) tiene presentación emoji, así
   que iOS lo pintaba a color y rompía el tono editorial. El trazo va en
   currentColor para heredar el color y los hovers de cada contexto, y mide
   en em para escalar con el texto que acompaña. */
.icon-arrow {
  flex: none;
  width: 0.85em;
  height: 0.85em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1;                  /* hairline: mismo peso que las reglas finas */
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* ---------- Hero ---------- */
/* dvh, no svh: `svh` es la altura de la ventana CON las barras de Safari
   desplegadas, así que cuando se retraen el Hero se quedaba corto y asomaba una
   franja de la sección siguiente (marfil) por debajo de la foto. `dvh` sigue a
   la ventana en cada estado y el Hero siempre llena la pantalla. vh queda de
   respaldo para navegadores sin unidades dinámicas. */
.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: calc(var(--nav-h) + 24px) var(--gutter) 96px;
  overflow: hidden;
  isolation: isolate;             /* own stacking context for the media layer */
}

.hero__media { position: absolute; inset: 0; z-index: -1; }
.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  will-change: transform;         /* GSAP settles the scale on load */
}

/* Scrim in black, kept very subtle, so the marfil wordmark stays legible over
   the background video without flattening the footage. */
.hero__scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(120% 78% at 50% 40%, transparent 42%, rgba(0, 0, 0, 0.40) 100%),
    linear-gradient(to bottom,
      rgba(0, 0, 0, 0.38) 0%,
      rgba(0, 0, 0, 0.30) 45%,
      rgba(0, 0, 0, 0.72) 100%);
}

.hero__content {
  position: relative;
  z-index: 1;
  width: 100%;
  max-width: var(--page-max);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(36px, 6vw, 56px);   /* aire entre el logotipo y el CTA, sin abrir un hueco */
  text-align: center;
  color: var(--marfil);
  will-change: transform;
}

.hero__wordmark { margin: 0; }
.hero__line { display: block; overflow: hidden; padding-bottom: 0.05em; }
.hero__line-in { display: block; will-change: transform; }

/* Official stacked brand logo (Logo White 02). Sized by width; the brand
   letterforms are never recreated in a font. */
.hero__logo {
  width: clamp(260px, 60vw, 572px);   /* +30% — the wordmark carries the Hero on its own now */
  height: auto;
  filter: drop-shadow(0 2px 30px rgba(0, 0, 0, 0.35));
}

/* JS-on entrance: hide the animated bits only when JS is active. GSAP owns them
   from initHero (inline styles win); the .is-ready fallback reveals if GSAP is
   absent so content is never permanently hidden. */
.js .hero__line-in { transform: translateY(110%); }
.js.is-ready .hero__line-in { transform: none; }

/* ---------- La Casa (historia) ----------
   Editorial section: text + one photo. Mobile base stacks text then photo;
   md turns it into two columns (text left, tall photo right). Reveals are
   simple opacity/position on scroll (script.js) — the Hero door stays the one
   orchestrated exception. Content is visible by default so it never gets stuck
   hidden if GSAP is unavailable. */
.casa {
  background: var(--marfil);
  color: var(--carbon);
  /* Ritmo vertical uniforme (ver --section-py). Primera sección del cuerpo:
     su padding superior es el aire bajo el Hero. */
  padding: var(--section-py) var(--gutter);
}
/* Sin cap de page-max: La Casa se alinea al gutter igual que Servicios y la
   Galería (mismo borde izquierdo para todo el flujo del cuerpo). El eyebrow
   queda así en el mismo eje que el título "Galería". */
.casa__inner {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: clamp(24px, 5vw, 36px);
}

/* Eje del cuerpo: IZQUIERDA en todos los anchos (cliente 2026-07-21, segunda
   pasada). Se probó centrar todo el cuerpo en móvil para unificarlo con
   /historia, y funciona en titulares cortos pero se rompe en los párrafos
   largos, en las listas y sobre todo en el formulario, donde el texto flota en
   medio de una línea que sí ocupa el ancho completo. La consistencia se
   resuelve al revés: /historia también se alinea a la izquierda en móvil.
   Centrados quedan solo el panel del menú y el footer. */

/* Azul-lavalle nunca en headlines/texto (brief): el titular vive en carbon y
   deja el azul solo para el botón. */
.casa__title {
  color: var(--carbon);
  font-size: clamp(2rem, 7.4vw, 3.9rem);
  line-height: 1.14;
  letter-spacing: -0.01em;
  text-wrap: balance;
}
/* El único acento en itálica de la landing (variación puntual permitida). */
.casa__title em { font-style: italic; font-weight: inherit; }

/* Cuerpo en Inter Tight 300 (cliente 2026-07-21: la serif se queda en los
   titulares, los párrafos pasan a la sans). Baja de 1.08rem a 1rem porque Inter
   Tight tiene bastante más altura de x que Newsreader y al mismo cuerpo se leía
   un punto más grande que el párrafo que sustituye. */
.casa__body {
  margin: clamp(15px, 3vw, 20px) 0 0;
  max-width: 48ch;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.66;
  color: var(--carbon);
}

/* El botón va al mismo eje izquierdo que el texto, en todos los tamaños.
   Historia de esta línea, para no volver a darle vueltas: el 2026-07-21 se
   centró en móvil (se leía "sin terminar" contra el margen); el 2026-07-22 el
   cliente lo devolvió a la izquierda, ya con el botón del formulario alineado
   igual. La versión centrada se probó de verdad y se descartó de verdad. */
.casa__cta { text-align: left; }
.casa__btn { margin-top: clamp(20px, 3.6vw, 26px); }

.casa__media {
  margin: 0;
  overflow: hidden;               /* clips the inner image scale-settle */
  border-radius: 4px;
  background: rgba(9, 57, 76, 0.04);
}
/* El encuadre se sube dentro del marco (cliente 2026-07-22: "se está viendo más
   el techo que el piso"). La foto del hall es vertical (1067x1600) y el recorte
   centrado se comía el piso: en 1/1 cortaba 266px arriba y 266px abajo, y la
   mitad superior se iba en el artesonado. Con 68% la ventana de recorte baja
   ~118px, así que el candil queda cerca del centro óptico y el piso de barro
   entra completo. No se lleva a 100% (bottom) a propósito: ahí desaparece el
   artesonado, que es la firma de la casa. Sirve para los dos recortes, el 3/2 de
   móvil y el 1/1 de md. */
.casa__img {
  width: 100%;
  aspect-ratio: 3 / 2;            /* mobile: full facade in view */
  object-fit: cover;
  object-position: center 68%;
}

/* ---------- Servicios ----------
   Lista editorial vertical: numeración en sans, título y copy en Newsreader
   300, separados por hairlines en gris piedra. El detalle vivo es la línea
   superior de cada fila, que se redibuja en bronce al hover (misma familia de
   gestos que el hover del navbar: sutil, sin salto). */
.servicios {
  background: var(--marfil);
  color: var(--carbon);
  padding: var(--section-py) var(--gutter);
}
/* Frontera Servicios → Galería, apretada a mano (cliente 2026-07-22, segunda
   pasada: "redúcelo lo más posible").
   Es la única frontera del sitio SIN .rule: la cierra el border-bottom del
   último renglón de la lista. Con --section-py por lado quedaban 92px entre esa
   hairline y el rótulo "Galería", y a diferencia de las otras fronteras aquí no
   hay una línea a media altura que justifique tanto aire, así que se leía como
   hueco. 44px en desktop / 28px en móvil: la línea sigue cerrando Servicios y el
   rótulo ya se lee como principio del bloque de fotos.
   NO toca el tamaño ni el recorte de las imágenes: solo el padding de las cajas.
   El padding-top de Servicios se queda en --section-py porque es lo que mantiene
   centrado su rótulo entre las dos hairlines (ver .servicios__head).
   El clamp va 16→22 y no más apretado: se probó 11→17 (34px en desktop) y a
   768px el vw cae por debajo del piso, así que la frontera se cerraba a 22px
   justo donde el título mide 35px y "Galería" quedaba pegada a la línea. Con
   16/2.2vw/22 la curva no se desploma en anchos intermedios: 32px en móvil,
   ~34px a 768 y 44px en desktop. */
.servicios { padding-bottom: clamp(16px, 2.2vw, 22px); }
/* El rótulo va ÓPTICAMENTE CENTRADO entre la hairline de la sección anterior y
   la primera del listado (cliente 2026-07-22: "que no se vea chueco"). Antes el
   aire de arriba era --section-py y el de abajo 18px fijos, así que el título
   colgaba pegado al listado.
   El margen de abajo iguala al padding de arriba a propósito: la caja de línea
   de "Servicios" reparte su medio-interlineado casi por igual (la palabra no
   tiene descendentes, así que la tinta va de ~8.6px bajo el borde superior de la
   caja a la línea base, ~8.2px sobre el inferior). Con esos dos hombros
   prácticamente iguales, aire arriba = aire abajo sale de dar el mismo valor a
   los dos lados. Si se cambia --section-py, esto sigue centrado solo. */
.servicios__head {
  margin: 0 0 var(--section-py);
}
/* Headline editorial (hereda Newsreader 300 del base h2). Misma escala que
   tenía Los Espacios: rótulo de sección, no el mensaje. */
.servicios__title {
  margin: 0;
  font-size: clamp(1.55rem, 4.6vw, 2.5rem);
  letter-spacing: -0.01em;
  color: var(--carbon);
}

.servicios__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Fila: hairline arriba; la última cierra con hairline abajo. El ::after es
   la misma línea en bronce, que se dibuja de izquierda a derecha al hover. */
.servicios__item {
  position: relative;
  padding: clamp(17px, 2.3vw, 24px) 0;
  border-top: 1px solid rgba(167, 161, 154, 0.4); /* --gris-piedra hairline */
}
.servicios__item:last-child {
  border-bottom: 1px solid rgba(167, 161, 154, 0.4);
}
.servicios__item::after {
  content: "";
  position: absolute;
  top: -1px;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--bronce-anzures);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.7s var(--ease-out);
}

/* Numeración: lenguaje UI (sans, tracking abierto), como en el carrusel que
   esta sección reemplaza. */
.servicios__num {
  display: block;
  font-family: var(--font-sans);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  color: var(--gris-piedra);
  transition: color 0.45s var(--ease-out);
}
.servicios__name {
  margin: 7px 0 5px;
  font-size: clamp(1.35rem, 3.8vw, 1.9rem);
  letter-spacing: -0.01em;
}
/* Copy en Inter Tight 300, ancho de lectura contenido. */
.servicios__copy {
  margin: 0;
  max-width: 52ch;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: clamp(0.94rem, 1.4vw, 1.04rem);
  line-height: 1.62;
  color: rgba(44, 44, 44, 0.78); /* --carbon suavizado */
}

/* Hover — pointer devices only, so touch never gets a stuck state. */
@media (hover: hover) {
  .servicios__item:hover::after { transform: scaleX(1); }
  .servicios__item:hover .servicios__num { color: var(--bronce-anzures); }
}

@media (prefers-reduced-motion: reduce) {
  .servicios__item::after { transition: none; }
  .servicios__num { transition: none; }
}

/* ---------- Galería (teaser) ----------
   Mosaico de filas justificadas hacia /galeria: el mismo sistema de esa página
   y de /proyectos (mosaico.css + mosaico.js), para que el teaser y la galería
   completa se lean como una sola pieza. Cada fila cierra al ras en ambos
   márgenes y ninguna foto se recorta. Base móvil: una foto por fila; md+ las
   filas se arman con la regla de script.js. Head alineado al gutter, como
   Servicios. */
.galeria {
  background: var(--marfil);
  color: var(--carbon);
  padding: var(--section-py) var(--gutter);
}
/* La otra mitad de la frontera apretada (ver la nota en .servicios). El
   padding-bottom se queda en --section-py: abajo sí hay una .rule antes del CTA
   y esa frontera el cliente ya la aprobó. */
.galeria { padding-top: clamp(16px, 2.2vw, 22px); }
/* Móvil: título y enlace apilados. En una sola línea a 390px el
   "VER GALERÍA Y ESPACIOS" quedaba pegado al filo derecho. El título se queda
   en el eje izquierdo, como el de Servicios; el enlace se centra por ser una
   acción (ver .casa__cta). Vuelve a la fila título-izquierda / enlace-derecha
   en md. */
/* margin-bottom recortado en la misma pasada: el rótulo tiene que colgar del
   bloque de fotos, no flotar entre la hairline de arriba y el mosaico. */
.galeria__head {
  margin: 0 0 clamp(10px, 1.3vw, 15px);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 6px;
}
.galeria__title {
  margin: 0;
  font-size: clamp(1.55rem, 4.6vw, 2.5rem);
  letter-spacing: -0.01em;
  color: var(--carbon);
}
/* Link editorial discreto (no botón): hover bronce + flecha que avanza. */
.galeria__link {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;           /* mismo eje izquierdo que el texto (2026-07-22) */
  gap: 8px;
  min-height: 44px;
  font-family: var(--font-sans);
  font-size: 0.74rem;
  font-weight: 400;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--carbon);
  transition: color 0.3s var(--ease-out);
}
.galeria__link:hover { color: var(--bronce-anzures); }
.galeria__link-arrow { transition: transform 0.3s var(--ease-out); }
.galeria__link:hover .galeria__link-arrow { transform: translateX(4px); }

/* El grid del teaser es el mosaico compartido (mosaico.css): filas
   justificadas, sin recorte. Aquí solo se anula su cap en --page-max, que en
   /galeria existe por nitidez pero en la landing descuadraría el mosaico
   respecto al head, que se alinea al gutter como La Casa y Servicios. Sin el
   cap las filas del teaser siguen dibujándose por encima de 1x incluso en
   pantallas de 2560px, así que no se pierde nitidez. */
@media (min-width: 768px) {
  .galeria .mosaico__grid {
    max-width: none;
    margin-inline: 0;
  }
}

/* ---------- CTA / Contacto ----------
   Sección clara como el resto del sitio (el azul vive en los botones y en el
   Footer, que es el ancla azul de cierre). Acciones como pills sólidos azul
   (la convención de botón del sitio). El mapa dibujado se eliminó (cliente
   2026-07-20): la sección es el formulario, y la dirección sobrevive como una
   línea fina con enlace a Google Maps. Los [data-magnetic] reciben la física de
   resorte en script.js; aquí solo viven sus estados estáticos y de hover. */
.cta {
  position: relative;
  background: var(--marfil);
  color: var(--carbon);
  padding: var(--section-py) var(--gutter);
  overflow: hidden;                 /* clips the magnetic drift */
}
/* scroll-margin: al llegar por hash nativo (subpágina → index.html#cotiza) el
   ancla quedaría bajo el navbar fijo; esto lo compensa igual que el offset de
   Lenis en scrollTo. */
.cta__anchor { position: absolute; top: 0; left: 0; scroll-margin-top: var(--nav-h); }

/* Base: encabezado y formulario apilan. En md+ se reparten en dos columnas
   (ver la media query): el bloque de texto sostiene la izquierda y el
   formulario ocupa el ancho que antes se llevaba el mapa. */
.cta__inner {
  display: flex;
  flex-direction: column;
  gap: clamp(26px, 4vw, 40px);
}

.cta__title {
  margin: 0;
  max-width: 16ch;
  font-size: clamp(2.1rem, 8vw, 4.4rem);
  line-height: 1.14;
  letter-spacing: -0.01em;
  color: var(--carbon);
  text-wrap: balance;
}
.cta__body {
  margin: clamp(14px, 2.6vw, 20px) 0 0;
  max-width: 44ch;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.66;
  color: var(--carbon);
}

/* Ubicación: línea fina, mismo idioma que "o envíalo sin WhatsApp". Es lo único
   que queda del mapa, y a propósito no pesa. */
.cta__ubicacion { margin: clamp(10px, 1.8vw, 16px) 0 0; }
.cta__ubicacion-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;                 /* touch target */
  font-family: var(--font-sans);
  font-size: 0.74rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--gris-piedra);
  transition: color 0.3s var(--ease-out);
}
.cta__ubicacion-link .icon-arrow { transition: transform 0.3s var(--ease-out); }
.cta__ubicacion-link:hover { color: var(--bronce-anzures); }
.cta__ubicacion-link:hover .icon-arrow { transform: translate(3px, -3px); }

/* ---------- Formulario (editorial) ----------
   Campos sin caja: solo el hairline inferior en gris piedra. Al foco, una
   segunda línea en bronce se dibuja de izquierda a derecha (mismo gesto que
   los divisores de Servicios). Floating label: el label descansa como
   placeholder en Newsreader y sube a etiqueta sans pequeña al foco o cuando
   el campo tiene valor. */
/* El espaciado head→formulario vive en el gap de .cta__inner. */
.cta__form { margin: 0; }

.cta__form-grid {
  display: grid;
  grid-template-columns: 1fr;
  column-gap: clamp(24px, 4vw, 44px);
}

.cta__field {
  position: relative;
  margin: 0 0 clamp(18px, 2.6vw, 24px);
  padding-top: 16px;                /* room for the floated label */
}
.cta__field--full { width: 100%; }
/* Campo que cruza la retícula entera (Nombre). En móvil no cambia nada: la
   retícula es de una sola columna y "1 / -1" ya es el ancho completo; a partir
   de md es lo que evita que la fila de cinco campos cierre con medio hueco. */
.cta__field--wide { grid-column: 1 / -1; }

.cta__input {
  display: block;
  width: 100%;
  padding: 8px 0 10px;
  border: 0;
  border-bottom: 1px solid rgba(167, 161, 154, 0.5);
  border-radius: 0;
  background: transparent;
  /* Campos y labels arrastrados al mismo cambio que los párrafos (2026-07-21):
     con el copy de arriba ya en sans, la serif del formulario quedaba como lo
     único no-titular en Newsreader de la página y se leía como un descuido.
     Además devuelve el formulario a la regla del brief (UI en Inter Tight). */
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 1rem;
  line-height: 1.4;
  color: var(--carbon);
  transition: border-color 0.35s var(--ease-out);
}
.cta__input--area { resize: vertical; min-height: 44px; }
/* El foco vive en la línea bronce (::after del field), no en el outline
   global: un rectángulo alrededor de un campo sin caja leería como error. */
.cta__input:focus-visible { outline: none; }

/* Línea de foco: se dibuja en bronce sobre el hairline. */
.cta__field::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: var(--bronce-anzures);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 0.5s var(--ease-out);
}
.cta__field:focus-within::after { transform: scaleX(1); }

/* Floating label: posición de reposo = donde iría el placeholder. */
.cta__label {
  position: absolute;
  left: 0;
  top: 24px;
  font-family: var(--font-sans);
  font-weight: 300;
  font-size: 1rem;
  color: var(--gris-piedra);
  pointer-events: none;
  transform-origin: left top;
  transition: transform 0.35s var(--ease-out), color 0.35s var(--ease-out),
    letter-spacing 0.35s var(--ease-out), font-size 0.35s var(--ease-out);
}
.cta__field:focus-within .cta__label,
.cta__input:not(:placeholder-shown) + .cta__label {
  transform: translateY(-24px);
  font-family: var(--font-sans);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--bronce-anzures);
}
/* Con el campo lleno pero sin foco, la etiqueta reposa en gris. */
.cta__input:not(:placeholder-shown):not(:focus) + .cta__label {
  color: var(--gris-piedra);
}

/* Error (validación en script.js): el hairline y el label marcan en bronce
   pleno; el aviso es una línea de texto, sin globos nativos. */
.cta__field.is-error .cta__input { border-bottom-color: var(--bronce-anzures); }
.cta__field.is-error .cta__label { color: var(--bronce-anzures); }
.cta__form-error {
  margin: 0 0 18px;
  font-family: var(--font-sans);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  color: var(--bronce-anzures);
}

/* Tipo de evento: chips de radio, hairline como los inputs. Bronce solo como
   acento del seleccionado (nunca fill). */
.cta__tipos {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin: 0 0 clamp(18px, 2.6vw, 24px);
  padding: 0;
  border: 0;
}
.cta__tipos-legend {
  width: 100%;
  padding: 0;
  margin: 0 0 12px;
  font-family: var(--font-sans);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gris-piedra);
}
.cta__chip { position: relative; }
.cta__chip input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
}
.cta__chip span {
  display: inline-flex;
  align-items: center;
  min-height: 44px;                 /* touch target */
  padding: 0 18px;
  border: 1px solid rgba(167, 161, 154, 0.5);
  border-radius: 999px;
  font-family: var(--font-sans);
  font-size: 0.78rem;
  letter-spacing: 0.05em;
  color: var(--carbon);
  cursor: pointer;
  transition: border-color 0.35s var(--ease-out), color 0.35s var(--ease-out);
}
.cta__chip input:checked + span {
  border-color: var(--bronce-anzures);
  color: var(--bronce-anzures);
}
.cta__chip input:focus-visible + span {
  outline: 2px solid var(--bronce-anzures);
  outline-offset: 3px;
}
@media (hover: hover) {
  .cta__chip span:hover { border-color: rgba(167, 161, 154, 0.9); }
}

/* Un solo botón desde 2026-07-22 (antes iba con la alternativa "sin WhatsApp",
   y por un momento con una nota de la pestaña nueva que el cliente quitó).
   Eje izquierdo en TODOS los tamaños, alineado con el arranque de los campos
   (cliente 2026-07-22: centrado en móvil se leía descolocado contra la reja).
   Desde el 2026-07-22 ya no es una excepción: .casa__cta y .galeria__link
   siguen el mismo eje izquierdo en móvil. Las tres acciones de la landing se
   alinean igual. */
.cta__form-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-start;
  gap: clamp(18px, 3.2vw, 30px);
  margin-top: clamp(14px, 2vw, 20px);
}

/* Honeypot anti-bot: fuera de pantalla, invisible y fuera del flujo. No usa
   display:none para que algún bot lo lea y lo llene (que es lo que queremos). */
.cta__hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* Correo visible junto al botón, tratado como un campo más del formulario:
   rótulo en mayúsculas (idéntico a .cta__label / .cta__tipos-legend) + el
   correo como "valor" (carbón, tamaño de input). Así la alternativa pertenece
   al sistema del formulario en vez de leerse como una leyenda pegada aparte. */
.cta__form-alt {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-family: var(--font-sans);
}
.cta__form-alt-label {
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gris-piedra);
}
.cta__form-mail {
  align-self: flex-start;
  font-size: 1rem;
  letter-spacing: 0.01em;
  color: var(--carbon);
  text-decoration: none;
  /* Hairline como los subrayados de los inputs: afordancia de enlace en el
     mismo peso y tono que las reglas finas del formulario. */
  border-bottom: 1px solid rgba(167, 161, 154, 0.55);
  padding-bottom: 3px;
  transition: color 0.3s var(--ease-out), border-color 0.3s var(--ease-out);
}
@media (hover: hover) {
  .cta__form-mail:hover {
    color: var(--bronce-anzures);
    border-bottom-color: var(--bronce-anzures);
  }
}
.cta__form-mail:focus-visible {
  outline: 2px solid var(--bronce-anzures);
  outline-offset: 3px;
  border-radius: 1px;
}

/* Hairline vertical que empareja el botón (acción principal) con la vía
   alterna: el lenguaje de reglas finas del sitio, aquí uniendo dos formas de
   contacto. Solo en fila (md+); apilado en móvil no lleva línea. */
@media (min-width: 768px) {
  .cta__form-alt {
    padding-left: clamp(18px, 3.2vw, 30px);
    border-left: 1px solid rgba(167, 161, 154, 0.4);
  }
}

/* Confirmación tras el envío (reemplaza al formulario). Titular sereno en la
   voz editorial, no un banner. */
.cta__form-success {
  margin: 0;
  font-family: "Newsreader", Georgia, serif;
  font-weight: 300;
  font-size: clamp(1.3rem, 2.4vw, 1.75rem);
  line-height: 1.3;
  color: var(--carbon);
  max-width: 30ch;
}
.cta__form-success:focus { outline: none; }
/* Apenas por encima del .btn base (48px de alto, 26px de flancos). Sin flecha,
   el texto queda solo en la caja y cualquier exceso de padding se nota, así
   que el cuerpo extra es mínimo. Nada de min-width: "Enviar" es corta y un
   ancho mínimo la estira hasta convertirla en barra. */
.cta__submit {
  min-height: 50px;
  padding: 0 30px;
}

@media (prefers-reduced-motion: reduce) {
  .cta__field::after,
  .cta__label,
  .cta__input,
  .cta__chip span,
  .cta__ubicacion-link,
  .cta__ubicacion-link .icon-arrow,
  .cta__form-mail { transition: none; }
}

/* ---------- Footer ----------
   Ancla azul de cierre del sitio (contrapeso del Hero). El alzado en líneas de
   la casa (assets/img/footer-lineas.svg) funciona como marca de agua; encima va UN solo
   bloque compacto: logotipo grande + fila de links pegada debajo. Nada de aire
   repartido entre elementos sueltos — el aire vive alrededor del grupo, no
   dentro. El azul se mantiene ("el footer sí déjalo azul"). */
.footer {
  position: relative;
  isolation: isolate;               /* el facade queda por debajo del contenido */
  overflow: hidden;                 /* recorta el line-art a la caja del footer */
  background: var(--azul-lavalle);
  color: var(--marfil);
  padding: clamp(20px, 2.6vw, 26px) var(--gutter) clamp(12px, 1.6vw, 16px);
  text-align: center;
}

/* Escenario: caja compartida por el dibujo y el bloque de contenido. */
.footer__stage {
  position: relative;
  width: 100%;
  /* 760, no 880 (pasada de compactación): el alzado es 3:1, así que su ancho es
     lo que fija el alto del footer. A 880 medía ~293px contra ~200px de
     contenido, y esa diferencia salía como azul vacío arriba y abajo del
     bloque. A 760 el dibujo sigue enmarcando el bloque sin sobrarle tanto. */
  max-width: 760px;
  margin-inline: auto;
}

/* Alzado de la fachada. En móvil el dibujo es demasiado bajo (3:1) para
   sostener el bloque, así que se amplía y se ancla abajo como fondo. */
.footer__facade {
  position: absolute;
  left: 50%;
  bottom: -2%;
  z-index: -1;
  width: 148%;
  aspect-ratio: 2172 / 724;
  transform: translateX(-50%);
  background: url("assets/img/footer-lineas.svg") center bottom / contain no-repeat;
  opacity: 0.14;
  pointer-events: none;
}

/* Un solo grupo: logotipo y links separados por un gap corto. */
.footer__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(11px, 1.6vw, 15px);
}
.footer__logo { display: inline-flex; min-height: 44px; align-items: center; }
.footer__logo-img { height: clamp(42px, 5.4vw, 66px); width: auto; }

/* Links: los cinco destinos (Instagram incluido) viven en el mismo <nav>, así
   que forman UN bloque que envuelve centrado — en móvil cae en 3 + 2 y en md
   en una sola fila. Antes eran dos cajas: una rejilla 2x2 con el nav y, colgando
   debajo, Instagram solo. Con "Galería y espacios" al doble de ancho que
   "Contacto", esa rejilla salía descuadrada y el enlace suelto remataba en un
   3+1+1 que se leía desordenado. */
.footer__links {
  position: relative;
  isolation: isolate;   /* mantiene el velo (md+) por encima del alzado */
  display: flex;
  justify-content: center;
}

.footer__nav {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  column-gap: 18px;
  row-gap: 0;
}

.footer__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  font-family: var(--font-sans);
  font-weight: 400;
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(247, 244, 239, 0.88);
  transition: color 0.3s var(--ease-out);
}
.footer__link:hover { color: var(--bronce-anzures); }

/* Línea de cierre: legal y dirección, un peso por debajo de los links. */
.footer__base {
  margin-top: clamp(4px, 0.8vw, 8px);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
}
.footer__addr,
.footer__legal {
  margin: 0;
  font-family: var(--font-sans);
  font-size: 0.62rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(247, 244, 239, 0.42);
}
/* La dirección abre Google Maps (2026-07-20): es lo que queda del mapa que
   vivía en el CTA, así que la ubicación sigue a un clic desde cualquier página.
   El padding le da área táctil sin cambiar el ritmo de la línea. */
a.footer__addr {
  display: inline-block;
  padding: 8px 0;
  transition: color 0.3s var(--ease-out);
}
a.footer__addr:hover { color: var(--bronce-anzures); }

/* ---------- The House (marcas del grupo) ----------
   Tira de cierre con las marcas hermanas: la misma pieza que en el resto de
   los sitios de la empresa (referencia: el footer de Cooperativa). Va DEBAJO
   de la firma, separada por una hairline al ancho del alzado, así que el
   bloque de arriba (logotipo + links + dibujo) no se toca y esto se lee como
   un pie del pie, un peso por debajo de la dirección.
   Las alturas son por marca, no una altura común: son tres logotipos con
   proporciones muy distintas (Cooperativa es una palabra larga de 9.8:1,
   Penumbra es un bloque apilado casi cuadrado, loop. es corto y pesado), así
   que igualarlos por caja los descuadra. Se equilibran a ojo por altura de
   mayúscula. */
.footer__house {
  max-width: 760px;              /* el mismo ancho que el alzado */
  margin: 16px auto 0;
  padding-top: 14px;
  border-top: 1px solid rgba(247, 244, 239, 0.13);
}
.house__logos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  column-gap: 26px;
  row-gap: 6px;
  margin: 0;
  padding: 0;
  list-style: none;
}
.house__item { display: inline-flex; align-items: center; }
/* El área táctil (44px) vive en el enlace, no en la imagen: así los logos
   pueden ir chicos sin dejar de ser tocables en móvil. Penumbra no tiene sitio
   todavía, así que va como imagen suelta; al centrarse en la fila queda
   alineada con los otros dos igual. */
.house__link {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.house__img {
  display: block;
  width: auto;
  opacity: 0.72;
  transition: opacity 0.3s var(--ease-out);
}
.house__link:hover .house__img { opacity: 1; }
/* Alturas calibradas sobre los archivos v3 del cliente (recortados al trazo).
   Las tres marcas se igualan por altura de mayuscula, no por caja: los
   archivos tienen proporciones muy distintas (Cooperativa 9.3:1, loop 3.5:1,
   Penumbra 0.9:1) y una altura comun las descuadra.
   Los dos logotipos horizontales se alinean ademas por LINEA BASE y no por
   centro: "loop." tiene descendente en la p (su base esta al 78% del archivo)
   y COOPERATIVA es toda mayuscula (base al 98%), asi que centrarlos por caja
   deja una palabra flotando mas arriba que la otra. El empuje de loop va en %,
   relativo a su propio alto, para que valga igual en movil y en desktop.
   Penumbra (2026-08-26) se re-exporto desde el activo que Cooperativa usa en
   su propio footer -sin el renglon "Gallery" que traia el archivo anterior-,
   asi que ya no necesita empuje: el recorte nuevo llega centrado al trazo y
   basta con align-items:center en .house__logos. */
.house__img--cooperativa { height: 10px; }
.house__img--penumbra { height: 36px; }
.house__img--loop { height: 14px; transform: translateY(8.9%); }

/* ---------- Reduced motion ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  /* Show the Hero in its final state, no entrance, even if JS never reveals. */
  .js .hero__line-in { transform: none; }
}

/* =========================================================================
   md — @media (min-width: 768px)
   ========================================================================= */
@media (min-width: 768px) {
  :root { --nav-h: 76px; --gutter: 40px; }
  .nav__logo-img { height: 32px; }
  /* Mismo cálculo que en base, con el alto de md: 32 × 3.434 ≈ 110. */
  .nav--iso .nav__logo { min-width: 110px; }
  /* Con el CTA de vuelta en la barra, el trigger recupera el centro absoluto. */
  .nav__trigger {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    justify-content: center;
    margin-right: 0;
    font-size: 0.86rem;
  }
  .nav__trigger-label { display: inline-block; }
  .nav .nav__cta { display: inline-flex; }
  .nav__cta { min-height: 46px; padding: 0 22px; font-size: 0.9rem; letter-spacing: 0.01em; }
  .nav__cta-long { display: inline; }
  .nav__cta-short { display: none; }

  /* Panel: de vuelta al eje izquierdo y a la escala original del enlace. */
  .nav__links { align-items: stretch; text-align: left; }
  .nav__link { font-size: clamp(1.55rem, 4.7vw, 2.85rem); }
  .nav__panel-foot { text-align: left; }

  /* La Casa: dos columnas — texto a la izquierda, foto a la derecha.
     El recorte es CUADRADO, no 4/5 (pasada de compactación): la columna de la
     imagen medía ~660px contra ~400px de texto, y el centrado repartía esos
     ~260px de diferencia como vacío arriba y abajo del texto. Era el hueco más
     grande de la landing. En 1/1 la foto mide ~530px y la diferencia baja a
     ~60px por lado, que sí lee como aire y no como hueco. El recorte lo sigue
     resolviendo object-fit: cover centrado. */
  .casa__inner {
    display: grid;
    grid-template-columns: 1.02fr 0.78fr;
    column-gap: clamp(44px, 6vw, 104px);
    align-items: center;
  }
  .casa__body { max-width: 42ch; }
  .casa__img { aspect-ratio: 1 / 1; }
  /* Las acciones vuelven al eje izquierdo: aquí conviven con su columna de
     texto y ya no quedan sueltas contra el margen (ver .casa__cta en la base). */
  .casa__cta { text-align: left; }

  /* Servicios: la fila se abre en tres columnas — número, título, copy a la
     derecha — para leer como índice editorial. */
  .servicios__item {
    display: grid;
    grid-template-columns: 72px 1fr minmax(0, 46ch);
    column-gap: clamp(28px, 4vw, 64px);
    align-items: baseline;
  }
  .servicios__name { margin: 0; }

  /* Galería (teaser): título izquierda, enlace derecha sobre la línea base. */
  .galeria__head {
    flex-direction: row;
    align-items: baseline;
    justify-content: space-between;
    gap: 20px;
  }
  /* Sin esto el align-self de móvil ganaría al baseline de la fila. */
  .galeria__link { align-self: auto; }

  /* CTA: dos columnas que arrancan a la misma altura — texto a la izquierda
     (columna contenida, se lee de un vistazo) y formulario a la derecha, que
     es donde ahora vive el peso de la sección. Sin el mapa, el formulario se
     queda con el ancho mayor en lugar de dejar un hueco. */
  .cta__inner {
    display: grid;
    grid-template-columns: 0.78fr 1.12fr;
    column-gap: clamp(44px, 6vw, 100px);
    align-items: start;
    max-width: 1320px;              /* el bloque no se estira de más en pantallas anchas */
    margin-inline: auto;
  }
  .cta__title { font-size: clamp(2.4rem, 4.4vw, 3.6rem); }
  /* Formulario: pares de campos por fila. */
  .cta__form { margin-top: 0; }
  .cta__form-grid { grid-template-columns: 1fr 1fr; }

  /* Footer: a partir de md el alzado entra en flujo y el bloque de contenido se
     monta encima, en la misma celda de grid. La fila mide lo que mida el más
     alto de los dos, así que el contenido nunca se desborda del dibujo. El
     bloque va centrado (no repartido): el aire queda alrededor, no adentro. */
  .footer__stage { display: grid; }
  .footer__facade {
    position: static;
    grid-area: 1 / 1;
    width: 100%;
    bottom: auto;
    aspect-ratio: 2172 / 724;
    transform: none;
    background-size: 100% auto;
    background-position: center bottom;
    opacity: 0.13;
  }
  .footer__inner {
    grid-area: 1 / 1;
    justify-content: center;
    padding-block: clamp(10px, 1.4vw, 16px);
  }
  /* Velo del color del footer detrás de la fila de links: apaga las líneas del
     alzado justo donde vive el texto chico. Invisible sobre el azul plano, pero
     deja los links limpios sin bajar más la opacidad del dibujo. Solo aquí: en
     móvil la fila es un bloque alto y el velo se comería media fachada. */
  .footer__links::before {
    content: "";
    position: absolute;
    inset: -8px -7%;
    z-index: -1;
    background: radial-gradient(
      ellipse 56% 58% at 50% 50%,
      var(--azul-lavalle) 0%,
      rgba(9, 57, 76, 0.82) 44%,
      rgba(9, 57, 76, 0) 100%
    );
    pointer-events: none;
  }
  /* Los cinco enlaces caben en una sola fila; solo se abre el gap. */
  .footer__nav { column-gap: clamp(22px, 2.6vw, 34px); }
  .footer__link { font-size: 0.72rem; }

  /* Dirección y legal en una sola línea, separadas por un punto medio. */
  .footer__base { flex-direction: row; flex-wrap: wrap; justify-content: center; gap: 0; }
  .footer__legal::before { content: "·"; margin: 0 0.6em; opacity: 0.6; }

  /* The House: la tira gana un punto de escala y abre el gap, igual que los
     links de arriba. */
  .footer__house { margin-top: 18px; padding-top: 16px; }
  .house__logos { column-gap: clamp(30px, 3.4vw, 44px); }
  .house__img--cooperativa { height: 11px; }
  .house__img--penumbra { height: 39px; }
  .house__img--loop { height: 14px; }
}

/* =========================================================================
   xl — @media (min-width: 1280px)
   ========================================================================= */
@media (min-width: 1280px) {
  :root { --gutter: 64px; }
}
