:root{
  --sand:#F0E4D0; --cream:#FAF3E7; --pale:#D9DCC0; --sage:#BAC095;
  --mid:#8A9459; --olive:#636B2F; --deep:#3D4127;
  /* el rojo de alerta (docs/colores.html): uno de los tres tonos que admiten
     texto pequeño, con 4,5 de contraste. Para los mensajes de error */
  --alerta:#A94A2A;
  --pad:clamp(20px,5vw,64px);
  --r-pill:999px;
}
/* Manrope servida desde la propia web (fuentes/), no desde Google Fonts: así
   ninguna visita manda su IP a un tercero y cookies.js puede decir que no se
   habla con nadie de fuera. Es la fuente variable de Google, un archivo por
   juego de caracteres que cubre todos los pesos (400 a 800). Las rutas son
   relativas a este CSS, así que valen también para 404.html, que carga
   /estilos.css con ruta absoluta. */
@font-face{font-family:"Manrope";font-style:normal;font-weight:400 800;font-display:swap;
  src:url(fuentes/manrope-latin-ext.woff2) format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Manrope";font-style:normal;font-weight:400 800;font-display:swap;
  src:url(fuentes/manrope-latin.woff2) format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}

*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:smooth}
/* Excepción para las vistas previas embebidas (la primera mancha de
   portfolio.html y la primera tarjeta de #trabajos): esa copia se recorre
   sola escribiendo scrollTop en CADA fotograma — ver recorrer() en main.js.
   Con scroll-behavior:smooth, cada una de esas escrituras arranca su propia
   animación suave hacia un destino que apenas se ha movido, y la escritura
   del fotograma siguiente la cancela antes de que llegue a ningún sitio: el
   recorrido se quedaba clavado a los ~19px de los 13924 que tiene por
   delante. En vista previa nadie pulsa un ancla, así que no hay nada que
   suavizar. Sin esta línea el recorrido no funciona en NINGUNA de las dos
   páginas. */
html.portfolio-preview{scroll-behavior:auto}
body{
  font-family:'Manrope',system-ui,sans-serif;
  font-weight:600;
  /* justificado a los dos márgenes por defecto, no solo a la izquierda —
     hereda a todo el texto de cuerpo (leads, prose, tarjetas...) salvo
     donde ya hay un text-align propio (centrado, p. ej. .card/.trio
     article/.quote), que gana por ser explícito. En líneas de una sola
     línea (títulos, etiquetas) no se nota ningún cambio: justify solo
     afecta a párrafos que envuelven en más de una línea. */
  text-align:justify;
  background:var(--sand); color:var(--deep);
  line-height:1.6; overflow-x:hidden; position:relative;
  -webkit-font-smoothing:antialiased;
}
body::after{
  content:''; position:fixed; inset:0; pointer-events:none; z-index:200;
  opacity:.38; mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}
:focus-visible{outline:2.5px solid var(--olive);outline-offset:3px;border-radius:6px}

/* ============ saltar al contenido ============ */
/* Primer elemento tabulable de cada página. Sin él, quien navega con teclado
   tiene que pasar por los cuatro enlaces del menú en TODAS las páginas antes
   de llegar al texto.

   Se esconde con transform, no con display:none ni visibility:hidden ni un
   left:-9999px: los dos primeros lo sacarían del orden de tabulación —o sea,
   no habría enlace que enfocar— y el tercero deja el elemento fuera de la
   pantalla, así que al recibir el foco el navegador scrollea de lado para
   enseñarlo. Con transform el elemento sigue ocupando su sitio real (arriba
   a la izquierda) y solo se pinta desplazado, así que no hay nada que
   perseguir. El -200% lo sube su propio alto por encima del borde.

   :focus y no :focus-visible: aquí siempre viene del teclado, y :focus cubre
   además el foco dado por código.

   El 300 de z-index lo pone por encima de todo lo que hay: la barra (60), el
   índice lateral (55), la intro de la portada (150) y la capa de ruido (200).

   No lleva outline propio: los 12px de separación del borde dejan sitio para
   que el anillo olivo de la regla de arriba se dibuje entero sobre el --sand
   de la página (4,6 de contraste), que es donde cae con outline-offset:3px.

   La transición no necesita excepción de movimiento reducido: el bloque del
   final de este archivo ya deja toda transition en .01ms. */
.saltar{position:fixed;top:12px;left:12px;z-index:300;
  display:inline-flex;align-items:center;padding:13px 24px;
  border-radius:var(--r-pill);background:var(--olive);color:var(--sand);
  font-size:15px;font-weight:700;text-decoration:none;
  transform:translateY(-200%);transition:transform .18s ease}
.saltar:focus{transform:none}
.page{padding:0 var(--pad) 22px;position:relative;z-index:1}
/* El pie de las páginas sueltas va pegado al borde de abajo. .page reserva
   22px por debajo (16px por debajo de 560px) y ahí asomaba una banda de
   arena bajo el panel oscuro del pie. Con :has se apaga solo donde hay pie
   propio: la portada no entra, porque el suyo vive dentro del cajón y ese
   respiro de abajo sí es suyo. */
.page:has(>.svc-footer,>.mis-footer){padding-bottom:0}

/* ============ vídeo de fondo (inicio → creatividad → método) ============ */
.vidbg{position:fixed;inset:0;z-index:0;overflow:hidden;background:var(--sand)}

/* El jardín de Misión entra solo al salir de Proceso. El recorte sigue el
   borde inferior de esa sección para que ninguna flor invada lo anterior. */
.portada__jardin{--cortina:100%;position:fixed;inset:0;z-index:0;
  pointer-events:none;overflow:hidden;visibility:hidden;
  clip-path:inset(var(--cortina) 0 0)}
.portada__jardin--visible{visibility:visible}
.portada__flores{position:absolute;inset:0;transform:translateY(var(--cortina))}
.portada__jardin-defs{position:absolute;width:0;height:0;overflow:hidden}
.portada__flor{--f:1;position:absolute;left:var(--x);top:var(--y);
  width:calc(var(--w) * var(--f));height:calc(var(--w) * var(--f));
  margin-left:calc(var(--w) * var(--f) / -2);margin-top:calc(var(--w) * var(--f) / -2);
  color:var(--c);opacity:var(--o);animation:portada-flor-mece var(--dur) ease-in-out infinite;
  animation-play-state:paused}
.portada__flor svg{display:block;width:100%;height:100%;
  animation:portada-flor-gira var(--giro) linear infinite;animation-play-state:paused}
.portada__jardin--visible .portada__flor,
.portada__jardin--visible .portada__flor svg{animation-play-state:running}
@keyframes portada-flor-mece{0%,100%{translate:0 0}50%{translate:0 -14px}}
@keyframes portada-flor-gira{to{rotate:360deg}}
@media (max-width:820px){
  .portada__flor{--f:.6;opacity:calc(var(--o) * .55)}
  .portada__flor--extra{display:none}
}
@media (prefers-reduced-motion:reduce){
  .portada__flores{transform:none}
  .portada__flor,.portada__flor svg{animation:none}
}

/* al 100% se veían demasiado grandes a pantalla completa: scale(.8) los
   reduce un 20% y los centra (transform-origin es center por defecto, así
   que encoge hacia el centro de su propio recuadro, no hacia una esquina).
   Es un cambio puramente visual en CSS — no toca ni el tamaño real del
   vídeo ni ninguna cuenta de scroll/currentTime en main.js, así que no
   puede desincronizar nada.

   mask-image difumina el borde. La primera versión usaba un degradado
   radial (una elipse) y comía demasiado vídeo: una elipse no puede pegarse
   a las cuatro esquinas de un rectángulo sin curvarse mucho antes de
   llegar a ellas, así que redondeaba de más y se notaba como una viñeta
   ovalada de verdad, no como un borde suavizado. Ahora son dos degradados
   LINEALES (uno horizontal, uno vertical) que solo se desvanecen en una
   franja del 6% pegada a cada lado — el 88% central de cada eje se queda
   totalmente opaco — combinados con mask-composite:intersect: un punto
   solo es opaco si lo son AMBOS degradados a la vez, así que el 6% de
   franja se aplica en los cuatro bordes por igual (y en las esquinas, que
   caen dentro de las dos franjas a la vez, se funden con un poco más de
   suavidad) sin comerse el resto del rectángulo. Revela el --sand de
   .vidbg de fondo, el mismo --sand del body: el vídeo se funde con la
   página en vez de cortarse en seco. */
.vidbg__clip{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover;
  opacity:0; will-change:opacity;
  /* translateY va antes que scale: así el desplazamiento final en pantalla
     son 20px reales, sin que el .8 del scale se lo coma (translateY(20px)
     scale(.8) daría solo 16px visibles, porque el scale, al ir después,
     encogería también el desplazamiento). */
  transform:translateY(20px) scale(.8);
  -webkit-mask-image:
    linear-gradient(to right, transparent, black 6%, black 94%, transparent),
    linear-gradient(to bottom, transparent, black 6%, black 94%, transparent);
  -webkit-mask-composite:source-over,source-in;
  mask-image:
    linear-gradient(to right, transparent, black 6%, black 94%, transparent),
    linear-gradient(to bottom, transparent, black 6%, black 94%, transparent);
  mask-composite:intersect;
}
/* hueco de scroll dedicado para que 4-arbol tenga su propio presupuesto,
   sus 10s completos. 80vh (el mismo que tuvo 2-bola, por tener también
   10s) se sentía rápido de más: torno tiene 220vh para 7s, es decir 31,4vh
   por segundo de vídeo, mientras que a 80vh/10s árbol solo tenía 8vh por
   segundo — casi 4 veces menos scroll por segundo de contenido. Este
   cálculo iguala su ritmo al de torno: 220vh / 7s × 10s de árbol. #servicios
   empieza justo al acabar este hueco — cuando árbol termina sus 10s, no
   antes (ver .servicios__pinwrap más abajo: ahí es donde se reproduce
   5-rotoscopia, detrás de la sección ya anclada). Si algún día cambia la
   duración de torno o de árbol, este valor se reajusta solo. */
.vidgap{height:calc(220vh / 7 * 10)}

