/* ============================================================================
   Dr. Leonardo Figueroa — Implantología
   Sistema de 2 colores + acento cálido. Serif italic como único ornamento.
   ========================================================================== */

:root{
  --ink:      #14151D;
  --ink-2:    #1C1D26;
  --bone:     #EAE8E8;
  --beige:    #D6D1D0;
  --slate:    #424346;
  --gold:     #B38C61;

  /* Dos superficies extra para que ninguna sección contigua repita fondo.
     Antes había tres oscuras seguidas al final y la página se aplanaba: el
     ojo perdía el corte entre secciones. La escala es deliberadamente corta
     (un paso arriba y uno abajo de cada base) para no romper el registro
     formal — no son colores nuevos, son profundidades del mismo. */
  --ink-deep: #0B0C12;
  /* La portada volvió a mundo CLARO, y esta vez a un gris neutro: el cliente
     mandó dos referencias suyas y eligió explícitamente la clara ("EL FONDO
     CLARO COMO LA FOTO 1"). En ese archivo el fondo va de ~#DCDCDC abajo a la
     izquierda hasta ~#EFEFEF en la cuña de arriba a la derecha; #E3E3E3 es el
     tono medio y la cuña la hace `.cover__aura`.

     Neutro y no el #DEDCDA cálido que tuvo antes: sobre un gris frío el
     retrato en blanco y negro no se tiñe, que es lo que se ve en su
     referencia. El día anterior pasó por azul (#151926), azul muy oscuro y
     negro; el recorrido completo está en la nota del proyecto.

     El valor exacto lo manda el ECO: las letras son blanco puro, así que lo
     único que las hace visibles es cuánto más oscuro sea este gris. Con
     #E3E3E3 y el aura encendida quedaban 26 puntos de diferencia a la
     izquierda y 21 a la derecha —medidos sobre los píxeles— y el cliente las
     vio difuminadas. Con #D9D9D9 son 38 y 33. */
  --cover-bg: #D9D9D9;
  --sand:     #E3DED6;

  --ink-10:   rgba(20,21,29,.10);
  --ink-40:   rgba(20,21,29,.42);
  --bone-15:  rgba(234,232,232,.15);
  --bone-40:  rgba(234,232,232,.42);

  --sans: 'Inter Tight', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
  --serif:'Instrument Serif', 'Iowan Old Style', Georgia, 'Times New Roman', serif;

  /* Dúo editorial de la portada, tomado de las referencias que trajo el
     cliente (Editorial Society / the WOMAN.): una didona de contraste alto
     para el nombre en versalitas y una cursiva de trazo fino para la línea
     de especialidad. `--serif` (Instrument) sigue siendo la letra de texto
     del resto del sitio; estas dos son SÓLO de exhibición y por eso viven
     aparte: mezclarlas en párrafos rompería la legibilidad.
     Bodoni Moda es variable con eje óptico (opsz 6..96): a tamaño de titular
     el propio archivo afina las astas y abre el contraste. */
  --display:'Bodoni Moda', 'Instrument Serif', 'Didot', Georgia, serif;
  --script: 'Cormorant Garamond', 'Instrument Serif', Georgia, serif;
  /* Sólo para el eco gigante de la portada. Una condensada de palo seco muy
     pesada: es la única forma de tener a la vez mucha mancha y mucho alto,
     porque cada letra ocupa poco ancho y eso permite subir el cuerpo. Con
     Bodoni no se podía — ya estaba en su peso máximo (900) y ahí se acaba. */
  --eco: 'Anton', 'Arial Narrow', 'Haettenschweiler', sans-serif;

  --e-out: cubic-bezier(.16,1,.3,1);
  --e-io:  cubic-bezier(.76,0,.24,1);
  --e-expo: cubic-bezier(.19,1,.22,1);

  /* La referencia va casi a sangre: 40px de aire sobre 1518px de lienzo (2,6%).
     Con 84rem centrados quedaban 288px muertos por lado en 1920 y todo se veía
     chico. El texto no se desparrama porque cada bloque tiene su propio
     max-width en ch. */
  --pad:   clamp(1.25rem, 2.6vw, 3.25rem);
  --maxw:  116rem;
  /* La barra creció de 4,5 a 5,25rem: con el monograma real dentro (que es
     vertical, no horizontal como el trazado provisional que había) 4,5rem
     dejaban la marca pegada a los cantos y la barra se leía apretada. */
  --hdr-h: clamp(4.5rem, 7vh, 5.5rem);
  --split: 50%;

  /* Ritmo vertical único. Antes cada sección traía su propio clamp (5-9rem,
     3.5-6rem, 6-12rem) y el bloque se sentía saturado porque nada rimaba.
     Un solo par de valores ordena toda la página. */
  --sec-y:    clamp(3.5rem, 7.5vh, 5.75rem);
  --sec-y-lg: clamp(4.5rem, 10vh, 7.5rem);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%}
html.js{scroll-behavior:smooth}
body{
  margin:0; background:var(--bone); color:var(--ink);
  font-family:var(--sans); font-size:clamp(1rem,.95rem + .2vw,1.0625rem);
  line-height:1.55; -webkit-font-smoothing:antialiased; overflow-x:hidden;
}
body.is-loading{overflow:hidden}
img,video,svg{display:block; max-width:100%}
img{height:auto}
a{color:inherit; text-decoration:none}
button{font:inherit; color:inherit; background:none; border:0; cursor:pointer}
ul,ol{margin:0; padding:0; list-style:none}
h1,h2,h3,p,dl,dd,figure{margin:0}
::selection{background:var(--ink); color:var(--bone)}

:focus-visible{
  outline:2px solid var(--gold); outline-offset:3px; border-radius:2px;
}

.sr{
  position:absolute!important; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0;
}
.skip{
  position:fixed; top:.75rem; left:.75rem; z-index:200;
  padding:.7rem 1.1rem; background:var(--ink); color:var(--bone);
  font-size:.875rem; border-radius:2px; transform:translateY(-160%);
  transition:transform .3s var(--e-out);
}
.skip:focus{transform:none}

/* ======================================================== PRELOADER ======= */
.loader{
  position:fixed; inset:0; z-index:300;
  background:var(--ink); color:var(--bone);
  display:flex; flex-direction:column; align-items:center; justify-content:center;
  gap:2.5rem;
  transition:opacity .8s var(--e-expo), visibility .8s;
}
.loader.is-done{opacity:0; visibility:hidden; pointer-events:none}
/* Sin JS nadie lo quita: no puede tapar la página. */
html.no-js .loader{display:none}
.loader__ring{width:5rem; height:5rem; position:relative}
.loader__ring svg{width:100%; height:100%; transform:rotate(-90deg)}
.loader__ring circle{
  fill:none; stroke:var(--bone); stroke-width:1.5;
  stroke-dasharray:157; stroke-dashoffset:157;
  transition:stroke-dashoffset 1.4s var(--e-expo);
}
.loader.is-drawing circle{stroke-dashoffset:0}
.loader__services{
  display:flex; flex-direction:column; align-items:center; gap:.35rem;
  font-size:.75rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--bone-40); opacity:0;
  transition:opacity .6s var(--e-out) .3s;
}
.loader.is-drawing .loader__services{opacity:1}

/* ---------------------------------------------------- primitivas de texto */
.h-serif{
  font-family:var(--serif); font-weight:400;
  font-size:clamp(2rem,1.15rem + 2.9vw,3.75rem);
  line-height:1; letter-spacing:-.015em;
}
.h-serif em, .hero__h1 em, .cta__h em, .story__stage em{font-style:italic}

.eyebrow{
  display:flex; align-items:center; gap:1rem;
  font-size:.75rem; letter-spacing:.14em; text-transform:uppercase;
  font-weight:500; color:var(--slate);
}
/* El filete del eyebrow: dorado, con una luz que lo recorre de punta a
   punta. Es el mismo gesto que ya tenía el riel de "En mis palabras" —
   pedido del usuario de que se repita como motivo en todo el sitio.

   Va con `background-size` + `background-position` animados y no con un
   pseudo-elemento desplazándose: el filete mide 1px de alto y clamps de
   ancho, y mover una capa dentro de una caja tan fina obliga a `overflow`
   y a un tercer nodo. Desplazar el degradado hace lo mismo con una sola
   propiedad y sin tocar el layout. */
.eyebrow::before{content:''}
/* Los eyebrow centrados (relato, casos, instrumental) llevan filete a los
   dos lados; el resto sólo a la izquierda. La pinta vive en una sola regla
   compartida para no mantener el degradado por duplicado. */
