/* =========================================================
   kull·arq — Sistema de diseño
   Paleta oficial: negro grafito #212325, taupe #8C7F77,
   gris claro #F2F2F2, verde salvia #8F9B87
   Tipografía: General Sans (alternativa libre a Aeonik) +
   Azeret Mono (alternativa libre a Aeonik Mono) para subtítulos.
   Aeonik/Aeonik Mono solo se habilitarían con licencia web
   comercial de CoType Foundry (las de prueba no son publicables).
   ========================================================= */

:root{
  /* Paleta oficial KULL·ARQ (manual de marca) */
  --c-black:#212325;       /* negro grafito — superficies oscuras dominantes */
  --c-white:#FFFFFF;
  --c-offwhite:#F2F2F2;
  --c-stone:#6E6259;       /* taupe (oscurecido para AA) — texto muted sobre claro */
  --c-sand:#C2CCB9;        /* verde salvia (aclarado, 5.8:1 en ambas direcciones sobre navy) — acento y texto sobre oscuro */
  --c-sand-dark:#55614F;   /* verde salvia oscuro (6.5:1 sobre blanco) — texto y fondo de hover con más margen de contraste */
  --c-slate:#3A4550;       /* gris con tinte navy — copy secundario sobre fondo claro (reemplaza grises sueltos #333/#444/#3a3a3a/#4a4a4a) */
  --c-mist:#DCE1E6;        /* gris claro con tinte navy — copy secundario sobre fondo oscuro (7.3:1) */
  --c-fog:#B3BCC4;         /* gris medio con tinte navy — copy terciario/fine print sobre fondo oscuro (5:1) */
  --c-line:rgba(38,38,38,.12);
  --c-line-invert:rgba(255,255,255,.18);

  --font-serif:'General Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-sans:'General Sans', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Arial, sans-serif;
  --font-mono:'Azeret Mono', ui-monospace, 'SF Mono', 'Roboto Mono', monospace;

  --container:1280px;
  --gutter:clamp(20px, 5vw, 64px);
  --radius:2px;
  --ease:cubic-bezier(.16,1,.3,1);
  --dur:.6s;

  --fs-h1:clamp(3rem, 7.5vw, 6.4rem);
  --fs-h2:clamp(2rem, 4vw, 3.4rem);
  --fs-h3:clamp(1.4rem, 2.4vw, 1.9rem);
  --fs-lead:clamp(1.1rem, 1.6vw, 1.35rem);
  --fs-body:1rem;
  --fs-eyebrow:.78rem;
}

*,*::before,*::after{ box-sizing:border-box; }
html{ scroll-behavior:smooth; }

/* ---------- transición nativa entre páginas ----------
   sitio sin build tooling ni router: cada link es una navegación real de
   documento completo. La View Transitions API cross-document (soportada en
   Chrome/Edge, se ignora sola en el resto) le da continuidad visual a ese
   salto sin escribir un router falso encima: el navegador cruza-desvanece
   el documento saliente con el entrante. Solo se ajusta duración/curva para
   que use el mismo lenguaje de easing que el resto del sitio en vez del
   default del navegador. */
@view-transition{ navigation:auto; }
::view-transition-old(root),
::view-transition-new(root){
  animation-duration:.5s;
  animation-timing-function:var(--ease);
}

