/* ==========================================================================
   ASTRAFOLD — sistema visual
   --------------------------------------------------------------------------
   Reglas duras:
   · Sólo se animan transform, translate, opacity y filter. Nada de layout.
   · Una única curva de easing como firma (--e).
   · Casi todo el movimiento es SCRUBBED: función directa de --q (progreso de
     fijación del acto) o de --slot (posición en la galería). Ambos los escribe
     el bucle a partir del scroll, así que todo es reversible por construcción.
   · Las transiciones CSS quedan para lo que sí es un evento: hover y carga.
   ========================================================================== */

:root{
  /* ── Identidad Astrafold ──────────────────────────────────────────────
     Azul profundo → violeta → púrpura → magenta. El negro es estructura, no
     el ambiente: el color tiene cuerpo y evoluciona durante el recorrido.
     La paleta de la escena WebGL vive en PALETTE (main.js) y sigue el mismo
     arco. Los colores de los PROYECTOS no viven aquí: entran acotados a su
     pieza de galería mediante --proj. ---------------------------------- */
  --bg:        #07041c;
  --ink:       #f6f3ff;
  --ink-soft:  rgba(246,243,255,.80);
  --ink-mute:  rgba(246,243,255,.58);
  --line:      rgba(246,243,255,.18);

  --af-blue:   #4a5cff;
  --af-violet: #8b5cff;
  --af-purple: #c05cff;
  --af-pink:   #ff5cc0;

  --accent:    #9b7cff;
  --accent-2:  #ff6cc4;

  /* GEIST para lo que tiene que golpear —titulares, marca, navegación— y
     MANROPE para lo que hay que leer. Geist es geométrica y muy neutra: a
     tamaño grande sostiene el peso sin decorar. Manrope tiene la altura de x
     alta y las contraformas abiertas que hacen legible un párrafo pequeño
     sobre fondo oscuro, que es todo el cuerpo de texto de esta página. */
  --f-display: 'Geist', ui-sans-serif, system-ui, sans-serif;
  --f-text:    'Manrope', ui-sans-serif, system-ui, sans-serif;

  --e:      cubic-bezier(.5, 0, 0, 1);
  --e-soft: cubic-bezier(.32, 0, .18, 1);

  --d-fast:  .34s;
  --d-base:  .62s;
  --d-slow:  1.05s;

  --gut: clamp(20px, 5vw, 72px);
  --maxw: 1420px;
}

/* ── Reset acotado ─────────────────────────────────────────────────────── */

*,*::before,*::after{ box-sizing:border-box; }
html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  /* `clip`, NO `hidden`: overflow:hidden convierte el elemento en contenedor
     de scroll y rompe position:sticky en los descendientes, que es el
     mecanismo sobre el que se apoya toda esta experiencia. */
  overflow-x:clip;
}
body{
  margin:0; background:var(--bg); color:var(--ink);
  font-family:var(--f-text); font-weight:300;
  font-size:clamp(15px,1.05vw,17px); line-height:1.6; letter-spacing:.005em;
  -webkit-font-smoothing:antialiased; -moz-osx-font-smoothing:grayscale;
  overflow-x:clip;
}
h1,h2,h3,p,ul{ margin:0; }
/* La marca del hero es una imagen: el documento necesita su <h1> igual. */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}
ul{ padding:0; list-style:none; }
a{ color:inherit; text-decoration:none; }
img,canvas{ display:block; max-width:100%; }
:focus-visible{ outline:2px solid var(--accent); outline-offset:4px; border-radius:3px; }

/* ══ Escena WebGL ═══════════════════════════════════════════════════════ */

.stage{
  position:fixed; inset:0; z-index:0; pointer-events:none;
  /* Respaldo sin WebGL2: la página conserva atmósfera y toda la coreografía
     del DOM sigue funcionando. */
  /* Respaldo sin WebGL2: no imita el agujero —eso sería una imagen fija de
     un fenómeno que aquí es procedural— pero sí conserva el espacio. */
  background:
    radial-gradient(110% 80% at 62% 30%, #291a7a 0%, transparent 58%),
    radial-gradient(80% 60% at 22% 88%, #6b1180 0%, transparent 62%),
    var(--bg);
}
.stage__gl{ width:100%; height:100%; }
.stage__veil{
  position:absolute; inset:0;
  /* Velo mucho más suave que antes: la escena ya es oscura de por sí y un
     velo fuerte se comía las estrellas, que son las que dan la escala. El
     contraste del texto lo sostiene el halo local de cada bloque. */
  background:
    radial-gradient(135% 105% at 50% 6%, transparent 42%, rgba(5,2,16,.42) 100%),
    linear-gradient(180deg, rgba(5,2,16,.42) 0%, transparent 24%,
                    transparent 74%, rgba(5,2,16,.42) 100%);
}

/* ══ ACTOS ══════════════════════════════════════════════════════════════
   Cuatro. El raíl es alto; el escenario dentro se queda fijo. Mientras se
   recorre el raíl la pantalla NO se desplaza: se transforma.
  ───────────────────────────────────────────────────────────────────────── */

main{ position:relative; z-index:1; }

.act{
  position:relative;
  height:calc(var(--vh, 2) * 100svh);
  /* Sin valor inicial, el primer paint evalúa clamp() con una variable
     indefinida y descarta la regla entera. */
  --q:0; --p:0;
}
.act__stage{
  position:sticky; top:0;
  height:100svh; overflow:clip;
  display:flex; flex-direction:column; justify-content:center;
}

.wrap{ width:100%; max-width:var(--maxw); margin-inline:auto; padding-inline:var(--gut); }
.wrap--center{ text-align:center; }

/* ══ Cabecera ═══════════════════════════════════════════════════════════ */

/* LIQUID GLASS — el lenguaje, acotado.
   Una superficie de vidrio se lee por cuatro cosas: lo que hay detrás
   (blur + saturación), un borde de 1 px que atrapa la luz, un brillo
   especular arriba y una sombra que la despega. Se aplica sólo donde flota
   sobre la escena: la cabecera y los botones. No a la interfaz entera. */
:root{
  --glass-bg: linear-gradient(180deg, rgba(255,255,255,.075), rgba(255,255,255,.028));
  --glass-line: rgba(255,255,255,.12);
  --glass-hi: inset 0 1px 0 rgba(255,255,255,.18), inset 0 -1px 0 rgba(255,255,255,.04);
  --glass-shadow: 0 12px 40px -12px rgba(0,0,0,.55);
}

.nav{
  position:fixed; inset:14px 0 auto 0; z-index:20;
  padding-inline:var(--gut); pointer-events:none;
  transition:transform var(--d-base) var(--e), opacity var(--d-base) var(--e);
}
.nav.is-hidden{ transform:translateY(-130%); opacity:0; }
.nav__bar{
  pointer-events:auto;
  width:100%; max-width:1180px; margin-inline:auto;
  display:flex; align-items:center; gap:clamp(12px,2vw,28px);
  padding:7px 8px 7px 12px; border-radius:100px;
  background:var(--glass-bg);
  border:1px solid var(--glass-line);
  box-shadow:var(--glass-hi), var(--glass-shadow);
  backdrop-filter:blur(14px) saturate(1.4);
  -webkit-backdrop-filter:blur(14px) saturate(1.4);
  position:relative;
}
/* Brillo especular: una luz que resbala por el canto superior. */
.nav__bar::before{
  content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:radial-gradient(60% 120% at 22% -30%, rgba(255,255,255,.14), transparent 60%);
}
.nav__brand{ display:flex; align-items:center; gap:12px; margin-right:auto; flex:none; padding-right:6px; }
.nav__mark{
  width:clamp(36px, 3vw, 44px); height:auto; aspect-ratio:1; flex:none;
  transition:transform var(--d-base) var(--e);
}
.nav__brand:hover .nav__mark{ transform:rotate(90deg) scale(1.05); }
.nav__word{ font-family:var(--f-display); font-weight:700; font-size:15px; letter-spacing:.2em; }
.nav__links{ display:flex; gap:2px; flex:none; }
/* Enlaces como píldoras dentro del vidrio: el hover es un vidrio más claro. */
.nav__links .lnk{
  white-space:nowrap; padding:9px 14px; border-radius:100px; font-size:13.5px;
  transition:color var(--d-fast) var(--e), background-color var(--d-fast) var(--e);
}
.nav__links .lnk:hover, .nav__links .lnk:focus-visible{ background:rgba(255,255,255,.07); color:var(--ink); }
.nav__end{ display:flex; align-items:center; gap:clamp(8px,1.2vw,14px); flex:none; }

/* Idioma: discreto, tipográfico. */
.lang{ display:inline-flex; align-items:center; gap:4px; padding-inline:6px; font-size:11.5px; letter-spacing:.14em; }
.lang__b{
  background:none; border:0; padding:6px 4px; cursor:pointer; font:inherit; font-size:inherit; letter-spacing:inherit;
  color:var(--ink-mute); transition:color var(--d-fast) var(--e);
}
.lang__b[aria-pressed="true"]{ color:var(--ink); }
.lang__b[aria-disabled="true"]{ cursor:default; opacity:.7; }
.lang__b:not([aria-disabled="true"]):hover{ color:var(--ink); }
.lang__sep{ color:var(--ink-mute); opacity:.6; }
.lnk{
  position:relative; display:inline-block;
  font-size:13px; letter-spacing:.04em; color:var(--ink-soft); padding-block:2px;
  transition:color var(--d-fast) var(--e);
}
.lnk span{ display:block; }
.lnk:hover, .lnk:focus-visible{ color:var(--ink); }

/* ── Botón ─────────────────────────────────────────────────────────────── */

.btn{
  position:relative; display:inline-flex; align-items:center; justify-content:center;
  padding:12px 26px; border-radius:100px;
  border:1px solid var(--glass-line);
  background:var(--glass-bg);
  color:var(--ink); font:inherit; font-size:14px; font-family:var(--f-display); font-weight:500;
  letter-spacing:.01em; cursor:pointer; -webkit-appearance:none; appearance:none;
  white-space:nowrap; flex:none; isolation:isolate; overflow:hidden;
  box-shadow:var(--glass-hi), 0 8px 24px -10px rgba(0,0,0,.6);
  /* Sin backdrop-filter en los botones: sobre este fondo el desenfoque no se
     ve y cada uno se re-muestrearía en cada frame de la escena. El vidrio lo
     dan la superficie, el canto y el brillo. */
  transform:translateY(0) scale(1);
  transition:transform .18s var(--e), box-shadow .3s var(--e), background-color .3s var(--e), border-color .3s var(--e);
}
.btn span{ position:relative; z-index:2; }
/* Especular arriba + refracción en el canto: dos degradados estáticos. */
.btn::before{
  content:''; position:absolute; inset:0; z-index:0; border-radius:inherit; pointer-events:none;
  background:radial-gradient(90% 70% at 50% -20%, rgba(255,255,255,.22), transparent 62%);
  transition:opacity .3s var(--e);
}
.btn::after{
  content:''; position:absolute; inset:0; z-index:1; border-radius:inherit; padding:1px; pointer-events:none;
  background:linear-gradient(135deg, rgba(255,255,255,.35) 0%, rgba(255,255,255,0) 38%,
                             rgba(255,255,255,0) 62%, rgba(255,140,220,.28) 100%);
  -webkit-mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask:linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite:xor; mask-composite:exclude;
  opacity:.7; transition:opacity .3s var(--e);
}
.btn:hover, .btn:focus-visible{
  border-color:rgba(255,255,255,.2);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.26), inset 0 -1px 0 rgba(255,255,255,.05), 0 14px 30px -12px rgba(0,0,0,.65);
  transform:translateY(-1px) scale(1.01);
}
.btn:hover::after, .btn:focus-visible::after{ opacity:1; }
/* Pressed: baja un píxel, se comprime apenas, el canto se hunde. */
.btn:active{
  transform:translateY(1px) scale(.985);
  box-shadow:inset 0 2px 8px rgba(0,0,0,.35), inset 0 1px 0 rgba(255,255,255,.06);
  transition-duration:.08s;
}
/* Primario: el mismo vidrio, teñido con la luz de la marca. El texto sigue
   siendo blanco sobre un fondo suficientemente oscuro. */
.btn--primary{
  background:linear-gradient(180deg, rgba(150,112,255,.40), rgba(112,68,224,.26));
  border-color:rgba(196,170,255,.34);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.28), inset 0 -1px 0 rgba(255,255,255,.04), 0 12px 30px -12px rgba(139,92,255,.6);
}
.btn--primary:hover, .btn--primary:focus-visible{
  background:linear-gradient(180deg, rgba(160,124,255,.48), rgba(120,76,232,.32));
  border-color:rgba(206,184,255,.45);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.34), 0 16px 36px -12px rgba(139,92,255,.7);
}
.btn:focus-visible{ outline:2px solid rgba(196,170,255,.7); outline-offset:3px; }
.btn--sm{ padding:10px 18px; font-size:13px; }
.btn--lg{
  margin-top:34px; padding:17px 36px;
  font-weight:600; font-size:clamp(15px,1.4vw,18px);
}

