@charset "utf-8";
/* ═══════════════════════════════════════════════════════════════════════════
   contactia.cl · hoja de estilo unica
   ───────────────────────────────────────────────────────────────────────────
   Reescritura del mockup aprobado (out/mockups/sitio-web/fusion-v2) SIN React
   y SIN Framer Motion. El diseno es el mismo hasta el pixel; lo que cambia es
   quien lo mueve.

   CONTRATO DE ESTE ARCHIVO
   · El sitio se lee ENTERO con JavaScript apagado. Todo lo que empieza oculto
     para poder animarse vive detras de `html.js`, y esa clase la pone un script
     de una linea en el <head>. Sin JS no hay `.js`, no hay nada oculto, y la
     pagina se ve completa. Es el requisito que define el trabajo: WhatsApp y
     LinkedIn no ejecutan JS al generar la vista previa de un enlace.
   · CERO peticiones externas. Tipografias, iconos y filtros salen de este
     dominio. No hay un solo https:// a un tercero fuera de los enlaces de
     contacto.
   · Lo que en el mockup hacia Framer Motion se reparte en tres tecnicas:
       - `animation-timeline: scroll()` / `view()` para lo ligado al scroll
         (barra de progreso, parallax del hero, parallax del cierre),
       - `transition` + una clase que pone un IntersectionObserver para lo que
         se dispara UNA vez al entrar en pantalla (todos los `whileInView`),
       - una sola variable `--film-p` escrita por rAF para el film, que es lo
         unico que necesita un valor continuo dentro de un tramo medido.
   · Las animaciones ligadas al scroll van dentro de `@supports`. Donde no hay
     soporte no se rompe nada: se ve el estado final, quieto.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════ TIPOGRAFIAS (self-hosted) ══════════════════════
   Las tres son VARIABLES: un archivo por subconjunto cubre todos los pesos, y
   por eso el rango va en `font-weight`. Con `font-display:swap` el texto se
   lee desde el primer cuadro con la tipografia de respaldo.
   `unicode-range` es lo que evita que el navegador descargue el subconjunto
   `latin-ext`: para un sitio en castellano nunca hace falta. */