body{
  margin:0;
  font-family:var(--font-sans);
  font-size:var(--fs-body);
  line-height:1.6;
  color:var(--c-black);
  background:var(--c-white);
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
ul{ list-style:none; margin:0; padding:0; }
button{ font-family:inherit; cursor:pointer; }
input,textarea,select{ font-family:inherit; font-size:1rem; }
h1,h2,h3,h4,p{ margin:0; }

h1,h2,h3,h4{
  font-family:var(--font-serif);
  font-weight:500;
  line-height:1.05;
  letter-spacing:-.005em;
}

.visually-hidden{
  position:absolute; width:1px; height:1px; overflow:hidden;
  clip:rect(0 0 0 0); white-space:nowrap;
}

/* ---------- layout ---------- */
.container{
  width:100%;
  max-width:var(--container);
  margin-inline:auto;
  padding-inline:var(--gutter);
}
.section{
  position:relative; z-index:1; background:var(--c-white); /* tapa el hero sticky al pasar por encima */
  padding-block: clamp(64px, 10vw, 140px);
}
.section--tight{ padding-block: clamp(40px, 6vw, 80px); }
.section--dark{ background:var(--c-black); color:var(--c-white); }
.section--offwhite{ background:var(--c-offwhite); }

.grid{ display:grid; gap:var(--gutter); }
.grid-2{ grid-template-columns:repeat(2,1fr); }
.grid-3{ grid-template-columns:repeat(3,1fr); }
.grid-4{ grid-template-columns:repeat(4,1fr); }

.eyebrow{
  display:inline-flex; align-items:center; gap:10px;
  font-family:var(--font-mono);
  font-size:var(--fs-eyebrow);
  font-weight:500;
  letter-spacing:.1em;
  text-transform:uppercase;
  color:var(--c-stone);
}
.eyebrow::before{
  content:"";
  width:28px; height:1px;
  background:currentColor;
}
.section--dark .eyebrow, .page-banner .eyebrow{ color:var(--c-sand); }
.hero .eyebrow{ color:var(--c-white); }

.lead{ font-size:var(--fs-lead); color:var(--c-slate); line-height:1.55; }
.section--dark .lead{ color:var(--c-mist); }

/* ---------- buttons ----------
   duraciones cortas (200ms hover, 120ms press) a propósito: un CTA que
   tarda 600ms en cambiar de color se siente pesado incluso si el usuario
   solo lo pasa a mirar una vez. ease-out fuerte (--ease) para que la
   respuesta se perciba inmediata, no un ease-in que arranca lento. */
/* forma: los CTA son la única familia de elementos que rompe el
   SHAPE CONSISTENCY LOCK del sitio (--radius:2px, esquinas casi rectas en
   tarjetas/fotos/lightbox). Regla documentada: el pill (full-radius) queda
   reservado para botones de acción — filter-btn ya lo usaba — así el ojo
   distingue "esto se puede tocar" de "esto es contenido/arquitectura". */
.btn{
  display:inline-flex; align-items:center; gap:14px;
  padding:17px 34px;
  font-size:.85rem;
  font-weight:600;
  letter-spacing:.06em;
  text-transform:uppercase;
  border:1px solid var(--c-black);
  background:var(--c-black);
  color:var(--c-white);
  border-radius:999px;
  transition:background 200ms var(--ease), color 200ms var(--ease), border-color 200ms var(--ease), transform 200ms var(--ease), box-shadow 200ms var(--ease), backdrop-filter 200ms var(--ease), -webkit-backdrop-filter 200ms var(--ease);
  white-space:nowrap;
}
.btn:active{ transform:scale(.97); transition-duration:120ms; }
/* la flecha llega con un pequeño rebote (overshoot) en vez de frenar seco:
   un detalle que casi nadie nota conscientemente pero que hace que el
   botón se sienta "vivo" en la única vez que lo va a hover-ear un visitante */
.btn svg{ width:16px; height:16px; transition:transform 260ms cubic-bezier(.34,1.56,.64,1); }
.btn:focus-visible, .text-link:focus-visible{
  outline:2px solid var(--c-sand-dark); outline-offset:3px;
}

@media (hover:hover) and (pointer:fine){
  /* verde salvia oscuro (no el claro): sobre fondos claros el sand claro casi
     no se distingue del fondo. El oscuro sí da contraste real. */
  .btn:hover{
    background:var(--c-sand-dark); border-color:var(--c-sand-dark); color:var(--c-white); transform:translateY(-2px);
    box-shadow:0 10px 24px -12px rgba(85,97,79,.55);
  }
  .btn:hover svg{ transform:translateX(5px); }

  .btn--outline:hover{
    background:var(--c-black); color:var(--c-white); border-color:var(--c-black); transform:translateY(-2px);
    box-shadow:0 10px 24px -12px rgba(33,35,37,.5);
  }
  /* se queda en la misma familia "glass" que el estado de reposo (más
     tinte, más blur) en vez de saltar a un relleno sólido con el texto
     invirtiendo de blanco a negro: ese salto se sentía como dos fondos
     distintos peleando en 200ms. */
  .btn--on-dark:hover{
    background:rgba(194,204,185,.32); backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
    border-color:var(--c-sand); color:var(--c-white); transform:translateY(-2px);
    box-shadow:0 10px 24px -12px rgba(194,204,185,.6);
  }
  .site-header.is-solid .btn--on-dark:hover{ background:var(--c-black); border-color:var(--c-black); color:var(--c-white); }
}
/* en touch no hay :hover real — sin este bloque, el estado hover de
   btn--outline/btn--on-dark quedaba "pegado" tras el tap hasta tocar otra
   parte de la pantalla. Se resuelve igual que ya estaba resuelto para .btn. */
@media not all and (hover:hover){
  .btn:active{ background:var(--c-sand-dark); border-color:var(--c-sand-dark); color:var(--c-white); }
  .btn--outline:active{ background:var(--c-black); color:var(--c-white); border-color:var(--c-black); }
  .btn--on-dark:active{ background:var(--c-sand); border-color:var(--c-sand); color:var(--c-black); }
  .site-header.is-solid .btn--on-dark:active{ background:var(--c-black); border-color:var(--c-black); color:var(--c-white); }
}

.btn--outline{
  background:transparent; color:var(--c-black); border-color:var(--c-black);
}

/* paleta: en vez del ghost-button transparente de siempre, un lavado de
   sage al 14% + blur de vidrio — así el CTA principal ya se lee como "el
   acento de la marca" en reposo, no solo cuando se hace hover. Sigue
   funcionando sobre cualquier foto de fondo porque el borde y el blur
   sostienen la legibilidad sin depender del contraste de la imagen. */
.btn--on-dark{
  border-color:rgba(255,255,255,.55);
  background:rgba(194,204,185,.14);
  backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
  color:var(--c-white);
}

/* el CTA del header usa .btn--on-dark porque arranca sobre foto/fondo oscuro,
   pero una vez que el header se pone sólido (blanco) ese mismo botón blanco
   sobre blanco queda invisible — lo pasamos a un estilo oscuro a juego con
   el logo, que ya hace el mismo swap en .is-solid. */
.site-header.is-solid .btn--on-dark{ border-color:var(--c-black); color:var(--c-black); background:rgba(85,97,79,.08); }

.text-link{
  position:relative; display:inline-block;
  font-weight:600; font-size:.9rem; letter-spacing:.03em;
  padding-bottom:4px;
  transition:color 200ms var(--ease);
}
.text-link::after{
  content:""; position:absolute; left:0; right:0; bottom:0; height:1px;
  background:currentColor; transform:scaleX(0); transform-origin:left;
  transition:transform 240ms var(--ease);
}
@media (hover:hover) and (pointer:fine){
  .text-link:hover::after{ transform:scaleX(1); transform-origin:left; }
  .text-link:hover{ color:var(--c-sand-dark); }
}

/* ---------- header ---------- */
.site-header{
  position:fixed; top:0; left:0; right:0; z-index:100;
  padding:22px 0;
  transition:background var(--dur) var(--ease), padding var(--dur) var(--ease), border-color var(--dur) var(--ease);
  border-bottom:1px solid transparent;
}
.site-header .container{
  display:flex; align-items:center; justify-content:space-between;
  gap:24px;
}
.site-header.is-light{ color:var(--c-white); }
.site-header.is-dark{ color:var(--c-black); }
.site-header.is-solid{
  background:rgba(255,255,255,.92);
  backdrop-filter:blur(10px);
  border-color:var(--c-line);
  padding:16px 0;
  color:var(--c-black);
}

/* ---------- header con el menú móvil abierto ----------
   el bug real de la X invisible no era de color: .site-header vive en su
   propio contexto de apilamiento (position:fixed + z-index:100), así que
   el z-index:210 del burger solo vale DENTRO de ese contexto. .mobile-nav
   es un hermano posterior con z-index:200, y como header (100) < mobile-nav
   (200), el overlay tapa el header completo (burger incluido) sin importar
   su z-index interno. Acá se sube el header entero por encima del overlay
   mientras el menú está abierto, y de paso se lo deja transparente con
   look claro (texto/logo blanco), para no arrastrar la barra blanca
   borrosa de .is-solid encima del menú oscuro si se abre habiendo
   scrolleado. */
.site-header:has(+ .mobile-nav.is-open){
  z-index:210;
  background:transparent;
  backdrop-filter:none;
  border-color:transparent;
  color:var(--c-white);
}
.site-header:has(+ .mobile-nav.is-open) .logo-icon--white{ display:block; }
.site-header:has(+ .mobile-nav.is-open) .logo-icon--black{ display:none; }

.logo{
  display:flex; align-items:center; gap:12px; white-space:nowrap;
  transition:transform 150ms var(--ease);
}
/* feedback de press en el logo, igual que en los botones del sitio */
.logo:active{ transform:scale(.97); }
.logo svg{ width:26px; height:26px; }
.logo-mark{ position:relative; width:26px; height:26px; flex-shrink:0; display:inline-flex; }
.logo-icon{ position:absolute; inset:0; width:100%; height:100%; object-fit:contain; }
.logo-icon--black{ display:none; }
.site-header.is-solid .logo-icon--white{ display:none; }
.site-header.is-solid .logo-icon--black{ display:block; }
.logo-text{
  display:inline-flex;
  align-items:baseline;
  white-space:nowrap;
  font-family:var(--font-sans);
  font-size:1.05rem; font-weight:500;
  letter-spacing:.14em; text-transform:lowercase;
}
.logo-text .logo-dot{ display:inline-block; color:var(--c-sand); margin:0 .05em; }

/* ---------- logo: gesto sutil al pasar el mouse ----------
   nada grande: solo el isotipo responde (gira/escala), el punto entre
   "kull" y "arq" queda quieto para no repartir la atención entre dos
   piezas que se mueven a la vez. Usa la misma curva con rebote que ya
   define el vocabulario de motion del sitio (flecha de los botones,
   indicador del nav: cubic-bezier(.34,1.56,.64,1)) en vez de la curva
   suave genérica, para que el gesto se sienta coherente con el resto de
   los micro-detalles. Sigue siendo un efecto mínimo y rápido: el logo se
   ve pocas veces por sesión y no debe competir por atención. */
@media (hover:hover) and (pointer:fine){
  .logo-mark{ transition:transform 380ms cubic-bezier(.34,1.56,.64,1); }
  .logo:hover .logo-mark{ transform:scale(1.1) rotate(-8deg); }
}

.nav-desktop{ display:flex; align-items:center; gap:40px; }
.nav-desktop ul{ display:flex; gap:34px; position:relative; }
.nav-desktop a{
  position:relative; font-size:.85rem; font-weight:500; letter-spacing:.02em;
  padding-bottom:4px;
}
.nav-desktop a.is-active{ font-weight:600; }
.nav-desktop a:focus-visible, .logo:focus-visible{ outline:2px solid currentColor; outline-offset:4px; border-radius:2px; }
/* ---------- indicador deslizante del nav ----------
   un solo elemento se reposiciona (left/width) con JS bajo el link que
   tiene el mouse encima, en vez de que cada link dibuje su propio
   subrayado por separado: así se desliza de uno a otro en vez de
   aparecer/desaparecer. La curva con leve rebote es la misma que ya usa
   la flechita de los botones (mismo vocabulario de movimiento del sitio),
   y le da esa sensación de "inercia" al pasar de un link a otro. El JS
   pone left/width inline en cada hover; acá solo vive la transición. */
.nav-desktop__indicator{
  position:absolute; left:0; bottom:0; width:0; height:1px;
  background:currentColor; opacity:0;
  pointer-events:none;
  transition:left .45s cubic-bezier(.34,1.56,.64,1), width .45s cubic-bezier(.34,1.56,.64,1), opacity .2s var(--ease);
}

.nav-cta{ display:flex; align-items:center; gap:22px; }

.burger{
  display:none;
  width:44px; height:44px;
  align-items:center; justify-content:center;
  background:none; border:none; color:inherit;
  position:relative; z-index:210;
  transition:transform .12s ease-out;
}
.burger:active{ transform:scale(.9); }
.burger:focus-visible{ outline:2px solid currentColor; outline-offset:2px; }
.burger .bar{
  position:relative; width:24px; height:2px; background:currentColor;
  transition:background var(--dur);
}
.burger .bar::before,.burger .bar::after{
  content:""; position:absolute; left:0; width:24px; height:2px; background:currentColor;
  transition:transform .35s var(--ease), top .35s var(--ease);
}
.burger .bar::before{ top:-8px; }
.burger .bar::after{ top:8px; }
/* con el header en estado .is-solid (scrolleado, texto negro) el burger
   hereda ese color:inherit; al abrir el menú, esa X queda dibujada en
   negro justo encima del fondo negro de .mobile-nav y desaparece. El
   menú se abre igual (el botón sigue ahí y sigue funcionando), pero
   visualmente no hay X para cerrar. Fuerza blanco acá, sin depender del
   estado del header, porque .mobile-nav siempre es de fondo oscuro. */
.burger.is-open{ color:var(--c-white); }
.burger.is-open .bar{ background:transparent; }
.burger.is-open .bar::before{ top:0; transform:rotate(45deg); }
.burger.is-open .bar::after{ top:0; transform:rotate(-45deg); }

.mobile-nav{
  position:fixed; inset:0; z-index:200;
  background:var(--c-black); color:var(--c-white);
  display:flex; flex-direction:column; justify-content:center;
  padding:100px var(--gutter) 60px;
  transform:translateY(-100%);
  transition:transform .55s var(--ease);
  overflow-y:auto;
}
.mobile-nav.is-open{ transform:translateY(0); }
.mobile-nav ul{ display:flex; flex-direction:column; gap:6px; }
.mobile-nav a{
  font-family:var(--font-serif); font-size:clamp(2rem,9vw,3rem);
  font-weight:500; padding:14px 0; border-bottom:1px solid var(--c-line-invert);
  display:block;
}
/* ---------- hero (home) ----------
   sticky: la imagen queda fija en pantalla mientras la sección siguiente
   sube por encima y la va tapando al hacer scroll (efecto "cortina"). */
.hero{
  position:sticky; top:0; z-index:0;
  height:100vh;
  display:flex; align-items:center;
  color:var(--c-white);
  /* fondo oscuro propio: mientras la ventana de .hero__media todavía está
     chica (clip-path angosto, ver más abajo) el resto de la sección no tiene
     otra imagen de la que depender, así que el texto blanco del eyebrow y el
     título quedan invisibles sobre el blanco del body. */
  background:var(--c-black);
  overflow:hidden;
  box-sizing:border-box;
}
.hero__media{
  position:absolute; inset:0; z-index:0;
  /* sin JS (o con prefers-reduced-motion), clip-path queda en "none": la
     foto se ve completa desde el arranque, exactamente como si este efecto
     no existiera. El JS es el único que la achica con un inset() chico al
     cargar y la vuelve a agrandar con el scroll. */
  clip-path:none;
}
.hero__media img{
  width:100%; height:100%; object-fit:cover; object-position:62% 50%;
  /* foto a color, con la exposición lo más cerca posible de la real: al
     volver el hero a solo título (sin subtítulo ni CTAs) hay menos texto
     que proteger, así que se pudo subir el brillo bastante más sin perder
     el mínimo de contraste verificado abajo; el encuadre se corre levemente
     a la derecha para dejar que la construcción respire del lado opuesto
     al bloque de texto */
  filter:brightness(1.14) contrast(1.04) saturate(1.05);
}
.hero__media::after{
  content:""; position:absolute; inset:0;
  /* doble velo: el degradé vertical mantiene el texto siempre legible de
     arriba a abajo, y el degradé horizontal oscurece solo la mitad
     izquierda (donde vive el texto) dejando que la foto se vea con más
     nitidez y color del lado derecho, más "hero de foto" y menos "cartel".
     El h1 tiene su propio text-shadow (ver abajo) como red de contraste
     adicional. Con la foto más clara de arriba, este mismo velo (sin
     tocarlo) sigue sosteniendo ~3.3:1 en el peor pixel medido, con margen
     real sobre el mínimo WCAG AA de 3:1 para texto grande. */
  background:
    linear-gradient(100deg, rgba(7,9,13,.30) 0%, rgba(7,9,13,.23) 42%, rgba(7,9,13,.21) 100%),
    linear-gradient(180deg, rgba(9,12,17,.24) 0%, rgba(7,9,13,.40) 100%);
}
.hero__content{
  position:relative; z-index:1; padding:0 0 24px; width:100%;
  display:flex; flex-direction:column; align-items:center; text-align:center;
  will-change:opacity, transform;
}

/* ---------- hero__frame-slot: la ventana que crece hasta ser el fondo ----------
   la foto de .hero__media es una sola, siempre la misma, siempre a pantalla
   completa en el DOM; lo que cambia al cargar la página es un
   clip-path:inset() que al principio la recorta a una ventanita chica (del
   tamaño y posición de .hero__frame-slot, un div vacío que solo reserva ese
   espacio en el flujo del texto) y que el JS va agrandando hasta cubrir
   toda la sección. Todo el inset queda en manos de GSAP (un solo tween con
   onUpdate): sin JS, clip-path queda en "none" (ver arriba), así el hero se
   ve exactamente como sin este efecto. */
.hero__frame-slot{
  width:clamp(140px, 16vw, 220px);
  aspect-ratio:4/3;
  margin-bottom:22px;
}

.hero .eyebrow{ text-shadow:0 1px 4px rgba(0,0,0,.55); }
.hero .eyebrow::before{ content:none; }
.hero h1{
  /* tamaño propio del hero, más chico que --fs-h1 (pensado para títulos
     cortos de 3-5 palabras): este título tiene 8, así que a ese tamaño
     desborda a 3 líneas incluso con max-width acotado. clamp() más
     conservador + un ancho generoso (no en ch, para no atarlo al tamaño
     de fuente) para que quede en 2 líneas balanceadas en la mayoría de
     los viewports. */
  font-size:clamp(2.25rem, 4.4vw, 4.25rem); color:var(--c-white);
  max-width:min(780px, 86vw);
  margin-top:22px;
  letter-spacing:-.02em;
  text-wrap:balance;
  /* red de contraste propia (misma sombra que ya usa el eyebrow): con el
     velo de .hero__media::after más liviano, el texto ya no puede depender
     solo de ese degradé para leerse bien en cualquier punto de la foto. */
  text-shadow:0 1px 4px rgba(0,0,0,.55);
}
.hero h1 em{ font-style:italic; font-weight:500; }

/* ---------- entrada del hero: texto revelado detrás de una máscara ----------
   cada bloque (eyebrow, título, subtítulo) va dentro de un contenedor con
   overflow:hidden; el contenido interno arranca corrido 115% hacia abajo y
   "sube" a su lugar con delay escalonado — el efecto de reveal enmascarado
   se ve más premium que un simple fade, y como es una animación de entrada
   (no ligada a scroll) no compite con el fade que ya hace el JS al scrollear. */
.hero-reveal{ overflow:hidden; }
.hero-reveal > *{
  animation:heroRevealUp 1s var(--ease) both;
}
.hero-reveal--eyebrow > *{ animation-delay:.1s; animation-duration:.85s; }
@keyframes heroRevealUp{
  from{ opacity:0; transform:translateY(115%); }
  to{ opacity:1; transform:translateY(0); }
}

/* el título (h1) es un caso especial: en vez de que el bloque entero suba
   detrás de una única máscara, cada palabra tiene su propia máscara y su
   propio delay (fijado inline en el HTML, .05s por palabra). Se apaga la
   regla genérica ".hero-reveal > *" para el h1 (si no, el h1 completo
   también subiría como bloque mientras cada palabra sube adentro, doble
   movimiento) y se deja la entrada enteramente en manos de .word/.word__inner. */
.hero-reveal--title > *{ animation:none; opacity:1; }
.hero-reveal--title h1 .word{ display:inline-block; overflow:hidden; vertical-align:top; }
.hero-reveal--title h1 .word__inner{
  display:inline-block;
  animation:heroRevealUp .9s var(--ease) both;
}
.hero__scroll{
  position:absolute; right:var(--gutter); bottom:36px; z-index:1;
  display:flex; flex-direction:column; align-items:center; gap:10px;
  font-family:var(--font-mono);
  font-size:.7rem; letter-spacing:.12em; text-transform:uppercase; color:#ddd;
}
.hero__scroll .line{ width:1px; height:52px; background:rgba(255,255,255,.5); overflow:hidden; position:relative; }
.hero__scroll .line::after{
  content:""; position:absolute; top:-100%; left:0; width:100%; height:100%; background:var(--c-sand);
  animation:scrollLine 2.2s var(--ease) infinite;
}
@keyframes scrollLine{ to{ top:100%; } }

/* ---------- etiqueta vertical (esquina opuesta al "Scroll") ----------
   "Obra nueva y remodelaciones" vivía como eyebrow chico arriba del título:
   ese patrón (kicker trackeado + headline debajo, repetido en TODAS las
   secciones del sitio) es la señal más reconocible de diseño hecho con IA
   que existe. Acá se saca esa info de la antesala del título y se la trata
   como un detalle editorial aparte, en espejo con .hero__scroll del otro
   lado: mismo font-size/letter-spacing/línea vertical, leída de abajo hacia
   arriba (writing-mode + rotate). El título ahora abre solo, sin preámbulo. */
.hero__tag{
  position:absolute; left:var(--gutter); bottom:36px; z-index:1;
  display:flex; flex-direction:column; align-items:center; gap:10px;
  font-family:var(--font-mono);
  font-size:.7rem; letter-spacing:.12em; text-transform:uppercase; color:#ddd;
  writing-mode:vertical-rl; transform:rotate(180deg);
  text-shadow:0 1px 4px rgba(0,0,0,.55);
  opacity:0; animation:heroTagIn .8s var(--ease) .5s both;
}
.hero__tag-line{ width:1px; height:52px; background:rgba(255,255,255,.5); }
@keyframes heroTagIn{
  from{ opacity:0; transform:rotate(180deg) translateY(14px); }
  to{ opacity:1; transform:rotate(180deg) translateY(0); }
}

/* ---------- page banner (inner pages) ----------
   fondo negro liso + un radial-gradient suave, texto más grande que antes
   y con la misma entrada con máscara (.hero-reveal) que ya tiene el hero
   de la home, así las páginas internas no arrancan estáticas mientras la
   home sí anima. */
.page-banner{
  position:relative; padding:170px 0 100px;
  background:var(--c-black); color:var(--c-white);
  overflow:hidden;
}
.page-banner__grain{ position:absolute; inset:0; opacity:.5; background:radial-gradient(circle at 80% 20%, rgba(194,204,185,.12), transparent 55%); }
.page-banner h1{
  font-size:clamp(2.5rem, 5.2vw, 4.6rem); color:var(--c-white); max-width:20ch;
  margin-top:18px; letter-spacing:-.02em; position:relative; text-wrap:balance;
}
.page-banner .lead{ margin-top:20px; max-width:50ch; position:relative; color:var(--c-mist); }
.hero-reveal--lead > *{ animation-delay:.4s; animation-duration:.9s; }

/* ---------- foto de servicios con zoom sutil ----------
   bloque de imagen independiente, debajo del page-banner de texto (no se
   superponen). Mismo mecanismo que .hero: sticky + z-index:0 debajo de la
   .section siguiente (z-index:1), así la sección tapa el bloque tipo
   "cortina" al scrollear. La foto hace un leve zoom mientras se scrollea
   para que no se sienta estática/plana. */
.reveal-media{
  position:sticky; top:0; z-index:0; height:100vh;
  overflow:hidden; background:var(--c-black);
}
.reveal-media img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  filter:brightness(.97) contrast(1.04) saturate(1.02);
  will-change:transform;
}