/* ══ Tipografía compartida ══════════════════════════════════════════════ */

.eyebrow__dot{
  position:relative; width:6px; height:6px; border-radius:50%; background:var(--accent);
}
/* El pulso es un anillo que se ESCALA y se apaga —compositor, sin repintar—
   en vez de un box-shadow animado, que repintaba su capa en cada frame. */
.eyebrow__dot::after{
  content:''; position:absolute; inset:0; border-radius:50%;
  border:1px solid rgba(155,124,255,.55);
  animation:pulse 3.4s var(--e-soft) infinite;
}
@keyframes pulse{
  0%,100%{ transform:scale(1); opacity:.7; }
  60%    { transform:scale(3.4); opacity:0; }
}

.w{
  display:inline-block; overflow:hidden; vertical-align:bottom;
  padding-bottom:.09em; margin-bottom:-.09em;
}
.w__i{ display:inline-block; will-change:transform; }

/* ══ ACTO 1 · HERO + TRABAJO ═════════════════════════════════════════════
   Una sola escena. El texto y la galería comparten escenario fijado; la
   primera pieza está en la composición desde el primer frame, en la
   columna derecha y un poco más atrás.

   --in (0 → 1) es la ENTRADA, la escribe el bucle a partir de --q: 0 en el
   hero en reposo, 1 cuando la pieza ha tomado el encuadre. Todo lo que
   cambia entre esos dos estados es transform u opacity, y todo se solapa:
   el texto se retira MIENTRAS la pieza avanza. No hay un instante en que
   la pantalla esté vacía.
  ───────────────────────────────────────────────────────────────────────── */

.act--hero{ --in:0; --tin:.24; }
/* Wrappers de capa: GSAP les escribe transform; nada más vive en ellos. */
.layer{ position:absolute; inset:0; will-change:transform; }
.layer--copy{ z-index:3; pointer-events:none; }
.layer--work{ z-index:1; }

/* ── EL ENTORNO ────────────────────────────────────────────────────────
   Tres profundidades DOM, cada una un wrapper con su transform (GSAP), sobre
   el espacio WebGL que pone nebulosa, niebla de horizonte y astro lejano.
   Los elementos son WebP con alpha horneados con la luz del shader
   (tools/bake-env.html): una rasterización por capa, sin filtros, sin capas
   por elemento. Todo va DEBAJO del contenido (z 0) y en los MÁRGENES: al
   hacer scroll baja (la cámara asciende), lo cercano deprisa, lo lejano
   apenas, y nada cruza el titular, los CTA, la pieza ni el rótulo.

   Composición (desktop, estado de hero, 1440×900):
     lejanas (mid)   m1 borde izquierdo, entre cabecera y cartela · m2 margen
                     derecho sobre la pieza · m3 franja baja, centro ·
                     m4 empieza por encima del encuadre y entra en la galería
                     m5 borde izquierdo bajo la cartela: cruza la esquina
                     inferior izquierda a mitad de galería
     cercanas (near) n1 esquina inferior derecha, bajo la pieza · n2 borde
                     izquierdo, medio recortado, junto al titular sin tocarlo:
                     cruza la esquina inferior izquierda al empezar la galería ·
                     n3 esquina superior derecha, contra la luz del astro:
                     baja por el margen y cruza la esquina inferior derecha en
                     la segunda mitad de la galería
     terreno (fore)  franja baja de ~22 vh; sale por abajo durante la entrada.
   En la galería las vecinas ocupan los márgenes: por eso los tránsitos
   ocurren en las esquinas inferiores, que quedan libres bajo ellas.
   Sin animaciones propias dentro de las capas: una rotación CSS en las
   rocas cercanas (probada) metía frames de 100 ms en la galería — una
   animación de compositor dentro de un wrapper cuyo transform cambia en
   cada frame se rasteriza otra vez y otra vez. Con reduced-motion, nada. */
.env{ position:absolute; inset:0; z-index:0; pointer-events:none; overflow:hidden; }
.env > div{ position:absolute; inset:0; will-change:transform; }
/* Misma especificidad que las reglas por elemento: las de abajo ganan. */
.env__rock, .env__terrain{ position:absolute; display:block; height:auto; }

.env__rock--m1{ left:1vw;   top:14vh; width:44px; opacity:.85; }
.env__rock--m2{ right:2vw;  top:32vh; width:62px; opacity:.9; }
.env__rock--m3{ left:40vw;  top:87vh; width:34px; opacity:.7; }
.env__rock--m4{ right:3vw;  top:-28vh; width:52px; opacity:.85; }
.env__rock--m5{ left:2vw;   top:62vh; width:40px; opacity:.8; }

.env__rock--n1{ right:-4vw; top:83vh; width:min(16vw, 240px); }
.env__rock--n2{ left:-5vw;  top:28vh; width:min(10.5vw, 150px); }
.env__rock--n3{ right:-7vw; top:7vh;  width:min(13vw, 190px); }


/* Ancho 104 vw → alto 26 vw (2048×512). Se deja ver ~22 vh y el resto
   queda bajo el borde: la base es sólida, la cresta lejana se funde. */
.env__terrain{ left:-2vw; width:104vw; bottom:calc(22vh - 26vw); }

/* Tablet: la cartela ocupa todo el ancho y la pieza queda debajo, a la
   izquierda. Lo lejano va a las bandas alta y baja; lo cercano, al margen
   derecho de la pieza y a las esquinas; n3 empieza fuera y entra por la
   izquierda durante la galería. */
