/* ============================================================
   SKY WALLS — SISTEMA DE DISEÑO
   Todo se controla desde aquí. Nada hardcodeado más abajo.
   ============================================================ */
:root {
  /* ---- 1. COLOR — 3 colores. El resto son alphas de estos ---- */
  --bone: #f6f2ec; /* fondo: yeso cálido. Es el material del cliente */
  --ink: #221f1a; /* texto: carbón cálido, nunca negro puro */
  --gold: #a98748; /* del logo. Se usa poquísimo, a propósito */

  --ink-70: color-mix(in srgb, var(--ink) 70%, transparent);
  --ink-63: color-mix(in srgb, var(--ink) 63%, transparent); /* mínimo para 4.5:1 sobre --bone (WCAG AA) */
  --rule: color-mix(in srgb, var(--ink) 14%, transparent);
  --surface: color-mix(in srgb, var(--ink) 6%, var(--bone));
  --bone-85: color-mix(in srgb, var(--bone) 85%, transparent);
  --gold-30: color-mix(in srgb, var(--gold) 30%, transparent);

  /* Velos sobre foto. Derivados de --ink, nunca rgba suelto.
     El hero abre oscuro arriba, se abre al centro para no matar
     la foto, y cierra abajo para sostener el cue de scroll. */
  --veil-hero-top: color-mix(in srgb, var(--ink) 55%, transparent);
  --veil-hero-mid: color-mix(in srgb, var(--ink) 32%, transparent);
  --veil-hero-bottom: color-mix(in srgb, var(--ink) 68%, transparent);
  --veil-card: color-mix(in srgb, var(--ink) 74%, transparent);

  /* ---- 2. TIPOGRAFÍA ----
     Dos familias, ni una más: Marcellus para títulos (eco del logo, con
     el aire clásico que pedía el cliente) y Jost para todo el texto.
     Marcellus tiene un solo peso y NO tiene itálica: la jerarquía la dan
     tamaño, caja y color — nunca peso ni cursiva sintética. */
  --font-display: "Marcellus", Georgia, serif;
  --font-body: "Jost", system-ui, sans-serif;

  /* Escala fluida 360px → 1600px. Sin media queries.
     Marcellus es más ancha que una didona: pide cuerpos algo menores. */
  --fs-display: clamp(2.5rem, 1.1rem + 6.2vw, 6.5rem); /* 40 → 104 */
  --fs-h1: clamp(1.875rem, 1.1rem + 3.4vw, 3.75rem); /* 30 → 60 */
  --fs-h2: clamp(1.375rem, 1rem + 1.9vw, 2.5rem); /* 22 → 40 */
  --fs-h3: clamp(1.125rem, 0.95rem + 0.8vw, 1.5rem); /* 18 → 24 */
  /* La entradilla tiene que separarse del cuerpo: ~1.5x, no 1.18x */
  --fs-lead: clamp(1.1875rem, 0.9rem + 1.15vw, 1.625rem); /* 19 → 26 */
  --fs-body: clamp(1rem, 0.96rem + 0.2vw, 1.0625rem);
  --fs-small: 0.875rem;
  --fs-label: 0.6875rem;
  --fs-label-lg: 0.8125rem; /* side-label: un par de px más grande que --fs-label, para legibilidad */

  --lh-tight: 1.04; /* Marcellus tiene ojo medio alto: no aguanta 0.95 */
  --lh-snug: 1.18;
  --lh-body: 1.7;

  --tr-display: 0.01em; /* la serif clásica respira, no se aprieta */
  --tr-display-sm: 0.01em;
  --tr-wide: 0.12em; /* subtítulos tipo logo */
  --tr-label: 0.22em; /* etiquetas */

  /* ---- 3. ESPACIADO — escala 4px ---- */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-5: 1.5rem;
  --sp-6: 2rem;
  --sp-7: 3rem;
  --sp-8: 4rem;
  --sp-9: 6rem;
  --sp-10: 8rem;

  --section-y: clamp(var(--sp-8), 10vw, var(--sp-10));
  --gutter: clamp(var(--sp-5), 5vw, var(--sp-8));

  /* ---- 4. LAYOUT ----
     Tres medidas distintas, no una sola: el titular rompe corto y con
     fuerza, la entradilla es un párrafo breve, el cuerpo es lectura. */
  --maxw: 1520px;
  --maxw-heading: 16ch;
  --maxw-lead: 42ch;
  --maxw-text: 60ch;
  --border: 1px solid var(--rule);

  /* ---- 5. MOVIMIENTO ---- */
  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur-fast: 0.3s;
  --dur-slow: 1s;
  --dur-image: 1.4s; /* zoom de imagen en hover: lento a propósito */
  --scale-hover: 1.03;
  --blur-glass: 14px;
}

*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
/* El header es fijo: sin esto, saltar a un ancla deja el destino debajo
   de él. Solo actúa cuando el elemento es destino de scroll. */
[id] {
  scroll-margin-top: var(--sp-9);
}
body {
  background: var(--bone);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400; /* Jost 300 queda demasiado fino para lectura */
  line-height: var(--lh-body);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}
img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
a {
  color: inherit;
  text-decoration: none;
}
:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: 4px;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ---- PRIMITIVAS TIPOGRÁFICAS ---- */
.display {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  line-height: var(--lh-tight);
  letter-spacing: var(--tr-display);
}
.h1 {
  font-family: var(--font-display);
  font-size: var(--fs-h1);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-display);
}
.h2 {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-display-sm);
}
.h3 {
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-display-sm);
}
.lead {
  font-size: var(--fs-lead);
  line-height: 1.45; /* a 26px, 1.6 se abre demasiado */
  color: var(--ink-70);
  max-width: var(--maxw-lead);
}
.body {
  max-width: var(--maxw-text);
  color: var(--ink-70);
}
/* 500 en vez de 400: en mayúsculas a 11px la grotesca necesita algo
   más de peso para no desaparecer. */
.label {
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-63);
}
.eyebrow {
  font-family: var(--font-display);
  font-size: var(--fs-small);
  letter-spacing: var(--tr-wide);
  text-transform: uppercase;
  color: var(--gold);
}

/* ---- LAYOUT ---- */
.wrap {
  max-width: var(--maxw);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
/* Etiqueta de sección — kicker convencional encima del título/párrafo. */
.side-label {
  display: block;
  margin-bottom: var(--sp-3);
  font-size: var(--fs-label-lg);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-63);
}
.page-banner .side-label {
  color: var(--bone-85);
}
.section {
  padding-block: var(--section-y);
}
/* Para un título que encabeza la sección siguiente en vez de cerrar la suya. */
.section--flush-bottom {
  padding-bottom: 0;
}
/* Para el bloque que sigue justo al banner: se acerca a la foto en vez
   de quedar flotando en medio de la pantalla. */