/* ---------- manifesto / quote ---------- */
.manifesto{ text-align:center; }
/* el eyebrow le da al manifiesto un contexto propio (no queda flotando como
   una frase suelta): reusa el mismo componente .eyebrow del hero y los
   page-banner en vez de sumar un patrón nuevo, y entra como primer paso de
   la misma cascada que ya revela las líneas de la cita. */
.manifesto .eyebrow{
  justify-content:center; margin-bottom:22px;
  opacity:0; transform:translateY(14px);
  transition:opacity .6s var(--ease), transform .6s var(--ease);
}
.manifesto .eyebrow::before{ content:none; }
.manifesto.reveal-toggle.is-visible .eyebrow{ opacity:1; transform:translateY(0); }
.manifesto blockquote{
  font-family:var(--font-sans); font-style:normal; font-weight:500;
  font-size:clamp(1.7rem, 3.6vw, 2.6rem); line-height:1.25; max-width:34ch; margin-inline:auto;
  letter-spacing:-.01em;
}
/* cada frase de la cita revela por separado, en cascada, en vez de que
   todo el bloque aparezca de golpe. el delay vive en la regla base (no
   adentro de .is-visible) para que el mismo escalonado se use tanto al
   entrar como al salir del viewport */
.manifesto__line{ display:block; overflow:hidden; }
.manifesto__line-inner{
  display:block;
  transform:translateY(100%); opacity:0;
  transition:transform .8s var(--ease), opacity .8s var(--ease);
}
.manifesto__line:nth-child(1) .manifesto__line-inner{ transition-delay:.11s; }
.manifesto__line:nth-child(2) .manifesto__line-inner{ transition-delay:.22s; }
.reveal-toggle.is-visible .manifesto__line-inner{ transform:translateY(0); opacity:1; }
.manifesto cite{
  display:block; margin-top:26px; font-family:var(--font-mono); font-style:normal;
  font-size:.76rem; letter-spacing:.05em; text-transform:uppercase; color:var(--c-stone);
  opacity:0; transform:translateY(14px);
  transition:opacity .6s var(--ease) .33s, transform .6s var(--ease) .33s;
}
.reveal-toggle.is-visible .manifesto cite{ opacity:1; transform:translateY(0); }