/* legibilidad del texto sobre el vídeo: se probaron varias rondas de un
   fondo en caja detrás del texto (::before por bloque, luego un <span> con
   box-decoration-break:clone para tener una caja por línea) y todas
   acabaron en el mismo tipo de problema — una caja rectangular no sabe
   ajustarse al contorno real del texto, así que o sobra fondo donde no hay
   letras, o dos líneas consecutivas acaban demasiado juntas y la caja de
   una toca o se monta sobre la otra. Se abandonó el enfoque de caja del
   todo: en su lugar, text-shadow — la técnica estándar para separar texto
   de un fondo variable (fotos, vídeo) sin caja de por medio. Al aplicarse
   por GLIFO, no por bloque, la sombra sigue el contorno exacto de cada
   letra: nunca aparece donde no hay texto, y nunca puede "montarse" en la
   línea de al lado porque no es una caja, es solo un borde ceñido a cada
   carácter.

   Combina cuatro sombras nítidas de 1px (arriba/abajo/izquierda/derecha,
   sin desenfoque) para un borde definido, más dos sombras con blur de 1.5px
   para suavizarlo — la técnica habitual de "falso contorno" de texto en
   CSS. El alcance total (offset 1px + blur 1.5px ≈ 2.5px) se queda por
   debajo del half-leading que deja line-height:1.08 en el título más
   ajustado del sitio (~2.1px en el punto donde el vídeo sigue activo, ver
   la nota de más abajo), así que sigue sin tocar la línea de al lado. */
.vidhalo{
  text-shadow:
    1px 0 0 rgba(240,228,208,.95),
    -1px 0 0 rgba(240,228,208,.95),
    0 1px 0 rgba(240,228,208,.95),
    0 -1px 0 rgba(240,228,208,.95),
    0 0 1.5px rgba(240,228,208,.9),
    0 0 1.5px rgba(240,228,208,.9);
}
.vidhalo--fuerte{
  text-shadow:
    1px 0 0 rgba(240,228,208,1),
    -1px 0 0 rgba(240,228,208,1),
    0 1px 0 rgba(240,228,208,1),
    0 -1px 0 rgba(240,228,208,1),
    0 0 2px rgba(240,228,208,1),
    0 0 2px rgba(240,228,208,1);
}

/* ============ intro ============ */
/* Presupuesto de la secuencia entera: 2 s. A los 2 s no queda nada moviéndose
   (antes eran 4,2 s, y el hero no asomaba hasta el 3,1). Los tiempos de la
   barra, el logo de la barra, el hero y el índice lateral cuelgan de estos:
   si se toca uno, revisar la tabla de "La secuencia de apertura" en CLAUDE.md.
   Solo se ve al entrar de fuera: .sin-intro (más abajo) la salta. */
#intro{
  position:fixed; inset:0; z-index:150; background:var(--sand);
  display:grid; place-content:center;
  animation:introOut .4s cubic-bezier(.55,0,.35,1) 1s both;
}
#intro svg{width:min(30vw,210px);height:auto}
@keyframes introOut{
  0%{filter:blur(0);opacity:1}
  100%{filter:blur(16px);opacity:0;visibility:hidden}
}
.pre .ring{fill:none;stroke:var(--olive);stroke-width:2.5;stroke-dasharray:189;
           stroke-dashoffset:189;animation:draw .6s cubic-bezier(.65,0,.35,1) both}
.pre .r2{animation-delay:.1s}
.pre .solid{opacity:0;animation:fadeIn .24s ease-out .56s both}
.pre .ltr{stroke-dasharray:190;stroke-dashoffset:190;
          animation:draw .4s cubic-bezier(.65,0,.35,1) .66s both}
@keyframes draw{to{stroke-dashoffset:0}}
@keyframes fadeIn{to{opacity:1}}

/* ============ barra ============ */
.nav{
  --nav-dur:.65s; --nav-ease:cubic-bezier(.16,1,.3,1);
  position:sticky; top:14px; z-index:60; margin:14px auto 0; width:100%; max-width:100%;
  overflow:hidden;
  /* top/max-width/height/padding: sin rebote (se quitó el knockback a petición
     expresa). --nav-dur/--nav-ease son variables, no números repetidos a mano:
     .nav__mark svg (la regla del logo, más abajo) las reutiliza literalmente,
     para que sea imposible que la barra y el logo encojan con curvas o
     duraciones distintas y se vean desincronizados por en medio, aunque
     empiecen y acaben a la vez. */
  transition:top var(--nav-dur) var(--nav-ease),
             max-width var(--nav-dur) var(--nav-ease),
             height var(--nav-dur) var(--nav-ease),
             padding var(--nav-dur) var(--nav-ease),
             box-shadow var(--nav-dur) ease;
  display:flex; align-items:center; justify-content:space-between;
  height:78px; padding:0 24px;
  background:var(--cream); border-radius:var(--r-pill);
  box-shadow:0 10px 26px -14px rgba(61,65,39,.3);
  transform:translateY(-160%);
  animation:navDrop .55s cubic-bezier(.22,1,.36,1) 1.1s both;
}
@keyframes navDrop{to{transform:translateY(0)}}

/* barra comprimida: solo el logo */
/* top:14px y height:78px, los mismos valores que la barra sin comprimir:
   al contraer solo cambian max-width y padding, no la altura ni la
   posición vertical. */
.nav.compact{top:14px;max-width:226px;height:78px;padding:0 12px;
             box-shadow:0 14px 30px -14px rgba(61,65,39,.42)}
/* sin regla propia para .nav.compact .nav__mark svg: el logo se queda al
   mismo ancho (198px, la regla base de .nav__mark svg) comprimida o no. */
/* nav__menu sin transición en transform (aparece/desaparece de golpe en
   posición, no "deslizando" — antes transform seguía animando .5s tras
   quitar solo opacity, y se veía deslizar en vez de aparecer de golpe).

   Al DESAPARECER (.compact añadida) opacity es instantánea, sin retraso:
   pedido explícito, en el mismo momento en que la barra empieza a
   comprimirse. Al APARECER (.compact quitada) opacity hace fade in (.3s)
   con un retraso previo — pedido explícito, para que no aparezca de
   golpe. El retraso pasó de var(--nav-dur) entero (el fundido empezaba con
   la barra ya fija del todo) a calc(var(--nav-dur) * .7) (empezaba
   mientras la barra estaba a punto de terminar, pero seguía notándose
   tarde) a calc(var(--nav-dur) * .7 - .3s) — adelantado .3s más sobre esa
   segunda versión, pedido explícito. Es el truco habitual de "transition
   distinta según la dirección": duración y delay se toman de la regla del
   estado de LLEGADA, así que .nav__menu (destino al expandir) lleva el
   fundido con retraso y .nav.compact .nav__menu (destino al comprimir) lo
   pisa a instantáneo y sin retraso. */
.nav__menu{transition:opacity .3s ease calc(var(--nav-dur) * .7 - .3s)}
.nav.compact .nav__menu{opacity:0;pointer-events:none;transform:translateY(-8px)}
.nav.compact .nav__menu{transition:opacity 0s linear 0s}
/* reutiliza --nav-dur/--nav-ease de .nav (heredan por ser descendiente): la
   misma curva Y la misma duración que la barra, letra por letra, no solo
   "parecidas" — así no pueden desincronizarse por diseño, no porque a alguien
   se le olvide actualizar los dos sitios a la vez. */
.nav__mark svg{transition:width var(--nav-dur) var(--nav-ease)}

.nav__mark{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
           display:flex;align-items:center;pointer-events:none}
.nav__mark svg{width:198px;height:auto;display:block}

.nav__menu{flex:0 0 auto;display:flex;align-items:center;gap:4px;list-style:none}
.nav__menu li{animation:itemIn .45s cubic-bezier(.22,1,.36,1) backwards}
.nav__menu li:nth-child(1){animation-delay:1.28s}
.nav__menu li:nth-child(2){animation-delay:1.33s}
.nav__menu li:nth-child(3){animation-delay:1.38s}
.nav__menu li:nth-child(4){animation-delay:1.43s}
.nav__menu li:nth-child(5){animation-delay:1.48s}
@keyframes itemIn{0%{opacity:0;transform:translateY(-10px)}100%{opacity:1;transform:translateY(0)}}
.nav__link{
  display:inline-flex;align-items:center;gap:7px;padding:11px 15px;
  font:inherit;font-size:16px;font-weight:500;color:var(--deep);
  background:none;border:0;border-radius:var(--r-pill);cursor:pointer;
  text-decoration:none;white-space:nowrap;transition:background .2s ease}
.nav__link:hover{background:rgba(61,65,39,.08)}

/* lockup animado dentro de la barra */
.nl .ring{fill:none;stroke:var(--olive);stroke-width:2.5;stroke-dasharray:189;
          stroke-dashoffset:189;animation:draw .42s cubic-bezier(.65,0,.35,1) 1.3s both}
.nl .r2{animation-delay:1.37s}
.nl .solid{opacity:0;animation:fadeIn .2s ease-out 1.64s both}
.nl .ltr{stroke-dasharray:190;stroke-dashoffset:190;
         animation:draw .32s cubic-bezier(.65,0,.35,1) 1.68s both}
.nl .l{opacity:0;transform:translateY(110px);
       animation:rise .4s cubic-bezier(.22,1,.36,1) both;
       animation-delay:calc(1.48s + (var(--i) + 3) * .02s)}
@keyframes rise{0%{opacity:0;transform:translateY(110px)}40%{opacity:1}
                100%{opacity:1;transform:translateY(0)}}

/* ============ hero ============ */
.reveal{opacity:0;animation:up .58s cubic-bezier(.22,1,.36,1) both}
@keyframes up{0%{opacity:0;transform:translateY(26px)}100%{opacity:1;transform:translateY(0)}}
.d1{animation-delay:1.2s}.d2{animation-delay:1.27s}.d3{animation-delay:1.34s}
.d4{animation-delay:1.41s}

/* Sin intro: vienes de otra página de la web, recargas o vuelves atrás (la
   clase la pone el script del <head> de index.html). La barra, su logo y el
   índice lateral salen ya en su sitio; el hero conserva su subida, pero sin
   esperar a nada. Va aquí, después de .nav__menu li:nth-child(), porque
   empata con ella en especificidad y gana por orden. */
.sin-intro #intro{display:none}
.sin-intro .nav,.sin-intro .nav__menu li,.sin-intro .nl .ring,.sin-intro .nl .solid,
.sin-intro .nl .ltr,.sin-intro .nl .l,.sin-intro .toc{animation-duration:.01ms;animation-delay:0s}
.sin-intro .d1{animation-delay:0s}.sin-intro .d2{animation-delay:.07s}
.sin-intro .d3{animation-delay:.14s}.sin-intro .d4{animation-delay:.21s}

.hero{display:grid;grid-template-columns:1fr;
      padding:clamp(50px,7vw,92px) 0 clamp(14px,2vw,24px)}
#heroTexto{max-width:760px}
/* El h1 envuelve antetítulo y titular (ver index.html): solo agrupa. Sus dos
   <span> son bloques, como cuando el antetítulo era un <p> aparte, y el
   tamaño y el peso los siguen poniendo .hero__eyebrow y .hero__title. */
.hero__h1{font-size:inherit;font-weight:inherit}
.hero__h1>.hero__eyebrow,.hero__h1>.hero__title{display:block}
.hero__eyebrow{font-size:12px;letter-spacing:.09em;text-transform:uppercase;
               color:var(--olive);font-weight:600;margin-bottom:14px}