.section--flush-top {
  padding-top: var(--sp-7);
}
/* Even shorter: para cuando arriba solo hay un filete, no una foto — el
   salto se sentía igual de grande sin el peso visual de una imagen que
   lo justificara. Sin uso desde que What We Do perdió su portadilla. */
.section--flush-top-sm {
  padding-top: var(--sp-5);
}
/* Pareja de .section--tight-top: acorta el cierre de ESTA sección en vez
   del arranque de la siguiente. Junto con --tight-top de al lado suman
   64px, la mitad de lo que dejaban antes (128 + 32). */
.section--tight-bottom {
  padding-bottom: var(--sp-6);
}
/* Entre dos secciones los paddings se suman: 128 + 128 = 256px, el
   doble que cualquier otro salto de la página. Ese hueco hacía que el
   contenido siguiente no asomara nunca y la página pareciera terminada
   en las tarjetas. Con esto el salto queda en 160: más que los 128 que
   separan bloques hermanos, pero ya no un final. */
.section--tight-top {
  padding-top: var(--sp-6);
}
/* Para páginas que arrancan directamente con contenido, sin banner ni
   título de portadilla. El header es fijo: sin este colchón la primera
   fila de fotos quedaría por debajo de él. */
.section--page-top {
  /* El suelo es alto a propósito: lo primero que se ve es una foto a
     sangre, y pegada al header se lee como un fallo de maquetación.
     En móvil el header mide 86px. */
  padding-top: clamp(8rem, 14vw, 11rem);
}
/* Gallery: sin título ni frase de entrada, el primer nombre de proyecto
   es lo único que ancla la página bajo el header. Los portfolios de
   arquitectura/interiorismo sin hero que resuelven esto bien no meten
   texto de vuelta: meten más aire. Colchón mayor que el de las páginas
   de servicio, que sí tienen una foto a sangre justo debajo del header. */
.section--page-top-lg {
  padding-top: clamp(9rem, 17vw, 13.5rem);
}
/* ---- HEADER ---- */
.header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 200; /* por encima de .mobile-nav (150): el botón debe
    quedar clickeable incluso con el panel abierto, ya que ambos
    son fixed y comparan z-index en el mismo nivel (hijos de body) */
  /* Rejilla de tres columnas y no flex: con space-between el logo se
     desplazaría según lo que midiera el menú de al lado. Las columnas
     laterales son 1fr iguales, así que la del medio cae en el centro
     exacto del viewport pase lo que pase con los enlaces. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--sp-5);
  padding: var(--sp-5) var(--gutter);
  color: var(--bone);
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    padding var(--dur-fast) var(--ease);
}
/* Velo propio del header cuando va transparente sobre foto. El velo del
   hero es una elipse centrada: en las esquinas de arriba no llega casi
   nada, y ahí es justo donde está el menú. Medido sobre las tres fotos
   del home, el texto hueso quedaba entre 2.19:1 y 1.30:1 en los techos
   claros — ilegible (WCAG pide 4.5:1 a este cuerpo).
   La banda es corta y se apaga rápido: sostiene el menú sin volver a
   apagar el tercio superior, que es donde se ve el producto.

   Subida de 72/40 a 88/50 al quitar el velo general del hero: con el
   72 el menú se quedaba en 3,46:1 sobre los techos claros y a 11px
   pide 4,5. Con 88/50 sube a 5,0:1. Cuesta muy poco de foto porque es
   el filo de arriba del encuadre, donde no hay nada que enseñar. */
.header::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 168px;
  pointer-events: none;
  z-index: -1;
  background: linear-gradient(
    to bottom,
    color-mix(in srgb, var(--ink) 88%, transparent) 0%,
    color-mix(in srgb, var(--ink) 50%, transparent) 45%,
    transparent 100%
  );
  transition: opacity var(--dur-fast) var(--ease);
}
/* La banda sube la media a 6:1 largos, pero en un techo blanco quedan
   píxeles sueltos sobre 4.5:1. Oscurecer más la banda empezaba a apagar
   la foto; una sombra difusa resuelve esos brillos aislados sin tocarla.
   Solo mientras el header va sobre foto. */
.header:not(.is-stuck):not(.header--static) .nav a,
.header:not(.is-stuck):not(.header--static) .brand,
.header:not(.is-stuck):not(.header--static) .menu-btn span {
  text-shadow: 0 1px 3px color-mix(in srgb, var(--ink) 62%, transparent);
}
.header:not(.is-stuck):not(.header--static) .menu-btn span {
  box-shadow: 0 1px 3px color-mix(in srgb, var(--ink) 62%, transparent);
}
/* Con fondo hueso propio el velo sobra y además oscurecería el header. */
.header.is-stuck::before,
.header--static::before {
  opacity: 0;
}
/* El home no tiene marca: la columna central queda vacía y el menú se
   va a la derecha solo, sin necesitar regla propia. */