/* ---------- section head ---------- */
.section-head{
  display:flex; align-items:flex-end; justify-content:space-between; gap:40px;
  margin-bottom:56px; flex-wrap:wrap;
}
.section-head h2{ margin-top:16px; max-width:16ch; }
.section-head p{ max-width:38ch; }
/* variante apilada: título y texto de apoyo van uno debajo del otro
   (no en dos columnas) cuando el texto de la derecha es una explicación,
   no un link de "ver más" */
.section-head--stack{ display:block; }
.section-head--stack h2{ margin-top:0; max-width:20ch; }
.section-head--stack p{ margin-top:14px; max-width:60ch; }

/* ---------- service cards (home preview) ---------- */
.service-card{
  position:relative;
  border-top:1px solid var(--c-line);
  padding:36px 0;
  display:grid; grid-template-columns:minmax(160px,240px) 1fr auto;
  column-gap:32px; row-gap:6px; align-items:center;
  color:inherit; text-decoration:none;
  transition:padding-left .4s var(--ease), border-color .4s var(--ease);
}
.service-card:last-child{ border-bottom:1px solid var(--c-line); }
.service-card__body{ display:contents; }
/* el número va adentro del propio h3 (no como hermano suelto): .service-card__body
   usa display:contents para que h3 y p caigan directo en las columnas del grid
   de .service-card, así que agregar el numeral como hijo del h3 lo mantiene en
   su misma columna en vez de correr el resto de la grilla. El h3 pasa a ser un
   flex row para que el número quede a la izquierda y centrado con el título,
   incluso cuando el título envuelve a varias líneas. */
.service-card__body h3{ font-size:var(--fs-h3); display:flex; align-items:center; gap:14px; }
.service-card__num{
  flex-shrink:0;
  font-family:var(--font-mono); font-weight:500; font-size:.72rem;
  letter-spacing:.05em; color:var(--c-stone);
}
.service-card__label{ min-width:0; }
.service-card__body p{ margin-top:0; max-width:48ch; color:var(--c-slate); }
.service-card__arrow{
  width:44px; height:44px; border-radius:50%; border:1px solid var(--c-line);
  display:flex; align-items:center; justify-content:center; flex-shrink:0;
  transition:background .35s var(--ease), transform .35s var(--ease), border-color .35s var(--ease);
}
.service-card__arrow svg{ width:24px; height:24px; transform:translate(-1px, 1px); }
.service-card:active .service-card__arrow{ transform:scale(.92); }
.service-card:focus-visible{ outline:2px solid var(--c-black); outline-offset:-2px; }

@media (hover:hover) and (pointer:fine){
  .service-card:hover{ padding-left:14px; border-top-color:var(--c-black); }
  .service-card:hover .service-card__arrow{ background:var(--c-black); border-color:var(--c-black); transform:rotate(45deg); }
  .service-card:hover .service-card__arrow svg{ stroke:var(--c-white); }
}

/* ---------- miniatura que sigue al cursor sobre la lista de servicios ----------
   la lista de servicios era solo texto + flecha; al pasar el mouse por una
   fila aparece la foto de portada de ese servicio, siguiendo al cursor. Un
   único elemento fijo (mismo patrón que .nav-desktop__indicator: uno solo
   que se reposiciona, no uno por fila) cuya posición maneja JS con GSAP
   quickTo (xPercent/yPercent -50 centra el panel en el cursor, compuesto
   junto con x/y/scale en un solo transform por GSAP; por eso acá solo se
   anima opacity, cualquier transform declarado en CSS quedaría pisado por
   el inline style que arma GSAP en cada frame). Solo en mouse real: en
   touch no hay cursor que seguir, así que quedaría décorativo sin sentido. */
.service-hover-preview{
  position:fixed; top:0; left:0; z-index:150;
  width:260px; aspect-ratio:4/3;
  overflow:hidden;
  opacity:0;
  pointer-events:none;
  will-change:transform, opacity;
  transition:opacity .35s var(--ease);
}
.service-hover-preview.is-active{ opacity:1; }
.service-hover-preview img{
  width:100%; height:100%; object-fit:cover;
  filter:brightness(1.03) contrast(1.05) saturate(.92);
}
@media (hover:none), (pointer:coarse){
  .service-hover-preview{ display:none; }
}

/* ---------- cursor personalizado sobre fotos de proyecto ----------
   no reemplaza el cursor del sistema en toda la web (rompería el cursor de
   texto de los inputs del formulario, por ejemplo): aparece solo mientras el
   mouse está sobre una foto de proyecto (home o portafolio) y crece con un
   label ("Ver"). El elemento ancla (.custom-cursor) queda en 0x0 y solo lo
   mueve JS con x/y; el círculo visible es un hijo absoluto aparte
   (.custom-cursor__ring) que crece con width/height propios, sin pisar el
   transform que maneja GSAP en el ancla. */
.custom-cursor{ position:fixed; top:0; left:0; z-index:400; width:0; height:0; pointer-events:none; }
.custom-cursor__ring{
  position:absolute; top:0; left:0;
  width:14px; height:14px; margin:-7px 0 0 -7px;
  border-radius:50%; background:var(--c-black);
  display:flex; align-items:center; justify-content:center;
  opacity:0;
  transition:width .35s cubic-bezier(.16,1,.3,1), height .35s cubic-bezier(.16,1,.3,1), margin .35s cubic-bezier(.16,1,.3,1), opacity .25s var(--ease);
}
.custom-cursor.is-active .custom-cursor__ring{ opacity:1; }
.custom-cursor.is-target .custom-cursor__ring{ width:60px; height:60px; margin:-30px 0 0 -30px; }
.custom-cursor__label{
  font-family:var(--font-mono); font-size:.62rem; letter-spacing:.04em; text-transform:uppercase; color:var(--c-white);
  opacity:0; white-space:nowrap;
  transition:opacity .2s var(--ease) .1s;
}
.custom-cursor.is-target .custom-cursor__label{ opacity:1; }
@media (hover:none), (pointer:coarse){
  .custom-cursor{ display:none; }
}