/* line-height 1.08, no 1: a 1 no queda ningún hueco entre las líneas propias
   del título cuando envuelve (pasa casi siempre, es texto largo en letra muy
   grande) — ninguna decoración puede evitar tocarlas si el hueco es cero.
   1.08 es el mismo valor que ya usa h2 en el resto del sitio, no uno nuevo. */
.hero__title{font-size:clamp(42px,6.3vw,84px);font-weight:800;line-height:1.08;
             letter-spacing:-.035em;margin-bottom:clamp(18px,2.4vw,28px)}
/* El subrayado del título del hero: fondo --mid y LETRAS en --sand, que es
   el mismo color con el que .vidhalo perfila el resto de la frase
   (rgba(240,228,208,...) es --sand) — pedido explícito, «el color de las
   letras lo quiero con ese color arena».

   AVISO de contraste: --sand sobre --mid da 2,6, por debajo incluso del 3,0
   que se pide a un texto grande. Es una decisión de diseño tomada a
   sabiendas, no un descuido; está anotada en CLAUDE.md junto con la
   alternativa (fondo --olive sube esas mismas letras a 4,5). */
.mark-hl{display:inline-block;padding:.04em .28em .1em;margin:0 -.05em;
         color:var(--sand);background:var(--mid);border-radius:var(--r-pill)}
.hero__lead{max-width:36ch;font-size:clamp(16px,1.3vw,19px);line-height:1.7;
            color:rgba(61,65,39,.78);margin-bottom:clamp(24px,3vw,36px)}
/* pista de scroll: entra con el mismo .reveal escalonado que el resto del
   hero (d4, justo detrás del lead) y luego la flecha sigue con un vaivén
   suave, que es lo que la hace leerse como invitación y no como adorno.
   El bloque de prefers-reduced-motion del final ya la deja quieta: su
   regla con * anula duración e iteraciones de cualquier animación. */
/* justify-self:start porque .hero es un grid y los hijos de un grid se
   "blockifican": el inline-flex pasa a flex y la caja se estiraría a todo
   el ancho de la columna aunque el texto ocupe cuatro palabras */
.hero__scroll{display:inline-flex;justify-self:start;align-items:center;gap:9px;margin:0;
              font-size:13px;font-weight:600;letter-spacing:.02em;
              color:rgba(61,65,39,.72)}
/* el equivalente de .vidhalo para la flecha: vidhalo es text-shadow, que
   solo dibuja alrededor de los glifos y no hace nada sobre un SVG, así que
   sobre el vídeo se perdía. drop-shadow sí sigue el contorno del trazo —
   dos pasadas cortas en --sand, el mismo color y la misma idea de halo
   ceñido que usa .vidhalo, no una sombra suelta. */
.hero__scroll svg{width:16px;height:16px;flex:none;
                  filter:drop-shadow(0 0 1px rgba(240,228,208,.95))
                         drop-shadow(0 0 1px rgba(240,228,208,.95));
                  animation:bajar 2.4s ease-in-out infinite}
@keyframes bajar{0%,100%{transform:translateY(0)}50%{transform:translateY(5px)}}
.actions{display:flex;flex-wrap:wrap;gap:14px}
.btn{display:inline-flex;align-items:center;justify-content:center;padding:16px 30px;
     font:inherit;font-size:16px;font-weight:600;border-radius:var(--r-pill);
     text-decoration:none;cursor:pointer;transition:transform .18s,background .18s,color .18s}
.btn:active{transform:translateY(1px)}
.btn--solid{color:var(--sand);background:var(--olive);border:0}
.btn--solid:hover{background:var(--deep)}
.btn--ghost{color:var(--deep);background:transparent;border:2.5px solid var(--deep)}
.btn--ghost:hover{background:var(--deep);color:var(--sand)}

/* ============ pestaña y tarjeta emergente ============ */
/* display:flow-root: sin esto, el margin-bottom de .marquee (el único hijo
   que sigue en flujo normal, ver .drawer más abajo) podría colapsar con el
   borde inferior de .bottom-block y perder ese hueco. */
.bottom-block{position:relative;display:flow-root;margin-top:clamp(12px,1.6vw,24px);
              opacity:0;transform:translateY(30px);
              transition:opacity .75s cubic-bezier(.22,1,.36,1),transform .75s cubic-bezier(.22,1,.36,1)}
.bottom-block.in{opacity:1;transform:none}
/* .drawer se ancla por el borde INFERIOR (position:absolute;bottom:0 de
   .bottom-block, que es relative) — así TODO el conjunto (.handle
   incluido, el primer hijo de .lift) sube junto con el contenido al crecer
   .sheet, en vez de quedarse fijo. Como position:absolute no empuja nada
   del flujo normal, main.js se encarga de que nada quede tapado: al abrir
   (handle.click), hace scroll hacia arriba para que el conjunto quede a la
   vista, y además empuja hacia arriba (empujarPagina(), con
   transform:translateY) todo lo que hay por encima del hueco reservado
   (.marquee, #contacto) para que tampoco quede detrás del desplegable. */
.drawer{position:absolute;left:88px;right:88px;bottom:0;z-index:5}
.lift{filter:drop-shadow(0 0 0 rgba(28,27,25,0));
      transition:filter .7s cubic-bezier(.16,1,.3,1)}
.drawer.open .lift{filter:drop-shadow(0 -20px 52px rgba(28,27,25,.42))}

.sheet{max-height:0;overflow:hidden;border-radius:0 0 30px 30px;margin-top:-78px;
       transition:max-height .74s cubic-bezier(.16,1,.3,1)}
.drawer.open .sheet{max-height:620px}
/* Aquí había una máscara `.drawer.revealing .sheet` con un
   linear-gradient(to bottom,#000 72%,transparent 100%) mientras duraba la
   apertura. Se quitó a petición explícita: como .panel es --deep (oscuro),
   difuminarlo a transparente dejaba ver el --sand de la página por debajo,
   y en movimiento eso se leía como un manchón blanco creciendo en el borde
   inferior, no como un desvanecido. Ahora el borde de .sheet lo define solo
   su overflow:hidden y su border-radius, que es un corte limpio.
   Al irse la máscara, la clase .revealing se quedó sin ninguna regla, así
   que también se quitó del JS (era lo único que la usaba). */

/* el contenido entra escalonado desde el centro hacia fuera. Los índices van
   atados al número de hijos de .panel__in -- ahora 7: <ul>, <ul>, .person,
   .person, <ul>, <ul> y .panel__foot -- así que arrancan las dos personas (3 y
   4) y de ahí se abre hacia los lados. Al cambiar columnas hay que recontarlos. */
.panel__in>*{opacity:0;transform:translateY(16px);
             transition:opacity .42s ease,transform .42s cubic-bezier(.16,1,.3,1)}
.drawer.open .panel__in>*{opacity:1;transform:none;
                          transition:opacity .62s ease,transform .72s cubic-bezier(.16,1,.3,1)}
.drawer.open .panel__in>:nth-child(3),
.drawer.open .panel__in>:nth-child(4){transition-delay:.15s}
.drawer.open .panel__in>:nth-child(2),
.drawer.open .panel__in>:nth-child(5){transition-delay:.21s}
.drawer.open .panel__in>:nth-child(1),
.drawer.open .panel__in>:nth-child(6){transition-delay:.27s}
.drawer.open .panel__in>:nth-child(7){transition-delay:.33s}
.sheet__top{display:block;width:100%;height:78px;margin-bottom:-1px}

.handle{position:relative;display:block;width:100%;height:78px;z-index:2;
        background:none;border:0;padding:0;cursor:pointer}
.handle__shape{display:block;width:100%;height:78px}
.hump-line{fill:none;stroke:var(--deep);stroke-width:2;vector-effect:non-scaling-stroke;
           stroke-linecap:round}

.handle__c{position:absolute;left:50%;top:9px;transform:translateX(-50%);
           display:flex;flex-direction:column;align-items:center;gap:10px;pointer-events:none}
.handle__chev{width:22px;height:22px;stroke:var(--deep);
              transition:transform .6s cubic-bezier(.16,1,.3,1),stroke .5s ease .06s}
.handle__logo{width:44px;height:auto;display:block}
.handle__logo path{fill:var(--deep);transition:fill .5s ease .06s}
.drawer.open .handle__chev{transform:rotate(180deg);stroke:var(--sand)}
.drawer.open .handle__logo path{fill:var(--sand)}
.handle:hover .handle__chev{transform:translateY(-3px)}
.drawer.open .handle:hover .handle__chev{transform:rotate(180deg) translateY(-3px)}

/* Pie en crema con letra oliva, pedido explícito, y en las siete páginas de
   golpe: las hojas sueltas solo repiten el border-radius, los colores viven
   aquí. Al invertirse el fondo hay que invertir todo lo de dentro — sobre
   crema, el --sand del texto se queda en 1,1 de contraste y el --pale de los
   enlaces en 1,3: no se leería nada. El reparto nuevo son los tres tonos que
   el sistema de color admite para texto pequeño: los nombres de las dos
   personas en --deep (9,6), que los deja al frente igual que los dejaba el
   blanco sobre el fondo oscuro; los enlaces en --olive (5,2); y los cargos y
   la línea de abajo en --deep rebajado a .78 (5,2), el mismo secundario que
   usa el resto del sitio. El --sage de los cargos es el que no podía
   quedarse: sobre crema cae a 1,7. */
.panel{background:var(--cream);color:var(--olive)}
/* La rejilla del pie es simétrica alrededor de las dos .person: dos columnas
   de enlaces, las dos personas (más anchas, 1.3fr) y otras dos de enlaces.
   El número de columnas laterales tiene que coincidir con el de <ul> que hay
   a cada lado en el marcado (2 y 2). Si sobran columnas -- lo que pasaba al
   quitar Instagram/LinkedIn/GitHub/Blog/Empleo, que dejó 4 <ul> en una rejilla
   de 8 columnas -- la fila se descuelga hacia la izquierda y las dos personas
   dejan de caer en el centro. */
.panel__in{display:grid;align-items:end;gap:20px;padding:0 34px 32px;text-align:center;
           grid-template-columns:repeat(2,minmax(0,1fr)) 1.3fr 1.3fr repeat(2,minmax(0,1fr))}
/* El nombre de cada persona es un <p> y no un h4: un h4 suelto en el pie,
   sin h2 ni h3 por encima, rompía el orden de títulos que siguen los
   lectores de pantalla. Se ve igual que cuando era h4. */
.panel .person__nombre{font-size:16px;font-weight:800;letter-spacing:-.01em;margin-bottom:2px;
          color:var(--deep)}