.header.is-stuck,
.header--static {
  background: var(--bone-85);
  backdrop-filter: blur(var(--blur-glass));
  border-bottom: var(--border);
  color: var(--ink);
  padding-block: var(--sp-3);
}
.brand {
  grid-column: 2;
  justify-self: center;
  font-family: var(--font-display);
  font-size: var(--fs-small);
  letter-spacing: var(--tr-wide);
  text-transform: uppercase;
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
/* El archivo del logo trae el ícono + "SKY WALLS" + tagline apilados
   en una sola imagen — a la altura de un header (antes 32px) el texto
   quedaba ilegible. Se recortó solo el ícono (siempre dorado, mismo
   en ambas variantes) y el nombre vuelve a ser texto real. */
.brand__icon {
  display: block;
  height: 38px;
  width: auto;
}
.nav {
  grid-column: 3;
  justify-self: end;
  display: flex;
  gap: var(--sp-5);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
}
.nav a {
  position: relative;
  padding-block: var(--sp-1);
}
.nav a::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.nav a:hover::after {
  transform: scaleX(1);
}
@media (max-width: 860px) {
  .nav {
    display: none;
  }
}

/* ---- MOBILE MENU — botón de 2 líneas + panel off-canvas ---- */
.menu-btn {
  grid-column: 3;
  justify-self: end;
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: var(--sp-1);
  width: 28px;
  height: 28px;
  padding: 0;
  background: none;
  border: none;
  /* El navegador le pone a <button> su propio color (buttontext, negro):
     no hereda el del header. Las barras usan currentColor, así que sobre
     la foto del home salían negras sobre oscuro — el botón desaparecía. */
  color: inherit;
  cursor: pointer;
}
.menu-btn span {
  display: block;
  width: 100%;
  height: 1px;
  background: currentColor;
  transition: translate var(--dur-fast) var(--ease), rotate var(--dur-fast) var(--ease);
}
/* Con el panel abierto el botón queda ENCIMA de él (bone): heredar el
   hueso del header lo haría desaparecer justo cuando hace de aspa. */
.menu-btn.is-open {
  color: var(--ink);
}
.header:not(.is-stuck):not(.header--static) .menu-btn.is-open span {
  box-shadow: none;
}
.menu-btn.is-open span:first-child {
  translate: 0 3px;
  rotate: 45deg;
}
.menu-btn.is-open span:last-child {
  translate: 0 -3px;
  rotate: -45deg;
}
@media (max-width: 860px) {
  .menu-btn {
    display: flex;
  }
}

.mobile-nav-scrim {
  position: fixed;
  inset: 0;
  z-index: 140;
  background: color-mix(in srgb, var(--ink) 45%, transparent);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-fast) var(--ease), visibility var(--dur-fast) var(--ease);
}
.mobile-nav-scrim.is-open {
  opacity: 1;
  visibility: visible;
}
.mobile-nav {
  position: fixed;
  inset: 0 0 0 auto;
  z-index: 150;
  width: min(82vw, 400px);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: var(--sp-9) var(--gutter) var(--sp-7);
  background: var(--bone);
  border-left: 1px solid var(--gold-30);
  translate: 100% 0;
  transition: translate var(--dur-fast) var(--ease);
}
.mobile-nav.is-open {
  translate: 0 0;
}
.mobile-nav__links {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
}
.mobile-nav__index {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-63);
  margin-bottom: var(--sp-2);
}
.mobile-nav a {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  color: var(--ink);
  letter-spacing: var(--tr-display-sm);
}
.mobile-nav a:hover,
.mobile-nav a:focus-visible {
  color: var(--gold);
}
.mobile-nav__footer {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  padding-top: var(--sp-6);
  border-top: var(--border);
}
.mobile-nav__footer .label {
  color: var(--ink-63);
}
.mobile-nav__email {
  font-size: var(--fs-small);
  color: var(--ink-63);
  transition: color var(--dur-fast) var(--ease);
}
.mobile-nav__email:hover {
  color: var(--ink);
}
@media (prefers-reduced-motion: reduce) {
  .mobile-nav,
  .mobile-nav-scrim,
  .menu-btn span {
    transition: none;
  }
}

/* ---- HERO: foto full screen + secuencia de fundidos ---- */
.hero {
  /* La foto del home llega intacta: no hay velo ni fondo de ningún tipo
     encima, ni sobre la imagen ni bajo el texto. Los banners de las
     páginas internas sí siguen usando el velo de :root, que está bien
     calibrado para sus fotos, más oscuras. */
  position: relative;
  height: 100svh;
  min-height: 560px;
  overflow: hidden;
  color: var(--bone);
}
.hero__media {
  position: absolute;
  inset: 0;
}
/* Slider de fondo: sólo cambia la foto. El logo, el titular y el CTA
   nunca se mueven — un carrusel que arrastra el texto se lee barato y
   deja el mensaje ilegible medio segundo en cada transición. */
.hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1.4s var(--ease);
}
.hero__slide.is-active {
  opacity: 1;
}
.hero__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Ken Burns: la foto activa deriva lentamente. Arranca desde el zoom
   final de la anterior para que el fundido no dé un salto de escala. */
.hero__slide.is-active img {
  animation: heroDrift 8s linear forwards;
}
@keyframes heroDrift {
  from {
    scale: 1.06;
  }
  to {
    scale: 1.12;
  }
}
.hero__media::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Vacía a propósito. Aquí vivía el velo que cubría la foto; el cliente
     lo quiso fuera. Se conserva la capa por si alguna vez hace falta
     volver a asentar algo encima sin tocar el marcado. */
  background: none;
}
/* Los tres elementos se apilan en columna y entran en cascada.
   Ninguno se desvanece al final: la composición queda fija. */
.hero__inner {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-6);
  padding: var(--sp-8) var(--gutter);
  text-align: center;
}
/* Igual que .reveal: sin JS no se oculta. El hero entero (logo, titular
   y CTA) dependía de que el script añadiera .is-playing — sin él la
   portada quedaba en negro sobre la foto. */
.js .hero__line {
  opacity: 0;
}
/* El logo cae entero en la mitad limpia, o sea sobre la foto sin tocar,
   y es blanco: en el techo blanco de hero-1 el fondo le da 0.9:1. La
   solución no es oscurecer la foto otra vez, es que la marca traiga su
   propia sombra. Dos capas: una corta y dura que perfila cada letra, y
   otra ancha y suave que la despega del fondo. Cuesta cero luz de foto. */
/* Asistencia mínima al texto de la portada.

   El cliente la quiere "que apenas se vea": nada de velos ni manchas
   sobre la foto, solo lo justo para que el texto no se pierda. Así que
   la ayuda va donde no cuesta imagen:

     - El titular y el botón llevan un halo suave, pegado a la letra.
       Un text-shadow no tapa un solo píxel fuera del glifo: la foto se
       sigue viendo entera y el texto gana un borde oscuro de 1-2px que
       lo despega del fondo.
     - El botón, además, un tinte muy ligero. Sin nada era invisible
       (0,9:1): solo le quedaba el filete en hueso sobre foto clara y
       no se encontraba el único botón de la portada.

   Los valores son deliberadamente bajos y NO llegan a la norma WCAG
   (el titular pide 3:1 y el botón 4,5:1). Es una decisión de cliente
   tomada sabiendo el coste, después de varias versiones que sí
   cumplían. Si algún día se quiere cumplir sin tapar foto, lo más
   barato es subir el halo a ink 78%/58% y el tinte del botón a ink
   65%, que lo lleva a 4,7:1. */
/* El titular: contorno detrás de la letra, no fondo detrás del bloque.

   paint-order invierte el orden de pintado — primero el trazo y encima
   el relleno — así que el contorno queda POR DEBAJO del glifo y solo
   asoma por fuera. La letra conserva su forma exacta (no engorda, que
   es lo que pasa con un text-stroke normal) y gana un borde oscuro de
   unos 3px que la separa de lo que haya detrás.

   La gracia es que esto no tapa foto: el oscuro existe solo en el
   perímetro de cada letra, no en una caja ni en una banda. Es lo que
   pedía el cliente — fondo original, texto legible.

   El ancho va en em y no en px para que el contorno crezca y encoja
   con el titular: a 51px del escritorio son 5,6px y a 40px del móvil
   4,4px, o sea el mismo grosor relativo.

   Si algún navegador viejo no entiende paint-order, el trazo se pinta
   encima y la letra se ve algo más gruesa. Se sigue leyendo. */