/* ---------- projects grid ---------- */
/* grid asimétrico: un proyecto destacado (más grande, a la izquierda) y
   los otros dos apilados a la derecha, en vez de 3 columnas idénticas */
.project-preview-grid{ display:grid; grid-template-columns:1.15fr 1fr; gap:var(--gutter); }
.project-preview-grid__stack{ display:grid; grid-template-rows:1fr 1fr; gap:var(--gutter); }

.project-card{ position:relative; display:block; overflow:hidden; color:inherit; text-decoration:none; }
.project-card:focus-visible{ outline:2px solid var(--c-black); outline-offset:4px; }
.project-card__frame{ position:relative; overflow:hidden; aspect-ratio:16/11; background:var(--c-black); }
.project-card--feature .project-card__frame{ aspect-ratio:4/5; }
.project-card__frame img{
  width:100%; height:100%; object-fit:cover;
  filter:grayscale(.85) contrast(1.05) brightness(1.02);
  transition:transform 1.1s var(--ease), filter .6s var(--ease);
}
@media (hover:hover) and (pointer:fine){
  .project-card:hover .project-card__frame img{ transform:scale(1.06); filter:grayscale(0) contrast(1.05); }
}
.project-card:active .project-card__frame img{ filter:grayscale(0) contrast(1.05); }
.project-card__tag{
  display:block; margin-bottom:6px;
  font-family:var(--font-mono); font-size:.7rem; font-weight:500;
  letter-spacing:.06em; text-transform:uppercase; color:var(--c-stone);
}
.project-card__info{ padding-top:18px; display:flex; justify-content:space-between; align-items:flex-start; gap:14px; }
.project-card__info h3{ font-size:1.2rem; }
.project-card__meta{ font-size:.8rem; color:var(--c-stone); display:block; margin-top:4px; }
.project-card__view{
  flex-shrink:0; width:42px; height:42px; border-radius:50%; border:1px solid var(--c-line);
  display:flex; align-items:center; justify-content:center;
  transition:background .35s var(--ease), border-color .35s var(--ease), transform .35s var(--ease);
}
.project-card__view svg{ width:24px; height:24px; transform:translate(-1px, 1px); }
.project-card__view:active{ transform:scale(.92); }
@media (hover:hover) and (pointer:fine){
  .project-card:hover .project-card__view{
    background:var(--c-black); border-color:var(--c-black); transform:rotate(45deg);
  }
  .project-card:hover .project-card__view svg{ stroke:var(--c-white); }
}

/* ---------- project showcase (portafolio) ----------
   portadas grandes: la primera y la cuarta de cada grupo de 4 ocupan todo
   el ancho, las dos del medio van en pareja — inspirado en el listado de
   proyectos de referencia. El patrón se repite cada 4 proyectos (nth-child
   con fórmula 4n, no índices fijos) para que la grilla siga funcionando
   sin importar cuántos proyectos haya en total. El hover revela
   categoría/ubicación/título sobre un velo oscuro (la señal de "clickeable"
   ahora la da el cursor personalizado, no un ícono fijo en el centro de la
   foto: tener los dos a la vez quedaba redundante y se superponían); en
   touch (sin hover) esa info queda visible siempre sobre un degradé, para
   no depender del hover. */
.project-showcase{ display:grid; grid-template-columns:repeat(2, 1fr); gap:32px; }
.project-showcase > .project-feature:nth-child(4n+1),
.project-showcase > .project-feature:nth-child(4n){ grid-column:1 / -1; }

.project-feature__link{ position:relative; display:block; overflow:hidden; }
.project-feature__media{ position:relative; overflow:hidden; background:var(--c-black); }
.project-showcase > .project-feature:nth-child(4n+1) .project-feature__media,
.project-showcase > .project-feature:nth-child(4n) .project-feature__media{ aspect-ratio:20/9; }
.project-showcase > .project-feature:nth-child(4n+2) .project-feature__media,
.project-showcase > .project-feature:nth-child(4n+3) .project-feature__media{ aspect-ratio:4/3; }
.project-feature__media img{
  width:100%; height:100%; object-fit:cover;
  transition:transform 1.1s var(--ease);
}
.project-feature__media::after{
  content:""; position:absolute; inset:0; z-index:1;
  background:rgba(9,12,17,.34); opacity:0; transition:opacity .45s var(--ease);
}
.project-feature__info{
  position:absolute; left:26px; right:26px; bottom:22px; z-index:2; color:var(--c-white);
  opacity:0; transform:translateY(10px);
  transition:opacity .4s var(--ease), transform .4s var(--ease);
}
.project-feature__row{
  display:flex; justify-content:space-between; gap:12px;
  font-family:var(--font-mono); font-size:.72rem; letter-spacing:.04em; text-transform:uppercase;
  color:var(--c-sand); margin-bottom:8px;
}
.project-feature__info h3{ font-size:1.35rem; color:var(--c-white); }

@media (hover:hover) and (pointer:fine){
  .project-feature__link:hover .project-feature__media img{ transform:scale(1.06); }
  .project-feature__link:hover .project-feature__media::after{ opacity:1; }
  .project-feature__link:hover .project-feature__info{ opacity:1; transform:translateY(0); }
}
@media not all and (hover:hover){
  .project-feature__media::after{
    opacity:1; background:linear-gradient(180deg, transparent 45%, rgba(9,12,17,.72) 100%);
  }
  .project-feature__info{ opacity:1; transform:none; }
}

/* filter bar */
.filter-bar{ display:flex; gap:10px; flex-wrap:wrap; margin-bottom:48px; }
.filter-btn{
  padding:11px 22px; border:1px solid var(--c-line); background:transparent;
  border-radius:40px; font-size:.82rem; font-weight:600; letter-spacing:.02em; color:var(--c-slate);
  transition:border-color .25s var(--ease), background .25s var(--ease), color .25s var(--ease), transform .12s ease-out;
}
.filter-btn:hover{ border-color:var(--c-black); color:var(--c-black); }
.filter-btn:focus-visible{ outline:2px solid var(--c-sand-dark); outline-offset:2px; }
.filter-btn:active{ transform:scale(.96); }
.filter-btn.is-active{ background:var(--c-black); border-color:var(--c-black); color:var(--c-white); }

/* lightbox */
.lightbox{
  position:fixed; inset:0; z-index:300; background:rgba(13,13,13,.94);
  display:flex; align-items:center; justify-content:center; padding:40px var(--gutter);
  opacity:0; visibility:hidden; transition:opacity .4s var(--ease);
}
.lightbox.is-open{ opacity:1; visibility:visible; }
.lightbox__wrap{
  position:relative;
  width:100%; max-width:1320px; max-height:92vh;
  display:flex; flex-direction:column; gap:16px;
  transform:translateY(28px) scale(.97); transition:transform .5s var(--ease);
}
.lightbox.is-open .lightbox__wrap{ transform:translateY(0) scale(1); }
.lightbox__panel{
  background:var(--c-white); width:100%; overflow:auto;
  display:grid; grid-template-columns:1.4fr 1fr; border-radius:2px;
  max-height:82vh;
}
/* la imagen de portada entra con un leve zoom-out al abrir, para que se
   sienta como si te asomaras al proyecto en vez de un corte seco. Además el
   panel entero (.lightbox__media) crece desde un punto chico en vez de solo
   aparecer: JS calcula ese punto a partir de la miniatura que se clickeó
   (transform-origin dinámico) y lo deja guardado como "efecto shared-element"
   aunque no sea un morph geométricamente exacto de la miniatura real (el
   layout del lightbox es muy distinto en mobile/desktop para eso); alcanza
   con que la foto grande nazca del lugar donde estaba el click. */
.lightbox__media{
  position:relative; aspect-ratio:3/2; max-height:82vh; overflow:hidden; background:var(--c-black);
  transform:scale(.4); transform-origin:50% 50%;
  transition:transform .55s cubic-bezier(.16,1,.3,1);
}
.lightbox.is-open .lightbox__media{ transform:scale(1); }
.lightbox__media img{
  width:100%; height:100%; object-fit:contain;
  transform:scale(1.08); transition:transform .6s var(--ease);
}
.lightbox.is-open .lightbox__media img{ transform:scale(1); }
.lightbox__body{ position:relative; padding:44px; display:flex; flex-direction:column; }
.lightbox__body .eyebrow{ margin-bottom:14px; color:var(--c-stone); }
.lightbox__body h3{ font-size:1.7rem; }
.lightbox__body p{ margin-top:16px; color:var(--c-slate); }
.lightbox__meta{ margin-top:auto; padding-top:28px; border-top:1px solid var(--c-line); display:grid; grid-template-columns:1fr 1fr; gap:16px; }
.lightbox__meta div span{ display:block; font-family:var(--font-mono); font-size:.68rem; text-transform:uppercase; letter-spacing:.04em; color:var(--c-stone); margin-bottom:4px; }
.lightbox__meta div strong{ font-weight:600; font-size:.92rem; }
.lightbox__close{
  /* vive adentro de .lightbox__wrap (no de .lightbox__body), así queda
     fija en la esquina del modal sin importar cuánto scrollees el panel
     interno o qué tan larga sea la descripción. Es un círculo blanco
     opaco (no semitransparente), así se lee bien tanto sobre la foto
     como sobre el panel blanco, caiga donde caiga. */
  position:absolute; top:20px; right:20px; width:40px; height:40px; border-radius:50%;
  background:var(--c-white); border:1px solid var(--c-line); color:var(--c-black);
  box-shadow:0 2px 10px rgba(13,13,13,.14);
  display:flex; align-items:center; justify-content:center;
  transition:background .25s var(--ease), border-color .25s var(--ease), color .25s var(--ease), transform .12s ease-out;
  z-index:5;
}
@media (hover:hover) and (pointer:fine){
  .lightbox__close:hover{ background:var(--c-black); border-color:var(--c-black); color:var(--c-white); }
}
.lightbox__close:active{ transform:scale(.92); }
.lightbox__close:focus-visible{ outline:2px solid var(--c-sand-dark); outline-offset:2px; }
.lightbox__close svg{ width:18px; height:18px; }