@media (max-width:1024px){
  .env__rock--m1{ left:3vw; top:-14vh; width:36px; }   /* entra en la galería */
  .env__rock--m2{ right:3vw; top:58vh; width:50px; }
  .env__rock--m3{ left:52vw; top:88vh; width:28px; }
  .env__rock--m4{ right:2vw; top:-24vh; width:42px; }
  .env__rock--m5{ left:.5vw; top:64vh; width:32px; }
  .env__rock--n1{ right:-6vw; top:84vh; width:min(22vw, 200px); }
  .env__rock--n2{ left:auto; right:-8vw; top:52vh; width:min(15vw, 130px); }
  .env__rock--n3{ right:auto; left:-9vw; top:-24vh; width:min(18vw, 160px); }
  .env__terrain{ bottom:calc(18vh - 26vw); }
}
/* Móvil: todo es una columna; sólo quedan libres la banda entre cabecera y
   cartela y la banda baja. Espacio, tres lejanas y terreno. */
@media (max-width:760px){
  .env__near, .env__rock--m4, .env__rock--m5{ display:none; }
  /* m1 empieza fuera y entra en la galería, en la banda entre cabecera y pieza. */
  .env__rock--m1{ left:6vw; top:-12vh; width:24px; }
  .env__rock--m2{ right:6vw; top:88vh; width:36px; }
  .env__rock--m3{ left:82vw; top:87vh; width:22px; }
  .env__terrain{ left:-6vw; width:112vw; bottom:calc(14vh - 28vw); }
}
@media (prefers-reduced-motion:reduce){
  .env{ position:absolute; inset:0 0 auto 0; height:100svh; }
  .env > div{ transform:none !important; will-change:auto; }
  /* Sin recorrido no hay tránsitos: fuera lo que empieza fuera y lo cercano,
     que en la composición estática quedaría medio detrás de la pieza. */
  .env__near, .env__rock--m4{ display:none; }
  .env__rock--m2{ top:8vh; }
}
.act--hero .act__stage{
  perspective:1700px;
  perspective-origin:50% 44%;
  justify-content:flex-start;
}
/* Ancla de #work: donde la entrada termina. */
.act__anchor{ position:absolute; left:0; width:1px; height:1px; top:calc(var(--tin) * (100% - 100svh)); }

/* ── El texto ── */
/* IDENTIDAD: el hero es tipografía y atmósfera. El trabajo está en la
   escena, pero al fondo y a la derecha, más pequeño, velado: un lugar al
   que se va a llegar, no la portada de otro proyecto. */
.hero{
  position:absolute; inset:0;
  display:grid; align-items:center;
  grid-template-columns:minmax(0, 1.25fr) minmax(0, 1fr);
  gap:clamp(24px, 4vw, 72px);
  padding-block:clamp(96px, 14vh, 150px) clamp(72px, 10vh, 120px);
  pointer-events:none;
}
.hero__copy{
  position:relative; pointer-events:auto;
  /* Sube y cede el sitio. Tres profundidades: el titular es lo primero que
     se va; el párrafo y los CTA lo siguen con menos recorrido. */
  transform:translate3d(calc(var(--in) * -1.5vw), calc(var(--in) * -5vh), 0);
  opacity:calc(1 - clamp(0, calc(var(--in) / .55), 1));
}
.act--hero.is-gone .hero__copy{ visibility:hidden; }
.hero__title{ transform:translate3d(0, calc(var(--in) * -3vh), 0); }
.hero__lead{  transform:translate3d(0, calc(var(--in) * -1.4vh), 0); }
.hero__cta{   transform:translate3d(0, calc(var(--in) * -.4vh), 0); }

/* El hueco de la pieza en estado de hero: no pinta nada, sólo mide. Más
   abajo y a la derecha que el centro de la columna: en profundidad. */
.hero__visual{
  width:min(100%, 44vw); aspect-ratio:16 / 10; max-height:46vh;
  justify-self:end; align-self:center;
  margin-top:clamp(40px, 10vh, 120px);
}

.hero__cat{
  display:inline-flex; align-items:center; gap:11px; white-space:nowrap;
  font-size:clamp(10px,.8vw,11.5px); letter-spacing:.24em; text-transform:uppercase;
  color:var(--ink-soft);
}
.hero__title{
  margin-top:clamp(18px,2.4vh,30px);
  font-family:var(--f-display); font-weight:600;
  font-size:clamp(2.6rem, 5.6vw, 5.8rem); line-height:.98; letter-spacing:-.042em;
  color:var(--ink); max-width:11ch; text-wrap:balance;
}
.hero__title .w__i{ transform:translateY(112%); }
.is-ready .hero__title .w__i{
  transform:none;
  transition:transform 1.1s var(--e) calc(.18s + var(--i,0) * .055s);
}
.hero__lead{
  margin-top:clamp(18px,2.6vh,30px); max-width:42ch;
  font-size:clamp(15px,1.25vw,18px); line-height:1.55; color:var(--ink-soft);
}
.hero__cta{
  display:flex; flex-wrap:wrap; align-items:center; gap:clamp(16px,2vw,28px);
  margin-top:clamp(26px,3.6vh,42px);
}
.hero__cta .btn--lg{ margin-top:0; }
/* La flecha avanza; el texto no se mueve. Una sola cosa cambia. */
.lnk--arrow{ display:inline-flex; align-items:center; gap:10px; font-size:14px; padding-block:6px; color:var(--ink); }
.lnk--arrow::before{
  content:'→'; color:var(--accent); font-size:15px;
  transition:translate var(--d-base) var(--e);
}
.lnk--arrow:hover::before, .lnk--arrow:focus-visible::before{ translate:5px 0; }
.lnk--arrow span{ box-shadow:inset 0 -1px 0 rgba(246,243,255,.22); transition:box-shadow var(--d-fast) var(--e); }
.lnk--arrow:hover span{ box-shadow:inset 0 -1px 0 rgba(246,243,255,.6); }

/* Entrada al cargar (translate; el scroll usa transform: no se pisan) */
.hero__cat, .hero__lead, .hero__cta{ opacity:0; translate:0 16px; }
.is-ready .hero__cat{ opacity:1; translate:none; transition:opacity .9s var(--e) .1s, translate .9s var(--e) .1s; }
.is-ready .hero__lead{ opacity:1; translate:none; transition:opacity 1s var(--e) .55s, translate 1s var(--e) .55s; }
.is-ready .hero__cta{ opacity:1; translate:none; transition:opacity 1s var(--e) .75s, translate 1s var(--e) .75s; }


/* ══ ACTO 3 · GALERÍA ═══════════════════════════════════════════════════
   UNA escena con cuatro estados, no cuatro escenas.

   `--slot` es la posición continua en la sala (0 … n-1) y la escribe el
   bucle. Cada pieza calcula su distancia a esa posición:

        --d  = índice − slot        (con signo: izquierda / derecha)
        --d2 = --d × --d            (simétrica: sirve para escala y opacidad)

   d² evita necesitar abs(), que aún no es fiable en CSS. La pieza activa
   domina el encuadre; las demás siguen ahí, más atrás y a los lados: se ve
   de dónde vienes y a dónde vas.
  ───────────────────────────────────────────────────────────────────────── */

.gal{
  position:relative; width:100%; height:100%; --slot:0;
  --hx:0px; --hy:0px; --hs:1;      /* estado de hero, medido por fitHero() */
  --o: calc(1 - var(--in, 0));
  /* LA ENTRADA ES UN SOLO TRANSFORM DE GRUPO. En reposo (--in = 0) toda la
     sala está desplazada y algo más pequeña, de modo que la primera pieza
     cae exactamente en el hueco del hero, ligeramente girada: se la ve un
     poco de lado, más atrás. Al avanzar --in la sala se endereza, se acerca
     y se centra. Las demás piezas vienen con ella: la composición entera
     entra en el campo de visión, no una imagen. */
  transform-origin:50% calc(var(--gal-pad) + var(--gal-h) / 2);
  transform:
    translate3d(calc(var(--o) * var(--hx)), calc(var(--o) * var(--hy)), 0)
    rotateY(calc(var(--o) * -9deg))
    scale(calc(1 - var(--o) * (1 - var(--hs))));
  transform-style:preserve-3d;

  /* GEOMETRÍA DE LA SALA.
     Antes la pieza se anclaba en un `top` porcentual fijo y el rótulo colgaba
     por debajo, en el hueco que quedara. Eso funciona con un nombre de una
     línea y falla con el más largo: pasa a dos líneas, el bloque crece hacia
     abajo y el escenario —que recorta— se lo come. Además la composición
     saltaba de altura al cambiar de proyecto.
     Ahora el alto del rótulo está RESERVADO y es el mismo para todas las
     piezas, y la captura se dimensiona con lo que sobra. No se encoge nada
     por decreto: el encuadre se calcula para que el conjunto quepa entero. */
  --gal-w:    min(66vw, 940px);            /* ancho máximo de la captura     */
  --gal-gap:  clamp(18px, 2.6vh, 32px);    /* aire entre captura y rótulo    */
  --gal-meta: clamp(132px, 17vh, 176px);   /* rótulo: nombre a dos líneas + descripción */
  --gal-hud:  clamp(56px, 8.4vh, 88px);    /* franja intocable del indicador */
  --gal-top:  clamp(64px, 9vh, 96px);      /* franja de la cabecera fija     */

  /* Alto de la captura: el que pida su ancho, o el que quede libre una vez
     descontado todo lo que va encima y debajo. Gana el menor. */
  --gal-h: min(
    calc(var(--gal-w) * .625),
    calc(100svh - var(--gal-top) - var(--gal-hud) - var(--gal-gap) - var(--gal-meta))
  );
  /* Aire sobrante repartido arriba y abajo: el conjunto queda centrado en la
     banda libre, entre la cabecera y el indicador. */
  --gal-pad: calc(var(--gal-top) + (100svh - var(--gal-top) - var(--gal-hud)
                   - var(--gal-gap) - var(--gal-meta) - var(--gal-h)) / 2);
}