.hero__line--2 {
  paint-order: stroke fill;
  -webkit-text-stroke: 0.11em color-mix(in srgb, var(--ink) 45%, transparent);
  text-shadow: 0 0 18px color-mix(in srgb, var(--ink) 38%, transparent);
}
/* El botón, macizo en oro.

   Como fantasma era invisible: filete hueso sobre foto clara, 0,9:1, y
   no se encontraba el único botón de la portada. Macizo se resuelve sin
   discusión y sin velo — ocupa 183x53px, el 0,9% de la pantalla, y se
   lee como un botón y no como una mancha encima de la foto.

   En oro y no en tinta porque es el color de la marca y porque destaca
   igual sobre las tres fotos: la clara, la de madera y la del salón
   gris. Con la tinta encima da 4,9:1, por encima del 4,5 que pide un
   texto de 11px. En hover entra el hueso macizo que ya traía
   .btn--light.

   Doble clase en el selector a propósito: .btn--light viene después en
   esta hoja y con una sola clase ganaba ella, dejando el texto en hueso
   sobre oro (3,0:1, por debajo de norma). */
.hero__line--3.btn--light {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--ink);
}
.hero__logo {
  width: clamp(260px, 36vw, 520px);
  height: auto;
  filter: drop-shadow(0 1px 2px color-mix(in srgb, var(--ink) 72%, transparent))
    drop-shadow(0 4px 28px color-mix(in srgb, var(--ink) 68%, transparent));
}
/* La frase en una línea es aritmética, no gusto: mide 21.6 veces su
   font-size en Marcellus y el ancho útil es 100vw menos dos gutters.
   Forzarla a una línea en móvil la dejaría en 16px — más chica que el
   texto corrido. Así que abajo van dos líneas, capadas a 48px.
   El corte está en 1200 y no en 768 por una razón concreta: 4vw a
   1200px da exactamente esos mismos 48px. Al cruzar el punto la frase
   se reordena en una línea sin cambiar de tamaño, en vez de pegar el
   bajón a 31px que salía poniendo el corte en tablet.
   Sin max-width a propósito: cualquier tope fijo aquí obliga a tres
   líneas en tablet, donde caben dos. El ancho disponible ya reparte
   solo — 3 líneas en móvil, 2 en tablet, 1 desde 1200. */
.hero__line--2 {
  /* El logo ya termina en texto ("LUXURY WALLS & CEILING") con sus
     filetes. Con sólo el gap de la columna el titular quedaba a 32px y
     se leía como una línea más del logo, no como frase aparte. */
  margin-top: var(--sp-6);
  font-size: clamp(2.25rem, 1rem + 4.2vw, 3rem);
  /* Sin esto, en tablet quedaba una línea de 682px sobre otra de 340:
     un titular centrado con la segunda línea a la mitad se ve roto. */
  text-wrap: balance;
}
@media (min-width: 1200px) {
  .hero__line--2 {
    white-space: nowrap;
    max-width: none;
    font-size: min(4vw, 6rem);
  }
}
/* Indicadores: tres reglas finas que se van llenando. Dicen que hay más
   fotos y en qué punto va, sin los puntitos genéricos de plantilla. */
.hero__dots {
  position: absolute;
  left: 50%;
  bottom: var(--sp-6);
  translate: -50% 0;
  z-index: 2;
  display: flex;
  gap: var(--sp-3);
}
/* Sin velo debajo, estos filetes de 2px en hueso caen sobre foto limpia.
   La sombra los despega sin costar luz de imagen, igual que en el logo. */
.hero__dot {
  position: relative;
  filter: drop-shadow(0 1px 3px color-mix(in srgb, var(--ink) 80%, transparent));
  width: 44px;
  height: 2px;
  padding: var(--sp-6) 0;
  background: none;
  border: 0;
  cursor: pointer;
  /* El área táctil crece con el padding, pero la barra visible sigue
     siendo la línea de 2px: se dibuja en ::before, no en el botón. */
  box-sizing: content-box;
}
.hero__dot::before {
  content: "";
  position: absolute;
  inset: var(--sp-6) 0;
  background: color-mix(in srgb, var(--bone) 55%, transparent);
}
/* Sobre la alfombra clara de la primera foto las reglas desaparecían.
   La sombra las despega del fondo sin recurrir a una caja. */
.hero__dot::before,
.hero__dot span {
  filter: drop-shadow(0 1px 2px color-mix(in srgb, var(--ink) 55%, transparent));
}
.hero__dot span {
  position: absolute;
  inset: var(--sp-6) 0;
  background: var(--bone);
  transform: scaleX(0);
  transform-origin: left;
}
/* Pantallas bajas (móvil en horizontal): sin min-height la portada
   sigue cabiendo en una sola pantalla y el CTA no se va bajo el borde. */
@media (max-height: 560px) {
  .hero {
    min-height: 0;
  }
  .hero__inner {
    gap: var(--sp-4);
    padding-block: var(--sp-9) var(--sp-5);
  }
  .hero__logo {
    width: clamp(130px, 15vw, 200px);
  }
  .hero__line--2 {
    font-size: var(--fs-h2);
  }
}
@keyframes fadeIn {
  to {
    opacity: 1;
  }
}
/* Quien pide menos movimiento ve la primera foto quieta: el JS no
   avanza y el Ken Burns se anula. */
@media (prefers-reduced-motion: reduce) {
  .hero__slide {
    transition: none;
  }
  .hero__slide.is-active img {
    animation: none;
  }
}
/* Cascada: logo → titular → CTA. Cada uno se queda al terminar. */
.is-playing .hero__line--1 {
  animation: fadeIn 1.6s var(--ease) 0.6s both;
}
.is-playing .hero__line--2 {
  animation: fadeIn 1.6s var(--ease) 1.8s both;
}
.is-playing .hero__line--3 {
  animation: fadeIn 1.2s var(--ease) 3s both;
}

/* ---- REVEAL ----
   Ojo con el selector `.js`: quien oculta el contenido es el CSS, pero
   quien lo devuelve es el JavaScript. Si el script no llega a correr —un
   error, un bloqueador, el CDN caído— la página se quedaba EN BLANCO.
   Con esta condición, sin JS no se oculta nada: se ve todo de golpe, sin
   animación, que es exactamente lo que debe pasar. La clase la pone un
   script en el <head>, antes del primer pintado, para que no haya
   parpadeo de contenido visible que se esconde. */