@font-face{font-family:"Bricolage Grotesque";font-style:normal;font-weight:200 800;font-display:swap;
  src:url("../fonts/bricolage-grotesque-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Bricolage Grotesque";font-style:normal;font-weight:200 800;font-display:swap;
  src:url("../fonts/bricolage-grotesque-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"Instrument Sans";font-style:normal;font-weight:400 700;font-display:swap;
  src:url("../fonts/instrument-sans-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"Instrument Sans";font-style:normal;font-weight:400 700;font-display:swap;
  src:url("../fonts/instrument-sans-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:400 700;font-display:swap;
  src:url("../fonts/jetbrains-mono-latin.woff2") format("woff2");
  unicode-range:U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD}
@font-face{font-family:"JetBrains Mono";font-style:normal;font-weight:400 700;font-display:swap;
  src:url("../fonts/jetbrains-mono-latin-ext.woff2") format("woff2");
  unicode-range:U+0100-02BA,U+02BD-02C5,U+02C7-02CC,U+02CE-02D7,U+02DD-02FF,U+0304,U+0308,U+0329,U+1D00-1DBF,U+1E00-1E9F,U+1EF2-1EFF,U+2020,U+20A0-20AB,U+20AD-20C0,U+2113,U+2C60-2C7F,U+A720-A7FF}

/* ══════════════════════════ TOKENS ══════════════════════════ */
:root{
  /* paleta de marca — SISTEMA.md, sin tocar */
  --cyan:#00D4FF; --purple:#7B2FFF; --magenta:#FF2D92;
  --ink:#0A0A0A; --muted:#3D4556; --gray:#6B7280; --line:#E5E7EB;
  --claro:#F9F7FC;
  --t-titulo:#FEFEFE; --t-cuerpo:#B9BCCB;

  /* ── VARIANTE WEB DEL OSCURO (no es un cambio de la marca) ──────────────
     SISTEMA.md fija el fondo oscuro en #161623. En pantalla completa, a
     pagina entera, el usuario lo encontro DEMASIADO FUERTE. Esta propuesta
     sube la base a #1E1A2E: misma familia violeta, un punto mas respirado,
     y las capas superiores otro punto mas claras todavia.
     Verificado (WCAG, contra --noche):
       #FEFEFE titulares .......... 16,7:1
       #B9BCCB cuerpo ............. 8,9:1
       #00D4FF cyan ............... 9,5:1
       #FF2D92 magenta ............ 4,8:1
       #7B2FFF purple ............. 2,9:1 → NUNCA como texto. Solo relleno,
                                    trazo y gradiente. Para rotulos purple
                                    sobre oscuro se usa --lila. */
  --noche:#1E1A2E;                 /* base del sitio */
  --noche-2:#262138;               /* superficie: tarjetas, cifras */
  --noche-3:#2F2945;               /* superficie elevada: marcos, chips */
  --lila:#C3AEFF;                  /* el purple legible sobre oscuro: 7,0:1 */
  --velo:rgba(255,255,255,.085);
  --velo-2:rgba(255,255,255,.155);

  --barra:linear-gradient(90deg,#00D4FF 0%,#7B2FFF 50%,#FF2D92 100%);
  --sombra-marca:0 26px 60px -18px rgba(123,47,255,.62);

  --display:"Bricolage Grotesque","Helvetica Neue",Arial,sans-serif;
  --body:"Instrument Sans","Helvetica Neue",Arial,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,SFMono-Regular,monospace;

  --gutter:clamp(20px,4.4vw,68px);
  --lane:clamp(30px,3.4vw,62px);   /* el carril del hilo conductor */
  --maxw:1360px;
  --ease:cubic-bezier(.16,1,.3,1);

  --film-p:0;                      /* 0→1 dentro del tramo pegajoso del film */
}

*,*::before,*::after{box-sizing:border-box}
/* CLIP Y NO HIDDEN, Y ES LA DIFERENCIA ENTRE QUE EL FILM FUNCIONE O NO.
   Hace falta cortar el desborde horizontal: sin el, el documento se desplaza
   6 px a la derecha en telefono por el resplandor que las escenas dejan
   sangrar fuera de su caja (overflow:visible).
   Pero `overflow-x:hidden` en <html>/<body> obliga al navegador a computar el
   overflow-y como `auto`, y eso convierte al documento en un CONTENEDOR DE
   SCROLL. A partir de ahi, `position:sticky` deja de pegarse al viewport: el
   `.escenario` del film scrollea con la pagina y las escenas 2 a 5 quedan
   arriba, fuera de pantalla. El sintoma no apunta al culpable: el sticky esta
   bien escrito y el sabotaje viene de dos reglas que parecen inofensivas.
   `clip` corta igual pero NO crea contexto de scroll. Si alguna vez hay que
   volver a `hidden`, el film deja de andar. */
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;overflow-x:clip;
     scroll-padding-top:96px;color-scheme:dark}
body{
  margin:0; background:var(--noche); color:var(--t-cuerpo);
  font-family:var(--body); font-size:17px; line-height:1.6;
  -webkit-font-smoothing:antialiased; overflow-x:clip;
}
img,svg{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
:focus-visible{outline:2px solid var(--cyan);outline-offset:3px;border-radius:4px}
/* sobre la lamina clara el cyan del foco da 1,7:1 y desaparece: ahi manda el purple */
.sec--claro :focus-visible{outline-color:var(--purple)}

h1,h2,h3,h4{font-family:var(--display);color:var(--t-titulo);margin:0;line-height:1.03;
  letter-spacing:-.025em;font-weight:700}
p{margin:0}
.sr{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip-path:inset(50%);white-space:nowrap;border:0}

/* El degradado del titular. SIN JS va sobre el bloque entero y se ve perfecto;
   con JS, `sitio.js` parte el titular en palabras y le calcula a cada una su
   `background-position`, porque `background-clip:text` sobre el h1 no alcanza a
   los <span> hijos: heredan `color:transparent` sin heredar el fondo y
   desaparecen. Las dos rutas pintan el mismo degradado.
   El relleno se pinta DENTRO de la caja del inline-block: con line-height 1.03
   la caja es mas baja que los glifos y a la "g" le desaparecia la cola. El
   padding agranda la caja de pintura y el margen negativo devuelve el hueco. */
.acento,.acento .w{background-image:var(--barra);
  -webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;color:transparent}
.acento .w{padding-block:.08em .2em;margin-block:-.08em -.2em}

/* reticula asimetrica: el margen izquierdo es el carril del hilo */
.wrap{max-width:var(--maxw);margin-inline:auto;width:100%;
  padding-left:calc(var(--gutter) + var(--lane));padding-right:var(--gutter)}

.label{font-family:var(--mono);font-size:11px;font-weight:500;letter-spacing:.22em;
  text-transform:uppercase;color:var(--cyan)}
.label--magenta{color:var(--magenta)} .label--purple{color:var(--lila)} .label--gray{color:#8A8FA6}

.grano{
  position:fixed; inset:-50%; z-index:900; pointer-events:none; opacity:.055; mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='220' height='220' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* con la base mas clara la vineta baja: si no, devuelve el negro que sobraba */
.vineta{position:fixed;inset:0;z-index:899;pointer-events:none;
  background:radial-gradient(128% 94% at 50% 44%,transparent 48%,rgba(9,7,18,.42) 100%)}

/* ══════════════════ REVELADO AL ENTRAR EN PANTALLA ══════════════════
   Reemplaza `whileInView` de Framer Motion: un solo IntersectionObserver pone
   `.vis` y el resto es transicion. Todo el bloque cuelga de `html.js`: sin
   JavaScript nada de esto existe y el contenido esta a la vista desde el
   primer cuadro. */
.js [data-rev]{opacity:0;transform:translateY(var(--rev-y,32px));
  transition:opacity .85s var(--ease) var(--rev-d,0s),transform .85s var(--ease) var(--rev-d,0s)}
.js [data-rev].vis{opacity:1;transform:none}

/* el titular palabra por palabra: mismo escalonado que el `staggerChildren` */
.js .pal .w{display:inline-block;opacity:0;transform:translateY(.5em);filter:blur(8px);
  transition:opacity .75s var(--ease),transform .75s var(--ease),filter .75s var(--ease);
  transition-delay:calc(var(--rev-d,0s) + var(--i) * 45ms)}
/* el titular con degradado no lleva desenfoque: `filter` sobre un texto
   recortado al fondo lo rasteriza y el degradado sale escalonado */
.js .pal.acento .w{filter:none;transition:opacity .75s var(--ease),transform .75s var(--ease)}
.js .pal.vis .w{opacity:1;transform:none;filter:blur(0)}

/* ══════════════════ HILO CONDUCTOR ══════════════════
   Un trazo continuo pegado al borde izquierdo que recorre la pagina entera, se
   dibuja con el scroll y va encendiendo los nodos de canal, uno por seccion.
   Los iconos son los 5 nodos del propio isotipo (SISTEMA.md), no una libreria
   externa. El <path> y los nodos los construye JS midiendo la pagina real: es
   la unica pieza que necesita medir el documento, y por eso sin JS no aparece. */
#hilo{
  position:absolute;top:0;left:clamp(2px,1.1vw,24px);
  width:clamp(38px,4.2vw,74px);z-index:5;pointer-events:none;overflow:visible;
}
html:not(.js) #hilo{display:none}
/* el carril del trazo va en purple translucido, no en blanco: asi se ve igual
   sobre el fondo oscuro y sobre la seccion clara de Mercados */
#hilo .trazo-base{fill:none;stroke:rgba(123,47,255,.22);stroke-width:1.4}
#hilo .trazo{fill:none;stroke:url(#gradHiloV);stroke-width:2.6;stroke-linecap:round;
  filter:url(#ctia-glow-strong)}
#hilo .cabeza{fill:var(--cyan);filter:url(#ctia-node-glow);opacity:0}
#hilo .cabeza-aro{fill:none;stroke:var(--cyan);stroke-width:1.2;opacity:0}
#hilo .nodo{opacity:.16;transform-box:fill-box;transform-origin:50% 50%;transform:scale(.68);
  transition:opacity .8s var(--ease),transform .8s var(--ease)}
#hilo .nodo.on{opacity:1;transform:scale(1)}
#hilo .halo{fill:none;stroke:var(--cyan);stroke-width:1.1;opacity:0;
  transform-box:fill-box;transform-origin:50% 50%}
#hilo .nodo.on .halo{animation:hpulso 3.4s ease-out infinite}
@keyframes hpulso{0%{opacity:.6;transform:scale(.6)}70%,100%{opacity:0;transform:scale(1.55)}}
#hilo .rot{font-family:var(--mono);font-size:8.5px;letter-spacing:.18em;fill:#8A8FA6;
  text-transform:uppercase;opacity:0;transition:opacity .8s var(--ease)}
#hilo .nodo.on .rot{opacity:.85}
@media(max-width:900px){#hilo{left:0;width:34px}#hilo .rot{display:none}}

/* ══════════════════════ PROGRESO + NAV ══════════════════════ */
.progreso{position:fixed;top:0;left:0;right:0;height:3px;z-index:1000;background:rgba(255,255,255,.07)}
.progreso i{display:block;height:100%;background:var(--barra);transform-origin:0 50%;
  transform:scaleX(0);box-shadow:0 0 16px rgba(123,47,255,.85)}
/* La barra de progreso es el caso de libro de `animation-timeline: scroll()`:
   un valor continuo atado al scroll del documento, sin una sola linea de JS y
   sin un listener por cuadro. Donde no hay soporte se queda en scaleX(0), o
   sea invisible, que es exactamente lo que corresponde: es decoracion. */
@supports (animation-timeline:scroll()){
  .progreso i{animation:progreso linear;animation-timeline:scroll(root block)}
}
@keyframes progreso{from{transform:scaleX(0)}to{transform:scaleX(1)}}

.nav{position:fixed;top:3px;left:0;right:0;z-index:998;display:flex;align-items:center;gap:28px;
  padding:15px var(--gutter) 15px calc(var(--gutter) + var(--lane));
  transition:background .4s,backdrop-filter .4s,border-color .4s;border-bottom:1px solid transparent}
.nav[data-solid="1"]{background:rgba(30,26,46,.78);backdrop-filter:blur(18px) saturate(1.5);
  border-bottom-color:rgba(255,255,255,.09)}
/* el dibujo ocupa el archivo entero: 42 px de caja son 42 px de logo, no 30 px
   de logo dentro de 42 de caja (el PNG de branding/export/corp/ trae casi un
   cuarto de ancho en transparencia y por eso el logo se veia chico) */
.nav__logo{height:42px;width:auto;flex:none}
@media(max-width:520px){.nav__logo{height:32px}}
.nav__links{display:flex;gap:24px;margin-left:auto;font-size:14px;color:#A6AABE}
.nav__links a{position:relative;padding:4px 0;transition:color .25s}
.nav__links a::after{content:"";position:absolute;left:0;right:100%;bottom:0;height:1px;
  background:var(--barra);transition:right .35s var(--ease)}
.nav__links a:hover{color:#fff}.nav__links a:hover::after{right:0}
@media(max-width:1040px){.nav__links{display:none}.nav{gap:14px}}

.btn{position:relative;display:inline-flex;align-items:center;gap:10px;padding:13px 24px;
  border-radius:999px;font-size:14px;font-weight:600;white-space:nowrap;overflow:hidden;
  transition:transform .3s var(--ease)}
.btn:active{transform:scale(.97)}
.btn--primario{background:var(--barra);color:#0A0A0A;box-shadow:0 10px 34px -8px rgba(123,47,255,.95)}
.btn--primario::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,#FF2D92,#00D4FF);opacity:0;transition:opacity .4s}
.btn--primario:hover::after{opacity:1}
.btn > *{position:relative;z-index:1}
.btn--fantasma{border:1px solid var(--velo-2);color:#fff;backdrop-filter:blur(8px);
  background:rgba(255,255,255,.03)}
.btn--fantasma:hover{border-color:rgba(0,212,255,.6);background:rgba(0,212,255,.1)}
.btn__ico{width:18px;height:18px;flex:none}
/* "Contactanos" es corto: sin este respiro el boton de la barra se encoge y la
   barra queda descompensada contra el logo de la izquierda */
.nav .btn--primario{padding-inline:30px}
@media(max-width:520px){.btn{padding:11px 17px;font-size:13px}}

/* ══════════════════════════ HERO ══════════════════════════ */
.hero{position:relative;min-height:100svh;display:flex;align-items:flex-end;
  overflow:hidden;isolation:isolate}
.hero__foto{position:absolute;inset:-14% 0;z-index:-3;will-change:transform;
  filter:brightness(.74) saturate(1.25) contrast(1.02)}
.hero__foto img{width:100%;height:100%;object-fit:cover;object-position:50% 88%}
.hero__grad{position:absolute;inset:0;z-index:-2;background:
  linear-gradient(180deg,rgba(24,20,38,.94) 0%,rgba(24,20,38,.62) 26%,rgba(24,20,38,.7) 56%,rgba(24,20,38,.94) 84%,var(--noche) 100%),
  linear-gradient(100deg,rgba(24,20,38,.8) 0%,rgba(24,20,38,.34) 46%,transparent 78%),
  radial-gradient(72% 58% at 80% 44%,rgba(123,47,255,.44),transparent 70%),
  radial-gradient(58% 46% at 8% 82%,rgba(0,212,255,.28),transparent 70%)}
/* foco que sigue al puntero sobre todo el hero */
.hero__luz{position:absolute;inset:0;z-index:-1;pointer-events:none;opacity:0;
  transition:opacity .7s var(--ease);
  background:radial-gradient(520px circle at var(--mx,50%) var(--my,40%),rgba(0,212,255,.17),rgba(123,47,255,.09) 44%,transparent 70%)}
.hero[data-luz="1"] .hero__luz{opacity:1}
.hero__inner{padding-block:128px 62px;width:100%}
.hero__grid{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:40px;align-items:end}
.hero__iso-col{justify-self:end}
@media(max-width:1140px){.hero__grid{grid-template-columns:1fr}.hero__iso-col{justify-self:start}}
.hero h1{font-size:clamp(34px,5vw,70px);font-weight:800;letter-spacing:-.04em;max-width:21ch}
.hero__sub{max-width:54ch;font-size:clamp(15.5px,1.5vw,18.5px);color:#B0B5CB;margin-top:22px}
.hero__cta{display:flex;flex-wrap:wrap;gap:13px;margin-top:28px}
.hero__iso{width:min(360px,70vw);filter:drop-shadow(0 34px 78px rgba(123,47,255,.62))}
@media(max-width:1140px){.hero__iso{margin-top:30px;width:min(240px,52vw)}
  .hero__inner{padding-block:108px 46px}}
.scrollcue{display:flex;align-items:center;gap:12px;margin-top:34px;font-family:var(--mono);
  font-size:10.5px;letter-spacing:.24em;text-transform:uppercase;color:#7B819A}
.scrollcue i{display:block;width:52px;height:1px;
  background:linear-gradient(90deg,var(--cyan),transparent);position:relative;overflow:hidden}
.scrollcue i::after{content:"";position:absolute;inset:0;background:var(--cyan);animation:barrido 2.2s infinite}
@keyframes barrido{0%{transform:translateX(-100%)}60%,100%{transform:translateX(100%)}}

/* la entrada del hero, escalonada. `@starting-style` reemplaza el par
   initial/animate de Framer: el elemento nace con el estilo de arranque y
   transita al suyo en cuanto se pinta. No hace falta ni una clase ni un
   observador porque el hero SIEMPRE esta en pantalla al cargar. */
.js .hero__entra{transition:opacity .95s var(--ease) var(--d,0s),transform .95s var(--ease) var(--d,0s)}
@supports (transition-behavior:allow-discrete){
  @starting-style{.js .hero__entra{opacity:0;transform:translateY(16px)}}
}
/* Sin @starting-style el hero aparece de golpe, ya colocado. Nunca en blanco. */

/* PARALLAX DEL HERO — lo que en el mockup eran seis `useTransform` sobre
   scrollYProgress. Aca es una sola linea de tiempo de scroll compartida y seis
   keyframes. El tramo `0 100vh` equivale al offset ['start start','end start']
   del original, porque el hero arranca en el origen del documento. */
@supports (animation-timeline:scroll()){
  @media (prefers-reduced-motion:no-preference){
    .hero__foto,.hero__texto,.hero__iso-col,.hero__flota{
      animation:linear both;animation-timeline:scroll(root block);animation-range:0 100vh}
    .hero__foto{animation-name:heroFoto}
    .hero__texto{animation-name:heroTexto}
    .hero__iso-col{animation-name:heroIso}
  }
}
@keyframes heroFoto{from{transform:translateY(0) scale(1)}to{transform:translateY(15%) scale(1.14)}}
@keyframes heroTexto{from{transform:translateY(0);opacity:1}
  72%{opacity:0}to{transform:translateY(-125px);opacity:0}}
@keyframes heroIso{from{transform:translateY(0) rotate(0)}to{transform:translateY(-240px) rotate(20deg)}}
/* la flotacion del isotipo es un bucle propio, independiente del scroll */
.hero__flota{animation:flotar 7s ease-in-out infinite}
@keyframes flotar{0%,100%{transform:translateY(0)}50%{transform:translateY(-16px)}}

/* ══════════════════════════ SECCIONES ══════════════════════════ */
.sec{position:relative;padding-block:clamp(84px,11vh,142px)}
.sec--claro{background:var(--claro);color:var(--muted)}
.sec--claro h2,.sec--claro h3{color:var(--ink)}
.sec--claro .label,.sec--claro .label--purple{color:var(--purple)}   /* 7,2:1 sobre #F9F7FC */
.sec__cab{max-width:790px;margin-bottom:clamp(40px,6vh,68px)}
.sec__cab h2{font-size:clamp(30px,4.2vw,58px);letter-spacing:-.034em;margin-block:16px 20px}
.sec__cab p{font-size:clamp(16px,1.6vw,19px)}
.sec--claro .sec__cab p{color:var(--muted)}

/* ══════════════════ RUTA ══════════════════ */
.ruta-grid{display:grid;grid-template-columns:minmax(0,.9fr) minmax(0,1.1fr);
  gap:clamp(24px,4vw,60px);align-items:center}
@media(max-width:1000px){.ruta-grid{grid-template-columns:1fr}}
.ruta-lista{display:flex;flex-wrap:wrap;gap:8px;margin-top:26px}
.ruta-lista span{font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;
  padding:8px 14px;border-radius:999px;border:1px solid var(--velo-2);color:#CBCEDC}
/* la ilustracion del camino con pines del sitio actual, en duotono de marca y
   detras de la ruta que se dibuja: las dos leen como una sola escena */
.ruta-art{position:relative}
.ruta-art__foto{position:absolute;inset:12% -8% auto -8%;z-index:0;opacity:0;
  filter:url(#duo-purple) saturate(1.25);
  transition:opacity 1.4s var(--ease),transform 1.4s var(--ease);transform:scale(1.08);
  -webkit-mask-image:radial-gradient(78% 66% at 50% 44%,#000 26%,transparent 78%);
  mask-image:radial-gradient(78% 66% at 50% 44%,#000 26%,transparent 78%)}
html:not(.js) .ruta-art__foto,.ruta-art__foto.vis{opacity:.34;transform:none}
.ruta-art__foto img{width:100%;height:auto}
.ruta-art > svg{position:relative;z-index:1}

/* ════════════ EL ALTA — el cierre del recorrido, en claro ════════════
   Es la unica lamina clara del recorrido y es a proposito: las cinco escenas
   pasan en penumbra y esta es la salida a la calle. La escena `alta` puede
   usarse aca porque se rehizo: hoy es un hall abierto con luz de dia lateral,
   con una ventana, un meson y una planta. NO es el pasillo en fuga con una luz
   encendida al fondo de la version anterior — eso se lee como "el tunel y la
   luz", la imagen cultural de la muerte, y en una plataforma de salud le da a
   una persona enferma el mensaje contrario. La regla se mantiene en pie:
   ninguna composicion del sitio puede ser "avanzar hacia una luz al final de
   algo". Esta no lo es: la luz entra de costado y no hay punto de fuga. */
.alta{overflow:hidden}
/* el dibujo va GRANDE: en `alta` la silla de ruedas y las piernas de quien va
   sentada se cruzan, y en miniatura no se separan. Se lleva la columna del
   dibujo hasta el borde izquierdo de la pagina para ganar ancho real. */
.alta__grid{display:grid;grid-template-columns:minmax(0,1.28fr) minmax(0,.72fr);
  gap:clamp(20px,3vw,44px);align-items:center}
.alta__art{margin-left:calc(var(--gutter) * -0.6);position:relative}
/* en telefono el dibujo se sale de los margenes de la caja de texto: a 320 px
   la silla y las piernas se cierran encima y deja de leerse que es */
@media(max-width:1000px){.alta__grid{grid-template-columns:1fr;gap:8px}
  .alta__art{margin-left:calc((var(--gutter) + var(--lane)) * -1);
             margin-right:calc(var(--gutter) * -1)}}
/* el aire de dia: una mancha tibia detras del dibujo, del lado de la ventana */
.alta__art::before{content:"";position:absolute;inset:-14% -10%;z-index:0;
  background:radial-gradient(58% 62% at 26% 34%,rgba(0,212,255,.16),transparent 70%),
             radial-gradient(52% 58% at 74% 60%,rgba(123,47,255,.13),transparent 72%)}
.alta__art > svg{position:relative;z-index:1}
.alta h2{font-size:clamp(28px,3.6vw,50px);letter-spacing:-.034em;margin-block:14px 18px;max-width:15ch}
.alta p{font-size:clamp(15.5px,1.5vw,18px);max-width:44ch}
.alta__pie{margin-top:26px;padding-top:20px;border-top:1px solid rgba(24,20,38,.12);
  font-family:var(--mono);font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--purple)}

/* ══════════════════════════ FILM ══════════════════════════
   Cinco escenas encadenadas por scroll dentro de un escenario pegajoso.
   El unico dato que aporta JavaScript es `--film-p` (0→1 dentro del tramo) y
   el indice `data-esc`; todo lo demas —cruces, parallax, riel, isotipo— se
   resuelve aca con transiciones y calc().
   SIN JAVASCRIPT el escenario deja de ser pegajoso y las cinco escenas se
   apilan como secciones normales, una debajo de otra. Se lee entero. */
.film{position:relative}
.film__anclas span{position:absolute;left:0;width:1px;height:1px}
.escenario{position:sticky;top:0;height:100svh;overflow:hidden;isolation:isolate;
  display:flex;align-items:center}

.esc__fondo{position:absolute;inset:0;z-index:0;opacity:0;
  transition:opacity .9s var(--ease)}
.esc__fondo .cap{position:absolute;inset:-9%;transform:scale(1.14);
  transition:transform 1.6s var(--ease)}
.esc__fondo img{width:100%;height:100%;object-fit:cover}
.esc__velo{position:absolute;inset:0;z-index:1;background:
  linear-gradient(96deg,rgba(30,26,46,.97) 0%,rgba(30,26,46,.92) 42%,rgba(30,26,46,.62) 74%,rgba(30,26,46,.8) 100%),
  linear-gradient(180deg,rgba(30,26,46,.9) 0%,rgba(30,26,46,.3) 26%,rgba(30,26,46,.42) 58%,rgba(30,26,46,.95) 100%)}
.esc__aura{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:radial-gradient(46% 52% at 74% 48%,rgba(123,47,255,.42),transparent 72%)}
@media(max-width:1000px){.esc__velo{background:
  linear-gradient(180deg,rgba(30,26,46,.9) 0%,rgba(30,26,46,.5) 22%,rgba(30,26,46,.8) 52%,rgba(30,26,46,.97) 100%)}}

.esc__stage{position:relative;z-index:3;width:100%}
.esc__grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.04fr);
  gap:clamp(20px,4vw,56px);align-items:center}
.esc__col{position:relative;min-height:clamp(320px,44vh,430px)}
.esc__art{position:relative;min-height:clamp(260px,46vh,480px)}
/* El SVG LLENA su caja y deja que preserveAspectRatio encaje el dibujo. Con
   `height:auto` + `max-height` la altura se recortaba pero el ancho seguia al
   100%: la caja quedaba mas chica que el dibujo, `overflow:visible` lo dejaba
   pintar igual por fuera y en telefono se veia UN pedazo de la escena a tamano
   natural, no la escena. Con height:100% el dibujo siempre entra entero. */
.esc__art svg{width:100%;height:100%;overflow:visible}

.esc__num{font-family:var(--display);font-size:clamp(56px,9vw,128px);font-weight:800;
  line-height:.78;letter-spacing:-.05em;color:transparent;
  -webkit-text-stroke:1.4px rgba(255,255,255,.28);margin-bottom:8px}
.esc h2{font-size:clamp(27px,3.7vw,52px);font-weight:700;letter-spacing:-.034em;
  margin-block:14px 20px;max-width:19ch}
.esc__body{font-size:clamp(15px,1.45vw,18px);color:#B0B5CB;max-width:48ch}
.esc__chips{display:flex;flex-wrap:wrap;gap:8px;margin-top:24px}
.chip{font-family:var(--mono);font-size:10.5px;letter-spacing:.14em;text-transform:uppercase;
  padding:7px 13px;border-radius:999px;border:1px solid var(--velo-2);color:#CBCEDC;
  background:rgba(255,255,255,.05);backdrop-filter:blur(6px)}
.chip--on{border-color:rgba(0,212,255,.6);color:#8FE6FF;background:rgba(0,212,255,.12)}

.motor{position:absolute;z-index:6;right:var(--gutter);top:clamp(76px,11vh,104px);
  width:clamp(92px,9vw,132px);display:flex;flex-direction:column;align-items:center;gap:8px;
  pointer-events:none}
.motor small{font-family:var(--mono);font-size:9px;letter-spacing:.2em;text-transform:uppercase;
  color:#8A8FA6;text-align:center}

.riel{position:absolute;z-index:6;left:calc(var(--gutter) + var(--lane));
  bottom:clamp(22px,4.4vh,46px);display:flex;gap:9px;align-items:center}
.riel b{display:block;width:42px;height:2px;background:rgba(255,255,255,.2);
  position:relative;overflow:hidden;border-radius:2px}
/* cada paso se llena con su quinto de `--film-p`: 0 antes de tocarle, 1 despues.
   Es la misma rampa por segmento del `useTransform` original, en calc(). */
.riel b i{position:absolute;inset:0;background:var(--barra);transform-origin:0 50%;
  transform:scaleX(clamp(0,calc(var(--film-p) * 5 - var(--n)),1))}

/* SOLO CON JAVASCRIPT: el escenario se pega y las escenas se cruzan.
   Sin el, todo lo de abajo no aplica y las cinco quedan apiladas y visibles. */
html:not(.js) .escenario{position:static;height:auto;display:block;padding-block:40px}
html:not(.js) .film{height:auto !important}
html:not(.js) .esc__fondo,html:not(.js) .motor,html:not(.js) .riel{display:none}
html:not(.js) .esc__envoltorio{padding-block:clamp(40px,7vh,80px)}
html:not(.js) .esc__art{min-height:0;margin-top:24px}

.js .esc__envoltorio{position:absolute;inset:0;display:flex;align-items:center;
  opacity:0;visibility:hidden;transition:opacity .55s var(--ease),visibility 0s .55s}
.js .esc__envoltorio[data-act="1"]{opacity:1;visibility:visible;transition:opacity .6s var(--ease) .16s,visibility 0s}
.js .esc__col > *{transform:translateY(40px);filter:blur(10px);
  transition:transform .6s var(--ease),filter .6s var(--ease)}
.js .esc__art{transform:scale(.9);transition:transform .7s var(--ease)}
.js [data-act="1"] .esc__col > *{transform:none;filter:blur(0);transition-delay:.16s}
.js [data-act="1"] .esc__art{transform:none;transition-delay:.16s}
.film[data-esc="0"] .esc__fondo[data-i="0"],.film[data-esc="1"] .esc__fondo[data-i="1"],
.film[data-esc="2"] .esc__fondo[data-i="2"],.film[data-esc="3"] .esc__fondo[data-i="3"],
.film[data-esc="4"] .esc__fondo[data-i="4"]{opacity:var(--op)}
.film[data-esc="0"] .esc__fondo[data-i="0"] .cap,.film[data-esc="1"] .esc__fondo[data-i="1"] .cap,
.film[data-esc="2"] .esc__fondo[data-i="2"] .cap,.film[data-esc="3"] .esc__fondo[data-i="3"] .cap,
.film[data-esc="4"] .esc__fondo[data-i="4"] .cap{transform:scale(1.02)}

@media(max-width:1000px){
  .esc__grid{grid-template-columns:1fr;gap:14px}
  .esc__art{min-height:30vh;order:-1}
  /* el riel se va en telefono: el texto de la escena crece y le pisaba encima.
     El numero grande de la escena ya dice en cual vas. */
  .riel{display:none}
  .esc__col{min-height:auto}
  .motor{display:none}
  /* En telefono la escena mide mas que la ventana: dibujo arriba, texto largo
     abajo. Centrada, se sale por los dos lados y `overflow:hidden` se come
     justo el dibujo. La alineacion tiene que ir en el ENVOLTORIO y no en el
     escenario: el envoltorio esta posicionado con `inset:0`, y un elemento
     absoluto se coloca contra la caja de relleno de su contenedor, asi que el
     padding del escenario no lo mueve un pixel. */
  .js .esc__envoltorio{align-items:flex-start;padding-top:78px}
  .escenario{align-items:flex-start}
}

/* ════════ ESCENAS DE LINEA CONTINUA: EL DIBUJO SE HACE SOLO ════════
   Los SVG vienen de `_img/escenas/` (los genera `scripts/escenas/escenas_linea.py`
   y los inserta aca `scripts/sitio/inlinar_escenas.py`). Traen `pathLength="1"`,
   `data-t` y un `animation-delay` propio en cada trazo. Normalizar la longitud
   a 1 es lo que permite que un solo par de reglas sirva para trazos de
   cualquier largo: sin `pathLength` habria que medir cada path con
   getTotalLength() en JS.
   Se enciende con data-dib="1" en el <svg>. SIN JAVASCRIPT el dibujo aparece
   entero y quieto, que es el estado final de todos modos.

   ⚠ REGLA QUE COSTO CARO — `animation` NO SE ACUMULA, SE REEMPLAZA.
   Cualquier regla que le de OTRA `animation` a un elemento trazado —aunque sea
   de una propiedad distinta, como opacity o transform— NO se suma: pisa la
   declaracion entera y el elemento se queda con `stroke-dashoffset:1`, o sea
   INVISIBLE, sin un solo error en consola. Por eso todo bucle que caiga sobre
   un elemento con `data-t` declara la LISTA COMPLETA (trazar primero, el bucle
   despues) y se acota con `[data-t]` para no tocar a los que no se trazan.
   Ojo tambien con el `animation-delay` INLINE que trae cada trazo: siendo
   inline gana siempre, y al haber un solo valor para dos animaciones la lista
   se repite, asi que el bucle arranca con el mismo retraso que el trazado. */
.escena{width:100%;height:auto;display:block;overflow:visible;
  --trazo:trazar 1.05s cubic-bezier(.16,1,.3,1) forwards}
.js .escena [data-t]{stroke-dasharray:1;stroke-dashoffset:1}
.js .escena[data-dib="1"] [data-t]{animation:var(--trazo)}
@keyframes trazar{to{stroke-dashoffset:0}}

/* ── bucles sobre elementos QUE SE TRAZAN (lista completa, ver el aviso) ── */
/* el reloj de la sala de espera. El pivote de una aguja es su ORIGEN, no su
   centro: `M0 0 L0 -24` tiene su caja de -24 a 0, asi que el origen cae en el
   borde inferior (0% 100%), y `M0 0 L20 6` en el superior izquierdo (0% 0%). */
.escena .aguja-h,.escena .aguja-m{transform-box:fill-box}
.escena .aguja-h{transform-origin:0% 100%}
.escena .aguja-m{transform-origin:0% 0%}
.js .escena[data-dib="1"] .aguja-h[data-t]{animation:var(--trazo),girar 26s linear infinite}
.js .escena[data-dib="1"] .aguja-m[data-t]{animation:var(--trazo),girar 6.5s linear infinite}
html:not(.js) .escena .aguja-h{animation:girar 26s linear infinite}
html:not(.js) .escena .aguja-m{animation:girar 6.5s linear infinite}
@keyframes girar{to{transform:rotate(360deg)}}
/* el halo de cada hito respira DESPUES de dibujarse. El `.aro` del tilde NO
   entra aca aunque el nombre invite: en el dibujo es el circulo que rodea al
   visto bueno, o sea estructura, y `respirar` lo apagaria a opacidad 0. */
.escena .halo-hito{transform-box:fill-box;transform-origin:50% 50%}
.js .escena[data-dib="1"] .halo-hito[data-t]{animation:var(--trazo),respirar 3s 1.1s ease-out infinite}
.escena .pulso{transform-box:fill-box;transform-origin:50% 50%}
.js .escena[data-dib="1"] .pulso{animation:respirar 2.8s 1.2s ease-out infinite}
@keyframes respirar{0%{opacity:.5;transform:scale(1)}70%,100%{opacity:0;transform:scale(1.9)}}
/* la chispa de la IA entra con rebote encima del trazo */
.escena .chispa-ia,.escena .chispa{transform-box:fill-box;transform-origin:50% 50%}
.js .escena[data-dib="1"] .chispa-ia[data-t],.js .escena[data-dib="1"] .chispa[data-t]{
  animation:var(--trazo),brotar .7s cubic-bezier(.34,1.56,.64,1) backwards}
@keyframes brotar{from{transform:scale(.2)}to{transform:scale(1)}}
/* las ondas del telefono laten. No llevan un retraso escalonado propio en la
   regla: cada onda ya trae el suyo en el marcado (0,70 · 0,82 · 0,94 s). */
.js .escena[data-dib="1"] .onda[data-t]{animation:var(--trazo),latir 1.6s 1.4s ease-in-out infinite}
@keyframes latir{0%,100%{opacity:.3}50%{opacity:1}}

/* ── lo que NO se traza entra por opacidad y despues titila ──
   Las particulas son un <g opacity=".8">: `asomar` termina en .8 y no en 1
   justamente para no pisar esa opacidad de origen. */
.js .escena .particulas{opacity:0}
.js .escena[data-dib="1"] .particulas{animation:asomar 1s 1.1s ease forwards}
@keyframes asomar{to{opacity:.8}}
.js .escena[data-dib="1"] .particulas circle{animation:titilar 3.4s ease-in-out infinite}
.js .escena[data-dib="1"] .particulas circle:nth-child(2){animation-delay:.7s}
.js .escena[data-dib="1"] .particulas circle:nth-child(3){animation-delay:1.4s}
.js .escena[data-dib="1"] .particulas circle:nth-child(4){animation-delay:2.1s}
@keyframes titilar{0%,100%{opacity:.35}50%{opacity:1}}
.escena .hub-aro{transform-box:fill-box;transform-origin:50% 50%}
.js .escena[data-dib="1"] .hub-aro{animation:respirar 3.2s .9s ease-out infinite}

/* ── EL TRAZO SOBRE FONDO CLARO: SIN CYAN ──
   Medido: el cyan de marca (#00D4FF) da 1,66:1 sobre el claro (#F9F7FC). Sobre
   oscuro esta perfecto (9,55:1), pero aca el tramo cyan del trazo simplemente
   desaparece —se veia en el marco de la ventana y en la linea del suelo—. No
   se corrige con opacidad ni con mas peso de linea: es el color. Se reorienta
   el gradiente al tramo alto de la MISMA rampa de marca, purple → magenta, que
   dan 5,4:1 y 3,3:1. El punto medio no es un color nuevo: es la interpolacion
   de esos dos, o sea el mismo degradado de siempre recortado. Se puede hacer
   desde CSS porque `stop-color` es un atributo de presentacion y una regla le
   gana. */
.escena--tinta linearGradient stop:nth-child(1){stop-color:#7B2FFF}
.escena--tinta linearGradient stop:nth-child(2){stop-color:#BD2EC8}
.escena--tinta linearGradient stop:nth-child(3){stop-color:#FF2D92}
.escena--tinta path{stroke-width:2.4}

/* La ruta del viaje se dibuja con el scroll, no con el reloj. `--ruta-p` la
   escribe sitio.js midiendo el path real; sin JS el camino esta completo.
   El selector lleva `[data-t]` para GANARLE en especificidad a la regla
   generica de trazado (`.js .escena [data-t]`), que si no dejaria el camino en
   dashoffset 1, o sea invisible. */
.js .escena .ruta[data-t]{stroke-dasharray:1;animation:none;
  stroke-dashoffset:calc(1 - var(--ruta-p,1))}

/* ══════════════════ EL ISOTIPO ══════════════════
   Un solo <symbol> montado tres veces con <use>. Que canales enciende cada
   instancia se decide con variables puestas EN el <use>: las propiedades
   personalizadas heredan dentro del arbol que clona <use>, y esa es la unica
   via que hay para diferenciar dos instancias del mismo simbolo. Es lo que en
   el mockup era la prop `encendidos` de React.
   Encendido = 1, apagado = 0; los estados intermedios los interpola la propia
   transicion, igual que los hacia Framer. */
.iso-nodo{transform-box:fill-box;transform-origin:50% 50%;
  opacity:calc(.13 + .87 * var(--on,0));transform:scale(calc(.7 + .3 * var(--on,0)));
  transition:opacity .75s var(--ease),transform .75s var(--ease)}
.iso-flujo{stroke-dasharray:1;stroke-dashoffset:calc(.9 - .9 * var(--on,0));
  opacity:calc(.26 + .74 * var(--on,0));
  transition:stroke-dashoffset 1.1s var(--ease),opacity 1.1s var(--ease)}
.iso-linea{stroke-dasharray:1;stroke-dashoffset:calc(1 - var(--on,0));
  opacity:calc(.45 * var(--on,0));
  transition:stroke-dashoffset .9s var(--ease),opacity .9s var(--ease)}
[data-n="telefono"]{--on:var(--on-telefono,0)}
[data-n="chat"]{--on:var(--on-chat,0)}
[data-n="email"]{--on:var(--on-email,0)}
[data-n="video"]{--on:var(--on-video,0)}
[data-n="headset"]{--on:var(--on-headset,0)}
/* los flujos entran escalonados, como el `delay: .06*i` del original */
.iso-flujo[data-n="chat"]{transition-delay:.06s}
.iso-flujo[data-n="video"]{transition-delay:.12s}
.iso-flujo[data-n="headset"]{transition-delay:.18s}
.iso-linea[data-n="email"]{transition-delay:.1s}
.iso-linea[data-n="chat"]{transition-delay:.2s}
.iso-linea[data-n="video"]{transition-delay:.3s}
.iso-linea[data-n="headset"]{transition-delay:.4s}

.iso-anillos,.iso-orbita,.iso-nucleo{transform-box:view-box;transform-origin:200px 200px}
.iso-anillos{animation:isoGirar 90s linear infinite}
.iso-orbita{stroke-dasharray:1;stroke-dashoffset:0;
  animation:isoGirar 36s linear infinite,isoRespira 4.5s ease-in-out infinite}
@keyframes isoGirar{to{transform:rotate(360deg)}}
@keyframes isoRespira{0%,100%{opacity:.35}50%{opacity:.75}}
/* El latido del nucleo tiene que poder estar o no estar segun la instancia, y
   una animacion no se puede "apagar" desde una variable. Lo que si se puede es
   que la variable module su AMPLITUD: con --latir en 0 el fotograma del medio
   es scale(1) y el bucle, aunque corra, no mueve un pixel. */
.iso-nucleo{animation:isoLatir 2.1s ease-in-out infinite}
@keyframes isoLatir{0%,100%{transform:scale(1)}
  50%{transform:scale(calc(1 + .06 * var(--latir,0)))}}
.iso-part circle{transform-box:fill-box;transform-origin:50% 50%;
  animation:isoTitila calc(2.6s + var(--i) * .32s) ease-in-out infinite;
  animation-delay:calc(var(--i) * .22s)}
@keyframes isoTitila{
  0%,100%{opacity:calc(var(--o) * .25);transform:scale(.7)}
  50%{opacity:var(--o);transform:scale(1.25)}}

/* EL ISOTIPO COMO MOTOR DEL FILM: enciende un canal por escena.
   hero=telefono · ruta=chat · film=headset · producto=video · plataforma=email
   son los cinco hitos del hilo conductor; esto es otra cosa: el recuento de
   canales que ya entraron en juego dentro del recorrido. */
.motor svg{--on-telefono:0;--on-chat:0;--on-email:0;--on-video:0;--on-headset:0;--latir:0}
.film[data-esc="1"] .motor svg{--on-chat:1}
.film[data-esc="2"] .motor svg{--on-chat:1;--on-telefono:1;--on-headset:1}
.film[data-esc="3"] .motor svg{--on-chat:1;--on-telefono:1;--on-headset:1;--on-video:1}
.film[data-esc="4"] .motor svg{--on-chat:1;--on-telefono:1;--on-headset:1;--on-video:1;--on-email:1;--latir:1}
/* el recuento "N / 5 canales" tiene que coincidir con lo que se ve encendido */
.motor small i{display:none;font-style:normal}
.film[data-esc="0"] .motor i[data-i="0"],.film[data-esc="1"] .motor i[data-i="1"],
.film[data-esc="2"] .motor i[data-i="2"],.film[data-esc="3"] .motor i[data-i="3"],
.film[data-esc="4"] .motor i[data-i="4"]{display:inline}

/* ══════════════════ PRODUCTO (las capturas reales) ══════════════════
   El selector de pantallas son RADIOS de verdad, no botones con JavaScript:
   asi funciona sin JS, con teclado y con lector de pantalla sin escribir una
   linea de accesibilidad a mano. El JS solo agrega la rotacion automatica. */
.prod{position:relative;overflow:hidden}
.prod__luz{position:absolute;inset:0;z-index:0;pointer-events:none;opacity:0;
  transition:opacity .7s var(--ease);
  background:radial-gradient(620px circle at var(--mx,50%) var(--my,40%),rgba(123,47,255,.2),rgba(0,212,255,.07) 46%,transparent 72%)}
.prod[data-luz="1"] .prod__luz{opacity:1}
.prod .wrap{position:relative;z-index:1}
.prod__grid{display:grid;grid-template-columns:minmax(0,300px) minmax(0,1fr);
  gap:clamp(22px,3vw,46px);align-items:start;border:0;padding:0;margin:0;min-width:0}
/* min-width:0 obligatorio: la barra de URL del marco es nowrap y su ancho
   minimo desbordaba la columna 34 px hacia la derecha en telefono */
.prod__grid > *{min-width:0}
@media(max-width:1040px){.prod__grid{grid-template-columns:1fr}}

.prod__tabs{display:flex;flex-direction:column;gap:8px}
@media(max-width:1040px){.prod__tabs{flex-direction:row;flex-wrap:wrap}.tab{flex:1 1 260px}}
@media(max-width:660px){.prod__tabs{flex-direction:column}.tab{flex:none;width:100%}}
.prod input[type=radio]{position:absolute;opacity:0;pointer-events:none;width:0;height:0}
.tab{position:relative;display:block;text-align:left;padding:15px 18px;border-radius:14px;
  border:1px solid var(--velo);cursor:pointer;
  background:rgba(255,255,255,.03);transition:border-color .35s,background .35s,transform .35s;
  overflow:hidden}
.tab:hover{border-color:rgba(0,212,255,.45);background:rgba(0,212,255,.07);transform:translateX(3px)}
.tab b{display:block;font-family:var(--display);font-weight:600;font-size:15.5px;color:#fff;
  letter-spacing:-.01em}
.tab span{display:block;font-size:12.8px;color:#9BA0B6;margin-top:3px;line-height:1.45}
.tab i{position:absolute;right:14px;top:15px;font-family:var(--mono);font-size:10px;
  letter-spacing:.16em;color:#7B7FA0;font-style:normal}
.prod input:checked + .tab{border-color:transparent;background:var(--noche-3);
  box-shadow:0 14px 34px -18px rgba(123,47,255,.9)}
.prod input:checked + .tab::before{content:"";position:absolute;left:0;top:0;bottom:0;width:3px;
  background:var(--barra)}
.prod input:focus-visible + .tab{outline:2px solid var(--cyan);outline-offset:3px}

/* marco de navegador — la captura es el activo mas creible que hay */
.marco{position:relative;border-radius:16px;overflow:hidden;border:1px solid var(--velo-2);
  background:var(--noche-3);box-shadow:var(--sombra-marca),0 0 0 1px rgba(0,212,255,.09)}
.marco__barra{display:flex;align-items:center;gap:8px;padding:11px 14px;
  background:rgba(255,255,255,.045);border-bottom:1px solid var(--velo)}
.marco__pt{width:10px;height:10px;border-radius:50%;flex:none}
.marco__url{margin-left:10px;flex:1 1 0;min-width:0;display:block;height:26px;line-height:26px;
  padding:0 12px;border-radius:999px;background:rgba(0,0,0,.28);font-family:var(--mono);
  font-size:10.5px;letter-spacing:.06em;color:#9BA0B6;overflow:hidden;white-space:nowrap;
  text-overflow:ellipsis;position:relative}
.marco__url span{position:absolute;inset:0 12px;line-height:26px;opacity:0;transition:opacity .3s}
/* 2,2:1 es el compromiso: por encima de esa proporcion la captura perdia
   columnas por los costados y el recuadro de foco dejaba de caer donde debia.
   A 2,2 el recorte es vertical (la ultima fila de la tabla) en casi todas. */
.marco__lienzo{position:relative;aspect-ratio:2.2;background:#fff;overflow:hidden}
.pantalla{position:absolute;inset:0;opacity:0;visibility:hidden;
  transition:opacity .6s var(--ease),transform .6s var(--ease),visibility 0s .6s;
  transform:scale(.99) translateX(-22px)}
.pantalla img{width:100%;height:100%;object-fit:cover;object-position:50% 0}
/* ⚠ LA 02 VA EN `contain` Y NO EN `cover`, Y NO ES UNA PREFERENCIA.
   La captura del caso se recorto a mano para sacar nombres y quedo de 560x205,
   o sea 2,73:1 -- mas apaisada que el marco. Con `cover` el marco recorta por
   los COSTADOS y se comia justo los extremos: a la izquierda la pestana Script
   y el primer paso del guion, a la derecha el boton de enviar y el contador
   1/5. Con `contain` entra entera y las bandas que sobran arriba y abajo son
   blancas, que es el mismo blanco del lienzo y de la propia aplicacion: se leen
   como aire de la pantalla, no como marco.
   El `.foco` de la 02 esta calculado sobre ESTE mapa (banda del 9,73 % arriba y
   abajo, ancho completo). Si esto vuelve a `cover`, el recuadro deja de caer
   donde debe. */
.pantalla[data-pant="2"] img{object-fit:contain;object-position:50% 50%}
/* la zona destacada: un recuadro que respira sobre la captura */
.foco{position:absolute;border-radius:10px;border:2px solid var(--magenta);pointer-events:none;
  box-shadow:0 0 0 6px rgba(255,45,146,.16),0 0 30px rgba(255,45,146,.45);opacity:0}
.foco b{position:absolute;left:0;top:-27px;font-family:var(--mono);font-size:9.5px;
  letter-spacing:.16em;text-transform:uppercase;color:#fff;background:var(--magenta);
  padding:4px 9px;border-radius:6px;white-space:nowrap}
/* La 02 lleva su rotulo A LA DERECHA, no a la izquierda: pegado al borde
   izquierdo cae justo encima de «Derivación a Especialista» y lo tapa. A la
   derecha queda entre la barra de avance y el «1/5», que es hueco en las dos
   medidas. Se probo tambien por debajo del recuadro y no sirve: en telefono el
   marco mide 145 px de alto, el rotulo se sale por abajo y `.marco` lo recorta
   con su `overflow:hidden`. */
.foco--pie b{left:auto;right:0}
/* Los cinco pies se apilan en LA MISMA celda de una rejilla, no con posicion
   absoluta: asi el hueco lo fija el pie mas largo y no un `min-height` a ojo.
   Con una altura fija adivinada, en telefono el pie de "El caso abierto" pasa
   de seis lineas y se derrama sobre la seccion siguiente. */
.prod__pies{display:grid;margin-top:16px}
.prod__pie{grid-area:1/1;font-size:13px;color:#8A8FA6;max-width:74ch;line-height:1.6;
  opacity:0;visibility:hidden;transition:opacity .45s var(--ease),visibility 0s .45s}
.prod__pie strong{color:#B0B5CB;font-weight:500}

/* el radio elegido manda sobre su lamina, su pie y su rotulo de URL.
   `:has()` evita repetir cinco veces la cadena de hermanos. */
.prod__grid:has(#pant-1:checked) [data-pant="1"],.prod__grid:has(#pant-2:checked) [data-pant="2"],
.prod__grid:has(#pant-3:checked) [data-pant="3"],.prod__grid:has(#pant-4:checked) [data-pant="4"],
.prod__grid:has(#pant-5:checked) [data-pant="5"]{
  opacity:1;visibility:visible;transform:none;transition-delay:0s}
.prod__grid:has(#pant-1:checked) .foco[data-pant="1"],.prod__grid:has(#pant-2:checked) .foco[data-pant="2"],
.prod__grid:has(#pant-3:checked) .foco[data-pant="3"],.prod__grid:has(#pant-4:checked) .foco[data-pant="4"],
.prod__grid:has(#pant-5:checked) .foco[data-pant="5"]{animation:focar 2.6s .55s var(--ease) forwards}
@keyframes focar{
  0%{opacity:0;transform:scale(.94)}
  20%{opacity:1;transform:scale(1)}
  55%{opacity:1}75%{opacity:.55}100%{opacity:1;transform:scale(1)}}
/* Sin :has() (navegadores viejos) las cinco laminas quedarian encimadas. El
   respaldo muestra solo la primera, que es un estado valido y legible. */
@supports not selector(:has(*)){
  .pantalla[data-pant="1"],.prod__pie[data-pant="1"]{opacity:1;visibility:visible;transform:none}
  .prod__tabs{display:none}
}

/* ══════════════════════════ CIFRAS ══════════════════════════ */
.cifras{display:grid;grid-template-columns:repeat(4,1fr);gap:1px;background:var(--velo);
  border:1px solid var(--velo);border-radius:22px;overflow:hidden}
@media(max-width:940px){.cifras{grid-template-columns:repeat(2,1fr)}}
@media(max-width:520px){.cifras{grid-template-columns:1fr}}
.cifra{background:var(--noche-2);padding:32px 26px 28px;position:relative;overflow:hidden;
  height:100%;transition:background .4s var(--ease)}
.cifra::before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background:var(--barra);
  transform:scaleX(0);transform-origin:0 50%;transition:transform .8s var(--ease)}
.cifra.vis::before,html:not(.js) .cifra::before{transform:scaleX(1)}
.cifra:hover{background:var(--noche-3)}
.cifra__n{font-family:var(--display);font-weight:800;font-size:clamp(36px,4.4vw,58px);
  letter-spacing:-.045em;line-height:1;color:#fff;font-variant-numeric:tabular-nums}
.cifra__n em{font-style:normal;background:var(--barra);-webkit-background-clip:text;
  background-clip:text;color:transparent}
.cifra__t{margin-top:12px;font-size:15px;color:#CBCEDC;font-weight:500}
.cifra__c{margin-top:8px;font-size:12.5px;color:#8A8FA6;line-height:1.5}
.nota{margin-top:22px;font-size:13px;color:#8A8FA6;max-width:72ch}
.nota strong{color:#B0B5CB;font-weight:500}

/* ══════════════════════════ MARQUESINA ══════════════════════════ */
.marq{position:relative;padding-block:24px;border-block:1px solid var(--velo);overflow:hidden;
  background:linear-gradient(90deg,rgba(123,47,255,.11),rgba(0,212,255,.09))}
.marq::before,.marq::after{content:"";position:absolute;top:0;bottom:0;width:20%;z-index:3;
  pointer-events:none}
.marq::before{left:0;background:linear-gradient(90deg,var(--noche) 16%,rgba(30,26,46,0) 100%)}
.marq::after{right:0;background:linear-gradient(270deg,var(--noche) 16%,rgba(30,26,46,0) 100%)}
.marq__pista{display:flex;width:max-content;animation:desfilar 34s linear infinite}
@keyframes desfilar{to{transform:translateX(-33.3333%)}}
.marq__it{display:flex;align-items:center;gap:13px;padding-inline:28px;flex:none}
.marq__it img{width:32px;height:32px}
.marq__it span{font-family:var(--display);font-weight:600;font-size:clamp(18px,2.2vw,29px);
  letter-spacing:-.02em;color:#EDEEF4;white-space:nowrap}
.marq__it b{width:5px;height:5px;border-radius:50%;background:var(--purple);margin-left:15px}

/* ══════════════ TARJETAS + ILUMINACION DEL CURSOR ══════════════
   El halo sigue al puntero dentro de la tarjeta (--mx/--my en px), la apuntada
   crece y revela su linea extra, y las vecinas se apagan. */
/* ── LA ESCENA DE LOS CANALES, AL LADO DEL ENCABEZADO DE LA PLATAFORMA ──
   El dibujo es literal —los cinco canales entrando a un hub— y por eso vive
   junto al pilar de opticanalidad y no suelto. Se le da un halo propio, igual
   que a `alta`, para que no flote sobre el fondo plano de la seccion. */
.plat__intro{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,.84fr);
  gap:clamp(16px,4vw,58px);align-items:center;margin-bottom:clamp(40px,6vh,68px)}
.plat__intro .sec__cab{margin-bottom:0}
.plat__art{position:relative}
.plat__art::before{content:"";position:absolute;inset:-16% -8%;z-index:0;
  background:radial-gradient(56% 60% at 50% 46%,rgba(123,47,255,.20),transparent 72%),
             radial-gradient(46% 50% at 74% 66%,rgba(0,212,255,.13),transparent 74%)}
.plat__art > svg{position:relative;z-index:1}
@media(max-width:960px){
  .plat__intro{grid-template-columns:1fr;gap:10px}
  .plat__art{max-width:540px;margin-inline:auto}
}

/* ── EL PAR DE DIBUJOS DE CAPACIDADES ──
   El aviso que llega y la hora que queda tomada. Van antes de la lista de
   chips: la lista es inventario y estos dos son lo que ese inventario hace. */
.capac__par{display:grid;grid-template-columns:1fr 1fr;
  gap:clamp(14px,4vw,60px);align-items:center;margin-bottom:clamp(28px,5vh,52px)}
@media(max-width:780px){.capac__par{grid-template-columns:1fr;gap:4px;
  max-width:520px;margin-inline:auto}}

.rejilla{display:grid;gap:18px}
.rejilla--4{grid-template-columns:repeat(4,1fr)}
@media(max-width:1120px){.rejilla--4{grid-template-columns:repeat(2,1fr)}}
@media(max-width:660px){.rejilla--4{grid-template-columns:1fr}}
.rejilla > *{height:100%}
.card{position:relative;border-radius:20px;padding:28px 26px 30px;height:100%;
  display:flex;flex-direction:column;
  background:linear-gradient(165deg,rgba(255,255,255,.07),rgba(255,255,255,.02));
  border:1px solid var(--velo);overflow:hidden;isolation:isolate;
  transition:transform .45s var(--ease),filter .45s var(--ease),opacity .45s var(--ease),border-color .4s}
.card::before{content:"";position:absolute;inset:0;border-radius:inherit;padding:1px;
  pointer-events:none;
  background:radial-gradient(260px circle at var(--mx,50%) var(--my,50%),rgba(123,47,255,1),rgba(0,212,255,.55) 38%,transparent 68%);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude;
  opacity:0;transition:opacity .35s}
.card::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  z-index:-1;
  background:radial-gradient(320px circle at var(--mx,50%) var(--my,50%),rgba(123,47,255,.3),transparent 62%);
  opacity:0;transition:opacity .35s}
.card:hover::before,.card:hover::after,.card:focus-within::before,.card:focus-within::after{opacity:1}
.card h3{font-size:20.5px;letter-spacing:-.022em;margin-block:15px 10px}
.card p{font-size:14.6px;color:#A3A8BE;line-height:1.62}
.card__k{font-family:var(--mono);font-size:11px;letter-spacing:.2em;color:#8A8FA6;
  margin-top:auto;padding-top:20px}
.card__ico{width:42px;height:42px;object-fit:contain}
/* la linea que solo aparece bajo el cursor */
.revela{max-height:0;opacity:0;overflow:hidden;transform:translateY(8px);
  transition:max-height .5s var(--ease),opacity .38s ease,transform .5s var(--ease)}
.revela p{padding-top:13px;margin-top:13px;border-top:1px solid var(--velo-2);
  font-size:13.4px;color:#9BA0B6}
.card:hover .revela,.card:focus-within .revela{max-height:340px;opacity:1;transform:none}
/* En Cumplimiento el detalle NO se esconde detras del cursor. Ahi viven las
   salvedades que el propio contenido marco como no publicables sin matiz —el
   estado real de la EIPD, el alcance de la integracion— y una salvedad que hay
   que descubrir pasando el mouse no es una salvedad: en telefono no hay hover,
   y el recorte cortaba la frase a la mitad. */
.card--abierta .revela{max-height:none;opacity:1;transform:none;overflow:visible}
.card--abierta .revela p{border-top-color:var(--velo)}
/* en pantalla tactil no hay cursor: el detalle de Plataforma se muestra entero
   o no se muestra nunca */
@media(hover:none),(pointer:coarse){
  .revela{max-height:none;opacity:1;transform:none;overflow:visible}
}
@media(hover:hover) and (pointer:fine){
  .rejilla:has(.card:hover) .card:not(:hover){transform:scale(.975);filter:saturate(.6) brightness(.94);opacity:.62}
  .rejilla .card:hover{transform:scale(1.022);z-index:4}
}

.capac{position:relative;display:flex;align-items:center;gap:13px;padding:16px 19px;
  border-radius:14px;border:1px solid var(--velo);background:rgba(255,255,255,.035);
  font-size:15px;color:#D6D9E5;overflow:hidden;
  transition:border-color .3s,background .3s,transform .3s;height:100%}
.capac::after{content:"";position:absolute;inset:0;pointer-events:none;opacity:0;
  transition:opacity .35s;
  background:radial-gradient(180px circle at var(--mx,50%) var(--my,50%),rgba(0,212,255,.2),transparent 66%)}
.capac:hover{border-color:rgba(0,212,255,.5);transform:translateY(-2px)}
.capac:hover::after{opacity:1}
.capac i{width:7px;height:7px;border-radius:50%;flex:none;background:var(--barra);
  position:relative;z-index:1}

/* ═══════════ CUMPLIMIENTO — el diferencial, no la letra chica ═══════════
   Misma tarjeta iluminada que Plataforma, pero con el icono de linea luminosa
   y el numero de la norma en el pie: es el dato que separa a un proveedor
   serio de uno que improvisa, asi que se muestra, no se esconde. */
.cump{position:relative;overflow:hidden}
.cump__aura{position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(64% 48% at 78% 12%,rgba(0,212,255,.13),transparent 66%),
             radial-gradient(56% 44% at 12% 88%,rgba(255,45,146,.11),transparent 68%)}
.cump .wrap{position:relative;z-index:1}
/* la tira de normas: son nombres propios, van en mono y con su numero */
.normas{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:clamp(28px,4vh,44px)}
.normas span{font-family:var(--mono);font-size:10.5px;letter-spacing:.15em;text-transform:uppercase;
  padding:8px 13px;border-radius:8px;border:1px solid var(--velo);color:#A8ADC4;
  background:linear-gradient(150deg,rgba(255,255,255,.055),rgba(255,255,255,.012))}
.normas span b{color:#8FE6FF;font-weight:500}
.card__luz{width:100%;height:76px;overflow:visible}
.card__luz svg{width:76px;height:76px;overflow:visible;display:block;margin-left:-6px;margin-top:-6px}
.cump__pie{margin-top:26px;padding-top:22px;border-top:1px solid var(--velo);
  font-size:13.4px;color:#8A8FA6;max-width:88ch;line-height:1.68}
.cump__pie strong{color:#B0B5CB;font-weight:500}
/* La atribucion de las ISO va SEPARADA y con su propio realce: son del holding
   Espex, NO de ContactIA, y esa distincion no puede quedar diluida en medio de
   un parrafo. */
.cump__iso{display:flex;gap:12px;align-items:flex-start;margin-top:16px;padding:14px 16px;
  border-radius:12px;border:1px solid rgba(255,45,146,.26);background:rgba(255,45,146,.055)}
.cump__iso i{width:6px;align-self:stretch;border-radius:3px;background:var(--barra);flex:none}
.cump__iso p{font-size:13.4px;color:#9BA0B6;line-height:1.66}
.cump__iso p b{color:#FFC2DF;font-weight:600}

/* los iconos de linea luminosa se trazan cuando la seccion entra en pantalla */
.ico-luz [data-t]{stroke-dasharray:1;stroke-dashoffset:0}
.js .ico-luz [data-t]{stroke-dashoffset:1;transition:stroke-dashoffset .8s var(--ease) var(--d,0s)}
.js .vis .ico-luz [data-t]{stroke-dashoffset:0}

/* ══════════════════════════ ACORDEON ══════════════════════════
   <details name> nativo: acordeon exclusivo, accesible y con teclado sin una
   linea de JavaScript. Donde el navegador no soporte el atributo `name` se
   pueden abrir varios a la vez, que es un estado perfectamente utilizable. */
.acc{border-top:1px solid var(--line)}
.acc__it{border-bottom:1px solid var(--line)}
.acc__it summary{width:100%;display:flex;align-items:center;gap:20px;padding:25px 4px;
  text-align:left;cursor:pointer;list-style:none}
.acc__it summary::-webkit-details-marker{display:none}
.acc__it summary h3{font-size:clamp(21px,3vw,34px);letter-spacing:-.03em;flex:1}
/* sobre el fondo claro el degradado NO puede arrancar en cyan: #00D4FF da 1,7:1
   sobre #F9F7FC y el titulo se vuelve ilegible. Arranca en purple (5,3:1). */
.acc__it[open] summary h3{background:linear-gradient(90deg,#7B2FFF 0%,#FF2D92 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent}
.acc__n{font-family:var(--mono);font-size:12px;color:var(--gray);letter-spacing:.16em}
.acc__mas{width:34px;height:34px;border-radius:50%;border:1px solid var(--line);display:grid;
  place-items:center;flex:none;position:relative}
.acc__mas::before,.acc__mas::after{content:"";position:absolute;background:var(--ink);
  border-radius:2px;transition:transform .4s var(--ease)}
.acc__mas::before{width:13px;height:1.6px}.acc__mas::after{width:1.6px;height:13px}
.acc__it[open] .acc__mas::after{transform:scaleY(0)}
.acc__p{padding:0 4px 28px;max-width:66ch;font-size:16.5px;color:var(--muted)}
/* la apertura se anima con una rejilla de una fila: es la unica forma portable
   de transitar de 0 a "lo que mida el contenido" */
.acc__caja{display:grid;grid-template-rows:0fr;transition:grid-template-rows .45s var(--ease)}
.acc__it[open] .acc__caja{grid-template-rows:1fr}
.acc__caja > div{overflow:hidden}

/* ══════════════════════════ CIERRE + PIE ══════════════════════════ */
.cierre{position:relative;overflow:hidden;isolation:isolate;
  padding-block:clamp(100px,15vh,180px)}
.cierre__foto{position:absolute;inset:-10%;z-index:-2;
  filter:brightness(1.05) url(#duo-magenta)}
.cierre__foto img{width:100%;height:100%;object-fit:cover;object-position:50% 86%;opacity:.42}
/* el pasillo del alta: va grande, centrado y muy bajo de opacidad, como
   atmosfera detras del titular y nunca compitiendo con el */
.cierre__velo{position:absolute;inset:0;z-index:-1;background:
  linear-gradient(180deg,var(--noche) 0%,rgba(30,26,46,.7) 32%,rgba(30,26,46,.9) 100%),
  radial-gradient(58% 58% at 62% 52%,rgba(255,45,146,.32),transparent 70%)}
/* el parallax del cierre usa `view()`: la seccion no arranca en el origen del
   documento, asi que la linea de tiempo tiene que ser la de SU paso por la
   ventana y no la del scroll global */
@supports (animation-timeline:view()){
  @media (prefers-reduced-motion:no-preference){
    .cierre__foto{animation:cierreFoto linear both;animation-timeline:view();animation-range:cover 0% cover 100%}
  }
}
@keyframes cierreFoto{from{transform:translateY(-10%)}to{transform:translateY(10%)}}
/* la ultima escena ocupa el aire que el cierre dejaba a la derecha. `align-items:
   center` y no `end`: el bloque de texto es alto (titular + bajada + dos botones
   + los datos de contacto) y con el dibujo alineado abajo quedaba un hueco
   grande arriba justo donde esta el titular, que es lo que hay que mirar. */
.cierre__grid{display:grid;grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  gap:clamp(20px,4vw,64px);align-items:center}
.cierre__art{position:relative}
@media(max-width:1000px){
  .cierre__grid{grid-template-columns:1fr;gap:20px}
  .cierre__art{max-width:540px}
}
.cierre h2{font-size:clamp(33px,5.4vw,74px);font-weight:800;letter-spacing:-.042em;max-width:17ch;
  margin-top:16px}
.cierre__sub{max-width:52ch;margin-top:22px;font-size:18px}
.contacto{display:flex;flex-wrap:wrap;gap:14px 36px;align-items:center;margin-top:36px}
.contacto a,.contacto span.dato{display:inline-flex;align-items:center;gap:10px;font-size:16.5px;
  color:#D6D9E5}
.contacto a:hover{color:#fff}
.contacto img{width:26px;height:26px}
/* El numero atiende llamadas Y WhatsApp: se marca con los DOS nodos del
   isotipo (telefono + chat, pegados). Nunca el logo verde de Meta: es marca de
   un tercero y rompe la paleta. */
.dosnodos{display:inline-flex;align-items:center}
.dosnodos img+img{margin-left:-6.5px}
.contacto small{font-family:var(--mono);font-size:10.5px;letter-spacing:.16em;text-transform:uppercase;
  color:#8A8FA6;margin-left:4px}
/* en telefono el numero y su aclaracion no caben en la misma linea: sin esto
   el "+56 2 2360 7736" se parte por la mitad para hacerle sitio al rotulo */
@media(max-width:640px){
  .contacto{flex-direction:column;align-items:flex-start;gap:16px}
  .contacto a,.contacto span.dato{flex-wrap:wrap}
  .contacto small{margin-left:0;flex-basis:100%}
}

/* el pie respira por abajo para que el boton flotante de WhatsApp no se apoye
   encima de los enlaces legales: el boton esta fijo al borde de la ventana, asi
   que la unica forma de despejarlo es que el ultimo contenido termine antes */
footer{border-top:1px solid var(--velo);padding-block:34px clamp(92px,11vh,124px);
  font-size:13px;color:#8A8FA6}
.foot{display:flex;flex-wrap:wrap;gap:26px 40px;align-items:flex-end;justify-content:space-between}

/* ── EL BLOQUE DEL HOLDING ──
   Mismo contenido que `pie_holding()` en branding/base/email/generar_firma.py.
   Los cuatro logos son MARCAS DE TERCEROS: van tal cual, sin recolorear, sin
   recortar y sin ningun efecto -- ni opacidad, ni filtro, ni mezcla. Por eso
   no heredan nada del pie y por eso el suelo es una placa blanca: en color dan
   0,97:1 (espex) y 1,97:1 (ecs) sobre el #1E1A2E de aca, o sea invisibles, y
   las variantes `-gris` son todavia mas oscuras (van para fondo claro).
   El blanco es PURO: `americatech` es el unico sin transparencia y su fondo
   blanco opaco se recortaria como un rectangulo sobre el claro de marca. */
.holding{max-width:640px}
/* sin tope de medida: la frase entra en una linea dentro de los 640 px del
   bloque, y con un `max-width` en `ch` el "a:" del final se caia solo a la
   segunda linea */
.holding__linea{margin:0;font-size:12px;line-height:1.6;color:#8A8FA6}
.holding__linea strong{color:#CBCEDC;font-weight:600}
.holding__marcas{display:flex;flex-wrap:wrap;align-items:center;gap:15px 28px;
  width:fit-content;margin-top:14px;padding:15px 22px;border-radius:14px;
  background:#fff;box-shadow:0 14px 34px -18px rgba(0,0,0,.9)}
/* `width:auto` no es decorativo: cada <img> lleva su width/height real en el
   marcado para reservar sitio y no dar un salto de maquetacion, y esos
   atributos son una pista de presentacion que sigue mandando sobre el ancho si
   el CSS solo declara la altura. Sin esto los cuatro salen de 434x26, o sea
   aplastados y todos del mismo ancho. */
.holding__marcas img{height:26px;width:auto;display:block}
@media(max-width:620px){
  .holding__marcas{gap:13px 20px;padding:13px 16px}
  .holding__marcas img{height:22px}
}
.foot__enl{display:flex;flex-wrap:wrap;gap:16px}
.foot__enl a{border-bottom:1px solid transparent;transition:color .25s,border-color .25s}
.foot__enl a:hover{color:#CBCEDC;border-bottom-color:var(--velo-2)}

.saltar{position:absolute;left:-9999px;top:0;z-index:1001;background:#fff;color:#0A0A0A;
  padding:10px 16px;border-radius:8px}
.saltar:focus{left:12px;top:12px}

/* ══════════════════ BOTON FLOTANTE DE WHATSAPP ══════════════════
   El numero recibe llamadas y WhatsApp. El icono es el nodo `chat` del propio
   isotipo, no el logo verde de Meta. */
.flota{position:fixed;right:clamp(14px,2.4vw,26px);bottom:clamp(14px,2.4vw,26px);z-index:997;
  display:flex;align-items:center;gap:11px;padding:12px 20px 12px 14px;border-radius:999px;
  background:var(--noche-3);border:1px solid var(--velo-2);color:#EDEEF4;font-size:14px;
  font-weight:600;box-shadow:0 18px 44px -14px rgba(0,0,0,.75),0 0 0 1px rgba(123,47,255,.28);
  backdrop-filter:blur(10px);transition:transform .35s var(--ease),box-shadow .35s,border-color .35s}
.flota img{width:26px;height:26px;flex:none}
.flota:hover{transform:translateY(-3px);border-color:rgba(0,212,255,.5);
  box-shadow:0 22px 52px -14px rgba(0,0,0,.8),0 0 0 1px rgba(0,212,255,.45)}
.flota::after{content:"";position:absolute;inset:-4px;border-radius:999px;
  border:1px solid rgba(0,212,255,.45);opacity:0;animation:flotapulso 3.6s ease-out infinite}
@keyframes flotapulso{0%{opacity:.55;transform:scale(.94)}70%,100%{opacity:0;transform:scale(1.12)}}
@media(max-width:560px){.flota span{display:none}.flota{padding:13px;gap:0}}

/* ══════════════════════════ MOVIMIENTO REDUCIDO ══════════════════════════ */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms !important;animation-iteration-count:1 !important;
    transition-duration:.001ms !important;animation-delay:0s !important}
  .scrollcue i::after{display:none}
  .flota::after{display:none}
  #hilo .trazo{stroke-dashoffset:0 !important}
  #hilo .nodo{opacity:1 !important;transform:none !important}
  /* el dibujo aparece entero, sin trazarse. Solo se fuerza la opacidad de lo
     que el CSS esconde para animarlo; lo demas conserva la opacidad que trae
     el propio SVG (las sombras van a .42 a proposito, no a 1). */
  .escena [data-t],.ico-luz [data-t]{stroke-dashoffset:0 !important}
  .escena .particulas{opacity:.8 !important}
  .escena .chispa,.escena .chispa-ia,.escena .halo-hito,.escena .hub-aro{transform:none !important}
  /* la ruta la resuelve sitio.js poniendo --ruta-p en 1: el camino aparece
     entero y la figura queda al final del recorrido, quieta */
  .iso-anillos,.iso-orbita,.iso-nucleo,.iso-part circle{transform:none !important}
  .iso-orbita{opacity:.6 !important}
  .iso-part circle{opacity:var(--o) !important}
  .js [data-rev],.js .pal .w{opacity:1 !important;transform:none !important;filter:none !important}
  .foco{opacity:1 !important;animation:none !important}
}