.gal__item{
  --d:  calc(var(--i) - var(--slot));
  --d2: calc(var(--d) * var(--d));
  position:absolute; left:50%;
  /* El ancho se deriva del alto disponible manteniendo el 16/10, así que la
     captura nunca invade la zona del rótulo. */
  width:min(var(--gal-w), calc(var(--gal-h) * 1.6));
  top:calc(var(--gal-pad) + var(--gal-h) / 2);
  translate:-50% -50%;
  transform:
    translate3d(calc(var(--d) * 44%), calc(var(--d2) * 2.4vh), calc(var(--d2) * -540px))
    /* Un par de grados con el puntero: profundidad controlada, no paralaje. */
    rotateY(calc(var(--d) * -24deg + var(--mx, 0) * 2.2deg))
    rotateX(calc(var(--my, 0) * -1.6deg))
    scale(calc(1 - var(--d2) * .07));
  /* Las vecinas tienen que LEERSE, no sólo insinuarse: a d=1 quedan cerca de
     0.5 de opacidad. Son la prueba de que hay más trabajo y de dónde viene
     el recorrido; si se apagan del todo, la galería vuelve a ser una tarjeta
     sola en pantalla. */
  opacity:calc(clamp(0, calc(1.28 - var(--d2) * .88), 1) * (.72 + .28 * var(--in, 1)));
  /* JS lo pone en `auto` sólo para la pieza activa: una pieza del fondo no
     debe poder capturar el clic y abrir el proyecto equivocado. */
  pointer-events:none;
  /* Cuatro elementos, transformados en cada frame durante todo el acto: es
     exactamente el caso para el que existe will-change. Sin esto el navegador
     puede re-rasterizar la captura completa —940 px con perspectiva y giro—
     cada vez que el transform cambia lo suficiente. */
  will-change:transform, opacity;
}
.gal__link{ position:relative; display:block; }
.gal__frame::before{
  content:''; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:linear-gradient(160deg, rgba(5,3,18,.55) 0%, rgba(5,3,18,.10) 55%, rgba(5,3,18,.35) 100%);
  opacity:calc(1 - var(--in, 1));
}

.gal__frame{
  display:block; position:relative; overflow:hidden;
  aspect-ratio:16 / 10; border-radius:14px;
  border:1px solid rgba(246,243,255,.14);
  background:#0a0716;
  box-shadow:0 28px 70px rgba(0,0,0,.50);
}
/* Un filo de luz arriba: material, no interfaz. Acotado al frame. */
.gal__frame::after{
  content:''; position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  background:linear-gradient(180deg, rgba(246,243,255,.09), transparent 18%);
}
.gal__img{ width:100%; height:100%; object-fit:cover; object-position:top center; }

/* Halo del color del proyecto. Contenido: sólo existe dentro de su pieza.
   Antes se resolvía con filter:blur(72px) sobre cuatro elementos que se
   transforman en CADA frame de la galería — un desenfoque de ese radio se
   recalcula con cada cambio de transform y era el candidato número uno al
   tirón al entrar en WORK. Un degradado radial da el mismo resultado sin
   pasar por el pipeline de filtros. */
.gal__glow{
  position:absolute; inset:-26%; z-index:-1; border-radius:50%;
  background:radial-gradient(closest-side,
    color-mix(in srgb, var(--proj) 80%, transparent) 0%,
    color-mix(in srgb, var(--proj) 30%, transparent) 42%,
    transparent 72%);
  pointer-events:none;
  opacity:clamp(0, calc(.34 - var(--d2) * .30), .34);
  transition:opacity var(--d-slow) var(--e);
}
.gal__link:hover .gal__glow{ opacity:.62; }

.gal__open{
  position:absolute; right:16px; bottom:16px; z-index:2;
  padding:10px 18px; border-radius:100px;
  background:rgba(7,4,28,.82); border:1px solid rgba(246,243,255,.28);
  font-size:11px; letter-spacing:.16em; text-transform:uppercase;
  opacity:0; translate:0 8px; pointer-events:none;
  /* visibility:hidden saca el elemento del pipeline. Con sólo opacity:0 el
     navegador seguía componiendo las cuatro insignias, y el backdrop-filter
     de cada una re-muestrea lo que tiene detrás EN CADA FRAME: durante un
     relevo, cuando todo se mueve, eso son cuatro desenfoques por frame. El
     desenfoque sólo se enciende en la que se está señalando. */
  visibility:hidden;
  transition:opacity var(--d-fast) var(--e), translate var(--d-fast) var(--e);
}
.gal__link:hover .gal__open, .gal__link:focus-visible .gal__open{
  visibility:visible;
  backdrop-filter:blur(10px); -webkit-backdrop-filter:blur(10px);
}
.gal__link:hover .gal__open, .gal__link:focus-visible .gal__open{ opacity:1; translate:0 0; }
/* Desde la superficie de clic: la insignia SIN backdrop-filter y sin tocar el
   halo. Un desenfoque de fondo dentro de un contexto 3D obliga a aplanar las
   cuatro piezas en una textura en cada frame de relevo, y el halo es un
   degradado grande cuya opacidad no conviene animar mientras todo se mueve.
   El fondo de la insignia es casi opaco: el desenfoque no se echa de menos. */
.gal.is-hover .gal__item.is-active .gal__open{ visibility:visible; opacity:1; translate:0 0; }

/* ── La superficie de clic ──
   Ocupa el rectángulo de la pieza en reposo (las mismas medidas que
   .gal__item) y va delante de todo en Z. Sin fondo: es la pieza lo que se
   ve; esto sólo recibe el puntero. Durante el hero no existe para el
   puntero (la pieza está al fondo, es un lugar al que se va a llegar). */
.gal__hit{
  position:absolute; left:50%; top:calc(var(--gal-pad) + var(--gal-h) / 2);
  translate:-50% -50%;
  width:min(var(--gal-w), calc(var(--gal-h) * 1.6)); aspect-ratio:16 / 10;
  /* Sin transform: en el mismo plano que la pieza activa y la última en el
     orden del DOM, así queda delante sin ser una capa compuesta más dentro
     del contexto 3D (un translateZ aquí, probado, metía picos de 100–200 ms
     en la galería). */
  border-radius:14px; cursor:pointer;
  -webkit-tap-highlight-color:transparent;
}
.act--hero:not(.is-gone) .gal__hit{ pointer-events:none; }
/* La del rótulo: medidas en px puestas por main.js; sin ellas, oculta. */
.gal__hit--go{ left:0; top:0; translate:none; width:auto; aspect-ratio:auto; border-radius:4px; }
.gal__hit--go[hidden]{ display:none; }

/* El texto pertenece sólo a la pieza activa: se apaga muy rápido con d². */
.act--hero{ --intro: clamp(0, calc((var(--in) - .58) / .42), 1); }
.gal__meta{
  position:absolute; left:0; top:calc(100% + var(--gal-gap));
  width:100%;
  display:grid; grid-template-columns:minmax(0, 1fr) minmax(0, 1fr); gap:clamp(20px,3vw,48px);
  align-items:start;
  translate:0 calc((1 - var(--intro, 1)) * 14px);
  /* Alto reservado, idéntico en todas las piezas: el nombre más largo cabe a
     dos líneas y la composición no salta al pasar de un proyecto a otro. */
  min-height:var(--gal-meta);
  opacity:calc(clamp(0, calc(1 - var(--d2) * 3.6), 1) * var(--intro, 1));
}
.gal__sector{
  display:flex; align-items:center; gap:12px;
  font-size:11.5px; letter-spacing:.2em; text-transform:uppercase; color:var(--ink-mute);
}
/* Marca del color del proyecto. Sustituye al ordinal, que delataba cuántos
   trabajos hay en cuanto se llegaba al último. */
.gal__sector i{ width:34px; height:2px; border-radius:2px; background:var(--proj); flex:none; }
.gal__name{
  margin-top:10px; font-family:var(--f-display); font-weight:600;
  font-size:clamp(1.5rem,3.2vw,2.8rem); line-height:1.04; letter-spacing:-.032em;
  /* Reparte las dos líneas en vez de dejar una palabra suelta abajo. */
  text-wrap:balance;
}
.gal__head{ margin-top:8px; color:var(--ink-soft); font-size:clamp(14px,1.2vw,16px); }
.gal__meta-r{ padding-top:6px; }
.gal__blurb{ font-size:clamp(13.5px,1.05vw,15px); line-height:1.55; color:var(--ink-soft); max-width:46ch; }
.gal__tags{ display:flex; flex-wrap:wrap; gap:6px 14px; margin-top:12px; }
.gal__tags span{
  font-size:10.5px; letter-spacing:.16em; text-transform:uppercase; color:var(--ink-mute);
}
.gal__go{
  display:inline-flex; align-items:center; gap:8px; margin-top:14px;
  font-family:var(--f-display); font-weight:500; font-size:13px; letter-spacing:.02em; color:var(--ink);
  box-shadow:inset 0 -1px 0 rgba(246,243,255,.25); transition:box-shadow var(--d-fast) var(--e);
}
.gal__go:hover{ box-shadow:inset 0 -1px 0 rgba(246,243,255,.7); }