.js .reveal {
  opacity: 0;
  transform: translateY(var(--sp-4));
  transition:
    opacity var(--dur-slow) var(--ease),
    transform var(--dur-slow) var(--ease);
}
.js .reveal.is-in {
  opacity: 1;
  transform: none;
}

/* ---- SECTION INTRO ---- */
.intro__statement {
  max-width: var(--maxw-heading);
  margin-bottom: var(--sp-5);
}
/* Agrupación: el titular y su entradilla van juntos (24px); la galería
   es otro bloque y necesita aire de verdad (96px). Antes iba pegada. */
.masonry,
.grid-2x2,
.work-cards {
  margin-top: var(--sp-9);
}
/* Ese margen separa la grilla del texto que la encabeza. Si no hay texto
   arriba, sobra: se sumaría al padding de la sección. */
.wrap > .masonry:first-child,
.wrap > .grid-2x2:first-child,
.wrap > .work-cards:first-child,
.wrap > .projects:first-child {
  margin-top: 0;
}

/* ---- WORK CARDS — etapas del proceso. Foto arriba, texto debajo
   (no superpuesto), en 3 columnas. ---- */
.work-cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--sp-7);
  /* Antes esto encogía la grilla ENTERA a 56rem y la centraba: las tres
     tarjetas quedaban chicas, sí, pero apeñuscadas en el medio de la
     página con enormes vacíos a los lados. El cliente quiere las dos
     cosas a la vez — fotos chicas Y la fila usando todo el ancho — así
     que ahora la grilla ocupa el wrap completo y es cada tarjeta
     (abajo) la que se acota, centrada en su propio tercio. */
  justify-items: center;
}
.work-card {
  width: 100%;
  max-width: 17.5rem;
}
@media (max-width: 760px) {
  .work-cards {
    grid-template-columns: 1fr;
    /* En móvil no hay tercios que llenar: la tarjeta vuelve a ocupar
       todo el ancho de la columna, como cualquier otro bloque apilado. */
    justify-items: stretch;
  }
  .work-card {
    max-width: none;
  }
}
/* Cuadrada a propósito: las portadas de origen son 365x365. Recortarlas
   a 3/4 perdía un cuarto de la foto y además obligaba a ampliarla. */
.work-card__media {
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--surface);
  /* El título pasó a ir ENCIMA de la foto (petición del cliente), así que
     el aire va arriba, no abajo. */
  margin-top: var(--sp-3);
}
.work-card__media img {
  transition: scale var(--dur-image) var(--ease);
}
.work-card:hover .work-card__media img {
  scale: var(--scale-hover);
}
.work-card__title {
  transition: color var(--dur-fast) var(--ease);
}
.work-card:hover .work-card__title {
  color: var(--gold);
}
/* Declaración de la empresa bajo los cajones: a todo el ancho de la
   página, sin el max-width de 46ch que trae .lead en el masthead. Así
   cae en una o dos líneas en escritorio, que es lo pedido. */
.work-statement {
  margin-top: var(--sp-8);
  max-width: none;
  text-wrap: pretty;
  text-align: center;
}
.work-card__label {
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-63);
  margin-bottom: var(--sp-2);
}

/* ---- CIERRE / LLAMADA A LA ACCIÓN ----
   Antes ninguna página pedía la acción: se podían ver las 45 fotos de la
   galería y no encontrar por dónde escribir, porque el único acceso a
   contacto era el enlace del menú. Este bloque cierra cada página.
   Lleva fondo --surface en vez de filete superior: el footer ya trae el
   suyo, y dos filetes seguidos se leen como un error de maquetación. */
.cta {
  background: var(--surface);
}
.cta__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-7);
}
/* El flex reparte según el ancho PREFERIDO del título (su max-width),
   no según el que acaba ocupando: por eso las acciones se caían de línea
   aunque en realidad cupieran por 4px. Con el título elástico y las
   acciones rígidas, el reparto es determinista. */
.cta__inner > :first-child {
  flex: 1 1 20rem;
}
.cta__title {
  max-width: 16ch;
  text-wrap: balance;
}
.cta__actions {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4) var(--sp-6);
}
/* El teléfono junto al botón y no dentro: en obra se llama antes que se
   rellena un formulario, y quien ya decidió no debería tener que dar un
   salto de página para encontrar el número. */
.cta__phone {
  position: relative;
  font-family: var(--font-display);
  font-size: var(--fs-h3);
  letter-spacing: var(--tr-display);
  white-space: nowrap;
}
.cta__phone::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -3px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.cta__phone:hover::after,
.cta__phone:focus-visible::after {
  transform: scaleX(1);
}
@media (max-width: 760px) {
  .cta__inner {
    align-items: flex-start;
  }
  .cta__title {
    max-width: none;
  }
  /* El `flex: 0 0 auto` que arriba impide que las acciones se caigan de
     línea aquí las volvía irreducibles: botón + teléfono suman 443px y
     desbordaban los 327 de un móvil. A ancho completo, envuelven. */
  .cta__actions {
    flex: 1 1 100%;
  }
}

/* ---- BUTTON / FOOTER ---- */
.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  /* <button> no hereda font-family: sin esto el CTA cae a Arial */
  font-family: var(--font-body);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  padding: var(--sp-4) var(--sp-6);
  border: 1px solid var(--gold-30);
  color: var(--ink);
  transition:
    background var(--dur-fast) var(--ease),
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
.btn:hover {
  background: var(--ink);
  color: var(--bone);
  border-color: var(--ink);
}
/* Sobre foto: el .btn base es tinta oscura y se perdería contra el velo. */
.btn--light {
  color: var(--bone);
  border-color: var(--bone-85);
}
.btn--light:hover {
  background: var(--bone);
  color: var(--ink);
  border-color: var(--bone);
}
.footer {
  padding-top: var(--sp-9);
  border-top: var(--border);
}
/* La última columna lleva dirección + teléfono + correo: necesita más
   ancho que las de enlaces, o la calle parte dejando un huérfano. */
.footer__grid {
  display: grid;
  /* Con la marca fuera de aquí (la lleva la banda grande de abajo) y la
     columna de etapas de vuelta, el footer tiene contenido para ocupar
     todo el ancho: cuatro columnas de borde a borde, igual que la banda.
     La primera y la última van más anchas — texto corrido y dirección
     necesitan más medida que dos listas de enlaces. */
  grid-template-columns: 1.5fr 1fr 1fr 1.45fr;
  gap: var(--sp-7);
  padding-bottom: var(--sp-8);
}
/* El párrafo vuelve a alinearse a la izquierda como una columna más:
   ya no cuelga de un logo centrado, así que centrarlo lo dejaba fuera
   del eje del resto de la fila. */