.story__panel .eyebrow::after,
.cases .eyebrow::after,
.equip .eyebrow::after{content:''}
.eyebrow::before,
.story__panel .eyebrow::after,
.cases .eyebrow::after,
.equip .eyebrow::after{
  width:clamp(1.5rem,4vw,3.5rem); height:1px; flex:none;
  background:linear-gradient(90deg,
    rgba(179,140,97,.35) 0%,
    rgba(179,140,97,.7)  38%,
    rgba(255,246,231,1)  50%,
    rgba(179,140,97,.7)  62%,
    rgba(179,140,97,.35) 100%);
  background-size:280% 100%;
  animation:filete 5.5s var(--e-io) infinite;
}
@keyframes filete{
  0%  {background-position:150% 0}
  55% {background-position:-50% 0}
  100%{background-position:-50% 0}
}
.num{
  font-family:var(--serif); font-weight:400; font-style:italic;
  font-size:1.55em; line-height:.8; letter-spacing:-.02em;
  background:linear-gradient(270deg,#b38b61,#dac8b7);
  -webkit-background-clip:text; background-clip:text; color:transparent;
}

/* Revelado enmascarado ---------------------------------------------------- */
.ln{display:block; overflow:hidden; padding-bottom:.08em; margin-bottom:-.08em}
.ln__i{display:block; transform:translateY(108%); transition:transform .7s var(--e-expo)}
.ln:nth-child(2) .ln__i{transition-delay:.07s}
.ln:nth-child(3) .ln__i{transition-delay:.14s}
.is-in .ln__i{transform:none}

.reveal{opacity:0; transform:translateY(1.4rem); transition:opacity .6s var(--e-out), transform .6s var(--e-out)}
.reveal:nth-child(2){transition-delay:.06s}
.reveal:nth-child(3){transition-delay:.12s}
.reveal:nth-child(4){transition-delay:.18s}
.reveal:nth-child(5){transition-delay:.24s}
.reveal.is-in{opacity:1; transform:none}

html.no-js .ln__i,
html.no-js .reveal{transform:none; opacity:1}

/* Image reveal — clip-path wipe */
.img-reveal{
  clip-path:inset(100% 0 0 0);
  transition:clip-path 1.1s var(--e-expo);
}
.img-reveal.is-in{clip-path:inset(0 0 0 0)}
html.no-js .img-reveal{clip-path:none}

/* Text-roll ---------------------------------------------------------------- */
.roll{display:inline-block; overflow:hidden; vertical-align:bottom; height:1.35em; line-height:1.35}
.roll > span{
  display:block; transition:transform .55s var(--e-io);
}
.roll > span > span{display:block}
.roll:hover > span, a:hover > .roll > span,
.btn:hover .roll > span, .roll:focus-visible > span{transform:translateY(-50%)}

/* Botones ----------------------------------------------------------------- */
.btn{
  display:inline-flex; align-items:center; gap:.75rem;
  font-size:.9375rem; font-weight:500; line-height:1;
}
.btn svg{width:.75rem; height:.75rem; flex:none; transition:transform .5s var(--e-out)}
.btn:hover svg{transform:rotate(90deg)}

.btn--pill{
  padding:1.05rem 1.5rem; border-radius:100px;
  background:var(--ink); color:var(--bone);
  transition:background .4s var(--e-out), color .4s var(--e-out);
}
.btn--pill:hover{background:var(--slate)}

.btn--line{
  padding:.9rem 0; border-bottom:1px solid var(--ink-40);
  transition:border-color .4s var(--e-out);
}
.btn--line:hover{border-color:var(--ink)}

.btn--big{
  padding:1.35rem 2.25rem; border-radius:100px;
  background:var(--bone); color:var(--ink);
  font-size:clamp(.95rem,.85rem + .3vw,1.1rem);
  transition:background .4s var(--e-out);
}
.btn--big:hover{background:#fff}
.btn--big svg{width:.9rem; height:.9rem}
.btn--big:hover svg{transform:translate(2px,-2px) rotate(0)}

.arrow{
  width:3.25rem; height:3.25rem; border:1px solid var(--ink-40); border-radius:50%;
  display:grid; place-items:center;
  transition:background .4s var(--e-out), color .4s var(--e-out), border-color .4s var(--e-out), opacity .3s;
}
.arrow svg{width:1.15rem; height:.6rem}
.arrow:hover:not(:disabled){background:var(--ink); color:var(--bone); border-color:var(--ink)}
.arrow:disabled{opacity:.28; cursor:default}

/* El tirón magnético lo escribe el JS en `transform`; esta transición es la
   que le da el retardo al entrar y el regreso suave al salir. Va después de
   la definición del botón a propósito: cada uno trae su propia `transition`
   y si esto fuera antes, la suya la borraría de la lista.

   Sólo queda en `.arrow`. Los CTA de texto (`.btn--pill`, `.btn--big`) lo
   perdieron por pedido explícito: al usuario le molesta que la palabra se
   corra hacia el cursor. En su lugar les sigue el puntero la luz interna
   (ver ATMÓSFERA). En las flechas no hay letra que se mueva, así que ahí
   el imán se queda. */
.arrow{transition:background .4s var(--e-out), color .4s var(--e-out),
                  border-color .4s var(--e-out), opacity .3s, transform .35s var(--e-out)}
@media (prefers-reduced-motion:reduce){
  .arrow{transform:none!important}
}

/* ======================================================== HEADER =========
   Dos estados, pedido del usuario: "banner superior minimalista y se vuelve
   difuminado al bajar".

   1. Arriba del todo — mínimo: sin velo, sin filete, sin fondo. Sólo la
      letra en hueso sobre la portada. La portada es oscura de lado a lado
      (negro liso a la izquierda, retrato sobre negro a la derecha), así que
      un único color de letra basta: no hace falta el calco partido con
      clip-path que se probó y se descartó en su momento.
   2. En la portada la barra va SIN velo: sólo las letras, como pidió el
      cliente. Puede hacerlo porque ahí el fondo es el gris claro liso de la
      sección (el retrato va recortado y no llega arriba), así que la letra
      tinta se apoya en él de sobra.

      El velo claro (`.is-solid`) entra cuando aterriza el hero — el mismo
      momento en que aparece "Agendar cita" — y ya no se va: de ahí para
      abajo hay secciones oscuras y la barra necesita superficie propia.

      Ojo si la portada volviera a ser oscura: hubo que adelantar ese velo al
      COMIENZO del deslizamiento del hero, porque durante esa pantalla la
      barra cruza a la vez la portada, el panel claro del hero y el vídeo, y
      ningún color de letra sirve para los tres.

   Se cambia sólo `background`/`box-shadow`/`color`, nunca `display` ni
   `position`: el header no se mueve de sitio entre los dos estados, sólo
   aparece la superficie debajo. */
.hdr{
  position:fixed; inset:0 0 auto 0; z-index:100;
  height:var(--hdr-h); padding:0 var(--pad);
  /* Tres columnas y no flex: con `1fr auto 1fr` el logo cae en el centro
     EXACTO de la pantalla. Con flex + `margin-inline:auto` se centraría
     respecto del hueco que dejan sus hermanos, así que se correría según lo
     largo que sea el nav o el botón. */
  display:grid; grid-template-columns:1fr auto 1fr; align-items:center;
  gap:clamp(1rem,3vw,2.5rem);
  color:var(--ink);
  background:transparent;
  box-shadow:none;
  transition:transform .55s var(--e-io), background .5s var(--e-out),
             color .5s var(--e-out), box-shadow .5s var(--e-out);
}
.hdr.is-hidden{transform:translateY(-100%)}

/* Al bajar: vidrio. El `backdrop-filter` sólo se declara acá — mantenerlo en
   la regla base con blur(0) crearía una capa de composición permanente sobre
   la portada sin ganar nada. */
/* El velo deja pasar el fondo — el cliente lo pidió expreso: "que no sea
   blanco siempre, que dependa del fondo, y que se vea harto el fondo".

   Y depende de HACIA DÓNDE se va, que es el gesto que pidió el cliente: la
   barra estorba lo mínimo mientras se avanza y se hace superficie cuando se
   retrocede a buscar el menú.

   · BAJANDO (`.is-baja`): el velo casi desaparece y quedan sólo las letras
     flotando sobre el contenido. Claro al 20% sobre lo claro, oscuro al 18%
     sobre las secciones que son oscuras de punta a punta; son los valores más
     bajos que aguantan el 4,5:1 medido sobre píxeles reales.
   · SUBIENDO (o quieto): **barra blanca de verdad, opaca, con letra tinta —
     también sobre las secciones oscuras.** Antes acá había un velo al 55% (y
     uno oscuro al 42% sobre lo oscuro) y el cliente reclamó que en las partes
     oscuras la barra se perdía: "que se ponga blanco totalmente blanco, o
     sea, blanco con las letras negras, para identificarlo bien". Por eso
     `.is-dark` ya no pinta nada cuando se sube: sólo cuenta bajando.

   `backdrop-filter` se declara SÓLO acá y no en la regla base: dejarlo
   arriba con blur(0) crearía una capa de composición permanente sobre la
   portada sin ganar nada. */
.hdr.is-solid{
  /* BLANCO PURO, opaco. Estuvo en #F7F6F5 y el cliente reclamó que "hay
     partes en las que no se pone totalmente blanco": no era transparencia,
     era que sobre las secciones claras (reseñas #EAE8E8, formación #E3DED6)
     un hueso a #F7F6F5 se confunde con la sección y la barra desaparece. Con
     #FFF y el filete algo más marcado la barra se lee como barra sobre los
     dos mundos. Nada de alfa acá: el pedido fue literal, "totalmente blanco,
     ni un poco transparente". */
  background:#FFFFFF;
  color:var(--ink);
  backdrop-filter:blur(22px) saturate(125%);
  -webkit-backdrop-filter:blur(22px) saturate(125%);
  box-shadow:inset 0 -1px 0 rgba(20,21,29,.13);
}
/* Al SUBIR la barra tiene que cerrarse de golpe, no fundirse durante medio
   segundo: con la transición larga de la regla base, media rueda de scroll
   hacia arriba se veía como una barra a medio blanquear. Bajando se conserva
   el fundido lento, que es lo que hace que la barra se retire con calma. */
.hdr.is-solid:not(.is-baja){
  transition:transform .55s var(--e-io), background .2s var(--e-out),
             color .2s var(--e-out), box-shadow .2s var(--e-out);
}
/* Dos gestos, dos velocidades: subiendo la barra se cierra en blanco en .2s
   (regla de arriba) y bajando se retira en los .5s de la regla base, para que
   el paso a transparente no se sienta como un corte.
   El estado "en reposo" (a los 5s quieto se ponía blanca sola) se probó y se
   RETIRÓ por pedido: "si se queda detenido, sigue transparente". Sólo el
   sentido del scroll manda. */
/* Bajando: NADA. Ni velo, ni desenfoque, ni filete — sólo las letras sobre el
   contenido. Pedido literal y final del cliente: "cuando bajo, totalmente
   transparente, ni una pizca de color".

   El `backdrop-filter` se apaga acá y no es un detalle: era lo que ponía la
   barra gris. Con el fondo casi transparente, un desenfoque de 22px chupa lo
   que pasa por debajo —los titulares claros de las secciones oscuras, el
   panel hueso, el vídeo— y lo promedia en una lechada que no es el color de
   ningún fondo.

   Sobre las secciones oscuras lo único que sobrevive es el COLOR de la letra:
   sin velo, una letra tinta sobre negro no se vería. */
.hdr.is-solid.is-baja{
  background:transparent;
  backdrop-filter:none; -webkit-backdrop-filter:none;
  box-shadow:none;
}
.hdr.is-solid.is-dark.is-baja{color:var(--bone)}
.hdr .mark{color:currentColor}
.hdr .burger span{background:currentColor}

/* Los tres bloques del banner se ven desde el primer fotograma. Antes el nav
   y el botón entraban recién con el vidrio (`.is-solid`) y la portada abría
   con la barra vacía: sin salida de navegación y sin llamada a la acción
   justo en la pantalla que más se mira. La entrada es un fundido corto que
   arranca cuando termina el preloader, igual que el resto de la portada. */
.hdr__nav,.hdr .btn--pill,.burger{
  transition:opacity .8s var(--e-out) .35s, transform .8s var(--e-out) .35s;
}
body.is-loading .hdr__nav,
body.is-loading .hdr .btn--pill,
body.is-loading .hdr .burger{opacity:0; transform:translateY(-.5rem)}
html.no-js .hdr{background:rgba(240,239,238,.88)}

.hdr__brand{display:flex; align-items:center; justify-content:center}
/* El monograma se mide por ALTO, no por ancho: es una pieza vertical y lo
   que tiene que rimar con la barra es su altura, no su mancha. */
.mark{
  height:clamp(2rem,3.8vh,2.7rem); width:auto;
  transition:filter .45s var(--e-out), opacity .45s var(--e-out);
}
/* El archivo de marca viene en blanco sobre alfa: se invierte a tinta salvo
   cuando la barra va en modo oscuro, que ahora es sólo mientras se BAJA sobre
   una sección oscura — subiendo la barra es blanca y el monograma va en tinta
   como el resto de las letras. */
.hdr__brand:hover .mark{opacity:.75}
.hdr .mark{filter:invert(1)}
.hdr.is-dark.is-baja .mark{filter:none}

/* Versalitas diminutas y muy espaciadas. El cliente pidió que la barra no
   sature la imagen: se baja el cuerpo a 10px, el peso a regular y el tinte
   a la mitad, y se compensa con tracking. Una barra así no compite con el
   retrato — se lee cuando se la busca y desaparece cuando no. */
.hdr__nav{
  display:flex; gap:clamp(1.4rem,3vw,2.75rem); justify-self:start;
  font-size:.625rem; font-weight:400; letter-spacing:.24em; text-transform:uppercase;
}
/* Sin tinte a media asta: el cliente no distinguía "Sobre mí / Reseñas /
   Casos clínicos" sobre la portada clara — a .55 la tinta quedaba en un gris
   medio. Van al 100% (o sea negro tinta) en todos los estados; el único sitio
   donde la letra sigue siendo hueso es bajando sobre secciones oscuras, donde
   una letra negra sencillamente no se vería. El aviso de hover pasa del tinte
   al oro de la paleta, que no le quita negro a nada. */
.hdr__nav a{color:inherit; opacity:1; transition:color .35s var(--e-out)}
.hdr__nav a:hover,.hdr__nav a:focus-visible{color:var(--gold)}
.hdr__right{display:flex; align-items:center; gap:clamp(.75rem,1.5vw,1.5rem); justify-self:end}

/* El CTA del banner es un filete de una línea con la misma versalita del
   nav, y sólo al pasar el cursor se rellena. La píldora rellena se mantiene
   en los CTA de sección.

   NO se ve en la portada: el cliente pidió que la primera pantalla no se
   sature de información. Entra recién cuando el hero de vídeo termina de
   deslizarse, que es cuando el panel de trayectoria ya quedó fijado (clase
   `.is-cta`, la pone `main.js`). Se anima con opacity/transform y NUNCA con
   `display`, para que el botón no salte en el reparto de la barra. */
.hdr .btn--pill{
  padding:.78rem 1.5rem;
  font-size:.625rem; font-weight:400; letter-spacing:.24em; text-transform:uppercase;
  text-indent:.24em;
  background:transparent; color:inherit;
  /* Filete de .24 a .42: el cliente lo pidió "un poco más negro". El relleno
     sigue apareciendo sólo al pasar el cursor. */
  box-shadow:inset 0 0 0 1px rgba(20,21,29,.42);
  transition:background .4s var(--e-out), color .4s var(--e-out),
             box-shadow .4s var(--e-out);
}
/* Sólo bajando sobre lo oscuro: subiendo la barra es blanca y el filete del
   botón tiene que ser el de tinta, no el de hueso. */
.hdr.is-dark.is-baja .btn--pill{box-shadow:inset 0 0 0 1px rgba(234,232,232,.26)}
.hdr.is-dark.is-baja .btn--pill:hover,
.hdr.is-dark.is-baja .btn--pill:focus-visible{
  background:var(--bone); color:var(--ink); box-shadow:inset 0 0 0 1px var(--bone);
}
html.js .hdr .btn--pill{
  opacity:0; transform:translateY(-.4rem); pointer-events:none;
  transition:opacity .7s var(--e-out), transform .7s var(--e-out),
             background .4s var(--e-out), color .4s var(--e-out),
             box-shadow .4s var(--e-out);
}
html.js .hdr.is-cta .btn--pill{opacity:1; transform:none; pointer-events:auto}
/* El icono baja a la mitad y a un cuarto de tinte: sigue girando al pasar el
   cursor, pero deja de leerse como un signo más plantado en la barra. */
.hdr .btn--pill svg{width:.5rem; height:.5rem; opacity:.8}
.hdr .btn--pill:hover,.hdr .btn--pill:focus-visible{
  background:var(--ink); color:var(--bone); box-shadow:inset 0 0 0 1px var(--ink);
}

.burger{display:none; width:2.75rem; height:2.75rem; place-items:center; gap:5px}
.burger span{display:block; width:22px; height:1.5px; background:var(--ink); transition:transform .4s var(--e-io), opacity .3s}
.burger[aria-expanded="true"] span:first-child{transform:translateY(3.25px) rotate(45deg)}
.burger[aria-expanded="true"] span:last-child{transform:translateY(-3.25px) rotate(-45deg)}

/* Menú móvil */
.menu{
  position:fixed; inset:0; z-index:99; background:var(--ink); color:var(--bone);
  padding:calc(var(--hdr-h) + 2rem) var(--pad) var(--pad);
  display:flex; flex-direction:column; justify-content:space-between;
}
.menu[hidden]{display:none}
.menu nav{display:flex; flex-direction:column; gap:.35rem}
.menu nav a{
  font-family:var(--serif); font-size:clamp(2rem,9vw,3.25rem); line-height:1.15;
  display:flex; align-items:baseline; gap:1rem;
}
.menu nav a i{font-family:var(--sans); font-style:normal; font-size:.75rem; color:var(--bone-40)}
.menu__foot{
  display:flex; justify-content:space-between; gap:1rem; flex-wrap:wrap;
  font-size:.8125rem; color:var(--bone-40); border-top:1px solid var(--bone-15); padding-top:1.25rem;
}
.menu__foot a{color:var(--bone)}

/* ======================================================== PORTADA ========
   Una sola idea por pantalla: él y su nombre. La ficha de cifras se mudó al
   hero — dos bloques entrando desde la derecha uno detrás de otro se leían
   como relleno, y el cliente los quiso juntos.

   Tres capas, y el orden es el punto de toda la pantalla:
     0. `--cover-bg` liso —azul oscuro, muestreado de la referencia que mandó
        el cliente— + `.cover__aura`, dos focos muy suaves que evitan que el
        plano quede estéril ahora que la foto ya no trae su fondo de estudio.
     1. `.cover__ghost` — el nombre gigante, a todo el ancho y sangrando por
        los dos lados. Va DETRÁS de la figura, que es lo que el cliente pidió
        expresamente: antes se leía encima de la cara y parecía una marca de
        agua mal puesta.
     2. `.cover__media` — el retrato RECORTADO del fondo de estudio (rembg).
        Ese recorte es lo que hace posible el punto 1: sobre la foto entera,
        opaca, ninguna capa de abajo se vería.

   El banner no lleva velo en esta pantalla; entra con el hero. Por eso el
   `--cover-bg` es liso también arriba: la letra en hueso del banner se apoya
   en él y no necesita ningún fondo propio.

   El alto de 260svh está atado al `margin-top:-200svh` del hero y no se toca
   solo: el hero entra en [alto−200svh, alto−100svh] y la portada se despega
   en alto−100svh. Con 260 quedan 60svh de retrato solo antes de que el hero
   empiece a entrar, y su entrada termina justo cuando la portada se va. Si se
   cambia uno de los dos números hay que recalcular el otro. */
.cover{position:relative; z-index:1; height:260svh; background:var(--cover-bg)}
.cover__sticky{
  --gut:max(var(--pad), (100% - var(--maxw)) / 2);
  position:sticky; top:0; height:100svh;
  overflow:hidden; isolation:isolate;
  /* --- Las sombras azules -------------------------------------------------
     El cliente pidió "algo de color, unas sombras azules muy difuminadas, de
     la paleta que ya trabajamos, nada distinto". El azul es el mismo
     `rgb(38,54,110)` que ya usó el eco cuando la portada era clara: no entra
     ningún tono nuevo.

     Van acá y no en `.cover__aura` porque los porcentajes de un
     `radial-gradient` de fondo son de ESTA caja, que mide una pantalla — el
     aura mide 120vw de lado y está anclada fuera de cuadro, así que colocar
     algo ahí obliga a convertir coordenadas y el primer intento quedó
     directamente fuera de la pantalla.

     Las dos caen a la IZQUIERDA, que es el lado contrario al foco blanco: son
     la sombra de esa luz, no una mancha de color. Siete paradas cada una para
     que el canto no se marque.

     Segunda pasada del 2026-09-05: "aún es muy, muy pequeño, que sea un
     poquito más notorio". La de abajo sube de .30 a .42 y crece de 78%/72% a
     88%/80%; la de arriba, de .19 a .25. Medido por mezcla sobre `--cover-bg`
     #D9D9D9: la esquina inferior izquierda pasa de rgb(163,168,185) a
     rgb(142,149,172) — el azul menos el rojo sube de 22 a 30 puntos, o sea se
     nota como color y no como gris frío, y la pantalla sigue siendo clara.
     La de ARRIBA se queda corta a propósito: por esa esquina cruza el banner y
     su letra necesita el fondo claro.

     PARA REVERTIR: dejar `background:var(--cover-bg)` a secas y devolver la
     sombra de `.cover__media` a `rgba(20,21,29,.22)`. */
  background:
    /* Tercera subida (2026-09-05): .42 → .54 abajo y .25 → .32 arriba. Sobre
       `--cover-bg` #D9D9D9 la esquina inferior pasa de rgb(142,148,172) a
       rgb(120,129,159) — el azul menos el rojo sube de 30 a 39 puntos. Sigue
       siendo una pantalla clara y el nav en tinta gana contraste, no lo
       pierde. Por si hay que volver: los picos anteriores fueron .30/.19,
       después .42/.25. */
    radial-gradient(88% 80% at 2% 98%,
      rgba(38,54,110,.54) 0%,
      rgba(38,54,110,.423) 18%,
      rgba(38,54,110,.297) 36%,
      rgba(38,54,110,.180) 54%,
      rgba(38,54,110,.090) 70%,
      rgba(38,54,110,.028) 85%,
      rgba(38,54,110,0)   100%),
    radial-gradient(70% 62% at 0% 2%,
      rgba(38,54,110,.32) 0%,
      rgba(38,54,110,.250) 18%,
      rgba(38,54,110,.175) 36%,
      rgba(38,54,110,.106) 54%,
      rgba(38,54,110,.052) 70%,
      rgba(38,54,110,.017) 85%,
      rgba(38,54,110,0)   100%),
    var(--cover-bg);
  color:var(--ink);
}
/* Grano: sin él el plano se lee como render limpio. Sobre fondo claro pesa la
   mitad que sobre oscuro, de ahí el .03 y el `multiply`. */
.cover__sticky::after{
  content:''; position:absolute; inset:0; z-index:6; pointer-events:none;
  opacity:.03; mix-blend-mode:multiply;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/></filter><rect width='180' height='180' filter='url(%23n)'/></svg>");
}

/* --- Ambiente ------------------------------------------------------------
   La cuña de luz de la esquina superior derecha de la referencia. Sobre un
   plano claro el fondo no se puede quedar liso: sin este foco la pantalla se
   lee como un render sin luz, y el retrato recortado se pega al fondo.

   Dos focos: el blanco de arriba a la derecha —el que se ve en la referencia—
   y una sombra muy suave abajo a la izquierda, que es lo que hace que el
   plano tenga peso y no flote. Nueve paradas por foco: con tres, el canto del
   degradado se marca como un círculo. Sin `filter:blur` — un radial-gradient
   ya nace difuso y no cuesta capa de composición. */
.cover__aura{
  position:absolute; z-index:0; pointer-events:none;
  width:120vw; height:120vw; right:-18vw; top:50%; translate:0 -54%;
  /* Bajó de .85 a .34: el foco cae justo detrás del renglón, y al blanquear
     ahí el fondo se comía la diferencia con unas letras que son blanco puro
     — medido, 21 puntos a la derecha contra 26 a la izquierda. Ahora la luz
     se nota pero no borra el eco. */
  background:radial-gradient(closest-side,
    rgba(255,255,255,.34) 0%,
    rgba(255,255,255,.29) 16%,
    rgba(255,255,255,.21) 32%,
    rgba(255,255,255,.13) 48%,
    rgba(255,255,255,.072) 62%,
    rgba(255,255,255,.032) 76%,
    rgba(255,255,255,.008) 90%,
    rgba(255,255,255,0)   100%);
}
.cover__aura::after{
  content:''; position:absolute;
  width:95vw; height:95vw; left:-32vw; bottom:-38vw;
  background:radial-gradient(closest-side,
    rgba(20,21,29,.055) 0%,
    rgba(20,21,29,.046) 14%,
    rgba(20,21,29,.035) 28%,
    rgba(20,21,29,.025) 42%,
    rgba(20,21,29,.016) 56%,
    rgba(20,21,29,.009) 70%,
    rgba(20,21,29,.003) 84%,
    rgba(20,21,29,0)   100%);
}

/* --- El nombre gigante, detrás ------------------------------------------
   Letras ALTAS y GRUESAS, que es lo que pidió el cliente. Las dos cosas
   juntas no salen de la tipografía sola.

   Grueso: va en Anton, una condensada de palo seco muy pesada. Estuvo en
   Bodoni 900 y se cambió porque el cliente la pidió más gruesa y ahí ya no
   había margen: 900 es el tope de esa familia. La condensada resuelve las dos
   cosas a la vez — como cada letra ocupa poco ancho, los 17 caracteres caben
   con un cuerpo un tercio más grande, y de ese cuerpo sale la mancha.

   Alto: el `scale` vertical estira sólo ese eje, así que alarga las letras sin
   ensancharlas ni afinar las astas (un `scaleX` sí las afinaría).

   Color: azul tinta de la paleta en vez del gris de antes. Lo pidió el
   cliente ("algún color de la paleta, un azul oscuro, un negro pero no tan
   negro") y de paso saca a la portada del blanco y negro puro.

   Va en UN renglón, de borde a borde y sangrando un poco por los dos lados
   (el cliente lo pidió así: "que ocupe todo el ancho, incluso que se pase").
   A 12,4vw los 17 caracteres miden ~108% del ancho de la ventana, y sobra
   sitio para el `letter-spacing` positivo que pidió — con la Bodoni, a ese
   ancho, el cuerpo tenía que bajar a 9,4vw.

   `--eco-alto`, `--eco-y` y `--eco-dy` las calcula `main.js`: el borde
   inferior de las mayúsculas tiene que caer en el filete dorado de la firma y
   dejar exactamente el mismo aire por arriba. Eso depende del alto de la
   ventana y del alto real de los glifos, así que no se puede escribir a mano
   en la hoja; los valores por defecto de arriba son el aterrizaje suave para
   cuando no hay JS.

   Es una mancha, no un texto: el tinte se queda bajo. A más tinte compite con
   la firma y la pantalla pasa a tener dos nombres. */
.cover__ghost{
  position:absolute; z-index:1; pointer-events:none; user-select:none;
  left:50%; top:var(--eco-y, 50%);
  translate:-50% calc(-50% + var(--eco-dy, 0px));
  /* El eje X lo pidió el cliente ("las letras estíralas más a lo ancho").
     Va como `scale` y no como cuerpo mayor a propósito: Anton es condensada,
     y estirarla es lo que la acerca a la letra ancha de la referencia. El
     cuerpo lo compensa. Estuvo en 12,4vw sin estirar y llegó a 10,4vw, que
     medido en el navegador daba el 111,6% del ancho y se comía la L entera;
     con 9,1vw el renglón mide el 97,6% y **entra completo con un margen a
     cada lado**, que es como está en la referencia del cliente. El eje Y lo
     calcula main.js: ver `--eco-alto` más abajo. */
  scale:var(--eco-ancho, 1.34) var(--eco-alto, 3.4);
  font-family:var(--eco);
  font-style:normal; font-weight:400;
  font-size:9.1vw; line-height:1; letter-spacing:.035em;
  text-transform:uppercase; white-space:nowrap;
  /* Con el fondo azul el eco se invierte: deja de ser un tono más oscuro que
     el fondo y pasa a ser luz. Hueso al 26% sobre #0B0E18 da rgb(67,71,82)
     —medido, no tanteado: la mezcla es `fondo + (color − fondo)·alfa`.

     Estuvo al 40% y el cliente lo bajó ("las letras blancas de atrás, no tan
     blancas"): al oscurecerse el fondo, el mismo alfa abría más distancia
     contra él y las letras se adelantaban. Al 26% vuelven a ser mancha de
     fondo y no un segundo titular compitiendo con la firma.

     Y van desenfocadas, también a pedido ("tipo difuminadas"). El filtro
     vive en el SVG del HTML: ver ahí por qué no puede ser `filter:blur()`. */
  /* Blanco puro y opaco, como en la referencia: sobre el gris claro el
     contraste es mínimo y por eso funciona — se lee como un relieve del
     propio fondo y no como un texto. `--bone` (#EAE8E8) se quedaría corto
     contra #E3E3E3; el blanco de verdad es lo que abre la diferencia.

     Ni desenfoque ni máscara: los dos existieron mientras el fondo era negro
     y las letras chocaban con la firma. Ahora la firma cae POR DEBAJO del
     renglón (ver ECO_ARRIBA/ECO_ABAJO en main.js), así que no hay nada que
     esquivar y el canto va limpio, como en la referencia. */
  color:#FFFFFF;
}

/* --- El retrato ----------------------------------------------------------
   Centrado, apoyado en el borde inferior.

   La foto es `LEONARDO 2.0` (la eligió el cliente en reemplazo de FOTO EPIC):
   el doctor sentado, de traje, sobre fondo de estudio claro. Recortada con
   rembg, igual que la anterior — el recorte es lo que deja ver el eco del
   nombre por detrás.

   **Centrar la imagen no centra al doctor**, aunque acá la diferencia sea
   chica: en este archivo la silla queda fuera del recorte y su cabeza cae al
   47,4% del ancho (medido sobre el canal alfa, banda por banda: 47,2% al 5%
   de alto, 47,4% al 10%, 47,3% al 15% — estable; del 20% para abajo la banda
   ya toma hombros y no sirve). `left:50%` + `translate:-47.4%` pone esa
   columna en el centro de la pantalla, y así queda centrado en cualquier
   ventana, no sólo en la que se probó.

   Apoyada en el borde inferior: la figura llega hasta abajo, que es como la
   quiere el cliente. El aire con el banner sale del alto — 80svh sobre una
   pantalla de 900 deja la coronilla a 180px, con el banner en 72. En el
   archivo la cabeza empieza en la fila 0, así que el borde superior de la
   imagen ES su pelo: a 88svh le rozaba el banner.

   Se probó también el archivo `LEO PRIME SIN FONDO` (3840x2160, mejor
   resolución) y se descartó: viene con una edición de mucho más contraste y
   el traje queda aplastado a negro — mediana 13 contra 32 de FOTO EPIC, sin
   textura de tela. La resolución no compensa perder la mitad de la figura. */
.cover__media{
  position:absolute; z-index:2; bottom:0;
  height:clamp(52%, 80svh, 88%);
  left:50%; translate:-46.8% 0;
  /* Sobre la silueta real (sigue el alfa, no la caja): es lo que evita que un
     recorte se lea como calcomanía pegada. Con el fondo claro vuelve a ser
     una sombra oscura y abierta — mientras el fondo fue negro hubo que
     quitarla, porque ahí no separaba nada y sólo manchaba las letras blancas
     del eco. */
  /* La sombra también va en el azul de la casa: es lo que ata la figura al
     ambiente en vez de dejarla flotando sobre un gris neutro. Revertir a
     `rgba(20,21,29,.22)` si el cliente prefiere el gris. */
  filter:drop-shadow(0 26px 50px rgba(38,54,110,.24));
}
.cover__media picture{display:contents}
.cover__media img{
  height:100%; width:auto;
  /* El reset global pone `max-width:100%`; acá estorba: en una ventana ancha
     y baja la figura tiene que poder salirse por los lados antes que perder
     alto — si pierde alto, deja de respetar los topes de arriba y abajo. */
  max-width:none;
  transition:scale 2.4s var(--e-expo), opacity 1.6s var(--e-out);
}

/* --- El hilo del pie -----------------------------------------------------
   Ya no queda marco: el cliente fue sacando primero la línea de arriba y
   después las dos laterales, porque con el eco a todo lo alto las líneas
   competían con él. Queda sólo el filete del pie.

   El elemento NO se borra aunque casi no se vea: `main.js` lo usa como
   referencia geométrica para colocar el eco (necesita saber dónde empieza y
   dónde termina la zona útil de la pantalla). */
.cover__frame{
  position:absolute; z-index:3; pointer-events:none;
  top:var(--hdr-h);
  left:var(--gut); right:var(--gut);
  bottom:clamp(1rem,3vh,1.75rem);
  border:0;
  border-bottom:1px solid rgba(20,21,29,.14);
}

/* --- La firma ------------------------------------------------------------ */
.cover__name{
  position:absolute; z-index:4;
  left:calc(var(--gut) + clamp(1.25rem,2.5vw,2.5rem));
  bottom:clamp(2.5rem,7vh,4.5rem);
  isolation:isolate;
  /* Con la figura centrada, la columna libre de la izquierda es angosta: el
     tope evita que el nombre se meta en la manga oscura, donde la tinta
     desaparece. */
  max-width:min(34%, 22rem);
}
/* La firma va LIMPIA: hueso puro, sin contorno, sin sombra y sin nada detrás.
   Se probaron las dos formas de rescatarla de las letras blancas del eco y el
   cliente rechazó las dos — la nube oscura detrás del bloque ("sacar la nube")
   y el contorno sobre cada letra ("la letra no enegrecida, tiene que ser
   elegante"). Lo que resuelve la superposición es el eco, que se apaga en su
   tramo bajo: ver el `mask-image` de `.cover__ghost`. */
/* Cursiva fina y grande, casi de pluma: el cliente la pidió "más elegante,
   como escrita a mano, pero que se identifique claramente". Es la misma letra
   que ya firma "Quién soy" y "Implantólogo" en el resto del sitio, así que no
   entra una familia nueva — y contra el palo seco macizo del eco, el contraste
   de las dos voces es justamente lo que ordena la pantalla. */
.cover__h1{
  margin:0;
  font-family:var(--script); font-style:italic; font-weight:400;
  font-size:clamp(2.25rem, min(4.7vw, 7.8vh), 4.6rem);
  line-height:1.02; letter-spacing:.005em;
  color:var(--ink);
}
/* Filete dorado entre el nombre y el oficio: el único acento de la pantalla. */
.cover__role{
  position:relative;
  margin:clamp(.9rem,2.2vh,1.4rem) 0 0; padding-top:clamp(.9rem,2.2vh,1.4rem);
  font-size:.625rem; font-weight:500; letter-spacing:.3em; text-transform:uppercase;
  text-indent:.3em;
  color:var(--slate);
}
.cover__role::before{
  content:''; position:absolute; top:0; left:0;
  width:2.5rem; height:1px; background:var(--gold);
}

/* --- Los años, abajo a la derecha ---------------------------------------
   Lo pidió el cliente copiando su referencia, y ahí está el equilibrio de la
   pantalla: la firma pesa abajo a la izquierda y esto la contrapesa a la
   misma altura, con la figura al medio. Tres renglones y nada más — el mismo
   "no satures de información" que ya sacó de esta portada el panel de
   trayectoria y la bajada del hero.

   El número va en la didona de exhibición y las dos líneas de apoyo en la
   misma versalita diminuta y espaciada del rol, así que no entra ni una
   familia ni un tamaño nuevo a la pantalla.

   ⚠ PENDIENTE: la cifra es la misma de relleno que las del hero (25 años);
   confirmar con el doctor. Ojo, ahí abajo también se muestra — si el cliente
   ve repetición, lo que sale es la del hero. */
.cover__exp{
  position:absolute; z-index:4;
  right:calc(var(--gut) + clamp(1.25rem,2.5vw,2.5rem));
  bottom:clamp(2.5rem,7vh,4.5rem);
  margin:0; text-align:right;
  display:flex; flex-direction:column; align-items:flex-end; gap:.5rem;
}
.cover__exp span{
  font-size:.625rem; font-weight:500; letter-spacing:.3em; text-transform:uppercase;
  text-indent:.3em;
  color:var(--slate);
}
.cover__exp b{
  font-family:var(--display); font-weight:500; font-variation-settings:'opsz' 44;
  font-size:clamp(1.5rem, 2.6vw, 2.25rem);
  letter-spacing:.06em; text-indent:.06em; line-height:1;
  text-transform:uppercase;
  color:var(--ink);
}

/* Entrada: la portada espera al preloader. Sólo con JS: sin él la clase
   `is-loading` no se quita nunca y esto dejaría la pantalla en blanco. */
html.js .cover__name,
html.js .cover__exp,
html.js .cover__ghost{transition:opacity 1.3s var(--e-out) .5s, transform 1.3s var(--e-out) .5s}
html.js body.is-loading .cover__name,
html.js body.is-loading .cover__exp{opacity:0; transform:translateY(1.25rem)}
html.js body.is-loading .cover__ghost{opacity:0}
html.js body.is-loading .cover__media img{scale:1.04; opacity:0}

/* Móvil y tabletas verticales: la figura se centra y crece, la firma baja al
   pie y el eco se achica para que siga siendo fondo y no un titular partido. */
@media (max-width:860px){
  .cover{height:auto}
  .cover__sticky{position:relative; height:100svh}
  /* Centrar la IMAGEN no centra al doctor: en el archivo recortado su cabeza
     cae al 47,4% del ancho. `left:50%` + `translate:-47.4%` pone esa columna
     en el centro de la pantalla, que es lo que hay que centrar. */
  .cover__media{
    left:50%; translate:-46.8% 0; bottom:0;
    height:clamp(46%, 62svh, 70%);
    filter:drop-shadow(0 18px 34px rgba(20,21,29,.22));
  }
  /* Velo al pie: el torso es traje negro y la firma en tinta no se leería. */
  .cover__sticky::before{
    content:''; position:absolute; z-index:3; inset:auto 0 0 0; height:32%;
    background:linear-gradient(180deg, rgba(18,18,20,0) 0%, rgba(18,18,20,.62) 100%);
    pointer-events:none;
  }
  /* En vertical el renglón no cabe ni de lejos, así que se baja el cuerpo para
     que se lea más nombre y se compensa con más estirado. Con 16vw y 1,22 el
     bloque ocupa lo mismo que a 20vw sin estirar, así que se lee igual pero
     con la letra ancha. Va con valores fijos, sin el cálculo de main.js:
     acá la firma está SOBRE la foto y no debajo del renglón. */
  .cover__ghost{font-size:16vw; top:44%; scale:1.22 2.6}
  .cover__aura{width:190vw; height:190vw; right:-45vw}
  .cover__frame{left:var(--pad); right:var(--pad); border-color:rgba(20,21,29,.12); top:var(--hdr-h)}
  /* La firma cede la mitad derecha para que los años quepan a su lado: en
     vertical no hay sitio para dos bloques anclados a bordes opuestos. */
  .cover__name{left:var(--pad); right:auto; max-width:62%; bottom:clamp(2rem,5vh,3rem)}
  .cover__h1{font-size:clamp(1.4rem,6vw,2rem); color:#F6F3F1}
  /* Cuerpo y tracking más cortos: en la mitad de ancho que le queda al bloque,
     con los valores de escritorio la línea se partía en dos y el separador "·"
     quedaba colgando al final del primer renglón. */
  .cover__role{color:rgba(234,232,232,.74); font-size:.5rem; letter-spacing:.2em}
  .cover__exp{right:var(--pad); bottom:clamp(2rem,5vh,3rem); gap:.35rem}
  .cover__exp span{color:rgba(234,232,232,.74); font-size:.5rem; letter-spacing:.24em}
  .cover__exp b{color:#F6F3F1; font-size:clamp(1.2rem,5vw,1.6rem)}
}

/* ======================================================== HERO =========== */
.hero{
  position:relative; z-index:2; height:125svh;
}
/* El hero entra desde la derecha POR ENCIMA de la portada fija: el margen
   negativo lo solapa con el último tramo del recorrido de la portada, así
   los dos sticky conviven 100svh con el hero ya pegado arriba, y GSAP lo
   desplaza de fuera a dentro (movimiento puramente horizontal, sin subir a
   la vez). El alto crece esos mismos 100svh para conservar el recorrido de
   siempre después de la entrada. Sólo con GSAP, sin reduced-motion y en
   pantallas anchas; en los demás casos fluye normal y sin solape. */
/* Por qué -200svh y no -100svh: un sticky se despega cuando a su contenedor
   le queda menos de una pantalla, o sea en (alto de la portada − 100svh). Con
   -100svh el hero arrancaba justo ahí y la portada se subía mientras el hero
   entraba. Con -200svh la entrada ocurre en la última pantalla en que la
   portada sigue fija, y ésta se despega recién cuando el hero ya la tapó.

   Y por qué 260svh de alto: el sticky se despega en `alto − 100svh`, así que
   de los 160svh clavados los primeros 100 se los lleva la entrada y quedan 60
   de pantalla quieta. Con los 225svh de la primera versión quedaban sólo 25 y
   el hero se iba apenas aterrizaba. */
@media (min-width:861px) and (prefers-reduced-motion:no-preference){
  html.js:not(.no-gsap) .hero{margin-top:-200svh; height:260svh}
}
.hero__sticky{
  position:sticky; top:0; height:100svh;
  padding:var(--hdr-h) var(--pad) var(--pad);
  display:flex; overflow:hidden; isolation:isolate;
}
/* Grano sobre las dos mitades: une el recorte del retrato con el panel y le
   saca el aire de render limpio. feTurbulence en data-URI, sin assets. */
.hero__sticky::after{
  content:''; position:absolute; inset:0; z-index:4; pointer-events:none;
  opacity:.055; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='180' height='180'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/></filter><rect width='180' height='180' filter='url(%23n)'/></svg>");
}
@media (prefers-reduced-motion: reduce){
  .hero__sticky::after{opacity:.03}
}
/* Hueso: la portada ya es clara, así que este panel la continúa y el corte
   con el vídeo es el contraste de la pantalla, no un salto de mundo. */
/* El panel dejó de ser hueso liso el 2026-09-05: el cliente pidió que "la
   parte inicial" no fuera tan blanco y negro, y la portada sola no alcanzaba
   — el hero entra deslizando encima de ella y devolvía la pantalla al blanco.
   Es el MISMO azul rgb(38,54,110) de las sombras de la portada y entra por la
   MISMA esquina (abajo a la izquierda), así que las dos pantallas se leen como
   una sola luz continua mientras una desliza sobre la otra.
   Se queda en .17 y pegado al pie: arriba cruza el banner y ahí el hueso tiene
   que seguir siendo hueso. El h1 en tinta conserva 11:1 de contraste.
   PARA REVERTIR: `background:var(--bone)` a secas. */
.hero__panel{
  position:absolute; inset:0; right:var(--split); z-index:0;
  background:
    radial-gradient(92% 74% at 0% 100%,
      rgba(38,54,110,.17) 0%,
      rgba(38,54,110,.133) 18%,
      rgba(38,54,110,.093) 36%,
      rgba(38,54,110,.056) 54%,
      rgba(38,54,110,.028) 70%,
      rgba(38,54,110,.009) 85%,
      rgba(38,54,110,0)   100%),
    var(--bone);
}
.hero__media{
  position:absolute; inset:0 0 0 var(--split); z-index:1; overflow:hidden;
}
.hero__media img,.hero__media video{width:100%; height:100%; object-fit:cover; object-position:50% 20%}
.hero__media video ~ .hero__poster{position:absolute; inset:0; z-index:-1}
.hero__scrim{
  position:absolute; inset:0;
  background:linear-gradient(180deg,rgba(20,21,29,.35) 0%,rgba(20,21,29,0) 35%,rgba(20,21,29,.62) 100%);
}
/* Bloque de presentación: vive entero en el lado claro. Se abandonó el calco
   partido (mitad tinta / mitad hueso) del h1 — el corte de color dentro de una
   palabra se leía mal. Acá hay un solo color de letra porque el retrato y el
   nombre nunca cruzan el split. */
/* El bloque va alineado a la izquierda y centrado en vertical: sin el retrato
   ni el eco del apellido (los dos los sacó el cliente), un bloque centrado en
   una columna vacía se quedaba flotando sin anclaje. Alineado al mismo margen
   que la firma de la portada, las dos pantallas riman. */
.hero__intro{
  --gut:max(var(--pad), (100% - var(--maxw)) / 2);
  position:absolute; z-index:3;
  left:0; width:var(--split); top:var(--hdr-h); bottom:0;
  display:flex; flex-direction:column;
  align-items:flex-start; justify-content:center;
  padding:0 clamp(1.5rem,3vw,3rem) 0 calc(var(--gut) + clamp(1.25rem,2.5vw,2.5rem));
  text-align:left; pointer-events:none;
}

.hero__h1{
  position:relative; margin:0;
  font-family:var(--serif); font-weight:400;
  font-size:clamp(2.5rem,6.1vw,5.5rem); line-height:.92; letter-spacing:-.024em;
  color:var(--ink);
}
.hero__h1 em{font-style:italic}

/* Única aparición del dorado en el hero: una regla corta que separa el nombre
   del cargo. Suficiente para fijar el acento de marca sin decorar. */
.hero__role{
  margin:clamp(.95rem,2.2vh,1.5rem) 0 0;
  padding-top:clamp(.95rem,2.2vh,1.5rem);
  position:relative;
  font-size:.6875rem; font-weight:500; text-transform:uppercase;
  /* El tracking ancho deja un hueco muerto al final de la línea y descentra
     el bloque; el text-indent del mismo valor lo compensa. */
  letter-spacing:.32em; text-indent:.32em;
  color:var(--slate);
}
.hero__role::before{
  content:''; position:absolute; top:0; left:0;
  width:2.5rem; height:1px; background:var(--gold);
}

/* Las cifras. La versión anterior las encajaba entre filetes verticales y el
   cliente la leyó como "muy cuadrada": la retícula pesaba más que el dato.
   Acá no hay caja — cada cifra cuelga de su propio filete, que **se dibuja**
   de izquierda a derecha al aterrizar el hero, uno detrás de otro. El
   movimiento es el que ordena la fila, no las líneas. */
.hero__stats{
  margin:clamp(1.6rem,4vh,2.75rem) 0 0;
  display:flex; flex-wrap:wrap;
  gap:clamp(1.25rem,3vw,2.75rem);
}
.hero__stat{flex:0 1 auto; min-width:7.5rem}
.hero__rule{
  display:block; height:1px; margin-bottom:clamp(.6rem,1.4vh,.9rem);
  background:currentColor; opacity:.28;
  transform-origin:left center;
}
.hero__num{
  display:flex; align-items:baseline; gap:.08em;
  font-family:var(--display); font-optical-sizing:auto; font-weight:400;
  font-size:clamp(1.5rem,2.3vw,2.15rem); line-height:1; letter-spacing:.005em;
  color:var(--ink);
  /* Sin esto los dígitos cambian de ancho durante el conteo y la fila tiembla. */
  font-variant-numeric:tabular-nums;
}
.hero__num i{font-style:normal; font-size:.5em; color:var(--gold); translate:0 -.72em}
.hero__stat dd{
  margin:.45rem 0 0;
  font-size:.5rem; font-weight:600; letter-spacing:.19em; text-transform:uppercase;
  color:var(--slate); white-space:nowrap;
}

/* El filete se dibuja y la cifra sube, escalonados. Cuelga de `html.js` y de
   la clase que pone el JS: sin JS nada de esto se esconde nunca. */
html.js .hero__rule{scale:0 1; transition:scale 1.1s var(--e-expo)}
html.js .hero__stat .hero__num,
html.js .hero__stat dd{
  opacity:0; transform:translateY(.7rem);
  transition:opacity .9s var(--e-out), transform .9s var(--e-out);
}
html.js .hero__stats.is-in .hero__rule{scale:1 1}
html.js .hero__stats.is-in .hero__num,
html.js .hero__stats.is-in dd{opacity:1; transform:none}
html.js .hero__stat:nth-child(1) .hero__rule{transition-delay:.05s}
html.js .hero__stat:nth-child(2) .hero__rule{transition-delay:.17s}
html.js .hero__stat:nth-child(3) .hero__rule{transition-delay:.29s}
html.js .hero__stat:nth-child(1) .hero__num,
html.js .hero__stat:nth-child(1) dd{transition-delay:.22s}
html.js .hero__stat:nth-child(2) .hero__num,
html.js .hero__stat:nth-child(2) dd{transition-delay:.34s}
html.js .hero__stat:nth-child(3) .hero__num,
html.js .hero__stat:nth-child(3) dd{transition-delay:.46s}
@media (prefers-reduced-motion:reduce){
  html.js .hero__rule{scale:1 1}
  html.js .hero__stat .hero__num,
  html.js .hero__stat dd{opacity:1; transform:none}
}

.hero__based{
  position:absolute; z-index:3; right:var(--pad); bottom:var(--pad);
  text-align:right; font-size:.8125rem; line-height:1.5; max-width:16ch;
  color:var(--bone);
}
.hero__based span{display:block; color:var(--bone-40); margin-bottom:.4rem}
.hero__based__link{
  display:inline; font-weight:600; white-space:pre-line;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat; background-position:0 100%; background-size:0% 1px;
  transition:background-size .4s var(--e-out);
}
.hero__based__link:hover,
.hero__based__link:focus-visible{background-size:100% 1px}


/* ======================================================== MI HISTORIA ==== */
/* 4 etapas. A 62svh por etapa el paso se siente cómodo sin obligar a scrollear
   cuatro pantallas completas para leer un párrafo. */
.story{background:var(--ink-deep); color:var(--bone); position:relative; height:250svh}
.story__sticky{
  position:sticky; top:0; height:100svh; overflow:hidden;
  /* La foto ya no se lleva media pantalla: el texto es lo que se viene a leer. */
  display:grid; grid-template-columns:minmax(0,.8fr) minmax(0,1.2fr);
  gap:clamp(1.5rem,4vw,4.5rem);
  /* La foto deja de ir a sangre: respira por los cuatro lados. */
  padding:calc(var(--hdr-h) + clamp(1rem,2.5vh,2rem)) var(--pad) clamp(1.5rem,4vh,3rem);
}
.story__media{
  position:relative; overflow:hidden; background:var(--ink-2);
  border-radius:3px; box-shadow:0 0 0 1px var(--bone-15);
}
/* Entrada: la foto se descubre desde el borde derecho mientras la sección
   sube. El barrido va en `clip-path` del contenedor y NO en la imagen, porque
   .story__photo ya tiene su propio `transform` en bucle (storyKen) y dos
   sistemas sobre la misma propiedad se pisan.
   Cuelga de `html.js` a propósito: si escondiera desde la hoja base, un fallo
   de JS dejaría la foto tapada para siempre. */
html.js .story__media{clip-path:inset(0 0 0 100%)}
html.no-gsap .story__media{clip-path:none}
@media (prefers-reduced-motion:reduce){
  html.js .story__media{clip-path:none}
}
.story__photo{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  object-position:50% 15%; opacity:0; transition:opacity 1.2s var(--e-expo);
  animation:storyKen 22s ease-in-out infinite alternate;
}
.story__photo.is-on{opacity:1}
@keyframes storyKen{from{transform:scale(1)}to{transform:scale(1.08)}}
.story__scrim{
  position:absolute; inset:0;
  background:linear-gradient(0deg,rgba(20,21,29,.92) 0%,rgba(20,21,29,.05) 42%,rgba(20,21,29,.3) 100%);
}
.story__h{
  position:absolute; left:clamp(1.5rem,4vw,3.5rem); bottom:clamp(6.5rem,15vh,9.5rem); z-index:2;
  display:grid; max-width:19rem;
}
.story__hi{
  grid-area:1/1;
  font-family:var(--serif); font-weight:400; font-style:italic; letter-spacing:-.02em;
  font-size:clamp(2.25rem,1.4rem + 3.6vw,4.75rem); line-height:.98;
  opacity:0; transform:translateY(10px);
  transition:opacity .6s var(--e-out), transform .6s var(--e-out);
}
.story__hi.is-on{opacity:1; transform:none}
.story__index{
  position:absolute; left:clamp(1.5rem,4vw,3.5rem); bottom:clamp(2rem,5vh,3rem); z-index:2;
  display:grid; gap:.4rem; font-size:.8125rem;
}
.story__index li{color:var(--bone-40); transition:color .4s var(--e-out)}
.story__index li.is-on{color:var(--bone); font-weight:500}
.story__panel{
  position:relative; align-self:center; justify-self:center;
  display:grid; justify-items:center; text-align:center;
  gap:clamp(1.75rem,3.5vh,2.75rem); max-width:44rem;
}
.story .eyebrow{color:var(--bone-40)}
.story__stages{display:grid}
.story__stage{
  grid-area:1/1; display:grid; gap:clamp(1.15rem,2.2vh,1.85rem);
  align-content:start; justify-items:center;
  opacity:0; visibility:hidden; transform:translateY(14px);
  transition:opacity .55s var(--e-out), transform .55s var(--e-out), visibility 0s .55s;
}
.story__stage.is-on{opacity:1; visibility:visible; transform:none; transition-delay:0s,0s,0s}

/* Los párrafos entran escalonados, no todos de golpe. */
.story__stage > *{
  opacity:0; transform:translateY(14px);
  transition:opacity .65s var(--e-out), transform .65s var(--e-out);
}
.story__stage.is-on > *{opacity:1; transform:none}
.story__stage.is-on > :nth-child(1){transition-delay:.10s}
.story__stage.is-on > :nth-child(2){transition-delay:.20s}
.story__stage.is-on > :nth-child(3){transition-delay:.30s}
html.no-js .story__stage > *{opacity:1; transform:none}

/* ---- Riel de progreso: marca en qué etapa va el relato ---- */
.story__prog{
  display:flex; gap:.55rem; align-items:center;
  padding-top:clamp(.5rem,1.5vh,1rem);
}
.story__prog span{
  position:relative; display:block; width:clamp(1.75rem,3.5vw,3.25rem); height:2px;
  background:var(--bone-15); overflow:hidden; border-radius:2px;
}
.story__prog span::after{
  content:''; position:absolute; inset:0;
  background:linear-gradient(90deg,#b38b61,#dac8b7);
  transform:scaleX(0); transform-origin:left;
  transition:transform .55s var(--e-out);
}
/* Las ya vistas quedan llenas; la activa se llena al entrar. */
.story__prog span.is-done::after{transform:scaleX(1)}
.story__prog span.is-on::after{transform:scaleX(1); transition-duration:.8s}
/* Y por la barra activa corre una luz. Es el gesto que el usuario señaló como
   el que sí le gusta ("ese banner que se va moviendo") y que pidió repetir
   como motivo — el filete del eyebrow usa el mismo recurso. Va en ::before
   porque ::after ya lo ocupa el relleno, y la barra padre tiene overflow
   hidden, así que la luz se recorta sola en los extremos. */
.story__prog span::before{
  content:''; position:absolute; inset:0; z-index:1; opacity:0;
  background:linear-gradient(90deg,
    transparent 0%, rgba(255,248,236,.9) 50%, transparent 100%);
}
.story__prog span.is-on::before{
  opacity:1; animation:rielLuz 2.8s var(--e-io) .5s infinite;
}
@keyframes rielLuz{
  0%  {transform:translateX(-115%)}
  60% {transform:translateX(115%)}
  100%{transform:translateX(115%)}
}
/* Es la declaración personal del doctor y ocupa una sección entera: con cuerpo
   de párrafo normal quedaba flotando en el vacío. */
.story__stage p{
  color:var(--beige); font-size:clamp(1.15rem,.95rem + .95vw,1.85rem);
  line-height:1.45; max-width:46ch; text-wrap:pretty;
}
.story__stage b{color:var(--bone); font-weight:500}
.story__stage .btn--line{color:var(--bone); border-color:var(--bone-40); justify-self:center}
.story__stage .btn--line:hover{border-color:var(--bone)}

/* ======================================================== SERVICIOS ====== */
.srv{
  background:var(--ink); color:var(--bone);
  padding:var(--sec-y-lg) var(--pad);
}
.srv__head{margin-bottom:clamp(2.25rem,5vw,3.5rem)}

.srv__grid{
  display:grid; grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(1.5rem,4vw,3rem);
}

/* Tarjeta apaisada: foto a sangre, número + título sobre un velo, y un banner
   que entra desde el borde derecho con el detalle del procedimiento. Comparte
   alto con el acordeón de instrumental para que las dos secciones oscuras
   rimen. JS controla la clase (no sólo :hover) para que foco y toque
   respondan igual. */
.srv__card{
  position:relative; overflow:hidden; isolation:isolate;
  border-radius:1rem; height:clamp(20rem,52vh,30rem); cursor:pointer;
  box-shadow:inset 0 0 0 1px var(--bone-15);
}
.srv__photo{position:absolute; inset:0; margin:0; background:var(--ink-2)}
.srv__photo picture{display:contents}
.srv__photo img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  filter:grayscale(1) brightness(.66); transform:scale(1.03);
  transition:filter .8s var(--e-out), transform 1.1s var(--e-expo);
}
.srv__card.is-open .srv__photo img{filter:grayscale(0) brightness(.9); transform:scale(1.07)}

.srv__veil{
  position:absolute; inset:0; z-index:1; pointer-events:none;
  background:linear-gradient(0deg,rgba(20,21,29,.9) 0%,rgba(20,21,29,.22) 52%,rgba(20,21,29,0) 78%);
}
.srv__n{
  position:absolute; top:1.25rem; left:1.4rem; z-index:2;
  font-family:var(--serif); font-size:1.1rem; color:var(--bone-40);
  font-variant-numeric:tabular-nums; transition:color .6s var(--e-out);
}
.srv__card.is-open .srv__n{color:var(--gold)}
/* El título se queda: es la etiqueta que el visitante está apuntando. Sólo
   sube un poco para ceder el aire al pie de la tarjeta. */
.srv__title{
  position:absolute; z-index:2; left:1.4rem; bottom:1.4rem; max-width:19ch;
  font-family:var(--serif); font-weight:400; letter-spacing:-.015em;
  font-size:clamp(1.3rem,.95rem + 1vw,1.85rem); line-height:1.08; color:var(--bone);
  transition:transform .7s var(--e-expo);
}
.srv__card.is-open .srv__title{transform:translateY(-.35rem)}

/* Pista de que la tarjeta hace algo: aparece en reposo y se retira al abrir. */
.srv__cue{
  position:absolute; z-index:2; right:1.4rem; bottom:1.6rem;
  display:flex; align-items:center; gap:.55rem;
  font-size:.6875rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--bone-40); white-space:nowrap;
  transition:opacity .45s var(--e-out), transform .55s var(--e-expo);
}
.srv__cue::after{
  content:''; width:1.75rem; height:1px; background:currentColor; opacity:.6;
}
.srv__card.is-open .srv__cue{opacity:0; transform:translateX(1rem)}

/* Banner lateral: entra desde el borde derecho con el detalle del
   procedimiento y el llamado a escribir. */
.srv__flyout{
  position:absolute; z-index:3; inset:0 0 0 auto;
  width:min(64%,25rem);
  display:grid; align-content:center; gap:clamp(.85rem,1.6vh,1.15rem);
  padding:clamp(1.25rem,2.2vw,1.9rem);
  /* El texto arranca pasado el degradado del vidrio: si cae dentro de la
     zona translúcida queda leyéndose sobre la foto y pierde contraste. */
  padding-left:clamp(4.5rem,22%,6rem);
  transform:translateX(101%);
  transition:transform .8s var(--e-expo);
}
.srv__card.is-open .srv__flyout{transform:translateX(0)}

/* El vidrio va en una capa aparte y enmascarado: el desenfoque nace difuso
   en el borde izquierdo y se cierra hacia la derecha, así no queda la costura
   dura que dejaba un panel con borde. */
.srv__flyout::before{
  content:''; position:absolute; inset:0; z-index:-1;
  backdrop-filter:blur(22px) saturate(125%);
  -webkit-backdrop-filter:blur(22px) saturate(125%);
  background:linear-gradient(100deg,
    rgba(20,21,29,.12) 0%,rgba(20,21,29,.84) 24%,rgba(20,21,29,.96) 100%);
  -webkit-mask-image:linear-gradient(90deg,transparent 0%,#000 20%);
  mask-image:linear-gradient(90deg,transparent 0%,#000 20%);
}
/* Un roce cálido en el filo por donde entra el panel. */
.srv__flyout::after{
  content:''; position:absolute; inset:0; z-index:-1; pointer-events:none;
  background:radial-gradient(70% 100% at 0% 50%,rgba(179,140,97,.16) 0%,rgba(179,140,97,0) 70%);
  opacity:0; transition:opacity .7s var(--e-out) .1s;
}
.srv__card.is-open .srv__flyout::after{opacity:1}

/* El contenido entra escalonado detrás del panel. El ocultado va colgado de
   `html.js` a propósito: si esconde desde la hoja base, cualquier fallo del
   JS deja el texto en opacity:0 para siempre. Escondiendo sólo cuando hay JS
   confirmado, el caso sin JS no necesita ninguna regla que lo deshaga. */
html.js .srv__flyout > *{
  opacity:0; transform:translateX(1.1rem);
  transition:opacity .5s var(--e-out), transform .6s var(--e-expo);
}
.srv__card.is-open .srv__flyout > *{opacity:1; transform:none}
.srv__card.is-open .srv__flyout > :nth-child(1){transition-delay:.16s}
.srv__card.is-open .srv__flyout > :nth-child(2){transition-delay:.22s}
.srv__card.is-open .srv__flyout > :nth-child(3){transition-delay:.28s}
.srv__card.is-open .srv__flyout > :nth-child(4){transition-delay:.34s}

.srv__lead{
  color:var(--bone); font-size:clamp(.9rem,.86rem + .2vw,1rem);
  line-height:1.5; text-wrap:pretty;
}
.srv__list{display:grid; gap:.45rem}
.srv__list li{
  position:relative; padding-left:1.15rem;
  font-size:.875rem; line-height:1.4; color:var(--beige);
}
.srv__list li::before{
  content:''; position:absolute; left:0; top:.62em;
  width:.45rem; height:1px; background:var(--gold);
}
.srv__meta{
  display:flex; flex-wrap:wrap; gap:.4rem 1.75rem;
  padding-top:.85rem; border-top:1px solid var(--bone-15);
}
.srv__meta dt{
  font-size:.625rem; letter-spacing:.14em; text-transform:uppercase;
  color:var(--bone-40); margin-bottom:.2rem;
}
.srv__meta dd{font-size:.875rem; color:var(--bone); font-variant-numeric:tabular-nums}
.srv__flyout .btn--pill{
  justify-self:start; padding:.85rem 1.35rem; font-size:.875rem;
  background:var(--bone); color:var(--ink);
}
.srv__flyout .btn--pill:hover{background:#fff}

/* Sin JS: la tarjeta se abre en vertical y todo queda a la vista. */
html.no-js .srv__card{height:auto; cursor:default}
html.no-js .srv__photo{position:relative; aspect-ratio:16/10}
html.no-js .srv__photo img{filter:grayscale(0) brightness(.85); transform:none}
html.no-js .srv__veil,
html.no-js .srv__cue{display:none}
html.no-js .srv__n,
html.no-js .srv__title{position:static; margin:0 1.4rem}
html.no-js .srv__n{margin-top:1.25rem; display:block}
html.no-js .srv__flyout{
  position:static; width:auto; transform:none; padding:1rem 1.4rem 1.6rem;
  padding-left:1.4rem;
}
html.no-js .srv__flyout::before,
html.no-js .srv__flyout::after{display:none}

/* ======================================================== FORMACIÓN ====== */
.edu{
  background:var(--sand); color:var(--ink);
  padding:var(--sec-y-lg) var(--pad);
}
.edu__head{display:grid; gap:clamp(1.25rem,3vw,2.25rem); margin-bottom:clamp(2.25rem,5vw,3.5rem)}

.edu__groups{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(15rem,1fr));
  gap:clamp(2.5rem,5vw,4rem) clamp(1.75rem,3.5vw,3rem);
}
.edu__group{display:grid; gap:1.25rem; align-content:start}
.edu__label{
  font-size:.6875rem; letter-spacing:.14em; text-transform:uppercase;
  font-weight:600; color:var(--gold);
  padding-bottom:.9rem; border-bottom:1px solid var(--ink-10);
}
.edu__list{display:grid; gap:1.35rem}
.edu__list li{display:grid; gap:.3rem}
.edu__list b{
  font-weight:500; font-size:clamp(1rem,.95rem + .3vw,1.15rem);
  line-height:1.3; text-wrap:pretty;
}
.edu__list b i{font-style:italic; font-weight:400; color:var(--slate)}
.edu__list span{font-size:.875rem; color:var(--slate)}

/* ======================================================== CASOS ========== */
/* Comparador antes/después. El lado izquierdo es el ANTES: al mover el cursor
   a la derecha crece esa zona (se ve más el antes) y al ir a la izquierda
   aparece el después, tal como se pidió.
   ⚠ Las dos fotos tienen el mismo ratio (1,30) pero distinto encuadre: en el
   "antes" la boca ocupa menos cuadro. Estas dos variables corrigen esa
   diferencia — si la alineación no calza exacta, se ajustan sólo acá. */
.cases{
  background:var(--ink); color:var(--bone);
  padding:var(--sec-y-lg) var(--pad);
  display:grid; justify-items:center; text-align:center;
  gap:clamp(2.25rem,5vh,3.5rem);
}
.cases .eyebrow{color:var(--bone-40); justify-content:center}

.cases__head{display:grid; justify-items:center; gap:clamp(1rem,2.5vh,1.6rem); max-width:46rem}
.cases__lead{color:var(--beige); max-width:52ch; text-wrap:pretty}

/* Los casos van en fila, hasta tres por línea. flex-wrap + justify-content
   centrado para que con dos casos queden centrados y no cojos a la izquierda,
   como pasaría con un grid de 3 columnas fijas. */
.cases__grid{
  display:flex; flex-wrap:wrap; justify-content:center; align-items:start;
  gap:clamp(1.5rem,3vw,2.5rem);
  width:100%; max-width:82rem; margin-inline:auto;
}
.cases__fig{
  margin:0; display:grid; justify-items:center; gap:.9rem;
  flex:0 1 25rem; max-width:25rem; min-width:16rem;
}

.cases__compare{
  /* Fotos reales entregadas por el cliente, ya montadas por él sobre lienzo
     negro y ya alineadas entre sí; acá sólo se recortan con una ventana común
     al par y se les difumina el borde. Se exportan a 900x643, o sea a este
     mismo 7/5, así que el `cover` no recorta nada — importa, porque cualquier
     recorte distinto entre el antes y el después desalinea los dientes. */
  /* Esquina un poco redondeada y nada más: el cliente rechazó cualquier
     tratamiento sobre el borde de la foto ("ese iluminado barato, como si
     fuera una sombra horrible"). Fondo negro liso, borde duro. */
  position:relative; width:100%; aspect-ratio:7/5;
  overflow:hidden; border-radius:10px; box-shadow:0 0 0 1px var(--bone-15);
  background:var(--ink-2); cursor:ew-resize; touch-action:pan-y;
  isolation:isolate;
}
.cases__img{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; object-position:50% 50%; user-select:none; -webkit-user-drag:none;
}
/* La franja del antes crece desde la izquierda hasta donde está el cursor. */
.cases__clip{
  position:absolute; inset:0; overflow:hidden;
  clip-path:inset(0 calc(100% - var(--split)) 0 0);
  will-change:clip-path;
}

.cases__tag{
  position:absolute; bottom:.6rem; z-index:3;
  font-size:.625rem; letter-spacing:.12em; text-transform:uppercase; font-weight:600;
  padding:.3rem .55rem; border-radius:2px;
  background:rgba(20,21,29,.62); backdrop-filter:blur(6px); color:var(--bone);
  opacity:.9; pointer-events:none;
}
.cases__tag--before{left:.6rem}
.cases__tag--after{right:.6rem}

.cases__handle{
  position:absolute; top:0; bottom:0; left:var(--split); z-index:4;
  translate:-50% 0; width:2.75rem; display:grid; place-items:center;
  pointer-events:none;
}
.cases__line{width:1px; height:100%; background:var(--bone); opacity:.85; position:absolute}
.cases__grip{
  position:relative; width:2.5rem; height:2.5rem; border-radius:50%;
  display:grid; place-items:center; color:var(--ink);
  background:var(--bone); box-shadow:0 2px 14px rgba(0,0,0,.35);
  transition:transform .35s var(--e-out);
}
.cases__grip svg{width:1rem; height:.7rem}
.cases__compare:hover .cases__grip{transform:scale(1.08)}

/* El range es el control accesible real: invisible pero enfocable con teclado. */
.cases__range{
  position:absolute; inset:0; width:100%; height:100%; margin:0;
  opacity:0; cursor:ew-resize; z-index:5;
}
.cases__range:focus-visible ~ .cases__handle .cases__grip{
  outline:2px solid var(--gold); outline-offset:3px;
}
.cases__cap{font-size:.8125rem; color:var(--bone-40)}

@media (prefers-reduced-motion: reduce){
  .cases__grip{transition:none}
}

/* ======================================================== RESEÑAS ======== */
.rev{background:var(--bone); padding:var(--sec-y-lg) 0}
.rev__head{
  padding:0 var(--pad); margin-bottom:clamp(2.5rem,6vw,4rem);
  display:flex; justify-content:space-between; align-items:baseline; gap:1rem; flex-wrap:wrap;
}
.rev__count{font-size:.875rem; color:var(--slate); font-variant-numeric:tabular-nums}
.rev__count b{font-weight:500; color:var(--ink)}
/* El desfile va a sangre y se desvanece en los bordes, así no se ve el corte. */
.rev__viewport{
  overflow:hidden; padding-inline:0;
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 5%,#000 95%,transparent 100%);
          mask-image:linear-gradient(90deg,transparent 0,#000 5%,#000 95%,transparent 100%);
}
/* La pista lleva la lista duplicada y se desplaza exactamente media pista:
   ahí la copia 2 cae justo donde estaba la copia 1 y el salto es invisible.
   Por eso la separación va en margin-right y NO en gap — con gap el 50% no
   coincide con el inicio de la segunda copia. */
.rev__track{
  display:flex; align-items:stretch; width:max-content;
  padding-block:.5rem;   /* aire para que el realce no se recorte */
  animation:revLoop var(--rev-dur,60s) linear infinite;
  will-change:transform;
}
@keyframes revLoop{
  from{transform:translate3d(0,0,0)}
  to  {transform:translate3d(-50%,0,0)}
}
/* Se detiene al pasar el cursor, al enfocar con teclado o con el botón. */
.rev__viewport:hover .rev__track,
.rev__viewport:focus-within .rev__track,
.rev.is-paused .rev__track{animation-play-state:paused}
/* Tarjetas verticales de alto igual: cita arriba, firma abajo. */
.rev__card{
  flex:0 0 min(23rem,80vw);
  margin-right:clamp(1rem,2.5vw,1.75rem);
  position:relative; overflow:hidden; border-radius:4px;
  background:#F1EFEF; box-shadow:inset 0 0 0 1px var(--ink-10);
  padding:clamp(1.4rem,2.6vw,2rem);
  min-height:clamp(19rem,32vh,24rem);
  display:grid; grid-template-rows:auto 1fr auto; gap:clamp(.9rem,2vw,1.35rem);
  transition:transform .55s var(--e-out), box-shadow .55s var(--e-out);
}
.rev__card > *{position:relative; z-index:1}

.rev__card:hover,
.rev__card:focus-within{transform:translateY(-6px); box-shadow:inset 0 0 0 1px var(--ink-10), 0 14px 34px rgba(20,21,29,.13)}

/* ---- Líquido -----------------------------------------------------------
   En reposo queda un dedo de agua abajo; al pasar el cursor sube de golpe,
   se pasa de largo y vuelve a asentarse — ese rebote no es una animación
   aparte, sale de la curva de la transición: el tramo >1 de la bezier hace
   que la altura sobrepase el destino antes de volver. Al salir baja más
   rápido de lo que subió (regla de oro del motion: la salida ~60-70% de la
   entrada, si no se siente pesada). */
.rev__liquid{
  position:absolute; left:0; right:0; bottom:0; z-index:0; pointer-events:none;
  height:9%;
  transition:height .62s var(--e-out);
}
.rev__card:hover .rev__liquid,
.rev__card:focus-within .rev__liquid{
  height:66%;
  transition:height 1.05s cubic-bezier(.18,1.42,.36,1);
}

/* Sin forma de onda dibujada: la superficie es el borde superior redondeado
   de cada capa. Dos capas con radios y tiempos distintos, desfasadas, dan la
   ondulación sin que aparezca ninguna línea dura. El degradado se apaga a
   transparente arriba para que no quede un canto recto contra la tarjeta. */
.rev__fill{
  position:absolute; left:-12%; right:-12%; top:0; bottom:0;
  border-radius:46% 54% 0 0 / 26% 22% 0 0;
  background:linear-gradient(0deg,
    #a87c4e 0%,
    #b38c61 26%,
    rgba(203,176,148,.9) 58%,
    rgba(226,215,200,.5) 84%,
    rgba(240,234,224,0) 100%);
  animation:revFlota 6s ease-in-out infinite;
  animation-play-state:paused;
}
.rev__fill--b{
  border-radius:56% 44% 0 0 / 20% 28% 0 0;
  opacity:.5;
  animation-duration:9.5s; animation-direction:reverse;
}
.rev__card:hover .rev__fill,
.rev__card:focus-within .rev__fill{animation-play-state:running}

/* Balanceo suave: sube y baja unos píxeles mientras se inclina apenas de un
   lado a otro. Es lo que mantiene el agua "viva" una vez asentada. */
@keyframes revFlota{
  0%,100%{transform:translate(-1.5%,2px) scaleY(.99)}
  50%    {transform:translate(1.5%,-3px) scaleY(1.02)}
}

.rev__stars{
  display:flex; gap:.25rem; color:var(--gold);
  transition:filter .5s var(--e-out), transform .5s var(--e-out);
}
.rev__stars svg{width:.8rem; height:.8rem; fill:currentColor}
/* Se quedan doradas y se encienden. Antes viraban a tinta (casi negras),
   que sobre el dorado del agua no tenía sentido. */
.rev__card:hover .rev__stars,
.rev__card:focus-within .rev__stars{
  transform:scale(1.06);
  filter:drop-shadow(0 0 3px rgba(179,140,97,.9))
         drop-shadow(0 0 9px rgba(255,246,230,.75));
}

.rev__card blockquote{
  margin:0; font-family:var(--serif); letter-spacing:-.01em;
  font-size:clamp(1.05rem,.9rem + .55vw,1.35rem); line-height:1.35; text-wrap:pretty;
  align-self:start;
}
.rev__card footer{
  font-size:.8125rem; color:var(--slate);
  padding-top:1.1rem; border-top:1px solid var(--ink-10);
  transition:color .5s var(--e-out), border-color .5s var(--e-out);
}
/* Sobre el dorado el texto se oscurece para no perder legibilidad. */
.rev__card:hover footer,
.rev__card:focus-within footer{color:var(--ink); border-color:rgba(20,21,29,.22)}

@media (prefers-reduced-motion: reduce){
  .rev__card{transition:none}
  .rev__card:hover{transform:none}
  /* El agua se queda quieta: ni rebote al subir ni balanceo. Sigue
     cambiando de nivel con el cursor, pero sin movimiento propio. */
  .rev__liquid{transition:none}
  .rev__card:hover .rev__liquid,
  .rev__card:focus-within .rev__liquid{transition:none}
  .rev__fill{animation:none}
  .rev__card:hover .rev__fill,
  .rev__card:focus-within .rev__fill{animation:none}
  .rev__stars{transition:none}
}
.rev__nav{
  display:flex; gap:1.25rem; align-items:center; flex-wrap:wrap;
  padding:clamp(2rem,4vw,3rem) var(--pad) 0;
}
.rev__toggle{
  display:inline-flex; align-items:center; gap:.6rem;
  padding:.6rem 1.1rem .6rem .85rem; border-radius:62rem;
  border:1px solid var(--ink-10); color:var(--ink); font-size:.8125rem;
  transition:border-color .35s var(--e-out), background .35s var(--e-out);
}
.rev__toggle:hover{border-color:var(--ink); background:rgba(20,21,29,.04)}
.rev__toggleIcon{display:grid; place-items:center; width:.9rem; height:.9rem}
.rev__toggleIcon svg{grid-area:1/1; width:100%; height:100%}
.rev__toggle .ic-play{display:none}
.rev__toggle[aria-pressed="true"] .ic-pause{display:none}
.rev__toggle[aria-pressed="true"] .ic-play{display:block}
.rev__hint{font-size:.8125rem; color:var(--slate)}

/* Sin animación para quien la desactiva: la lista queda quieta y se puede
   recorrer con scroll lateral normal. */
@media (prefers-reduced-motion: reduce){
  .rev__track{animation:none; width:auto; overflow-x:auto}
  .rev__viewport{overflow-x:auto; -webkit-mask-image:none; mask-image:none}
  .rev__nav .rev__toggle{display:none}
}

/* ======================================================== INSTRUMENTAL ===
   Nombres de categoría genéricos (no marcas específicas sin confirmar).
   ⚠ PENDIENTE: confirmar equipos reales y reemplazar fotos stock si aplica. */
/* Acordeón horizontal: las fotos van en tiras estrechas y la que recibe el
   cursor se despliega mostrando nombre y descripción. Ya no secuestra el
   scroll (antes eran 275svh); es una sección normal. */
.equip{
  position:relative; isolation:isolate;
  background:var(--ink-deep); color:var(--bone);
  padding:var(--sec-y-lg) var(--pad);
  display:grid; justify-items:center; gap:clamp(2.25rem,5vh,3.5rem);
}
/* Fondo: halo cálido muy tenue para que las tiras no floten sobre negro plano. */
.equip::before{
  content:''; position:absolute; inset:0; z-index:-1; pointer-events:none;
  /* Bajado el 2026-09-05 (.16 → .10 y .05 → .035): este halo nace en el BORDE
     SUPERIOR, o sea en medio del ancho de la pantalla, y era el que más
     contribuía al fondo lavado que el cliente describió como harina. */
  background:
    radial-gradient(120% 60% at 50% 0%,rgba(179,139,97,.10) 0%,rgba(179,139,97,0) 60%),
    radial-gradient(80% 50% at 50% 100%,rgba(234,232,232,.035) 0%,rgba(234,232,232,0) 70%);
}
.equip .eyebrow{color:var(--bone-40); justify-content:center}

.equip__head{display:grid; justify-items:center; text-align:center; gap:clamp(1rem,2.5vh,1.6rem); max-width:48rem}
.equip__lead{color:var(--beige); max-width:54ch; text-wrap:pretty}

/* ---- El acordeón ---- */
.equip__rack{
  display:flex; width:100%; gap:.5rem;
  height:clamp(20rem,52vh,30rem);
}
.equip__panel{
  position:relative; overflow:hidden; border-radius:4px;
  flex:1 1 0; min-width:0; cursor:pointer;
  box-shadow:inset 0 0 0 1px var(--bone-15);
  transition:flex-grow .75s var(--e-expo);
}
/* La tira activa se lleva ~3,4 veces el espacio de una en reposo. */
.equip__panel.is-on{flex-grow:3.4}
.equip__panel img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  filter:grayscale(1) brightness(.62);
  transform:scale(1.04);
  transition:filter .8s var(--e-out), transform .9s var(--e-expo);
}
.equip__panel.is-on img{filter:grayscale(0) brightness(.85); transform:scale(1)}

/* El difuminado que apoya el texto sobre la foto. */
.equip__veil{
  position:absolute; inset:0;
  background:linear-gradient(0deg,rgba(20,21,29,.92) 0%,rgba(20,21,29,.45) 38%,rgba(20,21,29,.12) 70%);
  opacity:.75; transition:opacity .7s var(--e-out);
}
.equip__panel.is-on .equip__veil{opacity:1}

.equip__idx{
  position:absolute; top:1rem; left:1rem; z-index:2;
  font-family:var(--serif); font-size:1.1rem; color:var(--bone-40);
  font-variant-numeric:tabular-nums; transition:color .6s var(--e-out);
}
.equip__panel.is-on .equip__idx{color:var(--gold)}

.equip__meta{
  position:absolute; z-index:2; left:0; right:0; bottom:0;
  padding:clamp(1rem,2vw,1.6rem);
  display:grid; gap:.5rem;
}
.equip__meta h3{
  font-family:var(--serif); font-weight:400; letter-spacing:-.015em;
  font-size:clamp(1.15rem,.85rem + 1.05vw,1.95rem); line-height:1.08;
  white-space:nowrap;
  /* En reposo no se ve nada: con opacidad parcial + nowrap el título quedaba
     cortado a la mitad y se montaba sobre el número. Mejor sin asomo: sólo
     el número queda visible hasta que la tira se abre. */
  opacity:0; transition:opacity .5s var(--e-out), transform .7s var(--e-expo);
  transform:translateY(.35rem);
}
.equip__panel.is-on .equip__meta h3{opacity:1; transform:none; white-space:normal}
.equip__meta p{
  font-size:.9375rem; color:var(--beige); line-height:1.5; max-width:40ch;
  opacity:0; transform:translateY(.75rem);
  transition:opacity .55s var(--e-out) .12s, transform .6s var(--e-out) .12s;
}
.equip__panel.is-on .equip__meta p{opacity:1; transform:none}

/* Sin JS ni con movimiento reducido: todo abierto y legible. */
html.no-js .equip__panel,
html.no-js .equip__panel.is-on{flex-grow:1}
@media (prefers-reduced-motion: reduce){
  .equip__panel,.equip__panel img,.equip__meta h3,.equip__meta p{transition:none}
}

/* ======================================================== CTA ============ */
.cta{
  position:relative; isolation:isolate; overflow:hidden;
  background:var(--ink); color:var(--bone);
  padding:var(--sec-y-lg) var(--pad);
  display:grid; justify-items:center; text-align:center; gap:clamp(1.5rem,4vw,2.5rem);
}
.cta__bg{position:absolute; inset:0; z-index:-1}
.cta__bg img{width:100%; height:100%; object-fit:cover; opacity:.22; filter:grayscale(1)}
.cta__bg::after{
  content:''; position:absolute; inset:0;
  background:radial-gradient(60% 60% at 50% 45%,rgba(20,21,29,.35) 0%,rgba(20,21,29,.92) 100%);
}
.cta__h{
  font-family:var(--serif); font-weight:400;
  font-size:clamp(2.25rem,1.2rem + 4.6vw,5rem); line-height:.95; letter-spacing:-.025em;
}
.cta__lead{max-width:44ch; color:var(--beige); text-wrap:pretty}
.cta__info{
  display:grid; grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));
  gap:clamp(1.5rem,4vw,3rem); width:100%; text-align:left;
  margin-top:clamp(2rem,6vw,4rem); padding-top:clamp(1.5rem,4vw,2.5rem);
  border-top:1px solid var(--bone-15);
}
.cta__info dt{
  font-size:.6875rem; letter-spacing:.12em; text-transform:uppercase;
  color:var(--bone-40); margin-bottom:.6rem;
}
.cta__info dd{margin:0; font-size:.9375rem; line-height:1.5}
.cta__info a{border-bottom:1px solid var(--bone-15); transition:border-color .35s}
.cta__info a:hover{border-color:var(--bone)}

/* ======================================================== FOOTER ========= */
.ft{
  background:var(--ink-deep); color:var(--bone); padding:0 var(--pad) clamp(2rem,5vw,3rem);
  display:grid; gap:clamp(2rem,5vw,3rem);
}
.ft__name{
  font-family:var(--serif); font-size:clamp(2.5rem,1rem + 7.5vw,8rem);
  line-height:.9; letter-spacing:-.03em; color:var(--bone-15);
  border-top:1px solid var(--bone-15); padding-top:clamp(1.5rem,4vw,2.5rem);
}
.ft__nav{display:flex; flex-wrap:wrap; gap:1rem clamp(1.5rem,4vw,3rem); font-size:.875rem}
/* Última fila del pie: los datos legales a la izquierda y el crédito de
   autoría a la derecha. En pantallas angostas el flex-wrap los apila y el
   crédito vuelve a alinearse a la izquierda (ver RESPONSIVE). */
.ft__base{
  display:flex; flex-wrap:wrap; justify-content:space-between; align-items:flex-end;
  gap:1rem clamp(1.5rem,4vw,3rem);
}
.ft__legal{margin:0; font-size:.75rem; color:var(--bone-40); line-height:1.7}
.ft__credit{
  margin:0; font-size:.75rem; color:var(--bone-40); line-height:1.7; text-align:right;
}
.ft__credit b{font-weight:500; color:rgba(234,232,232,.62)}
.ft__credit a{
  color:inherit;
  background-image:linear-gradient(currentColor,currentColor);
  background-repeat:no-repeat; background-position:0 100%; background-size:0 1px;
  transition:background-size .45s var(--e-out), color .45s var(--e-out);
}
.ft__credit a:hover,.ft__credit a:focus-visible{
  color:var(--bone); background-size:100% 1px;
}

/* ================================================ ANCHO DE CONTENIDO ===== */
/* El contenido se centra y deja margen a los bordes en pantallas anchas.
   Va después de las secciones a propósito: sus reglas usan el shorthand
   `padding`, así que este `padding-inline` tiene que llegar después para
   ganar en la cascada. El hero queda fuera: usa posicionamiento absoluto
   sobre el split bone/ink y debe ocupar el ancho completo. */
.hdr,.srv,.edu,.cta,.ft,.equip,.cases,
.rev__head,.rev__nav{
  padding-inline:max(var(--pad), (100% - var(--maxw)) / 2);
}
/* El desfile de reseñas queda fuera: va a sangre a propósito. */
/* .story__sticky queda fuera: su foto va a sangre hasta el borde. */

/* Pantallas de poca altura (laptops chicos, ventana no maximizada): el header
   fijo y los aires se comen el alto útil de las secciones ancladas. */
@media (min-width:761px) and (max-height:760px){
  :root{--hdr-h:4rem}
  .equip__rack{height:clamp(17rem,58vh,24rem)}
  .srv__card{height:clamp(17rem,58vh,24rem)}
}


/* ================================================== FLUJO DE COLOR =======
   Hasta acá cada sección era un plano liso y el paso de una a otra un corte
   recto de lado a lado. Funciona como recurso editorial en el salto oscuro
   → claro, pero entre dos oscuras contiguas (que se llevan un solo paso de
   valor) el corte se lee como una línea arbitraria, y el usuario pidió
   "mejorar el flujo de los colores".

   La solución es que cada sección **llegue al borde con el tono de su
   vecina**: rampa corta arriba, cuerpo plano en su color propio, rampa
   corta abajo hacia lo que viene. Así el color recorre la página en vez de
   escalonarse, y ninguna sección pierde su identidad (el 65% central sigue
   siendo su color de siempre).

   Por qué las rampas son cortas (~15% de la altura) y no un degradado de
   punta a punta: el bandeo de 8 bits aparece cuando un degradado muy suave
   se estira muchos píxeles. Concentrando el cambio en 100-150px hay más
   variación por píxel y la escalera no se ve. Es el mismo problema que ya
   dio la capa de luz, y acá se resuelve con geometría en vez de ruido —
   nada de tramados a sangre, que fue lo que se leyó como papel.

   El orden real de la página es el orden de este bloque; si se agrega o se
   saca una sección hay que recalcular los tonos de los bordes vecinos. */

/* hero (hueso + vídeo) → relato */
.story{
  background:linear-gradient(180deg,
    #0B0C12 0%, #0B0C12 62%, #101119 100%);
}
/* relato → casos → reseñas (sube hacia el claro) */
.cases{
  background:linear-gradient(180deg,
    #101119 0%, #14151D 16%, #14151D 80%, #1B1C26 100%);
}
/* casos → reseñas → instrumental (arranca en lo más claro y se asienta) */
.rev{
  background:linear-gradient(180deg,
    #EFEDED 0%, #EAE8E8 16%, #EAE8E8 80%, #E1DFDF 100%);
}
/* reseñas → instrumental → servicios (entra levantado y se hunde) */
.equip{
  background:linear-gradient(180deg,
    #17181F 0%, #0B0C12 22%, #0B0C12 100%);
}
/* instrumental → servicios → formación (vuelve a subir hacia la arena) */
.srv{
  background:linear-gradient(180deg,
    #0B0C12 0%, #14151D 16%, #14151D 78%, #1C1D27 100%);
}
/* servicios → formación → contacto */
.edu{
  background:linear-gradient(180deg,
    #E9E4DD 0%, #E3DED6 16%, #E3DED6 80%, #DAD4CB 100%);
}
/* formación → contacto → pie (entra levantado y baja hasta el pie) */
.cta{
  background:linear-gradient(180deg,
    #1A1B25 0%, #14151D 20%, #0E0F16 100%);
}
/* contacto → pie: la página termina en el negro más profundo */
.ft{
  background:linear-gradient(180deg,
    #0E0F16 0%, #0B0C12 30%, #08090E 100%);
}

/* ======================================================== RESPONSIVE ===== */
@media (max-width:1080px){
  /* El nav se va al menú del burger, pero la columna izquierda se conserva
     (queda vacía) para que el logo siga cayendo en el centro exacto. */
  .hdr__nav{display:none}
  .burger{display:grid}
  .story__sticky{grid-template-columns:1fr}
  .story__panel{padding-top:2rem}
  .srv__grid{grid-template-columns:1fr; gap:clamp(2.5rem,6vw,4rem)}
  /* Con 5 tiras el acordeón se aprieta: la cerrada baja a ~4rem útiles. */
  .equip__rack{gap:.35rem}
  .equip__panel.is-on{flex-grow:4.5}
}

@media (max-width:760px){
  :root{--hdr-h:4.25rem}
  .btn--pill{padding:.85rem 1.15rem; font-size:.8125rem}
  .hero{--split:0%; height:auto}
  .hero__sticky{position:relative}
  .hero__panel{display:none}
  /* Sin split, el bloque ocupa todo y va sobre el vídeo: se invierten los
     colores de letra. */
  .hero__intro{
    width:100%; justify-content:center;
    padding:0 var(--pad) clamp(3rem,10vh,6rem);
  }
  /* Sobre el vídeo el velo deja un fondo medio: al 42% de `--bone-40` el rol
     y la bajada quedaban por debajo del 4,5:1. Suben al 76%. */
  .hero__h1{color:var(--bone)}
  .hero__role{color:rgba(234,232,232,.76)}
  .hero__num{color:var(--bone)}
  .hero__stat dd{color:rgba(234,232,232,.7)}
  .hero__media{inset:0}
  .hero__media img{object-position:50% 20%}
  .hero__scrim{background:linear-gradient(180deg,rgba(20,21,29,.72) 0%,rgba(20,21,29,.4) 40%,rgba(20,21,29,.85) 100%)}
  .hero__based{color:var(--bone); max-width:none; right:var(--pad); bottom:auto; top:calc(var(--hdr-h) + 1rem)}
  .hero__based span{color:var(--bone-40); margin-bottom:.2rem}
  .hero__based__link{white-space:normal}

  .story{height:auto}
  .story__sticky{position:relative; height:auto; display:block}
  .story__media{aspect-ratio:3/4; width:100%}
  .story__hi{display:none}
  .story__hi:first-child{display:block; opacity:1; transform:none}
  .story__index{display:none}
  .story__panel{padding:1.75rem var(--pad) 0; max-width:none}
  .story__stages{display:block}
  .story__stage{
    opacity:1; visibility:visible; transform:none; position:static;
    padding-bottom:2rem; margin-bottom:2rem; border-bottom:1px solid var(--bone-15);
  }
  .story__stage:last-child{border-bottom:0; margin-bottom:0}

  /* Sin cursor el banner lateral no tiene sentido: la foto queda arriba y
     el detalle del procedimiento baja fijo, siempre legible. */
  .srv__card{height:auto; border-radius:1rem; cursor:default}
  .srv__photo{position:relative; aspect-ratio:16/10}
  .srv__veil,.srv__cue{display:none}
  .srv__n,.srv__title{position:static; margin:0 1.25rem}
  .srv__n{margin-top:1.1rem; display:block; color:var(--gold)}
  .srv__title{max-width:none; transform:none}
  .srv__flyout{
    position:static; width:auto; transform:none;
    padding:1rem 1.25rem 1.5rem; padding-left:1.25rem;
  }
  .srv__flyout::before,.srv__flyout::after{display:none}
  /* Lleva el mismo `html.js` que la regla que esconde: si acá fuera sólo
     `.srv__flyout > *` perdería por especificidad y el texto no aparecería. */
  html.js .srv__flyout > *{opacity:1; transform:none; transition:none}

  /* Sin cursor el acordeón no tiene sentido: las tiras se apilan y cada una
     muestra su ficha completa. */
  .equip__rack{
    flex-direction:column; height:auto; gap:.75rem; width:100%;
  }
  .equip__panel,
  .equip__panel.is-on{flex:0 0 auto; height:14rem; cursor:default}
  .equip__panel img,
  .equip__panel.is-on img{filter:grayscale(0) brightness(.8); transform:none}
  .equip__panel .equip__veil{opacity:1}
  .equip__meta h3{opacity:1; transform:none; white-space:normal}
  .equip__meta p{opacity:1; transform:none}
  .equip__idx{color:var(--gold)}

  .ft__name{font-size:clamp(2.5rem,14vw,5rem)}
}

/* ======================================================== FALLBACKS ====== */
html.no-gsap .cover{height:auto}
html.no-gsap .cover__sticky{position:relative}
html.no-gsap .hero{height:auto}
html.no-gsap .hero__sticky{position:relative}
html.no-gsap .story{height:auto}
html.no-gsap .story__sticky{position:relative}
html.no-gsap .story__stage{opacity:1; visibility:visible; transform:none; position:static}
html.no-gsap .story__hi:not(:first-child){display:none}

/* ======================================================== REDUCED MOTION = */
@media (prefers-reduced-motion:reduce){
  html.js{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms!important; animation-iteration-count:1!important;
    transition-duration:.001ms!important; scroll-behavior:auto!important;
  }
  .loader{display:none}
  .ln__i{transform:none}
  .reveal{opacity:1; transform:none}
  .img-reveal{clip-path:none}

  /* Las dos tarjetas quedan desplegadas: sin movimiento el banner lateral
     nunca se mostraría. */
  .srv__card{height:auto; cursor:default}
  .srv__photo{position:relative; aspect-ratio:16/10}
  .srv__photo img{filter:grayscale(0) brightness(.85); transform:none}
  .srv__veil,.srv__cue{display:none}
  .srv__n,.srv__title{position:static; margin:0 1.4rem}
  .srv__n{margin-top:1.25rem; display:block; color:var(--gold)}
  .srv__title{max-width:none; transform:none}
  .srv__flyout{
    position:static; width:auto; transform:none;
    padding:1rem 1.4rem 1.6rem; padding-left:1.4rem;
  }
  .srv__flyout::before,.srv__flyout::after{display:none}
  html.js .srv__flyout > *{opacity:1; transform:none}

  .cover{height:auto}
  .cover__sticky{position:relative}

  .hero{height:auto}
  .hero__sticky{position:relative}

  .story{height:auto}
  .story__sticky{position:relative}
  .story__photo{animation:none}
  .story__stage{opacity:1; visibility:visible; transform:none}
  .story__hi:not(:first-child){display:none}

  /* Todas las fichas abiertas y legibles, sin depender del cursor. */
  .equip__rack{flex-direction:column; height:auto; gap:.75rem}
  .equip__panel,.equip__panel.is-on{flex:0 0 auto; height:14rem}
  .equip__panel img{filter:grayscale(0) brightness(.8); transform:none}
  .equip__veil{opacity:1}
  .equip__meta h3{opacity:1; transform:none; white-space:normal}
  .equip__meta p{opacity:1; transform:none}

  .rev__track{transition:none}
}

/* ==================================================== ATMÓSFERA ==========
   Luz volumétrica sobre las secciones oscuras. Tres capas, todas dentro de
   `.aura` (z-index:-1, detrás del contenido: ninguna puede lavar el texto):

     1. dos focos ambientales anclados a esquinas opuestas, que entran y se
        retiran solos,
     2. un tercer foco que sigue al puntero con retardo.

   Regla de la casa, dicha por el usuario tres veces: la luz **viene desde
   las esquinas**, nunca nace en medio de nada. Y el registro sigue siendo
   clínico: se lee como ambiente de pabellón, nunca como neón.

   Las partículas se probaron dos veces y se rechazaron las dos ("parece
   polvo y suciedad", "estrellitas, esto no es un juguete"). No reintentar
   partículas en este sitio, en canvas ni en DOM.

   Sin filter:blur — un radial-gradient ya nace difuso y no cuesta capa de
   composición. Los blobs son deliberadamente MÁS GRANDES que la sección: si
   el borde del degradado entra en cuadro se lee como mancha, no como luz. */
.aura{
  position:absolute; inset:0; z-index:-1;
  overflow:hidden; pointer-events:none;
  contain:layout paint;
  /* La luz se apaga antes de tocar el borde de la sección. Sin esto queda
     RECORTADA en la juntura: aunque las dos secciones vecinas lleguen al
     mismo tono (que es justo lo que hace el bloque FLUJO DE COLOR), el
     salto de iluminación dibuja igual una línea recta de lado a lado —
     era el corte que seguía viéndose entre Instrumental y Servicios.
     También evita que el foco del puntero se corte en seco al pasar de
     una sección a la siguiente. */
  --velo:linear-gradient(180deg,
    transparent 0%, rgba(0,0,0,.45) 8%, #000 22%,
    #000 70%, rgba(0,0,0,.4) 88%, transparent 98%);
  -webkit-mask-image:var(--velo);
          mask-image:var(--velo);
}
.aura i,.aura span{
  position:absolute; display:block; border-radius:50%;
}

/* Dos decisiones que arreglan el "se ve como foto comprimida":

   1. NADA de scale en la animación. Al animar scale, el navegador rasteriza
      la capa una vez a su tamaño inicial y después la estira — un degradado
      ampliado sobre un raster viejo sale pixelado y con el borde del círculo
      marcado. Es exactamente el defecto que se veía. Acá sólo hay translate
      (que no necesita re-rasterizar) y la "respiración" la hace la opacidad.

   2. Caída larga y con muchas paradas. Con tres paradas y corte en 72% el
      degradado tiene un canto visible: se lee como círculo, no como luz.
      Estas paradas aproximan una gaussiana y llegan a alfa 0 recién en 100%,
      así no hay borde que ver en ningún punto.

   El color pasa a blanco (con un punto de azul, que es como se comporta la
   luz fría real): el dorado sobre fondo casi negro viraba a marrón sucio. */
.aura i:nth-child(1){
  width:120vw; height:95vw; top:-52%; left:-38%;
  /* Historia de este número, para no volver a girarlo a ciegas: estuvo en .14
     (el cliente: "se perdió la luz"), subió a .24 y entonces el reclamo fue el
     contrario — "parece manchado con harina". Las dos veces el problema NO era
     el pico sino la CAÍDA: el centro del blob cae fuera de cuadro, así que lo
     que se ve en pantalla son las paradas del medio, y con la curva anterior
     el 36% todavía valía .113 — medio velo blanco sobre TODA la sección.

     Ahora el pico baja poco (.24 → .16) y la caída se acorta: el 24% pasa de
     .163 a .078 y el 36% de .113 a .046. Resultado: la esquina conserva su
     brillo y el cuerpo de la sección vuelve al negro. La luz que se debe notar
     moviéndose es la del puntero (`.aura__cur`), no ésta. */
  background:radial-gradient(closest-side,
    rgba(238,244,255,.16) 0%,
    rgba(238,244,255,.118) 12%,
    rgba(238,244,255,.078) 24%,
    rgba(238,244,255,.046) 36%,
    rgba(238,244,255,.025) 48%,
    rgba(238,244,255,.012) 60%,
    rgba(238,244,255,.005) 72%,
    rgba(238,244,255,.0015) 86%,
    rgba(238,244,255,0)    100%);
  animation:auraA 38s var(--e-io) infinite alternate;
}
.aura i:nth-child(2){
  width:104vw; height:84vw; bottom:-50%; right:-34%;
  /* Misma corrección que el foco 1: pico .18 → .12 y caída corta. */
  background:radial-gradient(closest-side,
    rgba(255,255,255,.12) 0%,
    rgba(255,255,255,.089) 12%,
    rgba(255,255,255,.059) 24%,
    rgba(255,255,255,.035) 36%,
    rgba(255,255,255,.019) 48%,
    rgba(255,255,255,.009) 60%,
    rgba(255,255,255,.004) 72%,
    rgba(255,255,255,.001) 86%,
    rgba(255,255,255,0)    100%);
  animation:auraB 52s var(--e-io) infinite alternate;
}
/* El recorrido va en la diagonal de su propia esquina: la luz avanza hacia
   el centro y se retira hacia afuera, nunca cruza la sección de lado. Es lo
   que hace que se lea como algo que ENTRA desde la esquina en vez de una
   mancha que flota. Sólo translate y opacity — animar `scale` re-rasteriza
   el degradado y le marca el canto (ya pasó una vez). */
@keyframes auraA{
  from{transform:translate3d(-4vw,-3vh,0); opacity:.55}
  to  {transform:translate3d(10vw,6vh,0);  opacity:1}
}
@keyframes auraB{
  from{transform:translate3d(4vw,3vh,0);   opacity:1}
  to  {transform:translate3d(-9vw,-7vh,0); opacity:.52}
}

/* Tramado — ahora SÓLO dentro de la luz.
   Antes esto era `.aura::after`: una lámina de ruido a sangre sobre TODA la
   sección. Encima del degradado cumple su función (ocho bits por canal no
   alcanzan para una caída tan larga sobre casi negro y salen anillos), pero
   sobre el negro plano no hay banding que romper y el ruido queda a la vista
   como textura: eso es lo que el usuario rechazó, "el fondo como algo roto,
   como si fuera papel". Ahora el tramado viaja pegado a cada foco y va
   enmascarado con la misma caída del degradado. Donde hay luz hay tramado;
   donde no hay luz, el fondo queda liso. */
.aura i::after{
  content:''; position:absolute; inset:0; border-radius:50%;
  /* .26 → .16: con la luz a la mitad hay menos caída que romper, y el ruido
     que sobraba se sumaba a la sensación de fondo "manchado". */
  opacity:.16; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='d'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/></filter><rect width='140' height='140' filter='url(%23d)' opacity='.5'/></svg>");
  -webkit-mask-image:radial-gradient(closest-side,#000 0%,rgba(0,0,0,.74) 34%,rgba(0,0,0,.3) 62%,transparent 92%);
          mask-image:radial-gradient(closest-side,#000 0%,rgba(0,0,0,.74) 34%,rgba(0,0,0,.3) 62%,transparent 92%);
}

/* --- Luz que sigue al puntero -------------------------------------------
   Pedido literal: "que sea luz que se mueva, que a medida que yo muevo el
   cursor se mueve esta luz". Es un tercer foco por sección, del mismo
   material que los otros dos (nueve paradas, sin canto), que vive dentro de
   `.aura` y por lo tanto SIEMPRE queda detrás del texto.

   La inercia la da una transición CSS sobre `translate`, no un bucle de
   requestAnimationFrame: el JS sólo escribe el destino en píxeles y el
   navegador interpola. Menos código, y verificable — rAF no corre en una
   pestaña en segundo plano, las transiciones sí se pueden leer. El blanco
   va apenas cálido para que se distinga del ambiente frío; sigue siendo
   blanco, no dorado (el dorado sobre negro vira a marrón sucio). */
/* Nota: desde que se sacó el cursor propio (punto dorado + anillo, retirado
   por pedido — "que sea el cursor simplemente y que se ilumine un poco"),
   ESTA es la única señal que acompaña al puntero. Por eso va algo más
   ceñida y algo más marcada que los focos de ambiente: tiene que leerse
   como que el cursor ilumina, no como una tercera mancha suelta. */
.aura__cur{
  top:0; left:0;
  width:34rem; height:34rem; margin:-17rem 0 0 -17rem;
  opacity:0;
  /* Sube de .185 a .21 (2026-09-05, segunda pasada). No es que antes no
     estuviera: los focos de ambiente al .24 la tapaban, y el cliente lo leyó
     como que la animación se había quitado. Bajado el ambiente, ésta queda
     como la ÚNICA luz que se mueve y por eso puede permitirse ser la más
     marcada de las tres — sigue siendo un foco de 34rem, no un velo. */
  background:radial-gradient(closest-side,
    rgba(255,251,243,.21) 0%,
    rgba(255,251,243,.184) 12%,
    rgba(255,251,243,.141) 24%,
    rgba(255,251,243,.098) 36%,
    rgba(252,250,255,.062) 48%,
    rgba(248,249,255,.036) 60%,
    rgba(248,249,255,.017) 72%,
    rgba(248,249,255,.006) 86%,
    rgba(248,249,255,0)    100%);
  translate:var(--lx,50%) var(--ly,50%);
  transition:translate .72s var(--e-out), opacity .9s var(--e-out);
}
.aura.is-near .aura__cur{opacity:1}

/* Las secciones que reciben aura necesitan contexto propio: sin
   isolation el hijo en z-index:-1 se va detrás del fondo de la sección
   (o peor, detrás del body) y desaparece. */
.srv,.cases,.ft,.story,.equip,.cta{position:relative; isolation:isolate}

/* Destello al hacer clic: un anillo de luz que nace en el puntero y se
   apaga. Se crea y se destruye por evento, no vive en el DOM. */
.spark{
  position:fixed; z-index:7; pointer-events:none;
  width:12px; height:12px; margin:-6px 0 0 -6px; border-radius:50%;
  border:1px solid var(--gold);
  animation:spark .62s var(--e-out) forwards;
}
@keyframes spark{
  from{transform:scale(1);  opacity:.65}
  to  {transform:scale(9);  opacity:0}
}

/* --- Foco que sigue al cursor DENTRO de un elemento ----------------------
   `--mx/--my` las escribe el JS en porcentaje; en reposo el foco está
   apagado (`--lit:0`). Vale para las tarjetas oscuras y para los CTA. */
.srv__card,.equip__panel,.btn--pill,.btn--big{--mx:50%; --my:50%; --lit:0}
.srv__card::after,.equip__panel::after{
  content:''; position:absolute; inset:0; z-index:2; pointer-events:none;
  background:radial-gradient(22rem 22rem at var(--mx) var(--my),
    rgba(255,247,236,.17), rgba(179,140,97,.07) 42%, transparent 72%);
  opacity:var(--lit); transition:opacity .45s var(--e-out);
}

/* Los CTA ya no se desplazan hacia el cursor: el usuario lo rechazó dos
   veces, la última así de claro — "que me siga, no que se mueva la letra".
   Lo que persigue al puntero ahora es la LUZ dentro del botón; el botón se
   queda exactamente donde está. Los hijos suben a z-index:1 para que el
   barrido pase por debajo del texto y del ícono, no por encima. */
.btn--pill,.btn--big{position:relative; overflow:hidden}
.btn--pill > *,.btn--big > *{position:relative; z-index:1}
.btn--pill::after,.btn--big::after{
  content:''; position:absolute; inset:0; z-index:0; pointer-events:none;
  opacity:var(--lit); transition:opacity .45s var(--e-out);
}
.btn--pill::after{
  background:radial-gradient(7rem 7rem at var(--mx) var(--my),
    rgba(255,255,255,.28), transparent 68%);
}
.btn--big::after{
  background:radial-gradient(9rem 9rem at var(--mx) var(--my),
    rgba(179,140,97,.34), transparent 66%);
}

@media (prefers-reduced-motion: reduce){
  .aura i{animation:none}
  .aura__cur{display:none}
  .eyebrow::before,
  .story__panel .eyebrow::after,
  .cases .eyebrow::after,
  .equip .eyebrow::after{animation:none; background-position:50% 0}
  .story__prog span::before{display:none}
  .spark{display:none}
  .srv__card::after,.equip__panel::after,
  .btn--pill::after,.btn--big::after{transition:none}
}

/* ======================================================== PRINT ========== */
@media print{
  .loader,.hdr,.menu,.rev__nav{display:none}
  body{background:#fff; color:#000}
}