/* HUD: dentro de .gal para heredar --slot. */
.gal__hud{
  position:absolute; left:0; right:0; bottom:clamp(18px,3.4vh,40px); z-index:30;
  opacity:var(--intro, 1);
  display:flex; align-items:center; justify-content:center; gap:clamp(14px,2vw,26px);
  font-size:11px; letter-spacing:.22em; text-transform:uppercase; color:var(--ink-mute);
}
.gal__dots{ display:flex; align-items:center; gap:10px; }
.gal__dot{
  --d:  calc(var(--i) - var(--slot));
  --d2: calc(var(--d) * var(--d));
  position:relative; overflow:hidden;
  width:clamp(20px,2.6vw,38px); height:2px; border-radius:2px;
  background:rgba(246,243,255,.20);
}
.gal__dot::after{
  content:''; position:absolute; inset:0;
  background:linear-gradient(90deg, var(--af-violet), var(--af-pink));
  transform-origin:0 50%;
  transform:scaleX(clamp(0, calc(1 - var(--d2) * 1.7), 1));
}

/* ══ ACTO 4 · CIERRE ════════════════════════════════════════════════════ */

.cta__lead{
  margin-top:clamp(12px,1.8vh,20px);
  font-size:clamp(15px,1.3vw,18px); color:var(--ink-soft);
  opacity:var(--t); translate:0 calc((1 - var(--t)) * 16px);
}

.cta{
  /* Guiado por la ENTRADA del acto (--pe), no por su anclaje: el titular ya
     está a la vista cuando el acto toca el borde superior, y no puede llegar
     a ese momento en blanco. Empieza con el acto a un tercio de pantalla y
     está completo cuando llega arriba. */
  --t: clamp(0, calc((var(--pe, 1) - .34) / .40), 1);
  position:relative;
}
/* REVELADO 3D.
   Cada letra llega desde el fondo del espacio: entra girada sobre su base,
   desenfocada por distancia y con su propio retraso. No es un fundido — es la
   misma gramática del viaje, letras que salen del túnel.

   Nada de esto usa overflow:hidden. La versión anterior enmascaraba cada
   palabra para revelarla desde abajo, y ese recorte era la línea que cortaba
   la composición justo por encima del texto. */
.cta__title{
  font-family:var(--f-display); font-weight:700;
  font-size:clamp(2.4rem,9vw,8rem); line-height:1; letter-spacing:-.05em;
  color:#f6f3ff;
  /* Aire propio: sin esto los rasgos altos y bajos rozan el borde del bloque
     y a cierto tamaño parecen cortados. */
  padding-block:.10em .16em;
}
.cw{ display:inline-block; perspective:760px; }
.cx{
  /* Escalonado por letra. El multiplicador convierte el recorrido de --q en
     "cuántas letras han llegado ya". */
  --t: clamp(0, calc((var(--pe, 1) - .26) * 8 - var(--i, 0) * (4.4 / var(--nc, 8))), 1);
  display:inline-block; transform-origin:50% 92%;
  transform:
    translate3d(0, calc((1 - var(--t)) * .30em), calc((1 - var(--t)) * -420px))
    rotateX(calc((1 - var(--t)) * -82deg));
  opacity:var(--t);
  will-change:transform, opacity;
}
.cta .btn--lg{ opacity:var(--t); translate:0 calc((1 - var(--t)) * 28px); }

/* ── Contacto ─────────────────────────────────────────────────────────── */
/* Los canales reales. Discretos: el formulario es el camino principal. */
.chan{
  display:flex; flex-wrap:wrap; justify-content:center;
  gap:clamp(14px,2.4vw,30px);
  margin-top:clamp(22px,3.4vh,38px);
  font-size:13px; letter-spacing:.02em;
  opacity:var(--t);
}
.chan__a{ color:var(--ink-mute); transition:color var(--d-fast) var(--e); }
a.chan__a:hover{ color:var(--ink); }
.chan__a--flat{ cursor:default; }   /* dato, no enlace: no consta su prefijo */

/* Degradado sobre el titular partido.
   background-clip:text no puede vivir en el <h2>: al partirlo en letras el
   texto pasa a los <span> hijos, que heredan color:transparent pero no el
   fondo — el titular desaparece. Se pinta sobre cada .cx; el color sólido
   declarado arriba queda de respaldo. */
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .cta__title .cx{
    background:linear-gradient(168deg,#ffffff 4%,#e2ccff 44%,#ff6cc4 100%);
    -webkit-background-clip:text; background-clip:text; color:transparent;
  }
}

/* ── Pie ───────────────────────────────────────────────────────────────── */



/* ══ Responsive ═════════════════════════════════════════════════════════
   No es una reducción: cambia la composición y qué efectos siguen vivos. La
   intención se conserva — los actos siguen fijados y todo sigue scrubbed.
  ───────────────────────────────────────────────────────────────────────── */

@media (max-width:1024px){
  /* Menos separación lateral y menos giro: con el encuadre estrecho las
     piezas vecinas se salían del escenario y la activa quedaba diminuta. */
  .gal{ --gal-w:min(78vw, 700px); }
  .gal__item{
    transform:
      translate3d(calc(var(--d) * 52%), calc(var(--d2) * 2vh), calc(var(--d2) * -380px))
      rotateY(calc(var(--d) * -16deg + var(--mx, 0) * 2deg))
      rotateX(calc(var(--my, 0) * -1.4deg))
      scale(calc(1 - var(--d2) * .06));
  }
}

@media (max-width:760px){
  .nav__links{ display:none; }
  /* En vertical el conjunto (captura + rótulo) mide poco más de un tercio de
     la pantalla: centrarlo por su punto medio real evita el medio viewport
     vacío que quedaba debajo. */
  /* A ancho de columna, con el margen de la página: sangrando por los
     bordes el rótulo quedaba cortado por la izquierda y la captura se leía
     como recorte accidental, no como escena. */
  .gal{
    --gal-w:calc(100vw - var(--gut) * 2);
    --gal-gap:16px;
    --gal-meta:clamp(86px, 12vh, 112px);   /* sin titular: sector + nombre */
    --gal-top:clamp(56px, 8vh, 76px);
  }
  .gal__frame{ border-radius:10px; }
  .gal__name{ font-size:clamp(1.3rem,6vw,1.9rem); }
  .gal__head{ display:none; }   /* el titular del proyecto ya está en la captura */
}

@media (pointer:coarse){
  .lnk span{ transition:none; }
  /* Sin hover, el afordance debe verse: sólo en la pieza activa (una
     insignia, no cuatro) y sin backdrop-filter. */
  .gal__item.is-active .gal__open{ visibility:visible; opacity:1; translate:0 0; }
}

/* ══ Movimiento reducido ════════════════════════════════════════════════
   Se apaga el movimiento autónomo y los estados scrubbed se fijan en su
   valor legible. Los actos dejan de ser altos y la galería pasa a columna:
   sin transformación no hay nada que recorrer, y mantener raíles de cinco
   pantallas sería scroll vacío.
  ───────────────────────────────────────────────────────────────────────── */

@media (prefers-reduced-motion:reduce){
  html{ scroll-behavior:auto; }
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    transition-delay:0s !important;
  }
  .act{ height:auto !important; }
  .act__stage{
    position:static !important; height:auto !important;
    padding-block:clamp(70px,10vh,120px); overflow:visible !important;
  }
  .cta .btn--lg{ opacity:1 !important; translate:none !important; }

  .gal{ height:auto; display:flex; flex-direction:column; gap:64px; padding-inline:var(--gut); }
  .gal__item{
    position:static; width:100%; translate:none !important;
    transform:none !important; opacity:1 !important; pointer-events:auto !important;
  }
  .gal__meta{ position:static; margin-top:20px; opacity:1 !important; }
  .gal__glow{ opacity:.25 !important; }
  .gal__open{ visibility:visible; opacity:1; translate:0 0; }
  .gal__hit{ display:none; }   /* en columna, sin 3D, cada pieza recibe su clic */
  .gal__hud{ display:none; }
  .lead, .chan, .cta__lead{ opacity:1 !important; translate:none !important; }
  .hero__cat, .hero__lead{ opacity:1 !important; translate:none !important; }
  .cx{ transform:none !important; opacity:1 !important; filter:none !important; }
  .btn::after{ animation:none !important; }

}