.panel .role{font-size:13px;color:rgba(61,65,39,.78);margin-bottom:8px}
.panel a{color:var(--olive);text-decoration:none;font-size:13.5px;line-height:2;
         display:block;width:fit-content;margin:0 auto;transition:color .18s ease}
.panel a:hover{color:var(--deep);text-decoration:underline;text-underline-offset:3px}
.panel ul{list-style:none}
.panel__foot{grid-column:1/-1;display:flex;justify-content:center;flex-wrap:wrap;gap:8px 24px;
             padding-top:16px;margin-top:4px;border-top:1px solid rgba(61,65,39,.16);
             font-size:12px;color:rgba(61,65,39,.78)}

/* El pie estático, ya el mismo en las seis páginas. Era solo de las sueltas:
   la portada llevaba este mismo .panel dentro del cajón desplegable y se ha
   pasado a este, por petición. Vive aquí y no en la hoja de cada página
   porque ahora lo usan todas; servicios.css, contacto.css, portfolio.css y
   presupuesto.css repiten estas reglas con los mismos valores, así que de
   momento son duplicado inofensivo. */
.svc-footer{margin-top:clamp(60px,8vw,110px)}
/* La portada es la única página cuyo pie va detrás del carrusel, y ahí ese
   hueco se veía grande: baja a poco más de un tercio, pedido explícito. Va
   scoped por .bottom-block (que solo existe en index.html) en vez de tocar
   la regla de arriba, que es la de las seis páginas con este pie — las
   demás además la reescriben en su propio css, así que cambiarla ahí solo
   movería la portada por accidente, no por decisión. */
.bottom-block+.svc-footer{margin-top:clamp(26px,3vw,44px)}
.svc-footer .panel{border-radius:34px 34px 0 0}
.svc-footer .panel__in{padding-top:40px}
/* .panel__in>* arranca en opacity:0 y solo subía a 1 dentro de .drawer.open.
   Sin cajón hay que forzarlo, o el pie no se vería nunca. */
.svc-footer .panel__in>*{opacity:1!important;transform:none!important}
/* margin-bottom:0. Aquí hubo 110px mucho tiempo: debajo del carrusel estaba el
   tirador del cajón desplegable, y ese hueco —la otra mitad del padding-bottom
   de #contacto, partido en 110+110— era lo que dejaba el carrusel a medio
   camino entre la tarjeta y el tirador. Al pasar la portada al pie estático de
   las demás páginas, esa mitad de abajo la pone ahora el margin-top de
   .svc-footer, ya bajado a 44px en la portada (.bottom-block+.svc-footer):
   mantener aquí un margen propio sumaría a ese y devolvería el hueco que
   se acaba de quitar.
   Se va también transition:transform, que solo servía para que empujarPagina()
   subiera la marquesina al abrir el cajón. */
/* Crema y no --deep, pedido explícito: la cinta pasó de píldora oscura a
   píldora clara. Ya no lleva texto ninguno: ni los nombres de las marcas, que
   son ahora sus logotipos (marca/thehub.svg y marca/hipatia-lockup.svg), ni la
   etiqueta "Clientes:" que los presentaba, quitada también por pedido
   explícito. Por eso .marquee__group ya no fija tipografía ni color: dentro
   solo hay imágenes. Contra el --sand de la página la crema separa poco (1,1),
   igual que las tarjetas .card o el formulario de presupuesto: aquí el borde
   de la píldora lo dibuja la propia forma redondeada, no el color. */
.marquee{position:relative;margin:10px 88px 0;display:flex;align-items:center;height:74px;
         background:var(--cream);border-radius:var(--r-pill);overflow:hidden}
/* 503s, tras tres peticiones seguidas de "que vaya más lento" (eran 32s, luego
   180s). Cuidado con la cuenta, que tiene truco: slide recorre media cinta
   (-50%), así que la velocidad real no sale de esta duración sola sino del
   ancho del track. Al pasar de 10 a 36 grupos la distancia de cada ciclo se
   multiplicó por 3,6, y los 60s que había entonces subieron la velocidad en
   vez de bajarla.

   La última vez cambiaron las dos cosas a la vez —"el doble de espacio entre
   los logos" y "a la mitad de velocidad"—, y el hueco no es neutral para la
   velocidad: al doblarlo de 44 a 88px el track pasó de 7.992 a 11.160px, o sea
   media cinta de 3.996 a 5.580. Doblar solo la duración (360s) habría dejado
   15,5px/s, un 30 % más rápido de lo pedido. La cuenta buena es distancia
   nueva entre velocidad pedida: 5.580 / 11,1 = 503s.

   Media cinta son 5.580px: a 503s salen ~11px/s, un logo tarda algo más de dos
   minutos en cruzar la píldora. Si vuelven a cambiar el número de
   repeticiones, el hueco o el tamaño de los logos, hay que recalcular esta
   duración con la distancia nueva o la velocidad se mueve sola. */
.marquee__track{display:flex;flex:0 0 auto;gap:88px;padding-right:88px;
                white-space:nowrap;animation:slide 503s linear infinite}
/* El mismo hueco que el del track, y no por casualidad: los dos logos de un
   grupo tienen que quedar tan separados entre sí como del grupo siguiente, o
   la cinta se lee a pares en vez de como una fila regular. El padding-right
   del track repite la cifra por lo mismo, pero en la costura: es el hueco que
   cae entre el último grupo y el primero al enganchar el ciclo en -50%. Los
   tres van atados: si cambia uno, cambian los tres. */
.marquee__group{display:flex;align-items:center;gap:88px}
/* Los dos son logotipos apaisados y de proporción parecida (5:1 el de The Hub,
   4,5:1 el de Hipatia), así que van casi a la misma altura. Los 2px de
   diferencia son ópticos: en el de The Hub las letras llegan al borde de su
   caja, y en el lockup de Hipatia el círculo deja aire por arriba y por abajo.
   Las dos alturas se bajaron a la mitad de las que tenían (26 y 30px), pedido
   explícito.
   Van como <img> y no como SVG en línea porque el grupo está repetido muchas
   veces y cada trazado pesa varios KB.
   El número de repeticiones NO es decorativo: slide termina en -50%, así que
   la mitad del track tiene que ser más ancha que la píldora o al final de cada
   ciclo se ve un hueco vacío a la derecha. Con los logos a esta altura y el
   hueco en 88px cada grupo ocupa ~310px, o sea ~155px de media cinta por
   grupo: 36 grupos cubren una píldora de hasta ~5.580px, de sobra para un 4K
   y hasta para un 5K a pantalla completa. Doblar el hueco ENSANCHÓ esa
   holgura, así que no hubo que añadir grupos; encogerlo sí obligaría a
   rehacer la cuenta, igual que cambiar el tamaño de los logos —
   eran 10 grupos cuando medían el doble, y al bajarlos a la mitad se quedaron
   cortos ya en una pantalla de 1920. */
.marquee__logo{display:block;opacity:.62;
               transition:opacity .3s cubic-bezier(.16,1,.3,1),
                          transform .3s cubic-bezier(.16,1,.3,1)}
.marquee__logo--hub{height:13px;width:auto}
.marquee__logo--hipatia{height:15px;width:auto}
/* La cinta ya no se detiene al pasar por cualquier punto de la píldora, solo
   sobre una marca — pedido explícito; de ahí el :has() en .marquee__track en
   lugar del .marquee:hover que había antes. El mismo gesto que la para atenúa
   todos los logos y devuelve el señalado a plena opacidad, algo más grande.
   La tercera regla repite el :has() a propósito: sin él (.marquee__logo:hover,
   0,2,0) perdería contra la que atenúa (0,4,0) y el logo señalado se quedaría
   apagado como los demás. */