/* navegación de galería dentro del lightbox */
.lightbox__arrow{
  position:absolute; top:50%; transform:translateY(-50%); z-index:2;
  width:44px; height:44px; border-radius:50%; border:none;
  background:rgba(13,13,13,.45); color:#fff; display:flex; align-items:center; justify-content:center;
  transition:background .25s var(--ease), transform .15s ease-out;
}
.lightbox__arrow:active{ transform:translateY(-50%) scale(.88); }
.lightbox__arrow:focus-visible{ outline:2px solid var(--c-sand); outline-offset:2px; }
@media (hover:hover) and (pointer:fine){
  .lightbox__arrow:hover{ background:rgba(13,13,13,.75); }
}
.lightbox__arrow svg{ width:18px; height:18px; }
.lightbox__arrow--prev{ left:14px; }
.lightbox__arrow--next{ right:14px; }
.lightbox__counter{
  position:absolute; left:14px; bottom:14px; z-index:2;
  background:rgba(13,13,13,.55); color:#fff; font-size:.72rem; font-weight:600; letter-spacing:.04em;
  padding:6px 12px; border-radius:20px;
}
.lightbox__thumbs{
  display:flex; gap:8px; overflow-x:auto; padding:2px; scrollbar-width:thin;
}
.lightbox__thumb{
  flex:0 0 auto; width:64px; height:64px; border-radius:2px; overflow:hidden;
  border:2px solid transparent; opacity:.55; transition:opacity .25s var(--ease), border-color .25s var(--ease);
}
.lightbox__thumb img{ width:100%; height:100%; object-fit:cover; display:block; }
.lightbox__thumb.is-active{ opacity:1; border-color:var(--c-sand); }
@media (hover:hover) and (pointer:fine){
  .lightbox__thumb:hover{ opacity:.85; }
}

/* ---------- process timeline ---------- */
.process{ display:grid; grid-template-columns:repeat(4,1fr); gap:var(--gutter); }
/* la línea ya no está simplemente ahí: se dibuja de izquierda a derecha al
   entrar en viewport, con el mismo escalonado que ya trae .reveal-stagger.
   Refuerza que son pasos en secuencia, no cuatro tarjetas sueltas. */
.process__step{ position:relative; padding-top:24px; transition:opacity .35s var(--ease); }
.process__step::before{
  content:""; position:absolute; top:0; left:0; width:100%; height:2px;
  background:var(--c-black); transform-origin:left; transform:scaleX(0);
  transition:transform .7s var(--ease);
}
.process.is-visible .process__step::before{ transform:scaleX(1); }
.process.is-visible .process__step:nth-child(1)::before{ transition-delay:.05s; }
.process.is-visible .process__step:nth-child(2)::before{ transition-delay:.15s; }
.process.is-visible .process__step:nth-child(3)::before{ transition-delay:.25s; }
.process.is-visible .process__step:nth-child(4)::before{ transition-delay:.35s; }
.process__num{
  font-family:var(--font-serif); font-size:clamp(2.2rem, 4vw, 3rem); color:var(--c-sand-dark);
  transition:color .3s var(--ease);
}
.process__step h3{ font-size:1.15rem; margin-top:10px; }
.process__step p{ margin-top:10px; color:var(--c-slate); font-size:.92rem; }
/* al pasar el mouse por un paso, el resto se atenúa: ayuda a leer la
   secuencia de a un paso por vez en vez de los 4 compitiendo por atención */
@media (hover:hover) and (pointer:fine){
  .process:hover .process__step{ opacity:.45; }
  .process:hover .process__step:hover{ opacity:1; }
  .process__step:hover .process__num{ color:var(--c-black); }
}

/* ---------- zig-zag service detail ---------- */
.service-detail{
  display:grid; grid-template-columns:1fr 1fr; gap:clamp(30px,6vw,90px);
  align-items:center;
  padding-block:clamp(50px,8vw,100px);
  border-bottom:1px solid var(--c-line);
  scroll-margin-top:110px; /* despeja el header fijo al llegar por ancla (#obra-nueva, etc.) */
}
.service-detail:last-of-type{ border-bottom:none; }
.service-detail:nth-of-type(even) .service-detail__media{ order:2; }
.service-detail__media{ position:relative; aspect-ratio:1/1; overflow:hidden; }
.service-detail__media img{
  width:100%; height:100%; object-fit:cover;
  /* a color, con la misma corrección de exposición/contraste/saturación en
     las 3 fotos para que se vean parte de un mismo set aunque vengan de
     tomas distintas */
  filter:brightness(1.03) contrast(1.05) saturate(.92);
  transition:transform 1s var(--ease), filter .6s var(--ease);
}
@media (hover:hover) and (pointer:fine){
  .service-detail:hover .service-detail__media img{
    transform:scale(1.06);
    filter:brightness(1.08) contrast(1.05) saturate(1.02);
  }
}
.service-detail:active .service-detail__media img{ transform:scale(1.03); }
.service-detail__tag{
  display:inline-block; margin-bottom:14px;
  font-family:var(--font-mono); font-size:.72rem; font-weight:500;
  letter-spacing:.08em; text-transform:uppercase; color:var(--c-stone);
}
.service-detail__body h3{ font-size:var(--fs-h2); max-width:12ch; }
.service-detail__body p{ margin-top:20px; color:var(--c-slate); max-width:50ch; }
.service-detail__list{ margin-top:24px; display:flex; flex-direction:column; gap:10px; }
.service-detail__list li{ display:flex; gap:12px; align-items:flex-start; font-size:.94rem; color:var(--c-slate); }
.service-detail__list svg{ width:16px; height:16px; margin-top:3px; flex-shrink:0; }

/* el 3er bloque (Proyecto Residencial) rompe el patrón zig-zag: en vez de
   un tercer split imagen/texto seguido, pasa a foto panorámica arriba y
   texto centrado abajo, un ritmo distinto para cerrar la sección */
.service-detail--feature{ display:block; }
.service-detail--feature .service-detail__media{
  aspect-ratio:21/9; margin-bottom:48px;
}
.service-detail--feature .service-detail__body{ max-width:60ch; margin-inline:auto; text-align:center; }
.service-detail--feature .service-detail__body h3{ max-width:none; margin-inline:auto; }
.service-detail--feature .service-detail__body p{ margin-inline:auto; }
.service-detail--feature .service-detail__list{ align-items:center; }
.service-detail--feature .service-detail__list li{ text-align:left; }

/* ---------- values / about ----------
   antes eran 6 tarjetas idénticas en grilla 3x2 (mismo tamaño, mismo peso
   visual, sin jerarquía más allá del texto): se leía como relleno. Ahora es
   una lista numerada en dos columnas tipo "índice de plano": el numeral
   grande en mono le da a cada valor su propia ancla tipográfica, y las
   líneas separadoras horizontales (en vez del borde-por-tarjeta anterior)
   arman un ritmo de lectura vertical más editorial. grid-auto-flow:column
   agrupa 01-03 en la primera columna y 04-06 en la segunda (no intercalados
   fila por fila), como una columna de diario. */
.values{
  display:grid;
  grid-template-columns:repeat(2,1fr);
  grid-template-rows:repeat(3,auto);
  grid-auto-flow:column;
  column-gap:clamp(40px,6vw,90px);
}
.value-row{
  display:grid; grid-template-columns:64px 1fr; gap:22px;
  padding:30px 0; border-top:1px solid var(--c-line);
  transition:padding-left .4s var(--ease), border-color .4s var(--ease);
}
/* grid-auto-flow:column arma dos columnas de 3 filas en el orden del DOM
   (01-03 primera columna, 04-06 segunda): el cierre de cada columna es el
   3er y el 6to hijo, no ":last-child" (que solo sería el 06). */
.value-row:nth-child(3), .value-row:nth-child(6){ border-bottom:1px solid var(--c-line); }
.value-row__num{
  font-family:var(--font-mono); font-weight:500; font-size:1.7rem;
  line-height:1;
  /* antes las 6 numeraciones quedaban en salvia fijo todo el tiempo: seis
     acentos de color compitiendo a la vez es justo lo que hacía sentir la
     sección pesada. En reposo bajan a un taupe apagado (mismo rol que usan
     las etiquetas de tag en otras tarjetas del sitio) y solo la fila que
     tiene el mouse encima recupera el acento; el resto queda tranquilo, así
     el ojo no tiene que repartirse entre seis puntos igual de fuertes. */
  color:var(--c-stone);
  transition:color .4s var(--ease);
}
.value-row__body h3{ font-size:1.1rem; }
.value-row__body p{ margin-top:10px; color:var(--c-slate); font-size:.92rem; }