/* ══════════════════════════════════════════════════════════════════════════
   LA SEGUNDA MITAD — el argumento

   Los tres primeros actos secuestran la pantalla para demostrar de qué es
   capaz el estudio. A partir de aquí la escena baja el tono y el contenido
   pasa a flujo normal: mismo espacio, mismo fondo, otro registro.

   Todo lo que sigue comparte el MISMO sistema —una escala de espaciado, un
   único encabezado de sección, un solo tratamiento de lista— porque diez
   secciones con diez criterios distintos se leen como diez componentes
   pegados, que es exactamente lo que no queremos.
   ══════════════════════════════════════════════════════════════════════════ */

:root{
  --sec-y:  clamp(84px, 12vh, 168px);   /* aire vertical entre secciones */
  --gap-1:  clamp(10px, 1.2vh, 16px);
  --gap-2:  clamp(20px, 2.6vh, 34px);
  --gap-3:  clamp(38px, 5.2vh, 76px);
  --r-sm:   10px;
  --r-md:   18px;
  --hair:   rgba(246,243,255,.10);      /* el trazo más tenue admisible */
}

/* Un acto de FLUJO no fija la pantalla: es una sección normal sobre la misma
   escena. Conserva --q, así que su contenido se puede seguir coreografiando. */
.act--flow{ height:auto; }
.act--flow .act__stage{
  position:static; height:auto; overflow:visible;
  display:block; padding-block:var(--sec-y);
}

.sec{ position:relative; }
.sec__head{ max-width:60ch; margin-bottom:var(--gap-3); }
.sec__tag{
  font-size:11px; letter-spacing:.26em; text-transform:uppercase;
  color:var(--accent); margin-bottom:var(--gap-1);
}
.sec__title{
  font-family:var(--f-display); font-weight:600;
  font-size:clamp(2rem,4.4vw,3.6rem); line-height:1.04; letter-spacing:-.038em;
  color:var(--ink);
}
.sec__lead{
  margin-top:var(--gap-2); max-width:52ch;
  font-size:clamp(15px,1.25vw,17.5px); color:var(--ink-soft);
}

/* Entrada por sección: una sola vez, discreta. El scrubbing se queda para la
   primera mitad; mezclar los dos registros pone la página nerviosa. */
.act--flow .sec__head, .act--flow .caps, .act--flow .svc,
.act--flow .proc, .act--flow .faq,
.act--flow .lead, .act--flow .chan{
  opacity:0; translate:0 22px;
  transition:opacity .9s var(--e), translate .9s var(--e);
}
.act--flow.is-in .sec__head{ opacity:1; translate:none; }
.act--flow.is-in .caps, .act--flow.is-in .svc, .act--flow.is-in .proc,
.act--flow.is-in .faq,
.act--flow.is-in .lead, .act--flow.is-in .chan{
  opacity:1; translate:none; transition-delay:.12s;
}

/* ── CAPACIDADES ──────────────────────────────────────────────────────── */

.caps{
  display:grid; gap:var(--gap-3);
  grid-template-columns:repeat(auto-fit, minmax(230px, 1fr));
}
.caps__lab{
  font-size:10.5px; letter-spacing:.24em; text-transform:uppercase;
  color:var(--ink-mute); margin-bottom:var(--gap-2);
}
.caps__list{ display:grid; gap:11px; }
.caps__list li{
  font-family:var(--f-display); font-weight:500;
  font-size:clamp(15px,1.35vw,18px); color:var(--ink-soft); letter-spacing:-.012em;
}
.facts{ display:grid; gap:11px; }
.facts__row{
  display:flex; align-items:baseline; justify-content:space-between; gap:16px;
}
.facts__row dt{ font-size:13.5px; color:var(--ink-mute); }
.facts__row dd{
  font-family:var(--f-display); font-weight:600; font-size:14px;
  color:var(--ink); text-align:right;
}

/* ── SERVICIOS ────────────────────────────────────────────────────────── */

/* Lista a la izquierda, panel visual a la derecha. El panel es STICKY: se
   queda a la vista mientras la lista pasa, y cambia con la fila señalada. */
.svcwrap{
  display:grid; gap:clamp(28px,4vw,72px);
  grid-template-columns:minmax(0, 1.15fr) minmax(0, 1fr);
  align-items:start;
}
.svc{ display:grid; }
.svc__row{ cursor:default; outline:none; }
.svc__name, .svc__desc, .svc__n{ transition:color .55s var(--e), translate .55s var(--e); }
.svc__row:not(.is-on) .svc__value{ color:rgba(246,243,255,.34); }
.svc__row:not(.is-on) .svc__name{ color:rgba(246,243,255,.62); }
.svc__row:not(.is-on) .svc__desc{ color:rgba(246,243,255,.46); }
.svc__row.is-on .svc__name{ color:var(--ink); translate:4px 0; }
.svc__row.is-on .svc__desc{ color:var(--ink-soft); }
.svc__row.is-on .svc__n{ color:var(--af-pink); }
.svc__row:hover:not(.is-on) .svc__name{ color:var(--ink-soft); }
.svc__row:focus-visible{ box-shadow:inset 0 -1px 0 var(--hair), inset 2px 0 0 var(--accent); }

.svc__visual{
  position:sticky; top:clamp(96px,14vh,140px);
  aspect-ratio:16 / 11; border-radius:16px; overflow:hidden;
  background:#0a0716;
  box-shadow:0 40px 120px rgba(0,0,0,.55), 0 0 0 1px rgba(246,243,255,.10);
}
.svc__panel{
  position:absolute; inset:0; margin:0;
  opacity:0; transform:scale(1.04);
  transition:opacity .7s var(--e), transform 1.1s var(--e);
}
.svc__panel.is-on{ opacity:1; transform:none; }
.svc__panel img{ width:100%; height:100%; object-fit:cover; object-position:top center; }
.svc__panel::after{
  content:''; position:absolute; inset:0; pointer-events:none;
  background:
    linear-gradient(180deg, transparent 55%, rgba(5,2,16,.72) 100%),
    radial-gradient(90% 60% at 100% 100%, color-mix(in srgb, var(--proj) 30%, transparent), transparent 70%);
}
.svc__panel figcaption{
  position:absolute; left:18px; bottom:16px;
  font-family:var(--f-display); font-weight:600; font-size:13px; letter-spacing:.02em;
  color:var(--ink);
}
.svc__row{
  display:grid; align-items:baseline; gap:var(--gap-1) clamp(18px,3vw,44px);
  grid-template-columns:auto minmax(0,.9fr) minmax(0,1.5fr);
  padding-block:clamp(20px,2.8vh,32px);
  /* Un hilo por fila. Es el único trazo del sistema y tiene función: separa
     entradas de una MISMA lista. No es un adorno ni un marco. */
  box-shadow:inset 0 -1px 0 var(--hair);
  transition:background-color var(--d-fast) var(--e);
}
.svc__row:last-child{ box-shadow:none; }
.svc__n{
  font-family:var(--f-display); font-size:11.5px; letter-spacing:.18em;
  color:var(--accent); padding-top:.5em;
}
.svc__name{
  font-family:var(--f-display); font-weight:500;
  font-size:clamp(1.28rem,2.5vw,2rem); line-height:1.06; letter-spacing:-.03em;
}
.svc__desc{ font-size:clamp(14px,1.1vw,15.5px); color:var(--ink-soft); }
.svc__body{ display:grid; gap:8px; }
.svc__value{ font-size:clamp(13px,1vw,14.5px); line-height:1.55; color:rgba(246,243,255,.46); transition:color .55s var(--e); }
.svc__k{ display:block; font-size:10px; letter-spacing:.2em; text-transform:uppercase; color:var(--ink-mute); margin-bottom:3px; }
.svc__row.is-on .svc__value{ color:var(--ink-soft); }

/* ── PROCESO ──────────────────────────────────────────────────────────── */

.proc{
  --pp: clamp(0, calc((var(--pe, 1) - .28) / .55), 1);   /* progreso del proceso */
  position:relative;
  display:grid; gap:var(--gap-2);
  grid-template-columns:repeat(auto-fit, minmax(178px, 1fr));
  padding-top:18px;
}
/* La línea recorre los cinco pasos con el scroll. Un solo scaleX. */
.proc__line{
  position:absolute; left:0; right:0; top:0; height:1px; overflow:hidden;
  background:rgba(246,243,255,.09);
}
.proc__line i{
  position:absolute; inset:0; display:block; transform-origin:0 50%;
  transform:scaleX(var(--pp));
  background:linear-gradient(90deg, var(--af-blue), var(--af-violet), var(--af-pink));
}
.proc__step{ display:grid; gap:var(--gap-1); align-content:start; }
/* Cada paso se enciende cuando la línea lo alcanza: umbral por índice. */
.proc__step{ --on: clamp(0, calc((var(--pp) - var(--i) / var(--n, 5)) * 6), 1); }
.proc__n{
  display:flex; align-items:center; gap:10px;
  font-family:var(--f-display); font-weight:600; font-size:12px;
  letter-spacing:.18em; color:color-mix(in srgb, var(--ink) calc(var(--on) * 100%), var(--ink-mute));
}
.proc__n i{
  width:6px; height:6px; border-radius:50%; background:var(--accent);
  transform:scale(var(--on)); opacity:var(--on);
}
.proc__name{
  font-family:var(--f-display); font-weight:500;
  font-size:clamp(1.05rem,1.7vw,1.4rem); letter-spacing:-.024em;
  color:color-mix(in srgb, var(--ink) calc(40% + var(--on) * 60%), transparent);
}
.proc__desc{ font-size:14px; color:var(--ink-soft); opacity:calc(.55 + var(--on) * .45); }