.marquee__track:has(.marquee__logo:hover){animation-play-state:paused}
.marquee__track:has(.marquee__logo:hover) .marquee__logo{opacity:.22}
.marquee__track:has(.marquee__logo:hover) .marquee__logo:hover{opacity:1;transform:scale(1.14)}
@keyframes slide{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* ============ índice lateral ============ */
.toc{position:fixed;top:120px;right:var(--pad);z-index:55;list-style:none;
     text-align:right;padding:15px 17px;border-radius:20px;
     background:rgba(250,243,231,.66);
     -webkit-backdrop-filter:blur(10px) saturate(1.08);
             backdrop-filter:blur(10px) saturate(1.08);
     box-shadow:0 16px 36px -18px rgba(61,65,39,.42);
     opacity:0;animation:fadeIn .5s ease-out 1.5s both}
/* cada <li> puede colapsar su propia altura (max-height, no display:none,
   para poder animarlo) — mismos colores/blur/padding de la caja, "respeta
   la estética" pedido explícito, solo cambia cuánto contenido se ve. */
.toc li{margin-bottom:9px;max-height:24px;opacity:1;overflow:hidden;
        transition:max-height .4s cubic-bezier(.16,1,.3,1),
                   opacity .3s ease,
                   margin-bottom .4s cubic-bezier(.16,1,.3,1)}
.toc li:last-child{margin-bottom:0}
/* .compact lo añade/quita main.js: se añade en cada evento de scroll y se
   quita a los 3s de que el scroll pare (clearTimeout+setTimeout reiniciado
   en cada scroll, mismo patrón de bandera+rAF que el resto del índice —
   invariante 8). Todos los <li> colapsan a alto 0 salvo el que contiene el
   enlace .on (:has), que es "el punto por el que se está navegando" y
   pedido explícito que se quede siempre visible mientras se contrae. */
.toc.compact li{max-height:0;opacity:0;margin-bottom:0}
.toc.compact li:has(a.on){max-height:24px;opacity:1}
/* al pasar el ratón por encima, se vuelve a abrir entera aunque esté
   contraída — pedido explícito. Puro CSS (:hover), sin tocar el timer de
   3s en JS: en cuanto el ratón sale, .compact sigue como estaba (si el
   timer ya había saltado y se había quitado la clase, aquí ya no hay nada
   que colapsar; si seguía puesta, vuelve a colapsarse sola). */
.toc.compact:hover li{max-height:24px;opacity:1;margin-bottom:9px}
.toc.compact:hover li:last-child{margin-bottom:0}
.toc a{display:flex;align-items:center;justify-content:flex-end;gap:10px;
       font-size:13.5px;font-weight:500;color:rgba(61,65,39,.46);
       text-decoration:none;white-space:nowrap;transition:color .45s ease}
.toc a::before{content:'';width:20px;height:2px;border-radius:2px;background:var(--olive);
       opacity:0;transform:scaleX(.15);transform-origin:right;
       transition:opacity .45s ease,transform .45s cubic-bezier(.16,1,.3,1)}
.toc a:hover{color:rgba(61,65,39,.78)}
.toc a.on{color:var(--olive);font-weight:700}
.toc a.on::before{opacity:1;transform:none}

/* ============ bloques de texto ============ */
/* repeat(3, ...) fijo, no auto-fit, igual que en .cards — pero aquí la fila
   va justificada a la izquierda (start), no centrada: pedido explícito,
   a diferencia de #servicios (.cards), que se queda centrado. minmax(0,260px)
   dentro de un repeat(3,...): cada columna crece hasta 260px pero puede
   encogerse si no cabe (evita que se salgan de la pantalla antes del
   breakpoint de abajo). */
.trio{display:grid;grid-template-columns:repeat(3,minmax(0,260px));gap:32px;justify-content:start;
      margin-top:48px}
/* entra por scroll (IntersectionObserver, .trio.on en main.js), no por delay fijo
   como .reveal: las tarjetas suelen quedar fuera de la primera pantalla, así que
   un delay atado a la intro se dispararía antes de que se vean. */
.trio article{background:var(--sand);border-radius:26px;padding:28px 26px;
              aspect-ratio:1;display:flex;flex-direction:column;
              align-items:center;justify-content:center;text-align:center;
              opacity:0;transform:translateY(26px);
              transition:opacity .7s cubic-bezier(.22,1,.36,1),transform .8s cubic-bezier(.22,1,.36,1)}
.trio.on article{opacity:1;transform:none}
.trio.on article:nth-child(2){transition-delay:.1s}
.trio.on article:nth-child(3){transition-delay:.2s}
.trio h3{font-size:19px;font-weight:800;letter-spacing:-.015em;margin-bottom:8px}
.trio p{font-size:15px;color:rgba(61,65,39,.75)}
.remate{margin-top:44px;font-size:clamp(20px,2.4vw,30px);font-weight:800;letter-spacing:-.025em}

.prose p{font-size:clamp(16px,1.35vw,18.5px);line-height:1.75;color:rgba(61,65,39,.82);
         max-width:56ch;margin-bottom:18px}

/* ============ línea temporal del proceso ============ */
.proc{position:relative;height:172vh;padding:0} /* el recorrido de scroll real es height-100vh (lo que dura fijo en pantalla);
  antes eran 360vh de recorrido (460-100), ahora 72vh (360/5): 5 veces más rápido */
.proc__pin{position:sticky;top:0;height:100vh;display:flex;flex-direction:column;
           justify-content:center;padding-top:64px}
.proc__stage{width:100%;height:auto;display:block;margin-top:clamp(12px,3vh,32px);
             opacity:0;will-change:opacity,transform}
.proc__steps{display:grid;grid-template-columns:repeat(4,1fr);gap:18px;
             padding:clamp(10px,2vh,22px) 10% 0}
.proc__step{text-align:center;opacity:0;transform:translateY(16px);
            transition:opacity .6s ease,transform .7s cubic-bezier(.16,1,.3,1)}
.proc__step.on{opacity:1;transform:none}
.proc__step b{display:block;font-size:13px;font-weight:800;letter-spacing:.1em;margin-bottom:6px}
.proc__step h3{font-size:clamp(15px,1.4vw,20px);font-weight:800;letter-spacing:-.015em;margin-bottom:5px}
.proc__step p{font-size:clamp(12.5px,1vw,14.5px);color:rgba(61,65,39,.75);line-height:1.55}
/* mismo enlace que el de las dudas (.faq p a): el paso 01 lleva a Servicios */
.proc__step p a{color:var(--olive);font-weight:700;text-decoration:underline;text-underline-offset:3px}
.proc__step p a:hover{color:var(--deep)}

/* 1fr 1fr, no 2fr 1fr: al doblar el tamaño del vídeo (560px, antes 280px)
   el tercio derecho se quedaba corto — con la mitad del ancho total, la
   columna de la derecha tiene sitio real para centrarlo sin desbordar.
   place-items:center en .dudas__videowrap sigue centrando el vídeo DENTRO
   de esa columna, que es exactamente el hueco entre el borde del bloque de
   texto (el final de la columna izquierda) y el margen derecho de la
   página (el borde del grid, que coincide con el padding de .page) —
   pedido explícito, sin necesitar ningún posicionamiento absoluto. */
.dudas__grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(28px,4vw,64px);
             align-items:center}
.dudas__videowrap{display:grid;place-items:center}
/* mismo mecanismo de difuminado de borde que .vidbg__clip (dos degradados
   lineales + mask-composite:intersect, ver más arriba). Ancha (25%) en los
   lados izquierdo/derecho y abajo, pero recortada arriba (10%, no 25%): el
   objeto del vídeo queda cerca del borde superior, y una franja del 25%
   ahí se lo comía con el difuminado — pedido explícito. El degradado
   vertical ya no es simétrico (10% arriba, 75% abajo = 25% de franja
   abajo), solo el horizontal se quedó igual en los dos lados. */
.dudas__video{
  width:100%; max-width:560px; aspect-ratio:1;
  object-fit:cover; border-radius:24px;
  transform:translateY(20px);
  -webkit-mask-image:
    linear-gradient(to right, transparent, black 25%, black 75%, transparent),
    linear-gradient(to bottom, transparent, black 10%, black 75%, transparent);
  -webkit-mask-composite:source-over,source-in;
  mask-image:
    linear-gradient(to right, transparent, black 25%, black 75%, transparent),
    linear-gradient(to bottom, transparent, black 10%, black 75%, transparent);
  mask-composite:intersect;
}
.faq{max-width:840px}
.faq details{border-bottom:1px solid rgba(61,65,39,.15);padding:20px 0}
.faq summary{cursor:pointer;font-size:17.5px;font-weight:700;letter-spacing:-.01em;
             list-style:none;display:flex;justify-content:space-between;gap:24px;align-items:center}
.faq summary::-webkit-details-marker{display:none}
.faq summary::after{content:'+';font-size:24px;font-weight:400;color:var(--olive);line-height:1;
                    transition:transform .4s cubic-bezier(.16,1,.3,1)}
.faq details[open] summary::after{transform:rotate(45deg)}
.faq p{font-size:15.5px;line-height:1.7;color:rgba(61,65,39,.78);margin-top:12px;max-width:66ch}
/* enlace dentro de una respuesta: sin regla propia saldría en el azul
   del navegador, que no pinta nada en esta paleta. Mismo trato que el
   del aviso de cookies: olivo y subrayado, que se vea que se pincha. */
.faq p a{color:var(--olive);font-weight:700;text-decoration:underline;text-underline-offset:3px}
.faq p a:hover{color:var(--deep)}


@media (min-width:1240px){
  .hero,section{padding-right:180px}
}
/* ============ secciones ============ */
section{padding:clamp(58px,8vw,104px) 0}
/* creatividad va justo debajo del hero, con menos separación que el resto de secciones */
/* empuja el punto 2 (y con él, las tarjetas) fuera de la primera pantalla a
   propósito: así el scroll hasta ellas es real, no un pixel o dos, y su
   animación de entrada (revisarTrio() en main.js) se nota de verdad en vez
   de dispararse casi al cargar la página. */
#creatividad{padding-top:55vh}
.eyebrow{font-size:12px;font-weight:800;letter-spacing:.14em;text-transform:uppercase;
         color:var(--olive);margin-bottom:12px}
h2{font-size:clamp(28px,3.6vw,46px);font-weight:800;letter-spacing:-.025em;
   line-height:1.08;margin-bottom:12px}
.section__lead{font-size:17px;color:rgba(61,65,39,.75);max-width:52ch;margin-bottom:38px}

/* mismo criterio que .trio: repeat(3,...) fijo + justify-content:center
   centra la fila entera, la tarjeta del medio cae en el centro real de la
   pantalla y las otras dos quedan centradas en el hueco que les queda a
   cada lado. */
.cards{display:grid;grid-template-columns:repeat(3,minmax(0,260px));gap:32px;justify-content:center}
/* sin aspect-ratio ni height propios: align-items:stretch (el valor por
   defecto de .cards, un grid) hace que las tres .card tomen automáticamente
   la altura de la más alta de la fila — pedido explícito, "que tengan la
   altura de la más larga". Antes llevaba aspect-ratio:1 (cuadrado fijo), y
   si el texto de una tarjeta necesitaba más espacio que ese cuadrado, esa
   tarjeta sola crecía más que las otras dos. */
.card{background:var(--sand);border-radius:26px;padding:30px;
      display:flex;flex-direction:column;
      align-items:center;justify-content:center;text-align:center}
.card__icon{width:56px;height:56px;border-radius:20px;display:grid;place-content:center;
            margin-bottom:20px}
/* por debajo de este ancho, tres cuadrados de verdad (aspect-ratio:1) ya no
   caben sin quedarse minúsculos — una columna, centrada, con cada cuadrado
   a un tamaño legible en vez de encogido. */
@media (max-width:640px){
  .trio,.cards{grid-template-columns:minmax(0,300px)}
}
.card h3{font-size:21px;font-weight:800;letter-spacing:-.015em;margin-bottom:8px}
.card p{font-size:15px;color:rgba(61,65,39,.75)}

.split{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:clamp(28px,4vw,64px);
       align-items:center}

/* Círculos centrados en las caras, separados incluso durante la flotación. */
.nosotros__retratos{position:relative;isolation:isolate;width:100%;max-width:620px;
  aspect-ratio:.8;margin-inline:auto;overflow:hidden}
#nosotros h2{text-align:left}
.nosotros__retrato{position:absolute;width:62%;aspect-ratio:1;border-radius:50%;
  overflow:hidden;border:3px solid var(--olive);
  animation:nosotros-flotar 7s ease-in-out infinite}
.nosotros__retrato img{position:absolute;display:block;width:220%;max-width:none;height:auto;
  left:-66%;top:-90%}
.nosotros__retrato--vicente{left:0;top:3%}
.nosotros__retrato--alejandra{right:0;bottom:3%;animation-delay:-3.5s}
.nosotros__retrato--alejandra img{left:-60%}
@keyframes nosotros-flotar{
  0%,100%{transform:translateY(5px)}
  50%{transform:translateY(-5px)}
}
@media (prefers-reduced-motion:reduce){
  .nosotros__retrato{animation:none}
}
@media (max-width:940px){
  .nosotros__retratos{max-width:540px}
}