.footer__brand-desc {
  color: var(--ink-63);
  max-width: 34ch;
}
.footer__heading {
  margin-bottom: var(--sp-4);
}
.footer__links {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-3);
}
.footer__links a,
.footer__text {
  color: var(--ink-63);
}
.footer__links a {
  transition: color var(--dur-fast) var(--ease);
}
.footer__links a:hover {
  color: var(--ink);
}
/* flex-start y no center: la dirección ocupa dos líneas y el icono debe
   quedar junto a la primera, no centrado sobre el bloque entero. El
   desplazamiento lo alinea ópticamente con la altura de mayúscula. */
.footer__text {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
}
.footer__text + .footer__text {
  margin-top: var(--sp-3);
}
.footer__text a {
  transition: color var(--dur-fast) var(--ease);
}
.footer__text a:hover {
  color: var(--ink);
}
.footer__icon {
  width: 16px;
  height: 16px;
  flex: none;
  margin-top: 0.38em; /* alinea ópticamente con la altura de mayúscula */
}
/* "Big Footer": la marca escrita a lo ancho como cierre de página. El
   patrón funciona porque el header ya lleva la marca pequeña, así que
   abajo se puede permitir el gesto grande — y porque marca el final del
   scroll, que era justo lo que este footer no decía.
   El tamaño es aritmético: "SKY WALLS" mide 6.5 veces su font-size con
   este letter-spacing, y el ancho útil es 100vw menos dos gutters, así
   que 13.6vw es el mayor valor que cruza el ancho sin desbordar en todo
   el rango. En tono bajo: es un remate, no un segundo titular. */
.footer__wordmark {
  margin-top: var(--sp-8);
  margin-bottom: var(--sp-5);
  font-family: var(--font-display);
  /* El tope en rem lo manda .wrap, no la pantalla: por encima de
     ~1650px el contenedor ya topó en --maxw (1520px, menos dos gutters
     = 1392px de ancho útil) mientras 13vw seguía creciendo, y la marca
     desbordaba — con overflow:hidden eso salía como la última letra
     cortada. 13rem x 6.5 = 1352px: entra siempre, con holgura. */
  font-size: min(13vw, 13rem);
  line-height: 0.82;
  /* Red de seguridad para el fallback: Georgia es un 7% más ancha que
     Marcellus y, durante el swap de la fuente, a este cuerpo desbordaría
     y sacaría barra horizontal en TODA la página. Recortado se nota
     mucho menos que un layout que salta. */
  overflow: hidden;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  text-align: center;
  white-space: nowrap;
  color: color-mix(in srgb, var(--ink) 16%, transparent);
  /* El letter-spacing deja un hueco tras la última letra que entra en la
     caja centrada, así que las letras quedan escoradas a la izquierda.
     El valor salió midiendo, no de la teoría: text-indent también ensancha
     la línea que se está centrando, así que sólo la mitad del tirón acaba
     siendo desplazamiento. 0.025em deja el centrado por debajo de 1px. */
  text-indent: 0.025em;
  user-select: none;
}
.footer__bottom {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-7);
  justify-content: space-between;
  align-items: baseline;
  padding-block: var(--sp-5);
  border-top: var(--border);
}
.footer__credit a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--rule);
  transition: color var(--dur-fast) var(--ease);
}
.footer__credit a:hover {
  color: var(--ink);
  text-decoration-color: var(--ink-63);
}
/* 1220 no es un número redondo elegido a ojo: con cuatro columnas, la
   última vale 1.45/4.95 del espacio libre, y por debajo de ese ancho
   baja de los 277px que pide la dirección — la calle se parte y deja
   "305" huérfano. Ahí se pasa a dos columnas, donde sobra medida. */
@media (max-width: 1220px) {
  .footer__grid {
    grid-template-columns: 1fr 1fr;
  }
}
@media (max-width: 560px) {
  .footer__grid {
    grid-template-columns: 1fr;
    gap: var(--sp-6);
    padding-bottom: var(--sp-7);
  }
  .footer__brand-desc {
    max-width: none;
  }
}

/* ---- PAGE BANNER — foto + velo para el encabezado de páginas internas ---- */
.page-banner {
  position: relative;
  height: 64svh;
  min-height: 460px;
  overflow: hidden;
  color: var(--bone);
}
.page-banner__media {
  position: absolute;
  inset: 0;
}
.page-banner__media::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    var(--veil-hero-top) 0%,
    var(--veil-hero-mid) 45%,
    var(--veil-hero-bottom) 100%
  );
}
.page-banner__inner {
  position: relative;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding-bottom: var(--sp-8);
}
.page-banner .lead {
  color: var(--bone-85);
}

/* ---- MASONRY — columns flow, each photo keeps its natural ratio.
   No cropping, no shared row unit: simplest way to mix vertical and
   horizontal photos without fighting their proportions. ---- */
.masonry {
  columns: 2;
  column-gap: var(--sp-5);
}
.masonry figure {
  overflow: hidden;
  background: var(--surface);
  break-inside: avoid;
  margin-bottom: var(--sp-5);
}
.masonry figure img {
  height: auto;
  transition: scale var(--dur-image) var(--ease);
}
.masonry figure:hover img {
  scale: var(--scale-hover);
}
/* 3 columnas para las galerías de proceso: sus fotos son de 370px de
   ancho, así cada columna queda cerca del tamaño original y no se ven
   ampliadas ni blandas. Con 2 columnas se estirarían al doble. */
.masonry--3 {
  columns: 3;
}
@media (max-width: 1100px) {
  .masonry--3 {
    columns: 2;
  }
}
@media (max-width: 760px) {
  .masonry,
  .masonry--3 {
    columns: 1;
  }
}

/* ---- GRID 2x2 — for sections with exactly 4 photos. Masonry columns
   leave a ragged/empty edge at low counts; a fixed even grid reads as
   intentional instead. ---- */
.grid-2x2 {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-5);
}
.grid-2x2 figure {
  aspect-ratio: 4/3;
  overflow: hidden;
  background: var(--surface);
}
.grid-2x2 figure img {
  transition: scale var(--dur-image) var(--ease);
}
.grid-2x2 figure:hover img {
  scale: var(--scale-hover);
}
@media (max-width: 760px) {
  .grid-2x2 {
    grid-template-columns: 1fr;
  }
}

/* ---- PROJECTS — dos proyectos en paralelo. Los controles viven FUERA
   de la foto: las flechas encima del material son el gesto que más
   envejece una galería. ---- */
/* Caja baja y cuerpo de titular: la caja alta apretada grita y no es el
   registro del resto del sitio. */