@media (hover:hover) and (pointer:fine){
  .value-row:hover{ padding-left:14px; border-top-color:var(--c-black); }
  .value-row:hover .value-row__num{ color:var(--c-sand-dark); }
}

.two-col-editorial{ display:grid; grid-template-columns:.9fr 1.1fr; gap:clamp(30px,6vw,90px); align-items:start; }
.two-col-editorial figure{ position:sticky; top:120px; }
.two-col-editorial img{ width:100%; aspect-ratio:3/4; object-fit:cover; filter:grayscale(1) contrast(1.05); }
.two-col-editorial .drop-cap p:first-child::first-letter{
  font-family:var(--font-serif); font-size:3.6rem; float:left; line-height:.8; padding:6px 8px 0 0; color:var(--c-black);
}
.two-col-editorial p{ color:var(--c-slate); margin-bottom:20px; max-width:65ch; }
.two-col-editorial p:last-child{ margin-bottom:0; }

/* ---------- CTA band ---------- */
/* la sección se repite en 4 páginas como último gesto antes del contacto.
   el título tiene su propio clamp, más grande que el --fs-h2 compartido
   (para no mover otros usos del token). La transición hacia el footer no
   se resuelve tratando de que dos resplandores independientes coincidan en
   valor (ese intento anterior no se notaba, porque ambos ya quedan casi en
   negro puro mucho antes de llegar al borde): en vez de eso hay una línea
   de luz explícita justo en el borde entre las dos secciones (ver
   .site-footer::before), un gesto de diseño intencional en vez de un
   intento invisible de "que no se note el corte". */
.cta-band{
  position:relative; z-index:1; overflow:hidden;
  background:var(--c-black); color:var(--c-white); text-align:center;
  padding-top:clamp(80px,11vw,130px); padding-bottom:clamp(56px,7vw,76px);
}
.cta-band::before{
  content:""; position:absolute; inset:0; z-index:0; pointer-events:none;
  background:radial-gradient(circle at 50% 0%, rgba(194,204,185,.2), transparent 70%);
}
.cta-band .container{ position:relative; z-index:1; }
.cta-band h2{
  color:var(--c-white); max-width:22ch; margin-inline:auto;
  font-size:clamp(2.25rem, 5vw, 4rem); letter-spacing:-.02em; text-wrap:balance;
}
.cta-band .btn{ margin-top:40px; }

/* ---------- contact ---------- */
.contact-grid{ display:grid; grid-template-columns:1.2fr .8fr; gap:var(--gutter); }
.form-field{ margin-bottom:22px; }
.form-field label{ display:block; font-family:var(--font-mono); font-size:.74rem; font-weight:500; letter-spacing:.03em; text-transform:uppercase; color:var(--c-stone); margin-bottom:8px; }
.form-field input, .form-field select, .form-field textarea{
  width:100%; border:none; border-bottom:1px solid var(--c-line); background:transparent;
  padding:12px 2px; font-size:1rem; color:var(--c-black);
  transition:border-color .3s, box-shadow .3s;
}
.form-field textarea{ resize:vertical; min-height:110px; }
.form-field input:focus, .form-field select:focus, .form-field textarea:focus{
  outline:none; border-color:var(--c-black); box-shadow:0 1px 0 0 var(--c-black);
}
.form-row{ display:grid; grid-template-columns:1fr 1fr; gap:20px; }
.form-note{ margin-top:16px; font-size:.82rem; color:var(--c-stone); }
.contact-form{ transition:opacity .4s var(--ease), transform .4s var(--ease); }
.contact-form.is-leaving{ opacity:0; transform:translateY(-10px); pointer-events:none; }

.form-success{
  display:none; padding:26px; background:var(--c-offwhite); border:1px solid var(--c-line);
  opacity:0; transform:translateY(14px);
  transition:opacity .5s var(--ease), transform .5s var(--ease);
}
.form-success.is-visible{ opacity:1; transform:translateY(0); }
.form-success h3{ font-size:1.2rem; }
.form-success p{ margin-top:8px; color:var(--c-slate); }

.contact-info{ background:var(--c-black); color:var(--c-white); padding:44px; height:fit-content; }
.contact-info h3{ font-size:1.3rem; color:var(--c-white); }
.contact-info__item{ margin-top:28px; padding-top:20px; border-top:1px solid var(--c-line-invert); }
.contact-info__item span{ display:block; font-family:var(--font-mono); font-size:.7rem; letter-spacing:.05em; text-transform:uppercase; color:var(--c-sand); margin-bottom:6px; }
.contact-info__item a, .contact-info__item p{ font-size:1rem; }
.social-row{ display:flex; gap:14px; margin-top:30px; }
.social-row a{
  width:40px; height:40px; border:1px solid var(--c-line-invert); border-radius:50%;
  display:flex; align-items:center; justify-content:center; transition:background .3s, border-color .3s, transform .12s ease-out;
}
.social-row a:active{ transform:scale(.9); }
.social-row a:focus-visible{ outline:2px solid var(--c-sand); outline-offset:2px; }
@media (hover:hover) and (pointer:fine){
  .social-row a:hover{ background:var(--c-sand); border-color:var(--c-sand); color:var(--c-black); }
}
.social-row svg{ width:16px; height:16px; }

/* ---------- footer ---------- */
/* cierre entre .cta-band y el footer: una línea corta y un rombo centrado
   que retoma el punto medio del isotipo (kull·arq). No es un resplandor
   genérico de borde a borde, es una marca puntual y angosta, coherente con
   el mono/geometría del resto del sistema. Sin overflow:hidden en
   .site-footer para que el brillo del rombo pueda sangrar levemente hacia
   arriba, sobre el final de .cta-band (el footer va después en el DOM,
   así que pinta encima). */