/* el título de #metodo Y su cuerpo se anclan juntos, como un solo bloque:
   .prose está dentro de .metodo__pin (no es hermana de .metodo__pinwrap),
   así que sube y se ancla con el título, no aparece después al soltarse.

   .metodo__pinwrap empieza justo donde acaba #creatividad — ya no hay un
   .vidgap aparte entre los dos: ese hueco (antes 80vh sueltos, el tramo en
   el que se forma 2-bola) ahora es la primera parte de este mismo
   envoltorio, así que el bloque anclado aparece exactamente a la vez que
   empieza a formarse la bola, no más tarde. 220vh (el tramo de 3-torno) +
   80vh (el de 2-bola) = 300vh en total. main.js calcula los tres puntos de
   corte (fin de bola/inicio de torno, y fin de torno) aritméticamente a
   partir de dónde empieza este envoltorio, no midiendo un .vidgap que ya
   no existe.

   .metodo__pin va centrado en el viewport, no pegado arriba: top:50% +
   translateY(-50%) es el truco habitual para centrar un elemento sticky
   (top:0 lo pega al borde superior; top:50% hace que se quede quieto a
   media altura, y el translateY(-50%) sube la caja la mitad de su propio
   alto para que quede centrada de verdad, no con el borde superior a
   mitad de pantalla). El transform se anula en móvil (abajo, en el
   media query) porque ahí position pasa a static y el translateY sin
   sticky solo desplazaría el bloque hacia arriba sin motivo. */
.metodo__pinwrap{position:relative;height:300vh}
/* oculto hasta que main.js añade .visible (segundo 6 de 2-bola, no desde que
   arranca) — solo opacity, sin tocar transform: ya lleva translateY(-50%)
   para centrarse, y combinarlo con un segundo movimiento de entrada es
   innecesario y arriesga romper ese centrado. */
.metodo__pin{position:sticky;top:50%;transform:translateY(-50%);max-width:640px;
             opacity:0;transition:opacity .6s ease}
.metodo__pin.visible{opacity:1}

/* #servicios usa el mismo mecanismo que #metodo (envoltorio alto +
   contenido anclado en sticky), para que 5-rotoscopia pueda reproducirse
   entera detrás sin cortarse mientras la sección ya está a la vista. A
   diferencia de .metodo__pin, aquí no hace falta una clase .visible con
   retraso: el envoltorio empieza exactamente donde acaba .vidGapArbol —
   cuando 4-arbol termina sus 10s completos, no antes — así que
   .servicios__pin puede estar visible desde el primer instante de su
   propio envoltorio, sin esperar más.

   Altura del envoltorio: rotoscopia entera (7s) al ritmo de torno, 220vh
   — el mismo presupuesto que torno, porque dura lo mismo. */
.servicios__pinwrap{position:relative;height:220vh}
.servicios__pin{position:sticky;top:50%;transform:translateY(-50%)}
#metodo .prose{padding-top:24px;max-width:640px}
/* el título lleva un <br> a propósito en el HTML, justo después del punto
   ("La IA hace el trabajo repetitivo.<br>Las decisiones..."), para que
   ocupe exactamente dos líneas partidas ahí — pedido explícito. Un h2
   normal, con el max-width:640px que hereda de .metodo__pin, envolvería
   cada una de esas dos mitades otra vez (son más anchas que 640px a
   tamaño grande), dando 3-4 líneas en vez de 2. width:max-content saca al
   h2 de esa herencia: se dimensiona por su contenido (la más ancha de las
   dos mitades), así que ninguna de las dos vuelve a partirse. max-width
   (960px o el 95% del viewport, lo que sea menor) es solo una red de
   seguridad para pantallas estrechas: ahí sí puede volver a envolver, en
   vez de desbordar horizontalmente la página. */
#metodo h2{width:max-content;max-width:min(960px,95vw)}
.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;margin-top:30px}
.stats b{display:block;font-size:clamp(30px,3.4vw,44px);font-weight:800;letter-spacing:-.03em;line-height:1}
.stats span{font-size:14px;color:rgba(61,65,39,.7)}

/* El ancho de cada tarjeta sigue siendo relativo a la pantalla (1fr), pero
   deja de crecer a partir de 1080p. Ahora que .work__thumb va por
   proporción ya no se deforma al ensancharse, pero sí se hace enorme: en un
   monitor 2K las tres columnas se iban a ~800px de ancho (450 de alto) y la
   sección se comía la pantalla entera.
   El tope es exactamente el ancho que tiene la rejilla en una pantalla de
   1920: el viewport menos los dos --pad, que por encima de 1280px ya está
   pinzado en 64px. Por debajo de 1920 nunca llega a aplicarse, así que el
   comportamiento fluido de siempre se queda intacto — y el JS no necesita
   saber nada de esto, mide clientWidth y el tope ya viene aplicado.
   Sin margin-inline:auto a propósito: la rejilla se queda pegada a la
   izquierda, alineada con el h2 de la sección; centrada se despegaría de
   su propio título. */
.work{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px;
  max-width:calc(1920px - var(--pad) * 2)}
/* Sin tarjeta: el texto va directamente sobre el --sand de la página, como
   el resto de textos del sitio, y por eso lleva .vidhalo en el HTML (ver
   más abajo .work__body). El redondeo y el recorte se mueven de .work__item
   a .work__thumb: antes la tarjeta entera era un bloque redondeado y el
   thumb heredaba sus esquinas de arriba; ahora la imagen es la única caja
   que queda, así que necesita redondearse ella sola las cuatro.

   La altura sale de la PROPORCIÓN, no de un número fijo, y es 16:9 porque
   ese es el formato en el que se renderiza el iframe (1440x810, ver
   .work__frame): hueco y visor comparten proporción, así que la vista
   previa encaja clavada y no se recorta nada. Antes eran 280px fijos contra
   un iframe de 1440x970 escalado por el ancho — a 585px de columna el
   iframe medía 394 de alto dentro de un hueco de 280, y el overflow:hidden
   se comía los 114px de abajo (el 29% de la pieza). Si se cambia uno de los
   dos formatos hay que cambiar el otro con él. */
.work__thumb{aspect-ratio:16/9;display:grid;place-content:center;
  border-radius:26px;overflow:hidden}

/* Las piezas reales, embebidas y reducidas dentro de sus tarjetas.
   Mismo mecanismo que .pf-frame en portfolio.css: el iframe se renderiza
   a tamaño de escritorio y se encoge con transform:scale, para que lo de
   dentro se maquete como en un ordenador y no en un móvil.

   Aquí son 1440x810 (16:9) y en .pf-frame 1440x970, y la diferencia es a
   propósito: cada uno copia la proporción de SU hueco. La mancha de
   portfolio.css mide 584x394 (~1,48:1), que es justo lo que da 1440x970 a
   su escala; con 810 se quedaría un vacío abajo. .work__thumb es 16:9, así
   que aquí el visor va a 16:9.

   La diferencia con portfolio.css: allí la mancha mide siempre lo mismo y
   la escala es un número fijo. Aquí .work es una rejilla fluida
   (minmax(280px,1fr)), así que la escala depende del ancho real de la
   columna — la calcula el JS y la deja en --work-esc. El valor de reserva
   (.26) es el que sale con la tarjeta a su ancho mínimo, para que no se
   vea un salto si el JS aún no ha medido.

   position:relative va scopeado a --live y no a .work__thumb a secas: las
   otras dos no tienen iframe y no hay motivo para cambiarles el contexto de
   apilado (mismo criterio que el invariante 2). El overflow:hidden que hacía
   falta aquí ya lo trae ahora la regla base de .work__thumb, que lo necesita
   para redondearse las esquinas. */
.work__item--live .work__thumb{position:relative}
.work__frame{position:absolute;top:0;left:0;width:1440px;height:810px;border:0;
  pointer-events:none;background:var(--sand);
  transform:scale(var(--work-esc,.26));transform-origin:top left;
  opacity:0;transition:opacity .5s cubic-bezier(.16,1,.3,1)}
/* .lista la pone el JS al terminar de cargar: hasta entonces se ve el SVG
   de debajo, y la portada aparece por encima con un fundido en vez de
   soltar un rectángulo en blanco de golpe. */
.work__frame.lista{opacity:1}
/* Cada tarjeta se quedó en imagen + título, y nada más: el título va debajo
   del thumb y centrado (pedido explícito). Se probó antes superpuesto sobre
   la imagen, y el problema no era de maquetación sino de contraste — las
   miniaturas son colores de forma (--sage, --pale, --mid), que el sistema de
   color no admite como fondo de texto pequeño, y en la primera tarjeta el
   fondo es directamente un vídeo en marcha. Abajo el texto vuelve al --sand
   de la página, que da 8,4 de contraste con --deep y es donde vive el resto
   del texto del sitio.

   text-align:center explícito porque la base global es body{text-align:justify}:
   sin esto el título heredaría el justificado. */
.work__body{padding:16px 0 0;text-align:center}
.work__body h3{font-size:19px;font-weight:800}
/* La tercera tarjeta es un enlace al portfolio y no un trabajo: la tarjeta
   entera es el <a>. Mismo aspecto que las otras dos, y al pasar el ratón la
   miniatura sube un poco y el título se subraya, que es lo que dice "esto
   se pulsa" sin inventar un botón nuevo. */
.work__item--enlace{display:block;color:inherit;text-decoration:none}
.work__item--enlace .work__thumb{transition:transform .35s cubic-bezier(.16,1,.3,1)}
.work__item--enlace:hover .work__thumb{transform:translateY(-4px)}
.work__item--enlace:hover h3{text-decoration:underline;text-underline-offset:4px}
.section__lead a{color:var(--olive);font-weight:700;text-decoration:underline;text-underline-offset:3px}
.section__lead a:hover{color:var(--deep)}

.quote{background:var(--cream);border-radius:34px;padding:clamp(38px,6vw,72px);text-align:center}
.quote p{font-size:clamp(20px,2.3vw,30px);font-weight:500;line-height:1.45;
         letter-spacing:-.015em;max-width:22ch;margin:0 auto 18px}
.quote small{font-size:14px;color:rgba(61,65,39,.7)}

/* padding-bottom: espacio entre .cta y .bottom-block (el carrusel). Viene de
   lejos: fueron 110px, la mitad de un padding de 220 partido en 110+110 que
   dejaba el carrusel a medio camino entre la tarjeta y el tirador del cajón.
   Ese cajón ya no está —la portada usa el pie estático de las demás páginas—
   y el hueco se había quedado grande para lo que separa; baja a 40px, pedido
   explícito. Con los 24px de .bottom-block y los 10 de .marquee, entre la
   tarjeta y la cinta quedan unos 74px en vez de los 143 de antes.
   revisarCta() (main.js) sigue usando su propio criterio de "cerca del final
   del scroll" para activar .cta.abajo, no depende de este valor.

   padding-top: espacio entre el final de #dudas y #contacto — pedido
   explícito aparte, no confundir con el de abajo: uno separa #contacto de
   lo que viene ANTES, el otro de lo que viene DESPUÉS. */
/* transition en transform: empujarPagina() (main.js) le pone
   translateY(-Npx) al abrir .drawer, igual que a .marquee, para que la
   tarjeta ¿Empezamos? tampoco quede tapada por el desplegable. */
#contacto{padding-top:220px;padding-bottom:40px;
          transition:transform .74s cubic-bezier(.16,1,.3,1)}