/* ── PREGUNTAS ────────────────────────────────────────────────────────── */

.act--faq .sec__head{ margin-inline:auto; text-align:center; }
.act--faq .sec__lead{ margin-inline:auto; }
.faq{ max-width:72ch; margin-inline:auto; }
.faq__item{ box-shadow:inset 0 -1px 0 var(--hair); }
.faq__item:last-child{ box-shadow:none; }
.faq__h{ margin:0; }
.faq__q{
  width:100%; display:flex; align-items:center; justify-content:space-between;
  gap:20px; padding-block:clamp(18px,2.4vh,26px);
  background:none; border:0; color:var(--ink); cursor:pointer;
  font:inherit; font-family:var(--f-display); font-weight:500;
  font-size:clamp(15px,1.5vw,19px); letter-spacing:-.018em; text-align:left;
  transition:color var(--d-fast) var(--e);
}
.faq__q:hover{ color:var(--accent); }
.faq__ico{
  position:relative; flex:none; width:13px; height:13px;
  transition:transform var(--d-base) var(--e);
}
.faq__ico::before, .faq__ico::after{
  content:''; position:absolute; inset:50% 0 auto 0; height:1.5px;
  background:currentColor; border-radius:2px;
}
.faq__ico::after{ inset:0 auto 0 50%; width:1.5px; height:100%; transform:translateX(-50%); }
.faq__q[aria-expanded="true"] .faq__ico{ transform:rotate(135deg); }
.faq__a{ padding-bottom:clamp(18px,2.4vh,26px); max-width:66ch; }
.faq__a p{ font-size:14.5px; line-height:1.65; color:var(--ink-soft); }

/* ── SOLICITUD ────────────────────────────────────────────────────────── */

.cta{ text-align:center; display:grid; justify-items:center; }
.cta__tag{ margin-bottom:var(--gap-1); }
.cta__lead{ max-width:46ch; margin-inline:auto; }
/* Vidrio: el formulario es la única superficie de la sección, y flota. */
.glass{
  background:linear-gradient(180deg, rgba(255,255,255,.055), rgba(255,255,255,.022));
  border:1px solid var(--glass-line);
  box-shadow:var(--glass-hi), 0 30px 80px -30px rgba(0,0,0,.7);
  border-radius:var(--r-md);
}
.lead{
  display:grid; gap:var(--gap-2); width:min(100%, 620px);
  margin-top:var(--gap-3); padding:clamp(22px,3vw,36px);
  text-align:left;
}
.lead__grid{
  display:grid; gap:var(--gap-2);
  grid-template-columns:repeat(auto-fit, minmax(200px, 1fr));
}
.fld{ display:grid; gap:8px; }
.fld__lab{
  font-size:10.5px; letter-spacing:.22em; text-transform:uppercase; color:var(--ink-mute);
}
.fld__in{
  width:100%; padding:13px 15px; border-radius:var(--r-sm);
  background:rgba(5,3,18,.35); border:1px solid var(--hair);
  color:var(--ink); font:inherit; font-size:15px;
  transition:border-color var(--d-fast) var(--e), background-color var(--d-fast) var(--e);
}
.fld__in::placeholder{ color:rgba(246,243,255,.30); }
.fld__in:focus{
  outline:none; border-color:rgba(139,92,255,.55); background:rgba(5,3,18,.5);
}
.fld--area .fld__in{ resize:vertical; min-height:112px; line-height:1.55; }
.fld__in[aria-invalid="true"]{ border-color:rgba(255,92,140,.65); }
.lead__send{ justify-self:center; margin-top:var(--gap-1); }
.lead__note{ min-height:1.3em; font-size:13.5px; color:var(--ink-soft); text-align:center; }
.lead__note[data-ok]{ color:var(--ink); }
/* Estado de error: el correo directo, con el mensaje ya redactado, es un enlace. */
.lead__note a{ color:var(--ink); box-shadow:inset 0 -1px 0 rgba(246,243,255,.4); }
.lead__send:disabled{ opacity:.72; cursor:default; }

/* La confirmación sustituye al formulario: dejar el formulario ahí después de
   enviarlo invita a enviarlo otra vez. */
.lead.is-sent .lead__grid, .lead.is-sent .fld, .lead.is-sent .lead__send{ display:none; }
.lead.is-sent .lead__note{
  font-family:var(--f-display); font-size:clamp(16px,1.6vw,20px);
  line-height:1.5; color:var(--ink); max-width:46ch; margin-inline:auto;
}

.cta .chan{ justify-content:center; margin-top:var(--gap-3); }

/* ── PIE ──────────────────────────────────────────────────────────────── */

.act--footer .act__stage{ padding-block:var(--gap-3) clamp(24px,3.5vh,40px); }
.foot{
  position:static; display:block; padding:0;
  border:0; text-align:left; font-size:inherit; color:inherit;
}
.foot__top{
  display:grid; gap:var(--gap-2) var(--gap-3);
  grid-template-columns:minmax(220px, 1.6fr) repeat(4, minmax(120px, 1fr));
  padding-bottom:var(--gap-2);
  box-shadow:inset 0 -1px 0 var(--hair);   /* hilo del sistema, no un borde */
}
.foot__brand{
  font-family:var(--f-display); font-weight:700; font-size:14px;
  letter-spacing:.20em; color:var(--ink);
}
.foot__claim{ margin-top:12px; max-width:30ch; font-size:13.5px; line-height:1.6; color:var(--ink-mute); }
.foot__lab{
  font-size:10px; letter-spacing:.24em; text-transform:uppercase;
  color:var(--ink-mute); margin-bottom:16px;
}
.foot__col ul{ display:grid; gap:9px; }
.foot__col li{ font-size:13px; color:var(--ink-soft); }
.foot__col a{ transition:color var(--d-fast) var(--e); }
.foot__col a:hover{ color:var(--accent); }
.foot__end{
  display:flex; flex-wrap:wrap; align-items:center; gap:16px 28px;
  padding-top:var(--gap-2);
  font-size:12px; letter-spacing:.04em; color:var(--ink-mute);
}
.foot__end > :last-child{ margin-left:auto; }
.foot__up{ transition:color var(--d-fast) var(--e); }
.foot__up:hover{ color:var(--accent); }

/* ── Responsive de la segunda mitad ───────────────────────────────────── */

@media (max-width:1024px){
  .hero{ grid-template-columns:minmax(0, 1fr); gap:clamp(28px,5vh,48px); align-content:center; }
  .hero__visual{ width:min(100%, 560px); max-height:min(42vh, 360px); justify-self:start; margin-top:0; }
  .gal__meta{ grid-template-columns:minmax(0,1fr); gap:10px; }
  .gal__meta-r{ padding-top:0; }
  .gal__blurb{ display:none; }
  .hero__title{ max-width:14ch; }
  .svcwrap{ grid-template-columns:1fr; }
  .svc__visual{ position:relative; top:auto; order:-1; aspect-ratio:16 / 10; }
}
@media (max-width:860px){
  .svc__row{
    grid-template-columns:auto 1fr;
    gap:6px clamp(14px,3vw,24px);
  }
  .svc__body{ grid-column:2; }
}
@media (max-width:760px){
  .act--flow .act__stage{ padding-block:clamp(64px,9vh,110px); }
  /* Servicios llega justo cuando el raíl suelta el escenario, y en vertical
     la pieza deja bajo sí una banda vacía. Un fundido por TIEMPO (.9 s) deja
     esa banda seguida de una cabecera invisible durante todo un gesto de
     scroll. Aquí la sección se revela por la ENTRADA del acto (--pe), con la
     misma gramática que .cta y .proc: visible en cuanto asoma por abajo. Las
     mismas reglas se repiten con .is-in para ganar a las de flujo. */
  .act--services .sec__head, .act--services.is-in .sec__head{
    --t: clamp(0, calc((var(--pe, 1) - .06) / .22), 1);
    opacity:var(--t); translate:0 calc((1 - var(--t)) * 22px); transition:none;
  }
  .act--services .svc, .act--services.is-in .svc{
    --t: clamp(0, calc((var(--pe, 1) - .12) / .26), 1);
    opacity:var(--t); translate:0 calc((1 - var(--t)) * 22px); transition:none;
  }
  /* …y además empieza antes. Entre el indicador —el último elemento visible
     del raíl, anclado al borde del escenario— y la cabecera de Servicios hay
     67/76/86 px (360/375/412) de nada: el resto del escenario más el aire
     superior de la sección. Se recupera la mitad larga subiendo el inicio de
     la sección. El recorrido está ACOTADO para que queden ~35 px de aire bajo
     el indicador: la cabecera nunca se monta sobre la pieza ni sobre él. No
     toca el raíl, ni el alto de los actos, ni el aire interior de Servicios. */
  .act--services{ margin-top:calc(-1 * clamp(28px, 5vh, 48px)); }
  .hero{ padding-block:clamp(80px,12vh,112px) clamp(40px,6vh,64px); gap:clamp(20px,3.5vh,36px); align-content:start; }
  .hero__title{ font-size:clamp(2rem,9vw,3rem); max-width:100%; text-wrap:pretty; }
  .hero__lead{ max-width:100%; }
  .hero__visual{ max-height:min(34vh, 260px); }
  .gal__tags{ display:none; }
  .hero__cat{ white-space:normal; line-height:1.5; }
  /* Sin puntero no hay motivo para el giro de la sala. */
  .gal{ transform:translate3d(calc(var(--o) * var(--hx)), calc(var(--o) * var(--hy)), 0) scale(calc(1 - var(--o) * (1 - var(--hs)))); }
  .foot__end > :last-child{ margin-left:0; }
}