.site-footer{ position:relative; z-index:1; background:var(--c-black); color:var(--c-mist); padding-top:64px; }
.site-footer::before{
  content:""; position:absolute; top:0; left:50%; transform:translateX(-50%);
  width:min(420px, 46%); height:1px; pointer-events:none;
  background:linear-gradient(90deg, transparent, rgba(194,204,185,.4) 18%, rgba(194,204,185,.4) 82%, transparent);
}
.site-footer::after{
  content:""; position:absolute; top:0; left:50%; width:5px; height:5px; pointer-events:none;
  transform:translate(-50%, -50%) rotate(45deg);
  background:var(--c-sand);
  box-shadow:0 0 14px 1px rgba(194,204,185,.4);
}
.footer-top{ position:relative; z-index:1; display:grid; grid-template-columns:1.4fr 1fr 1fr 1fr; gap:var(--gutter); padding-top:26px; padding-bottom:60px; border-bottom:1px solid var(--c-line-invert); }
.footer-brand .logo{ color:#fff; }
.footer-brand p{ margin-top:20px; color:var(--c-fog); max-width:32ch; font-size:.92rem; }
.footer-col h4{ font-family:var(--font-mono); font-size:.72rem; font-weight:500; letter-spacing:.05em; text-transform:uppercase; color:var(--c-sand); margin-bottom:18px; }
.footer-col ul{ display:flex; flex-direction:column; gap:12px; }
.footer-col a, .footer-col p{ color:var(--c-mist); font-size:.92rem; }
.footer-col a:hover{ color:var(--c-sand); }
.footer-col a:focus-visible, .back-to-top:focus-visible{ outline:2px solid var(--c-sand); outline-offset:2px; }
.footer-bottom{ position:relative; z-index:1; display:flex; justify-content:space-between; align-items:center; padding-block:26px; font-size:.78rem; color:var(--c-fog); flex-wrap:wrap; gap:12px; }
.back-to-top{
  display:flex; align-items:center; gap:8px; color:var(--c-mist); font-size:.78rem; font-weight:600; letter-spacing:.04em;
  transition:color .3s var(--ease);
}
.back-to-top:hover{ color:var(--c-sand); }
.back-to-top svg{ width:14px; height:14px; }

/* ---------- reveal animation ---------- */
.reveal{
  opacity:0; transform:translateY(36px) scale(.985); filter:blur(6px);
  transition:opacity 1.1s var(--ease), transform 1.1s var(--ease), filter 1.1s var(--ease);
  will-change:opacity, transform, filter;
}
.reveal.is-visible{ opacity:1; transform:translateY(0) scale(1); filter:blur(0); }
.reveal-stagger > *{
  opacity:0; transform:translateY(30px) scale(.98); filter:blur(5px);
  transition:opacity .95s var(--ease), transform .95s var(--ease), filter .95s var(--ease);
  will-change:opacity, transform, filter;
}
.reveal-stagger.is-visible > *{ opacity:1; transform:translateY(0) scale(1); filter:blur(0); }
.reveal-stagger.is-visible > *:nth-child(1){ transition-delay:.05s; }
.reveal-stagger.is-visible > *:nth-child(2){ transition-delay:.15s; }
.reveal-stagger.is-visible > *:nth-child(3){ transition-delay:.25s; }
.reveal-stagger.is-visible > *:nth-child(4){ transition-delay:.35s; }
.reveal-stagger.is-visible > *:nth-child(5){ transition-delay:.45s; }
.reveal-stagger.is-visible > *:nth-child(6){ transition-delay:.55s; }

/* reveal bidireccional: entra y sale del viewport en ambos sentidos */
.reveal-toggle{
  opacity:0; transform:translateY(52px) scale(.94); filter:blur(10px);
  transition:opacity 1.1s var(--ease), transform 1.1s var(--ease), filter 1.1s var(--ease);
  will-change:opacity, transform, filter;
}
.reveal-toggle.is-visible{ opacity:1; transform:translateY(0) scale(1); filter:blur(0); }

/* ---------- reveal tipo "cortina" para fotos puntuales fuera del hero ----------
   mismo lenguaje visual que la ventana del hero y de .reveal-media (un panel
   sólido que se retira para descubrir la foto), aplicado acá a una imagen
   suelta en vez de a una sección entera: refuerza esa firma de movimiento en
   vez de dejarla aislada en el hero. Corre una sola vez al entrar en
   pantalla, con el mismo batch/trigger que .reveal (agrega is-visible una
   sola vez). No toca ningún transform de la <img>: cada componente ya tiene
   su propio hover/zoom (ej. .service-detail__media img), así que el panel
   se anima solo, sin pisar esas reglas. */
.curtain-reveal{ position:relative; overflow:hidden; }
.curtain-reveal::after{
  content:"";
  position:absolute; inset:0; z-index:3;
  background:var(--c-black);
  transform-origin:right center;
  transform:scaleX(1);
  pointer-events:none;
}
.curtain-reveal.is-visible::after{
  animation:curtainWipe .9s var(--ease) forwards;
}
@keyframes curtainWipe{
  to{ transform:scaleX(0); }
}

/* ---------- zoom lento del fondo del hero mientras queda fijo ---------- */
.hero__media{ will-change:transform; transform:scale(1.04); }

/* ---------- botones magnéticos ---------- */
@media (hover:hover) and (pointer:fine){
  .btn{ will-change:transform; }
}

/* ---------- indicador de progreso de scroll ---------- */
.scroll-progress{
  position:fixed; top:0; left:0; z-index:120;
  width:100%; height:2px;
  background:transparent;
  pointer-events:none;
}
.scroll-progress__bar{
  height:100%; width:0%;
  background:var(--c-sand);
  transform-origin:left;
}

/* ---------- accesibilidad: movimiento reducido ---------- */
@media (prefers-reduced-motion: reduce){
  *, *::before, *::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    scroll-behavior:auto !important;
  }
  .reveal, .reveal-stagger > *, .reveal-toggle, .manifesto__line-inner, .manifesto cite, .manifesto .eyebrow{
    transition:opacity .25s ease !important;
    transform:none !important;
    filter:none !important;
  }
  .hero__scroll{ display:none; }
  .hero__media, .project-card__frame, .btn, .service-detail__media img, .reveal-media img, .project-feature__media img, .lightbox__wrap, .lightbox__media, .lightbox__media img, .project-card__view, .process__step::before, .logo, .logo-mark, .logo-text .logo-dot{ transform:none !important; }
  .custom-cursor{ display:none !important; }
  .hero-reveal--title > *{ animation:none !important; opacity:1 !important; }
  .hero-reveal--title .word__inner{ animation:none !important; opacity:1 !important; transform:none !important; }
  .project-feature__media::after, .project-feature__info{ opacity:1; }
  .project-card__frame img,
  .service-card, .service-card__arrow,
  .btn, .btn svg,
  .site-header, .mobile-nav,
  .logo, .logo-mark, .logo-text .logo-dot,
  .nav-desktop__indicator{
    transition-duration:.15s !important;
  }
  .project-card:hover .project-card__frame img,
  .project-card:active .project-card__frame img{ transform:none; }
}

/* =========================================================
   RESPONSIVE — iPad (<=1024px) y Mobile (<=767px)
   ========================================================= */
@media (max-width:1024px){
  .nav-desktop{ display:none; }
  .burger{ display:flex; }
  .grid-4{ grid-template-columns:repeat(2,1fr); }
  .grid-3{ grid-template-columns:repeat(2,1fr); }
  .project-preview-grid{ grid-template-columns:1fr; }
  .project-preview-grid__stack{ grid-template-rows:none; grid-template-columns:1fr 1fr; }
  .process{ grid-template-columns:repeat(2,1fr); }
  /* una sola columna: ya no hay "cierre de columna" a mitad de lista, así
     que el borde inferior vuelve a ser solo el último ítem real (nth-child(3)
     ya no aplica, sería un borde de más a mitad de la lista). */
  .values{ grid-template-columns:1fr; grid-template-rows:none; grid-auto-flow:row; }
  .value-row:nth-child(3){ border-bottom:none; }
  .two-col-editorial{ grid-template-columns:1fr; gap:40px; }
  .two-col-editorial figure{ position:static; top:auto; max-width:420px; margin-inline:auto; margin-bottom:8px; }
  /* refuerzo redundante: el gap del grid ya separa foto y texto, pero se
     suma un margin-top directo sobre el bloque de texto (mecanismo de caja
     aparte, no depende del grid) como red de seguridad si algo neutraliza
     el gap. */
  .two-col-editorial .drop-cap{ margin-top:32px; }
  /* el sticky de arriba ya actuaba como contenedor de posicionamiento para
     el panel ::after de .curtain-reveal; al pasar a static acá hay que
     devolvérselo explícitamente, si no el panel se posiciona respecto de un
     ancestro mucho más grande que la foto y la "cortina" se ve rota. */
  .two-col-editorial figure.curtain-reveal{ position:relative; }
  .contact-grid{ grid-template-columns:1fr; }
  .lightbox__panel{ grid-template-columns:1fr; }
  .lightbox__media{ aspect-ratio:16/9; max-height:45vh; }
  .footer-top{ grid-template-columns:1fr 1fr; row-gap:40px; }
  .hero{ height:88vh; }
  .reveal-media{ height:88vh; }
  /* el botón "Iniciar Proyecto" del header competía por espacio con el
     logo y el burger en este rango (compactarlo con padding/font-size
     más chicos igual quedaba ajustado, y a 479px el .btn{width:100%}
     general lo hacía romper el layout del header). Se oculta acá; el
     link "Contacto" de la lista del menú cumple la misma función. */
  .nav-cta .btn{ display:none; }
}

@media (max-width:767px){
  .section{ padding-block:64px; }
  /* la etiqueta vertical "obra nueva y remodelaciones" vive en el mismo
     gutter que el título, y en mobile el h1 ocupa casi todo el ancho
     disponible: sin achicarla quedan casi pegados. Se acorta el texto
     (tipografía + tracking más chicos) para que la columna vertical sea
     más corta y no le pise el renglón al título. */
  .hero__tag{ font-size:.6rem; letter-spacing:.07em; gap:8px; }
  .hero__tag-line{ height:32px; }
  .grid-2, .grid-3, .grid-4{ grid-template-columns:1fr; }
  .project-preview-grid__stack{ grid-template-columns:1fr; gap:22px; }
  .project-preview-grid{ gap:22px; }
  .project-card--feature .project-card__frame{ aspect-ratio:4/3; }
  .project-showcase{ grid-template-columns:1fr; gap:22px; }
  .project-showcase > .project-feature:nth-child(4n+2) .project-feature__media,
  .project-showcase > .project-feature:nth-child(4n+3) .project-feature__media{ aspect-ratio:4/3; }
  .process{ grid-template-columns:1fr; gap:34px; }
  .service-card{ grid-template-columns:1fr auto; column-gap:20px; }
  .service-card__body{ display:block; }
  .service-card__body p{ margin-top:8px; }
  .service-card__arrow{ width:38px; height:38px; }
  .service-card__arrow svg{ width:20px; height:20px; }
  .service-detail{ grid-template-columns:1fr; }
  .service-detail:nth-of-type(even) .service-detail__media{ order:0; }
  .service-detail--feature .service-detail__media{ aspect-ratio:4/3; }
  .service-detail--feature .service-detail__body{ text-align:left; margin-inline:0; }
  .service-detail--feature .service-detail__body h3{ margin-inline:0; }
  .service-detail--feature .service-detail__body p{ margin-inline:0; }
  .service-detail--feature .service-detail__list{ align-items:flex-start; }
  .form-row{ grid-template-columns:1fr; }
  .contact-info{ padding:30px; }
  .footer-top{ grid-template-columns:1fr; }
  .footer-bottom{ flex-direction:column; align-items:flex-start; }
  .section-head{ flex-direction:column; align-items:flex-start; }
  .page-banner{ padding-top:120px; }
  .reveal-media{ height:70vh; }
}

@media (max-width:479px){
  :root{ --gutter:18px; }
  .btn{ width:100%; justify-content:center; }
  .cta-band .btn{ width:auto; }
}