/* .cta ya no es una tarjeta fija: de normal (sin .abajo) no lleva fondo ni
   padding — solo título + botón, sin tarjeta ni subtexto. Al llegar abajo
   del todo, revisarCta() (main.js) añade .abajo y aquí se despliega la
   tarjeta entera: fondo --sage, padding, y el subtexto (.cta__sub) crece
   entre el título y el botón — pedido explícito. Todo con transiciones
   largas (.6s/.5s) y curvas suaves para que se note como un despliegue,
   no un parpadeo. */
.cta{background:transparent;border-radius:34px;padding:0;
     display:flex;flex-direction:column;
     transition:background .6s cubic-bezier(.16,1,.3,1),
                padding .6s cubic-bezier(.16,1,.3,1)}
/* --sage es la salvia del sistema. Se pidió «salvia #B4BC8E», que es el
   valor RETIRADO de --sage: docs/colores.html documenta que se unificó en
   #BAC095 porque los dos se diferenciaban en un 5 % de luminosidad,
   imperceptible salvo comparándolos lado a lado. Va la variable y no el
   hex, que es lo que pide el proyecto. */
.cta.abajo{background:var(--sage);padding:clamp(38px,5vw,64px)}
.cta__top{display:flex;flex-direction:column;align-items:center;
          text-align:center;gap:18px}
/* el h2 no cambia de color al desplegarse la tarjeta: --deep vale para los
   dos estados y sobre --sage da 5,6 de contraste. Con la tarjeta en --mid
   se quedaba en 3,3 y solo pasaba por ser texto grande; con este fondo ya
   no depende del tamaño. La transición se conserva por si el color vuelve
   a moverse en el futuro. */
.cta h2{color:var(--deep);margin-bottom:0;transition:color .5s ease}
/* el subtexto se despliega con grid-template-rows 0fr→1fr (misma técnica
   que ya se usó para .cta__info, antes de quitarla): crece hasta su altura
   real sin adivinarla a mano. min-height:0 en el párrafo — sin eso, el
   min-height:auto por defecto de un ítem de grid puede impedir que la fila
   colapse del todo a 0fr. */
.cta__sub{display:grid;grid-template-rows:0fr;
          transition:grid-template-rows .6s cubic-bezier(.16,1,.3,1)}
.cta.abajo .cta__sub{grid-template-rows:1fr}
/* --deep sólido, no la transparencia habitual del texto secundario
   (rgba(61,65,39,.72)). Sobre --sage este párrafo llega a 5,6 y sigue sin
   ser el punto más flojo del sitio en contraste: a 16px pide 4,5 y sobre
   --mid se quedaba en 3,3. Si algún día la tarjeta vuelve a un fondo
   oscuro, este color tiene que irse con ella a --sand/--cream, no quedarse
   en --deep.
   24px y no 16px (pedido explícito, 13-09-2026): a partir de 24px cuenta como
   texto grande y le basta un 3,0 de contraste, así que deja de depender del
   color de la tarjeta. */
.cta__sub p{overflow:hidden;min-height:0;color:var(--deep);font-size:24px;line-height:1.4}
/* botón --deep sólido con letras --sand, pedido explícito al pasar la
   tarjeta a salvia. Sigue sin invertir con la tarjeta —es el mismo botón en
   los dos estados—, que era la condición del pedido anterior; lo que cambia
   es cuál es ese botón único (antes --sand con borde, y antes de eso claro
   sobre la tarjeta --olive oscura).

   Ya no lleva borde porque ya no le hace falta: la superficie sola separa
   5,6 de la tarjeta salvia y 8,4 del --sand de la página en el estado sin
   tarjeta. El borde de 2px estaba justamente porque un botón --sand no se
   distinguía ni de una cosa ni de la otra (1,5 contra la tarjeta clara).
   Las letras --sand sobre --deep dan 8,4.

   hover a --olive y no a --deep como .btn--solid: --deep ya es el reposo.
   Sobre --olive las letras --sand siguen en 4,5 y la superficie guarda 2,9
   contra la tarjeta. */
.cta .btn{background:var(--deep);color:var(--sand);border:0;
          transition:background .5s ease,color .5s ease}
.cta .btn:hover{background:var(--olive)}

/* ============ cuadro de cookies ============ */
/* El cuadro lo monta cookies.js; aquí solo vive su aspecto. Al ser un
   <dialog> abierto con showModal(), el fondo difuminado es su ::backdrop —la
   capa que pinta el propio navegador entre el diálogo y el resto de la
   página— y no un div nuestro: no hay z-index que pelear con la barra ni con
   el cajón (el diálogo vive en la capa superior del navegador, por encima de
   todo), ni una capa que acordarse de mostrar y esconder a la vez.
   backdrop-filter va con su prefijo -webkit- para Safari. */
/* position/inset/margin explícitos, en vez de dejarlo en manos del navegador:
   el reset de arriba (*{margin:0}) le gana al margin:auto que el <dialog> trae
   de serie en la hoja del navegador, y ese margin:auto es justo lo que centra
   un diálogo modal. Sin estas tres líneas el cuadro se pega a la esquina
   superior izquierda. La altura sigue siendo fit-content (eso el reset no lo
   toca), así que inset:0 + margin:auto lo centran en los dos ejes. */
.ck{position:fixed;inset:0;margin:auto;
    border:0;padding:0;color:var(--deep);background:var(--cream);
    width:min(640px,calc(100% - 40px));max-height:min(84vh,780px);
    border-radius:26px;overflow:hidden;box-shadow:0 30px 70px -30px rgba(28,27,25,.55)}
.ck::backdrop{background:rgba(28,27,25,.45);
              -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
.ck[open]{animation:ckIn .34s cubic-bezier(.16,1,.3,1)}
.ck[open]::backdrop{animation:ckFade .34s ease}
@keyframes ckIn{0%{opacity:0;transform:translateY(14px) scale(.985)}100%{opacity:1;transform:none}}
@keyframes ckFade{0%{opacity:0}100%{opacity:1}}
/* el scroll, si el texto no cabe, es del contenido: así el difuminado y las
   esquinas redondeadas del diálogo se quedan quietos. max-height:inherit
   copia el valor de .ck, para no repetir el número en dos sitios. */
.ck__box{max-height:inherit;overflow-y:auto;padding:38px 40px 34px;text-align:left}
/* body va en text-align:justify y font-weight:600 y eso hereda hasta aquí:
   un texto legal en columna estrecha y justificado abre ríos de espacio
   entre palabras, así que se vuelve a la izquierda y a un peso de lectura. */
.ck h2{font-size:26px;font-weight:800;letter-spacing:-.02em;margin-bottom:12px}
.ck h3{font-size:13px;font-weight:800;color:var(--olive);letter-spacing:.04em;
       text-transform:uppercase;margin:24px 0 6px}
.ck p{font-size:14.5px;font-weight:500;line-height:1.7;text-align:left}
.ck__lead{font-size:16px;line-height:1.65}
.ck a{color:var(--olive);text-decoration:underline;text-underline-offset:3px}
.ck a:hover{color:var(--deep)}
/* la X se posiciona contra .ck, no contra .ck__box (que es quien scrollea):
   .ck es el <dialog>, que el navegador ya deja en position:absolute, así que
   el botón se queda clavado en la esquina aunque el texto corra por debajo. */
.ck__x{position:absolute;top:12px;right:12px;width:38px;height:38px;
       display:grid;place-items:center;border:0;border-radius:var(--r-pill);
       background:transparent;color:var(--deep);cursor:pointer;
       transition:background .18s ease}
.ck__x:hover{background:rgba(61,65,39,.1)}
.ck__x svg{width:18px;height:18px}
/* Privacidad comparte la vista de Cookies, con párrafos y estado de borrador. */
.ck .pv-contenido p+p{margin-top:12px}
.ck .pv-contenido{overflow-wrap:anywhere}
.ck .pv-completo{margin-top:28px;padding-top:18px;border-top:1px solid var(--pale)}
.ck .pv-fecha,.pv-documento .pv-fecha{font-size:12px;color:var(--olive);margin-bottom:18px}
.pv-borrador{padding:16px 18px;margin-bottom:22px;border-left:3px solid var(--olive);
  border-radius:0 14px 14px 0;background:var(--pale)}
.ck .pv-borrador p,.pv-documento .pv-borrador p{font-size:13px;line-height:1.6}
.pv-pendiente{color:var(--alerta)}
.ck__box>h2{padding-right:18px;line-height:1.2}
.ck__x{z-index:1;background:var(--cream)}
/* mientras el cuadro está abierto la página de detrás no scrollea: cookies.js
   compensa además el ancho de la barra de desplazamiento con un padding, para
   que al quitarla no salte todo lo centrado. */
body.ck-abierto{overflow:hidden}

/* El menú y el logo se pelean por el mismo hueco. El logo va en
   position:absolute centrado en la barra (198px de ancho) y .nav__menu es el
   único hijo en flujo, así que arranca pegado al borde izquierdo: el hueco
   libre hasta el logo es (ancho - 2*--pad)/2 - 99 - 24, o sea medio logo y el
   padding de la barra. Cada entrada que se añade se come ese hueco y obliga a
   mover los dos cortes de abajo.

   Con cinco entradas (Inicio, Servicios, Portfolio, Misión, Contacto), medido
   en Edge sobre las páginas reales, con Manrope cargada y la barra de
   desplazamiento de Windows: a tamaño normal el menú ocupa 460px y apretado
   (menos padding, menos gap y un punto menos de cuerpo) 354px. De ahí los
   tres tramos: normal por encima de 1330, apretado entre 1100 y 1330, y sin
   menú por debajo de 1100. Justo en los dos cortes quedan 11px entre el menú
   y el logo, y el hueco pierde casi medio píxel por cada píxel de ancho: bajar
   cualquiera de los dos cortes unos 25px basta para que se toquen.

   Esconderlo en un portátil de 1024px tiene coste: no hay hamburguesa, y la
   navegación de repuesto es el pie y el logo. Con cuatro entradas se descartó
   por eso y el corte estaba en 940. Al añadir Inicio se eligió subirlo
   (pedido explícito) antes que quitar solo Inicio en esa franja o apretar las
   entradas hasta pegar al texto el fondo del hover. Si se añade o se renombra
   una entrada hay que volver a medir: estas cifras dependen del ancho real
   del texto, no se deducen. */

/* ============ aviso de la oferta de lanzamiento ============ */
/* Lo monta oferta.js en las siete páginas; aquí solo vive su aspecto.

   Abajo a la izquierda porque es la única esquina libre de las cuatro: arriba
   manda la barra, arriba a la derecha está el índice lateral de la portada y
   abajo a la derecha el botón del parallax de Servicios. De ese botón sale
   también el tope de ancho: descuenta sus 58px más su aire para que en un
   móvil estrecho el aviso se pare antes de llegar a él en vez de montársele
   encima.

   z-index:50 lo deja por debajo de la barra (60) y del índice lateral (55):
   si dos cosas se solapan al hacer scroll, la que se tapa es esta.

   No se puede cerrar ni se esconde al bajar, porque es un aviso de precio y
   tiene que seguir delante mientras se leen los precios. Justo por eso pide
   ser discreto: hairline, fondo translúcido y once píxeles y medio de letra.
   La cifra es lo único que va en oliva y en negrita — el ojo la pilla de
   lado sin que el aviso pelee con nada de la página. */
.oferta{
  position:fixed; left:var(--pad); bottom:20px; z-index:50;
  display:flex; align-items:baseline; gap:9px;
  max-width:min(280px,calc(100vw - var(--pad) - 96px));
  padding:9px 16px; border-radius:var(--r-pill);
  background:rgba(250,243,231,.84); border:1px solid rgba(99,107,47,.24);
  -webkit-backdrop-filter:blur(12px); backdrop-filter:blur(12px);
  box-shadow:0 16px 34px -24px rgba(61,65,39,.75);
  /* both, no forwards a secas: el bloque de prefers-reduced-motion del final
     deja la animación en 0,01ms, y sin fill la caja se quedaría en el estado
     inicial —transparente— para quien pida menos movimiento. */
  animation:oferta-entra .7s cubic-bezier(.16,1,.3,1) .35s both;
}
.oferta__cifra{flex:none;font-size:13px;font-weight:800;letter-spacing:-.01em;
  color:var(--olive)}
.oferta__texto{font-size:11.5px;font-weight:600;line-height:1.3;
  color:rgba(61,65,39,.78)}
@keyframes oferta-entra{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:none}}