.projects-intro {
  max-width: 24ch;
  margin-bottom: var(--sp-9); /* mismo salto título → contenido del resto del sitio */
}
/* minmax(0,…) y no 1fr: antes de que Swiper arranque, los slides son
   bloques en línea y su min-content reventaría la columna. */
/* Dos columnas. Con tres proyectos el tercero cae solo en la segunda
   fila y deja hueco a la derecha: es decisión del cliente, prioriza que
   los tres se vean al mismo tamaño antes que cerrar la retícula.
   minmax(0,…) y no 1fr: antes de que Swiper arranque los slides son
   bloques en línea y su min-content reventaría la columna. */
.projects {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--sp-7);
}
/* Antes esto era etiqueta micro + título, exactamente el mismo patrón
   que las tarjetas de Services: la galería parecía la misma página. Y
   con nombres tipo "Residence 01" la etiqueta "Project 01" repetía el
   número. Ahora el nombre va solo, con un filete que corre hasta el
   borde de la columna: un recurso de revista, no de tarjeta. */
.project__head {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  margin-bottom: var(--sp-6);
}
/* Había subido a --fs-h1 para que hiciera de titular ya que la página no
   tiene masthead — el cliente lo encontró demasiado grande (misma queja
   que con "Services"). Vuelve a --fs-h2, el tamaño de un subtítulo, que
   es lo que en verdad es: la etiqueta de cada bloque, no el título de
   la página.
   flex:none media a "Ocean Six, Pompano Beach": a tamaños grandes no
   cabía en la columna y se salía por fuera. min-width:0 deja que el
   flex item encoja bajo su ancho de contenido, así un nombre largo
   envuelve a dos líneas en vez de desbordar — se mantiene por si un
   nombre futuro vuelve a ser más largo que su columna. */
.project__name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-display);
  /* Desde que los tres nombres llevan ciudad y estado, todos envuelven a
     dos líneas y el corte por defecto dejaba "FL" solo en la segunda.
     balance reparte las palabras entre las dos líneas. */
  text-wrap: balance;
}
/* Con nombres cortos ("Residence 01") el filete llena el resto de la
   fila; con uno largo que ya envuelve a dos líneas no hay fila que
   llenar y se encoge a 0 — desaparece en vez de forzar el ancho. */
.project__rule {
  flex: 0 1 3rem;
  height: 1px;
  background: var(--rule);
}
.project__slider {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: var(--surface);
}
/* Mismo acercamiento lento que las fotos del resto del sitio. */
.project__slider img {
  transition: scale var(--dur-image) var(--ease);
}
.project__slider:hover img {
  scale: var(--scale-hover);
}
/* Lámina de video: mismo encuadre que una foto (cover, sin bordes). Sin
   el zoom en hover — con los controles nativos encima, agrandar el
   fondo mientras se apunta a play/pausa se sentía inestable. */
.project__slider video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Barra de avance al filo inferior de la foto. Con 24 fotos, un progreso
   continuo orienta mejor que una fila de puntos. */
.project__progress.swiper-pagination-progressbar {
  top: auto;
  bottom: 0;
  height: 2px;
  background: color-mix(in srgb, var(--bone) 30%, transparent);
}
.project__progress .swiper-pagination-progressbar-fill {
  background: var(--gold);
}
/* Pie de lámina: contador a la izquierda, flechas a la derecha. */
.project__controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-top: var(--sp-4);
  padding-top: var(--sp-4);
  border-top: var(--border);
}
.project__counter {
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-63);
  font-variant-numeric: tabular-nums; /* el número no baila al cambiar */
}
.project__current {
  color: var(--ink);
}
.project__sep {
  margin-inline: var(--sp-1);
}
.project__nav {
  display: flex;
  gap: var(--sp-2);
}
/* En hueso, el filete al 14% de tinta y la flecha en tinta pasaban
   desapercibidos: el cliente pidió que resalten. Van en oro macizo —
   el único color de marca — y al pasar por encima se invierten a tinta. */
.project__arrow {
  position: relative;
  overflow: hidden;
  width: 46px;
  height: 46px;
  display: grid;
  place-items: center;
  background: var(--gold);
  border: 1px solid var(--gold);
  color: var(--bone);
  cursor: pointer;
  transition:
    color var(--dur-fast) var(--ease),
    border-color var(--dur-fast) var(--ease);
}
/* El relleno sube desde abajo en vez de aparecer de golpe. */
.project__arrow::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--ink);
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform var(--dur-fast) var(--ease);
}
.project__arrow svg {
  position: relative;
  width: 20px;
  height: 20px;
  /* El 1.4 del atributo del SVG se veía fino sobre el oro. */
  stroke-width: 1.8;
  transition: translate var(--dur-fast) var(--ease);
}
.project__arrow:hover {
  color: var(--bone);
  border-color: var(--ink);
}
.project__arrow:hover::before {
  transform: scaleY(1);
}
.project__arrow[data-dir="next"]:hover svg {
  translate: 3px 0;
}
.project__arrow[data-dir="prev"]:hover svg {
  translate: -3px 0;
}
@media (max-width: 760px) {
  .projects {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-8);
  }
}

/* Los bloques del proceso, bajo su título común. El margen superior
   iguala al de las grillas: mismo salto de "título → su contenido". */
.process-blocks {
  display: grid;
  gap: var(--sp-10);
  margin-top: var(--sp-9);
}

/* ---- SPLIT — alternating media/text two-column block ("What We Do") ---- */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gutter);
  align-items: center;
}
@media (max-width: 760px) {
  .split {
    grid-template-columns: 1fr;
  }
}
.split__media {
  aspect-ratio: 4/3;
  overflow: hidden;
  background: var(--surface);
}

/* ---- AUTO-SLIDER — autoplay slideshow used inside .split ---- */
.auto-slider {
  height: 100%;
  --swiper-pagination-color: var(--gold);
  --swiper-pagination-bullet-inactive-color: var(--bone);
  --swiper-pagination-bullet-inactive-opacity: 0.6;
  --swiper-preloader-color: var(--gold);
}
.auto-slider img {
  height: 100%;
}

/* ---- LIGHTBOX ---- */
img.js-lightbox {
  cursor: pointer;
}
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  padding: var(--sp-6);
  background: color-mix(in srgb, var(--ink) 90%, transparent);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--dur-fast) var(--ease),
    visibility var(--dur-fast) var(--ease);
}
.lightbox.is-open {
  opacity: 1;
  visibility: visible;
}
.lightbox__img {
  max-width: min(100%, 1400px);
  max-height: 90vh;
  width: auto;
  height: auto;
  object-fit: contain;
}
.lightbox__close {
  position: absolute;
  top: var(--sp-6);
  right: var(--sp-6);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: none;
  border: 1px solid var(--bone-85);
  color: var(--bone);
  font-size: 24px;
  line-height: 1;
  cursor: pointer;
  transition: opacity var(--dur-fast) var(--ease);
}
.lightbox__close:hover {
  opacity: 0.7;
}