@media (prefers-reduced-motion:reduce){
  .act--flow .sec__head, .act--flow .caps, .act--flow .svc,
  .act--flow .proc, .act--flow .faq,
  .act--flow .lead, .act--flow .chan{ opacity:1 !important; translate:none !important; }
  .layer{ position:static; transform:none !important; will-change:auto; }
  .hero{ position:static; grid-template-columns:1fr; padding-inline:var(--gut); }
  .hero__visual{ display:none; }
  .hero__copy, .hero__title, .hero__lead, .hero__cta{ transform:none !important; visibility:visible !important; opacity:1 !important; }
  .gal{ transform:none !important; }
  .proc__line i{ transform:scaleX(1) !important; }
  .proc__step{ --on:1; }
}

/* ══════════════════════════════════════════════════════════════════════════
   CABECERA — navegación de agencia
   ══════════════════════════════════════════════════════════════════════════ */

.skip{
  position:absolute; left:-9999px; top:0; z-index:100;
  padding:12px 18px; border-radius:0 0 var(--r-sm) 0;
  background:var(--bg); color:var(--ink); font-size:13px;
}
.skip:focus{ left:0; }

.nav__cta{ margin-left:0; }

/* Botón de menú. Sólo existe en pantalla estrecha, pero su marcado está
   siempre: un menú que se inyecta al redimensionar pierde el foco. */
.burger{
  display:none; position:relative; width:42px; height:42px; border-radius:50%;
  background:none; border:0; cursor:pointer; color:var(--ink);
  transition:background-color var(--d-fast) var(--e);
}
.burger:hover{ background:rgba(255,255,255,.07); }
.burger span, .burger span::before, .burger span::after{
  content:''; position:absolute; left:11px; width:20px; height:1.5px;
  border-radius:2px; background:currentColor;
  transition:transform var(--d-base) var(--e), opacity var(--d-fast) var(--e);
}
.burger span{ top:50%; translate:0 -50%; }
.burger span::before{ left:0; top:-6px; }
.burger span::after{ left:0; top:6px; }
.burger[aria-expanded="true"] span{ transform:rotate(45deg); }
.burger[aria-expanded="true"] span::before{ transform:translateY(6px) rotate(-90deg); }
.burger[aria-expanded="true"] span::after{ opacity:0; }

.navpanel{
  position:fixed; inset:0; z-index:19;
  display:grid; place-items:center;
  background:rgba(5,2,16,.86);
  backdrop-filter:blur(20px) saturate(1.1);
  -webkit-backdrop-filter:blur(20px) saturate(1.1);
  opacity:0; visibility:hidden;
  transition:opacity var(--d-base) var(--e), visibility var(--d-base);
}
.navpanel.is-open{ opacity:1; visibility:visible; }
.navpanel__cta{ margin-top:8px; color:var(--ink) !important; font-size:clamp(1rem,4.4vw,1.2rem) !important; letter-spacing:0 !important; }
/* En el panel abierto, el vidrio del menú también se muestra encima. */
.nav-open .nav{ z-index:21; }
.navpanel nav{ display:grid; gap:clamp(14px,3vh,26px); text-align:center; }
.navpanel a{
  font-family:var(--f-display); font-weight:600;
  font-size:clamp(1.6rem,7vw,2.4rem); letter-spacing:-.03em;
  color:var(--ink-soft); transition:color var(--d-fast) var(--e);
}
.navpanel a:hover, .navpanel a:focus-visible{ color:var(--ink); }
html.nav-open{ overflow:hidden; }

@media (max-width:1180px){
  .nav__links .lnk:last-child{ display:none; }   /* "Contact": ya está el CTA */
  .lang{ display:none; }
}
@media (max-width:900px){
  .nav__links{ display:none; }
  .nav__cta{ display:none; }
  .burger{ display:block; }
  .nav{ inset:10px 0 auto 0; }
  .nav__bar{ padding:5px 6px 5px 10px; }
}

/* ── Legal ── */
.legal .sec__head{ max-width:60ch; }
.legal__body{ max-width:62ch; display:grid; gap:14px; }
.legal__body h2{
  font-family:var(--f-display); font-weight:500; font-size:clamp(1.1rem,1.6vw,1.35rem);
  letter-spacing:-.02em; margin-top:18px;
}
.legal__body p, .legal__body li{ font-size:15px; line-height:1.65; color:var(--ink-soft); }
.legal__body ul{ display:grid; gap:10px; padding-left:18px; list-style:disc; }
.legal__body a{ color:var(--ink); box-shadow:inset 0 -1px 0 rgba(246,243,255,.3); }
.legal__body code{ font-family:ui-monospace, monospace; font-size:.9em; }

/* ══ Páginas de lectura (about, 404) ════════════════════════════════════
   Comparten el sistema con la home pero no cargan el motor: una página de
   lectura no necesita WebGL, y añadir un segundo bucle por coherencia visual
   sería pagar rendimiento a cambio de nada. */

.page{ min-height:100svh; }

/* ── Portafolio (página de lectura) ────────────────────────────────────
   El acceso al portfolio, separado del recorrido de Work en la portada. Cada
   pieza usa el MISMO rótulo que la galería (.gal__sector, .gal__name…), pero
   en flujo: la captura arriba, el rótulo debajo. Sin tarjetas. */
.pf{ list-style:none; margin:clamp(40px,6vh,72px) 0 0; padding:0; display:grid; gap:clamp(56px,9vh,110px); }
.pf__item{ max-width:940px; }
.pf__shot{
  position:relative; display:block; overflow:hidden; border-radius:14px;
  aspect-ratio:16 / 10; background:#0a0716;
  border:1px solid rgba(246,243,255,.14); box-shadow:0 28px 70px rgba(0,0,0,.50);
}
.pf__shot img{ width:100%; height:100%; object-fit:cover; object-position:top center; display:block; }
.pf__shot::after{
  content:''; position:absolute; inset:0; pointer-events:none; border-radius:inherit;
  background:linear-gradient(180deg, rgba(246,243,255,.09), transparent 18%);
}
.pf__shot:hover .gal__open, .pf__shot:focus-visible .gal__open{ visibility:visible; opacity:1; translate:0 0; }
.pf__meta{
  display:grid; grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:clamp(20px,3vw,48px);
  align-items:start; margin-top:clamp(18px,2.6vh,32px);
}
.pf__home{ color:var(--ink); box-shadow:inset 0 -1px 0 rgba(246,243,255,.3); }
.pf__home:hover{ box-shadow:inset 0 -1px 0 rgba(246,243,255,.7); }
.pf--static{ display:grid; gap:12px; }
.pf--static a{ color:var(--ink); }
@media (max-width:760px){
  .pf__meta{ grid-template-columns:minmax(0,1fr); gap:10px; }
  .pf__shot{ border-radius:10px; }
}
@media (pointer:coarse){ .pf__shot .gal__open{ visibility:visible; opacity:1; translate:0 0; } }
.page .act--flow .sec__head, .page .act--flow .caps,
.page .act--flow .svc, .page .act--flow .proc{ opacity:1; translate:none; }
.about__p{ font-size:15px; line-height:1.65; color:var(--ink-soft); }

.nf{
  min-height:100svh; display:grid; align-content:center; justify-items:start;
  padding:0 var(--gut); max-width:var(--maxw); margin-inline:auto;
}
.nf__code{
  font-family:var(--f-display); font-weight:700;
  font-size:clamp(4rem,16vw,11rem); line-height:.86; letter-spacing:-.06em;
  background:linear-gradient(150deg,#ffffff 6%,#b9a6ff 46%,#ff6cc4 100%);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}
.nf__title{
  margin-top:clamp(14px,2.4vh,28px);
  font-family:var(--f-display); font-weight:600;
  font-size:clamp(1.5rem,3.4vw,2.6rem); line-height:1.08; letter-spacing:-.032em;
}
.nf__lead{ margin-top:var(--gap-2); max-width:44ch; color:var(--ink-soft); }
.nf__cta{ margin-top:var(--gap-3); }

@media (max-width:1024px){
  /* .foot es .wrap, pero su reset (padding:0) anulaba el margen lateral y el
     texto arrancaba en x=0. En pantalla pequeña recupera el gutter del sistema. */
  .foot{ padding-inline:var(--gut); }
  .foot__top{ grid-template-columns:1fr 1fr 1fr; }
  .foot__brandcol{ grid-column:1 / -1; }
}
@media (max-width:640px){
  /* A 390 px la columna de marca (210) más una de enlaces (140) ya no caben
     entre márgenes. Se apilan en dos y la marca ocupa el ancho: forzar cuatro
     columnas aquí sólo produce recorte. */
  .foot__top{ grid-template-columns:repeat(2, minmax(0,1fr)); gap:var(--gap-3) var(--gap-2); }
  .foot__brandcol{ grid-column:1 / -1; }
}