@media (max-width:1330px){
  .nav__menu{gap:2px}
  .nav__link{padding:11px 8px;font-size:14.5px}
}
@media (max-width:1100px){
  .nav__menu{display:none}
}

/* ============ menú del móvil ============ */
/* Lo monta menu.js en las ocho páginas: un botón «Menú» en el hueco que deja
   .nav__menu por debajo de 1.100 px, y un panel con las mismas cinco entradas
   debajo de la barra. El panel va fuera de .nav (en <body>) porque la barra
   lleva overflow:hidden y lo recortaría.

   Abre con la curva de entrada del sitio y cierra rápido y sin retraso, la
   misma asimetría que el resto. Con la barra comprimida el botón desaparece
   igual que desaparece el menú en el ordenador. */
.nav__toggle{display:none}
.nav-panel{display:none}
@media (max-width:1100px){
  .nav__toggle{display:inline-flex;align-items:center;gap:8px;height:46px;padding:0 16px 0 12px;
    border:0;border-radius:var(--r-pill);background:transparent;color:var(--deep);
    font:inherit;font-size:15px;font-weight:600;cursor:pointer;
    transition:background .2s ease,opacity .2s ease}
  .nav__toggle:hover{background:rgba(61,65,39,.08)}
  .nav__toggle svg{width:22px;height:22px;flex:none}
  .nav__toggle path{transform-origin:12px 12px;transition:transform .35s cubic-bezier(.16,1,.3,1)}
  .nav__toggle[aria-expanded="true"] .nav__toggle-a{transform:rotate(45deg) translateY(4px)}
  .nav__toggle[aria-expanded="true"] .nav__toggle-b{transform:rotate(-45deg) translateY(-4px)}
  .nav.compact .nav__toggle{opacity:0;pointer-events:none}

  .nav-panel{display:block;position:fixed;z-index:59;top:102px;left:var(--pad);right:var(--pad);
    max-width:520px;margin:0 auto;padding:10px;
    background:var(--cream);border-radius:30px;box-shadow:0 22px 46px -24px rgba(61,65,39,.45);
    opacity:0;transform:translateY(-8px);
    transition:opacity .16s ease,transform .16s ease}
  .nav-panel.abierto{opacity:1;transform:none;
    transition:opacity .3s cubic-bezier(.16,1,.3,1),transform .45s cubic-bezier(.16,1,.3,1)}
  .nav-panel__lista{list-style:none;display:grid;gap:2px}
  .nav-panel__link{display:block;padding:14px 18px;border-radius:20px;text-align:left;
    font-size:18px;font-weight:600;color:var(--deep);text-decoration:none;transition:background .2s ease}
  .nav-panel__link:hover{background:rgba(61,65,39,.08)}
  .nav-panel__link[aria-current="page"]{background:var(--pale)}
}
/* en un teléfono estrecho la palabra «Menú» se come el hueco hasta el logo:
   queda solo el icono, y la palabra sigue ahí para el lector de pantalla */
@media (max-width:520px){
  .nav__toggle{padding:0 12px}
  .nav__toggle span{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}
}
@media (max-width:940px){
  .split{grid-template-columns:1fr}
}
@media (max-width:1080px){
  /* debajo de 1080 ya no caben las 6 columnas: las dos .person suben a su propia
     fila (order:-1, media rejilla cada una) y los cuatro <ul> se reparten los
     cuatro huecos de abajo, dos y dos, sin romper la simetría. */
  .panel__in{grid-template-columns:repeat(4,minmax(0,1fr));align-items:start;
             gap:18px 16px;padding:2px 26px 28px}
  .person{grid-column:span 2;order:-1}
  .drawer.open .sheet{max-height:min(84vh,760px)}
}
@media (max-width:900px){
  .panel__in{grid-template-columns:repeat(2,minmax(0,1fr))}
  .person{grid-column:span 2}
}
@media (max-width:560px){
  .hero__title{font-size:clamp(36px,10vw,50px)}
  .btn{flex:1 1 auto;padding:15px 20px}
  .marquee{height:60px;margin:10px 70px 0}.marquee__group{gap:60px}
  .marquee__logo--hub{height:11px}.marquee__logo--hipatia{height:12px}
  .page{padding-bottom:16px}
  .nav__mark svg{width:150px}
  .drawer{left:70px;right:70px}
  .handle__logo{width:38px}
  .ck__box{padding:30px 24px 26px}
  .ck h2{font-size:22px}
}
@media (max-width:480px){
  .panel__in{grid-template-columns:repeat(2,minmax(0,1fr));gap:14px 12px;padding:2px 18px 24px}
  .person{grid-column:span 2}
  .panel a{font-size:13px;line-height:1.9}
  .panel .person__nombre{font-size:15px}
}
@media (max-width:400px){
  .panel__in{grid-template-columns:1fr;gap:12px}
  .person{grid-column:span 1}
}
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important}
  #intro{display:none}
  .nav,.reveal,.nav__menu li,.bottom-block,.trio article{opacity:1!important;transform:none!important}
  html{scroll-behavior:auto}
}

/* Las excepciones van después de las reglas base que modifican. */
@media (max-width:820px),(prefers-reduced-motion:reduce){
  .vidbg,.vidgap{display:none}
  .vidhalo{text-shadow:none}
  .metodo__pinwrap{height:auto}
  .metodo__pin{position:static;height:auto;transform:none;opacity:1}
  .servicios__pinwrap{height:auto}
  .servicios__pin{position:static;height:auto;transform:none}
  /* el vídeo boomerang de #dudas tampoco se carga en JS por debajo de este
     ancho (mismo guard sinVideo que el resto) — se oculta su hueco entero
     en vez de dejar un cuadro vacío. */
  .dudas__videowrap{display:none}
  .dudas__grid{grid-template-columns:1fr}
}

/* La miniatura móvil conserva el HTML de escritorio, sin cargar vídeos. */
.preview-estatica .vidbg,.preview-estatica .vidgap,.preview-estatica .dudas__videowrap{display:none}
.preview-estatica .vidhalo{text-shadow:none}


/* hidden debe prevalecer sobre display:grid/flex de los componentes. */
[hidden]:not([hidden="until-found"]){display:none!important}

@media (max-width:820px){
  body{text-align:left}
  .toc{display:none}
  #creatividad{padding-top:48px}
  #metodo h2{width:auto;max-width:100%}
  #contacto{padding-top:56px;padding-bottom:40px}
  .hero__title,h2{overflow-wrap:break-word;text-align:left}
  .trio,.cards{grid-template-columns:minmax(0,360px);gap:20px;justify-content:center}
  .trio article{aspect-ratio:auto;min-height:220px}
  .work{grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr))}
  .faq summary{gap:16px;text-align:left}
  .faq summary::after{flex:none}
  .marquee{margin-inline:0}
  .panel a{max-width:100%;overflow-wrap:anywhere}
  .panel__in>*{min-width:0}
  .nav{padding-inline:12px}
  .nav__mark svg{width:150px}
  .oferta{position:static;width:fit-content;max-width:calc(100% - 40px);margin:16px auto;
    animation:none}
}
@media (max-width:1100px){
  /* El menú sigue accesible al desplazarse, también sin ratón. */
  .nav.compact{max-width:100%;padding-inline:12px}
  .nav.compact .nav__toggle{opacity:1;pointer-events:auto}
  .nav-panel{max-height:calc(100dvh - 116px);overflow-y:auto}
}
@media (prefers-reduced-motion:reduce){
  #creatividad{padding-top:58px}
  .proc{height:auto;padding-block:58px}
  .proc__pin{position:static;height:auto;padding-top:0}
  .proc__stage,.proc__step{opacity:1;transform:none}
}

/* Teléfonos apaisados: el contenido no cabe en un anclaje de 100vh. */
@media (max-width:1100px) and (max-height:600px){
  .toc,.vidbg,.vidgap,.dudas__videowrap{display:none}
  .vidhalo{text-shadow:none}
  .metodo__pinwrap,.servicios__pinwrap{height:auto}
  .metodo__pin,.servicios__pin{position:static;height:auto;transform:none;opacity:1}
  .dudas__grid{grid-template-columns:1fr}
  #creatividad{padding-top:48px}
  #metodo h2{width:auto;max-width:100%;text-align:left}
  #contacto{padding-top:56px;padding-bottom:40px}
  .oferta{position:static;width:fit-content;max-width:calc(100% - 40px);margin:16px auto;animation:none}
}

/* Proceso sigue completo: solo se retira el escenario de los círculos. */
@media (max-width:820px),(max-width:1100px) and (max-height:600px){
  .proc{height:auto;padding-block:56px}
  .proc__pin{position:static;height:auto;padding-top:0}
  .proc__stage{display:none}
  .proc__steps{grid-template-columns:repeat(2,minmax(0,1fr));gap:28px;padding:32px 0 0}
  .proc__step{opacity:1;transform:none;text-align:left}
  .proc__step h3{font-size:18px}
  .proc__step p{font-size:15px}
}
@media (max-width:560px){
  .proc__steps{grid-template-columns:minmax(0,1fr);gap:24px}
}