/* ---- CONTACT — título grande + panel de ancho completo ---- */
.contact-title {
  padding-bottom: var(--sp-6);
}
.contact-panel {
  position: relative;
  min-height: clamp(560px, 68vh, 760px);
  overflow: hidden;
  color: var(--bone);
  display: flex;
  align-items: center;
}
.contact-panel__media {
  position: absolute;
  inset: 0;
}
.contact-panel__media::after {
  content: "";
  position: absolute;
  inset: 0;
  /* Se oscurece a la izquierda (donde va el texto sobre la foto) y se
     aclara hacia la derecha, donde la tarjeta ya trae su propio fondo. */
  background: linear-gradient(
    100deg,
    var(--veil-hero-bottom) 0%,
    var(--veil-hero-mid) 40%,
    transparent 68%
  );
}
.contact-panel__inner {
  position: relative;
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-8);
  padding: var(--sp-8) var(--gutter);
}
.contact-hero__text {
  max-width: 46ch;
  flex: 1 1 380px;
}
/* Era sp-5 cuando debajo iba una entradilla. El cliente la quitó, así
   que este margen es ahora todo el aire entre el titular y el teléfono:
   toma el valor que traía la entradilla. */
.contact-hero__headline {
  margin-bottom: var(--sp-7);
}
/* Antes este titular mezclaba las dos familias en una misma frase (caja
   alta en la de texto + cursiva en la de títulos). Ahora las dos líneas
   son Marcellus: el contraste lo dan caja, tamaño y color, no la familia
   — que es justo lo que pidió el cliente. */
.contact-hero__headline-caps {
  display: block;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: clamp(1.75rem, 1rem + 3.2vw, 2.75rem);
  line-height: 1.1;
  letter-spacing: var(--tr-wide);
}
.contact-hero__headline-accent {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(2.4rem, 1rem + 5.2vw, 4.75rem);
  line-height: 1.05;
  letter-spacing: var(--tr-display);
  color: var(--gold);
}
/* Ya es un número real: se trata como el dato principal del panel, no
   como el marcador discreto que era antes. */
.contact-hero__phone {
  position: relative;
  display: inline-block;
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--tr-display);
  color: var(--bone);
  margin-bottom: var(--sp-6);
}
.contact-hero__phone::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-fast) var(--ease);
}
.contact-hero__phone:hover::after,
.contact-hero__phone:focus-visible::after {
  transform: scaleX(1);
}
.contact-hero__meta {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  font-size: var(--fs-small);
  line-height: 1.6;
  color: var(--bone-85);
}
.contact-card {
  flex: 0 1 400px;
  background: var(--bone);
  color: var(--ink);
  padding: var(--sp-7) var(--sp-6);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
/* Sin itálica: Marcellus no la trae y la sintética del navegador
   se ve barata justo donde el sitio quiere verse caro. */
.contact-card__logo {
  width: clamp(140px, 55%, 180px);
  height: auto;
  margin-inline: auto;
  /* Debajo iba una frase que el cliente quitó. Sin ella el logo quedaba
     a un solo gap del primer campo. */
  margin-bottom: var(--sp-2);
}
.contact-field input,
.contact-field textarea,
.contact-field select {
  width: 100%;
  padding: var(--sp-4);
  background: var(--surface);
  border: 1px solid var(--rule);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
}
/* Solo vertical: arrastrar en horizontal desbordaba la tarjeta. */
.contact-field textarea {
  resize: vertical;
  min-height: 7rem;
  line-height: var(--lh-body);
}
.contact-field select {
  appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--ink-63) 50%),
    linear-gradient(135deg, var(--ink-63) 50%, transparent 50%);
  background-position:
    calc(100% - 22px) center,
    calc(100% - 17px) center;
  background-size:
    5px 5px,
    5px 5px;
  background-repeat: no-repeat;
}
.contact-field input::placeholder,
.contact-field textarea::placeholder {
  color: var(--ink-63);
}
.contact-field input:focus,
.contact-field textarea:focus,
.contact-field select:focus {
  outline: none;
  border-color: var(--gold);
}
.contact-card__submit {
  justify-content: center;
  margin-top: var(--sp-2);
  background: var(--ink);
  color: var(--bone);
  border-color: var(--ink);
}
.contact-card__submit:hover {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--ink);
}
.contact-card__fine {
  font-size: 0.75rem;
  line-height: 1.5;
  color: var(--ink-63);
  text-align: center;
}
/* SweetAlert2 en la paleta del sitio: esquinas rectas, yeso cálido,
   Marcellus en el título, botón oscuro que pasa a oro al hover. */
.swal-sky.swal2-popup {
  background: var(--bone);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: 0;
  font-family: var(--font-body);
  padding: var(--sp-6);
}
.swal-sky .swal2-title {
  font-family: var(--font-display);
  font-weight: 400;
  color: var(--ink);
}
.swal-sky .swal2-html-container {
  color: var(--ink-63);
}
.swal-sky .swal2-html-container a {
  color: var(--ink);
  text-decoration: underline;
}
.swal-sky__btn.swal2-confirm {
  margin-top: var(--sp-2);
  background: var(--ink);
  color: var(--bone);
  border-color: var(--ink);
}
.swal-sky__btn.swal2-confirm:hover {
  background: var(--gold);
  border-color: var(--gold);
  color: var(--ink);
}
.swal-sky .swal2-loader {
  border-color: var(--gold) transparent var(--gold) transparent;
}
.swal-sky .swal2-icon.swal2-success .swal2-success-ring {
  border-color: var(--gold-30);
}
.swal-sky .swal2-icon.swal2-success [class^="swal2-success-line"] {
  background-color: var(--gold);
}
.swal-sky .swal2-icon.swal2-error {
  border-color: color-mix(in srgb, #b23b3b 30%, transparent);
  color: #b23b3b;
}
.swal-sky .swal2-icon.swal2-error [class^="swal2-x-mark-line"] {
  background-color: #b23b3b;
}
@media (max-width: 760px) {
  .contact-panel {
    min-height: 0;
  }
  .contact-panel__inner {
    padding: var(--sp-7) var(--gutter);
  }
  .contact-card {
    flex-basis: 100%;
  }
}

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
  .hero__line,
  .reveal {
    opacity: 1 !important;
    transform: none !important;
  }
}
