/* ==========================================================================
   PURIFICADORES MANANTIALES

   Adaptación al frontend entregado por el cliente (Manantiales.dc.html,
   export del design canvas, 12/8/2026). Ese archivo es la fuente de verdad
   del aspecto; acá está implementado sobre el HTML real del sitio.

     · Tipografía — Barlow Condensed en titulares, Barlow para leer.
     · Paleta — tinta #1d2d3d, primario #2c455d, acento #416180,
       secundario #5980a6, banda alterna #f5f7f9, chip #eef6ff, pie #16222e.
     · Forma — 8px en lo que se toca, 14px en tarjetas, 16px en medios.
     · Sombras — teñidas con la tinta de marca, nunca negro puro.
     · Cabecera — adherida a todo el ancho, vidrio y filete inferior.
     · Ritmo — blanco, gris muy claro, blanco; el bloque de color oscuro
       queda reservado para la llamada final y el pie.

   QUÉ SE CONSERVÓ DEL SITIO, Y POR QUÉ
   El diseño entregado no incluye formulario de contacto, testimonios ni la
   lámina del filtro, y sí incluye equipo, beneficios y mapa. Se aplicó el
   sistema visual SIN borrar secciones: quitar el formulario de WhatsApp
   habría sido una regresión de conversión, no un cambio de estilo. Las
   secciones nuevas del diseño quedan pendientes de contenido real.

   Los nombres de los tokens siguen en castellano y con la raíz celeste
   porque los usa toda la hoja y el panel; lo que cambió son los valores.
   Renombrarlos habría tocado 500 reglas sin mejorar nada.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
    /* El panel sobrescribe estos dos */
    --primary: #416180;
    --accent: #2C455D;

    /* Paleta del MASTER. Los nombres en castellano se conservan porque los
       usa toda la hoja; lo que cambia son los valores. */
    --celeste: #416180;         /* Acento: rótulos, enlaces, trazo          */
    --celeste-claro: #5980a6;   /* Acento claro: viñetas, numeración        */
    --celeste-suave: #eef6ff;   /* Fondo de etiquetas y chips               */
    --celeste-nube: #f5f7f9;    /* Banda alterna                            */
    --celeste-hondo: #2c455d;   /* Primario: botones y superficies de acción*/
    --celeste-noche: #1d2d3d;   /* Tinta y bandas oscuras                   */
    --celeste-pie: #16222e;     /* El pie va un punto más oscuro que la CTA */

    /* Neutros del diseño, sobre blanco */
    --tinta: #1d2d3d;
    --tinta-2: #4a5a68;
    --tinta-3: #5d6b78;
    --blanco: #FFFFFF;
    --papel: #EEF3F9;           /* La página es blanca; la banda alterna
                                   es --celeste-nube (#f5f7f9)             */
    --linea: #e3e7eb;           /* rgba(29,45,61,.08) resuelto sobre blanco */
    --linea-2: #ccd3da;

    /* La acción. Medido con axe: #16A34A con texto blanco daba 3.29:1, por
       debajo del 4.5:1 de la norma. Este llega a 5.0:1 y sigue leyendo a
       verde de WhatsApp. */
    --accion-texto: #2c455d;    /* Enlaces e iconos de acento. Mismo tono que el
                                   relleno de los botones, pero garantizado
                                   legible SOBRE EL FONDO de la pagina: el
                                   relleno puede ser amarillo y el texto no. */
    --btn-texto: #fff;          /* Texto sobre los botones rellenos         */
    --wa-texto: #fff;           /* Texto sobre el boton de WhatsApp         */
    --sobre-oscuro: #fff;       /* Texto sobre las bandas oscuras           */
    --wa: #15803D;
    --wa-fuerte: #126A33;

    --estrella: #D97706;
    --error: #DC2626;           /* Destructive                              */

    /* Barlow Condensed para titular, Barlow para leer. La condensada es la
       decisión de identidad: comprime el titular sin bajarle el cuerpo, que
       es lo que le da el aire de cartel corporativo. */
    --font-head: 'Barlow Condensed', 'Arial Narrow', system-ui, sans-serif;
    --font-body: 'Barlow', system-ui, -apple-system, 'Segoe UI', sans-serif;

    /* Una sola escala de forma, según el diseño: 8px en lo que se toca,
       14px en tarjetas y medios. La píldora queda para el botón flotante. */
    --r-sm: 8px;
    --r-md: 10px;
    --r-lg: 14px;
    --r-pill: 999px;
    --r-media: 16px;

    /* Sombras teñidas con la tinta de marca, nunca negro puro: el negro
       sobre un azul frío ensucia el borde. */
    --sh-xs: 0 1px 2px rgba(29, 45, 61, .06);
    --sh-sm: 0 4px 12px rgba(29, 45, 61, .08);
    --sh-soft: 0 12px 32px rgba(29, 45, 61, .10);
    --sh-lift: 0 16px 32px rgba(29, 45, 61, .12);
    --sh-hero: 0 20px 48px rgba(29, 45, 61, .14);
    --sh-inner: 0 0 #0000;

    /* "Transiciones limpias de 150-200ms ease", según el MASTER */
    --ease: ease;
    --t-fast: 150ms ease;
    --t-base: 200ms ease;

    /* ---- MOVIMIENTO ------------------------------------------------------
       El panel regula estas tres. `--mo` es un multiplicador global: en 0 el
       movimiento decorativo desaparece pero el feedback de los controles
       (foco, pulsación, hover) se mantiene, porque eso no es decoración,
       es respuesta. Todas las duraciones decorativas se multiplican por él,
       así que un solo número apaga o exagera la página entera.
       Las curvas: salida suave para lo que entra, que es lo que pide la
       norma de animación; el "resorte" sólo en confirmaciones puntuales. */
    --mo: 1;
    /* Curva expo: arranca rápido y frena largo. Es la que da la sensación de
       peso, de que la pieza "llega" en vez de aparecer. */
    --ease-salida: cubic-bezier(.16, 1, .3, 1);
    --ease-resorte: cubic-bezier(.34, 1.56, .64, 1);
    --mo-reveal: calc(880ms * var(--mo));
    --mo-entrada: calc(1000ms * var(--mo));
    /* Recorrido largo a pedido: el movimiento se tiene que ver, no intuir.
       La norma general recomienda 8-16px para que lea como fundido; acá se
       va a 34px (y ~46px en "Expresivo") porque el encargo es justamente que
       se note. Queda dicho para que dentro de un año se sepa que fue una
       decisión y no un descuido. */
    --mo-y: calc(46px * var(--mo));
    --mo-x: calc(78px * var(--mo));
    --mo-paso: calc(110ms * var(--mo));

    /* ---- FONDO -----------------------------------------------------------
       Tres manchas muy tenues que derivan detrás del contenido. El diseño
       nuevo es blanco liso, así que van mucho más bajas que antes: son un
       velo, no un ambiente. El panel puede subirlas o apagarlas del todo. */
    --fondo-mancha-1: rgba(89, 128, 166, .22);
    --fondo-mancha-2: rgba(65, 97, 128, .18);
    --fondo-mancha-3: rgba(44, 69, 93, .13);

    /* Alto de la barra MÁS los 14px que la despegan del borde: es lo que
       usan el padding del hero y el salto de los enlaces internos, así que
       si no incluye el margen el contenido queda tapado al navegar. */
    --header-h: 86px;
    --maxw: 1180px;
}

/* --------------------------------------------------------------------------
   2. RESET
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }

html {
    scroll-behavior: smooth;
    scroll-padding-top: calc(var(--header-h) + 16px);
    -webkit-text-size-adjust: 100%;
    /* Los revelados laterales empujan hacia afuera antes de entrar, y con
       recorridos largos eso abría barra horizontal. `clip` recorta sin
       convertir la raíz en contenedor de scroll, que es lo que rompería el
       desplazamiento suave y el `sticky`. El `hidden` de arriba queda de
       respaldo para navegadores que no conocen `clip`. */
    overflow-x: hidden;
    overflow-x: clip;
}

body {
    font-family: var(--font-body);
    font-size: 1.0625rem;
    line-height: 1.6;
    color: var(--tinta-2);
    background: var(--papel);
    overflow-x: hidden;
    overflow-x: clip;
    -webkit-font-smoothing: antialiased;
}

/* Masa de agua de fondo. Va `fixed` para que el contenido pase por encima
   como si flotara sobre una pileta en vez de deslizarse sobre un papel, y
   en z-index negativo para no interceptar ni un clic. Son tres radiales de
   una sola parada: no hay borde ni brillo, así que no contradice el Flat
   Design — sólo evita que el celeste liso se lea como un PDF.
   Las bandas de sección opacas la tapan; se ve en los respiros entre ellas. */
body::before {
    content: '';
    position: fixed;
    inset: -20vmax;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(38vmax 38vmax at 18% 20%, var(--fondo-mancha-1), transparent 62%),
        radial-gradient(44vmax 44vmax at 84% 12%, var(--fondo-mancha-2), transparent 60%),
        radial-gradient(40vmax 40vmax at 62% 86%, var(--fondo-mancha-3), transparent 64%);
    animation: derivar-fondo 46s ease-in-out infinite alternate;
    will-change: transform;
}
@keyframes derivar-fondo {
    from { transform: translate3d(-2%, -1%, 0) scale(1); }
    to   { transform: translate3d(2%, 2%, 0) scale(1.07); }
}

/* Barlow Condensed va estrecha, así que aguanta un cuerpo mayor en el mismo
   ancho: el h1 llega a 54px como en el diseño sin partirse en cuatro líneas.
   El peso se queda en 600 y no en 700: en una condensada el 700 empasta los
   contrapunzones y el titular pierde aire. */
h1, h2, h3, h4 {
    font-family: var(--font-head);
    color: var(--tinta);
    font-weight: 600;
    line-height: 1.08;
    letter-spacing: -.01em;
    text-wrap: balance;
}
h1 { font-size: clamp(2.4rem, 5.2vw, 3.4rem); }
h2 { font-size: clamp(1.85rem, 3.4vw, 2.25rem); }
h3 { font-size: clamp(1.1rem, 1.4vw, 1.2rem); letter-spacing: 0; }

/* Barlow tiene una x-height generosa: 17px se lee como 18. El cuerpo va a
   1.7 de interlínea, que es lo que usa el diseño en los párrafos largos. */
body { font-size: 1.0625rem; line-height: 1.7; }

p { text-wrap: pretty; }

img, svg, video { max-width: 100%; display: block; }
img { height: auto; }
[hidden] { display: none !important; }

a { color: var(--accion-texto); text-decoration: none; transition: color var(--t-fast); }
a:hover { color: var(--celeste-noche); }

button, input, select, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
ul, ol { list-style: none; }

/* Superficies del navegador: selección, cursor de texto, barra de scroll y
   anillo de foco vienen con estilos de fábrica que no pertenecen a ningún
   diseño. Tematizarlas es lo más barato que distingue una página construida
   de una ensamblada. */
:focus-visible { outline: 2px solid var(--celeste-hondo); outline-offset: 3px; border-radius: 4px; }
::selection { background: var(--celeste-hondo); color: var(--btn-texto); }
html { caret-color: var(--accion-texto); scrollbar-color: var(--celeste-claro) var(--celeste-nube); scrollbar-width: thin; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--celeste-nube); }
::-webkit-scrollbar-thumb {
    background: var(--celeste-claro);
    border-radius: 999px;
    border: 3px solid var(--celeste-nube);
}
::-webkit-scrollbar-thumb:hover { background: var(--celeste); }

/* Los números se alinean en columna: cifras, precios y fichas técnicas */
.cifra strong, .product-price, .detail-price, .spec-table td {
    font-variant-numeric: tabular-nums;
    font-feature-settings: 'tnum' 1;
}

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.skip-link {
    position: absolute; top: -100px; left: 16px; z-index: 2000;
    background: var(--celeste-noche); color: var(--sobre-oscuro); padding: .8rem 1.3rem; font-weight: 600;
}
.skip-link:focus { top: 0; color: var(--sobre-oscuro); }

/* --------------------------------------------------------------------------
   3. ESTRUCTURA
   -------------------------------------------------------------------------- */
.container {
    width: 100%;
    max-width: var(--maxw);
    margin-inline: auto;
    padding-inline: clamp(1.25rem, 5vw, 3rem);
}

/* Bandas planas: Background del sistema, blanco y Foreground. Sin textura
   ni degradado; el ritmo lo da el color liso. */
/* Ritmo del diseño: blanco → gris muy claro → blanco, y la banda oscura
   sólo para la llamada final. 96px de aire vertical. */
.section { padding-block: clamp(4rem, 7vw, 6rem); }
.section--paper { background: var(--celeste-nube); }
.section--white { background: transparent; }
.section--deep {
    background: var(--celeste-noche);
    color: #b7c3cd;
}
.section--deep h2, .section--deep h3 { color: var(--sobre-oscuro); }

.rotulo {
    display: flex;
    align-items: baseline;
    gap: .8rem;
    font-family: var(--font-body);
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--celeste);
    margin-bottom: 1rem;
}
.rotulo::before {
    content: '';
    width: 34px; height: 2px;
    background: var(--celeste);
    flex-shrink: 0;
    transform: translateY(-.3em) scaleX(0);
    transform-origin: left;
    transition: transform 620ms var(--ease);
}
.is-visible .rotulo::before,
.rotulo.is-visible::before { transform: translateY(-.3em) scaleX(1); }
.section--deep .rotulo { color: var(--celeste-claro); }
.section--deep .rotulo::before { background: var(--celeste-claro); }

/* Rótulo de sección: texto suelto, sin fondo ni caja. En el diseño nuevo
   es sólo mayúsculas espaciadas en el azul de acento. */
.eyebrow {
    display: block;
    font-family: var(--font-body);
    font-size: .8125rem;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--celeste);
    margin-bottom: 1rem;
}
/* Sólo el encabezado interior sigue siendo oscuro. El hero de portada pasó
   a blanco, así que su rótulo va en el azul de acento: dejarlo en blanco lo
   volvía invisible sobre el fondo. */
.page-hero .eyebrow { color: rgba(255, 255, 255, .82); }
.hero .eyebrow { color: var(--celeste); }

.entrada { max-width: 60ch; font-size: 1.1rem; color: var(--tinta-2); margin-top: 1.1rem; }

/* --------------------------------------------------------------------------
   4. BOTONES
   -------------------------------------------------------------------------- */
.btn {
    position: relative;
    overflow: hidden;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .55rem;
    min-height: 48px;
    padding: .75rem 1.5rem;
    /* 1px en todos, transparente en los rellenos, para que el secundario
       (que sí lleva filete) no quede más alto que el primario al lado. */
    border: 1px solid transparent;
    border-radius: var(--r-sm);
    background: var(--celeste-hondo);
    color: var(--btn-texto);
    font-family: var(--font-body);
    font-size: .9375rem;
    font-weight: 600;
    letter-spacing: .01em;
    line-height: 1.15;
    white-space: nowrap;
    transition: background 200ms ease, color 200ms ease, border-color 200ms ease,
                transform 160ms cubic-bezier(.23, 1, .32, 1);
}
/* El hover oscurece el fondo en vez de bajar la opacidad: bajarla sobre
   blanco lava el botón y le quita contraste justo en el momento en que el
   puntero está encima. El diseño ya define el par claro/oscuro. */
.btn:hover { color: var(--btn-texto); background: var(--celeste-noche); }
.btn svg { width: 20px; height: 20px; flex-shrink: 0; }

.btn--wa {
    background: var(--wa);
    color: var(--wa-texto);
    font-weight: 600;
}
.btn--wa:hover { background: var(--wa); color: var(--wa-texto); }

/* Secundario: filete fino en el primario y relleno apenas insinuado al
   pasar por encima, como en el diseño. */
.btn--ghost {
    background: transparent;
    border-color: rgba(44, 69, 93, .3);
    color: var(--accion-texto);
}
.btn--ghost:hover {
    background: rgba(44, 69, 93, .06);
    border-color: rgba(44, 69, 93, .45);
    color: var(--accion-texto);
}

.btn--light {
    background: transparent;
    border-color: rgba(255, 255, 255, .35);
    color: var(--sobre-oscuro);
}
.btn--light:hover { background: rgba(255, 255, 255, .1); border-color: rgba(255, 255, 255, .55); color: var(--sobre-oscuro); }

.btn--block { width: 100%; }
.btn--sm { min-height: 44px; padding: .55rem 1.1rem; font-size: .9rem; }

.enlace-linea {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--font-head);
    font-weight: 600;
    color: var(--celeste-noche);
    padding-bottom: 3px;
    background-image: linear-gradient(var(--celeste), var(--celeste));
    background-size: 0% 2px;
    background-position: 0 100%;
    background-repeat: no-repeat;
    transition: background-size var(--t-base), color var(--t-fast);
}
.enlace-linea:hover { color: var(--celeste-noche); background-size: 100% 2px; }
.enlace-linea svg { width: 18px; height: 18px; transition: transform var(--t-base); }
.enlace-linea:hover svg { transform: translateX(4px); }

/* --------------------------------------------------------------------------
   5. CABECERA — barra flotante plana
   No poner `transform` sobre un ancestro del menú móvil: crea bloque
   contenedor y encierra al panel `fixed`. Por eso el fondo va en un
   pseudoelemento y el ocultarse anima `top`.
   -------------------------------------------------------------------------- */
/* Barra flotante, a pedido del cliente: despegada del borde y con márgenes
   laterales, no pegada a todo el ancho. El fondo NO va acá sino en el
   pseudoelemento de .header-inner, porque el que flota es el recuadro
   interior, no la franja. */
.site-header {
    position: fixed;
    top: 14px; left: 0; right: 0;
    z-index: 900;
    padding-inline: clamp(.75rem, 2vw, 1.5rem);
    background: none;
    border-bottom: none;
    transition: top var(--t-base);
}
.site-header.is-hidden { top: -96px; }

.header-inner {
    position: relative;
    width: 100%;
    max-width: var(--maxw);
    margin-inline: auto;
    padding: 11px 12px 11px 20px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}
/* El recuadro que flota. Va en un pseudoelemento y no en el propio
   .header-inner porque el vidrio no puede ir sobre un ancestro del menú
   móvil: `backdrop-filter` crea bloque contenedor y encerraría al panel,
   que es `fixed`. Está avisado desde la primera versión de este archivo. */
.header-inner::before {
    content: '';
    position: absolute; inset: 0; z-index: -1;
    border-radius: var(--r-lg);
    background: rgba(255, 255, 255, .88);
    border: 1px solid rgba(29, 45, 61, .08);
    backdrop-filter: blur(10px) saturate(140%);
    -webkit-backdrop-filter: blur(10px) saturate(140%);
    box-shadow: var(--sh-sm);
    transition: background var(--t-base), box-shadow var(--t-base), border-color var(--t-base);
}
/* Al bajar se opaca y levanta un punto: acusa que ya no está sobre el hero */
.site-header.is-scrolled .header-inner::before {
    background: rgba(255, 255, 255, .97);
    border-color: rgba(29, 45, 61, .12);
    box-shadow: var(--sh-soft);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .header-inner::before { background: #fff; }
}
@media (prefers-reduced-transparency: reduce) {
    .header-inner::before { background: #fff; backdrop-filter: none; -webkit-backdrop-filter: none; }
}
/* EN CELULAR NO HAY VIDRIO.
   La barra es fija y esta siempre en pantalla, asi que el navegador tiene que
   volver a desenfocar lo que pasa por detras en CADA fotograma del scroll. En
   Safari de iPhone eso es de lo mas caro que existe y hace que la web se
   sienta pesada aunque haya cargado hace rato. En una barra de 88px de alto
   sobre fondo claro, el desenfoque casi no se ve; el costo si se nota.
   En escritorio se mantiene, donde sobra potencia. */
@media (max-width: 900px) {
    .header-inner::before {
        background: rgba(255, 255, 255, .97);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
}
/* Ya no hay vidrio que degradar: el fondo siempre fue sólido. Los respaldos
   para `backdrop-filter` que había acá quedaron sin objeto y se retiran. */

.brand {
    display: inline-flex;
    align-items: center;
    gap: .6rem;
    font-family: var(--font-head);
    font-weight: 700;
    font-size: 1.02rem;
    letter-spacing: -.02em;
    color: var(--tinta);
    line-height: 1.1;
}
.brand:hover { color: var(--tinta); }
/* El logo del cliente es un emblema redondo sobre blanco, así que la marca
   deja de ser el cuadrado azul con la gota dibujada: sin fondo propio y
   recortada en círculo. El JPG no tiene transparencia, y el recorte es lo
   que evita que se vean las cuatro esquinas blancas — sobre todo en el pie,
   que es oscuro. */
/* En el diseño el logo va en cuadrado de canto corto, no en círculo */
.brand-mark {
    width: 46px; height: 46px;
    display: grid; place-items: center;
    border-radius: 10px;
    overflow: hidden;
    flex-shrink: 0;
    background: #fff;
}
.brand-mark svg { width: 19px; height: 19px; }
.brand-logo {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
}
.brand-text small {
    display: block;
    font-size: .66rem;
    font-weight: 600;
    letter-spacing: .14em;
    text-transform: uppercase;
    color: var(--tinta-3);
}
.site-footer .brand-text small { color: rgba(255, 255, 255, .82); }

.site-nav ul { display: flex; align-items: center; gap: clamp(1rem, 2.4vw, 2.1rem); }
/* Acotado a la lista: `.site-nav a` pisa el color del botón de WhatsApp */
.site-nav ul a {
    position: relative;
    font-family: var(--font-head);
    font-size: .95rem;
    font-weight: 600;
    color: var(--tinta-2);
    padding-block: .4rem;
}
.site-nav ul a::after {
    content: '';
    position: absolute;
    left: 0; bottom: 0;
    width: 100%; height: 2px;
    background: var(--celeste);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform var(--t-base);
}
.site-nav ul a:hover { color: var(--tinta); }
.site-nav ul a:hover::after,
.site-nav ul a[aria-current="page"]::after { transform: scaleX(1); transform-origin: left; }

.site-nav .nav-cta { display: none; }
/* Por encima del panel del menú móvil, que vale 950 y es HERMANO de este
   bloque dentro de la cabecera. Sin esto el panel abierto le queda encima al
   botón de las tres líneas, el segundo toque no llega nunca y el menú se
   podía abrir pero no cerrar. Subir el z-index de .site-header no alcanzaba:
   los dos viven dentro de su mismo contexto de apilado, así que la pelea se
   resuelve acá adentro, no afuera. */
.header-actions { display: flex; align-items: center; gap: .7rem; position: relative; z-index: 960; }

.nav-toggle {
    display: none;
    width: 44px; height: 44px;
    align-items: center; justify-content: center;
    background: var(--celeste-nube);
    border: none;
    border-radius: var(--r-sm);
    color: var(--tinta);
}
.nav-toggle svg { width: 22px; height: 22px; }

/* --------------------------------------------------------------------------
   6. HERO
   -------------------------------------------------------------------------- */
/* Bloque de color plano. Sin degradado y sin textura, como pide el estilo.
   texto normal sobre este fondo: con el Primary daría 3.7:1 y acá llega a
   5.4:1. Las olas y las burbujas siguen encima; son formas simples. */
.hero {
    position: relative;
    padding-top: calc(var(--header-h) + clamp(2rem, 5vw, 3.5rem));
    padding-bottom: clamp(5rem, 9vw, 7rem);
    background: transparent;
    color: var(--tinta-2);
    overflow: hidden;
    isolation: isolate;
}
.hero-bg {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    opacity: .2;
    z-index: -1;
}

.hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 6fr) minmax(0, 5fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: center;
    position: relative;
    z-index: 3;
}

.hero h1 { color: var(--tinta); margin-bottom: 1.25rem; }
.hero h1 .marca-agua { position: relative; white-space: nowrap; }
.hero h1 .marca-agua::after {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: .09em;
    height: .09em;
    background: var(--celeste-claro);
    transform: scaleX(0);
    transform-origin: left;
    animation: subrayar 900ms var(--ease) 700ms forwards;
}
@keyframes subrayar { to { transform: scaleX(1); } }

.hero-sub {
    font-size: clamp(1.05rem, 1.4vw, 1.2rem);
    color: var(--tinta-2);
    max-width: 46ch;
    margin-bottom: 2rem;
}
.hero-cta { display: flex; flex-wrap: wrap; gap: .8rem; }

.hero-visual { position: relative; }
/* La lámina del hero iba blanca sobre fondo blanco: sin borde ni contraste
   flotaba sin apoyarse en nada. Va sobre el gris del sistema y con filete,
   que es como se sostiene una pieza clara en una página clara. */
.hero-foto {
    position: relative;
    aspect-ratio: 4 / 5;
    border-radius: var(--r-media);
    overflow: hidden;
    background: var(--celeste-nube);
    border: 1px solid var(--linea);
    box-shadow: var(--sh-soft);
}
.hero-foto:has(img), .hero-foto:has(video) { border-color: transparent; box-shadow: var(--sh-hero); }
.hero-foto img, .hero-foto video { width: 100%; height: 100%; object-fit: cover; }

.lamina-pie {
    display: block;
    margin-top: .9rem;
    color: var(--tinta-3);
    font-size: .92rem;
}
.lamina-pie strong {
    display: block;
    font-family: var(--font-head);
    font-weight: 700;
    color: var(--tinta);
    font-size: 1.05rem;
    margin-bottom: .2rem;
}

/* --------------------------------------------------------------------------
   6b. EL CORTE DEL FILTRO
   -------------------------------------------------------------------------- */
.filtro { position: absolute; inset: 0; display: grid; grid-template-rows: 1fr auto; }
.filtro svg { width: 100%; height: 100%; min-height: 0; }

.filtro .tubo { fill: rgba(2, 132, 199, .06); stroke: var(--celeste-hondo); stroke-width: 1.6; }
.filtro .canio { stroke: var(--celeste-hondo); stroke-width: 2.4; stroke-linecap: round; fill: none; }
.filtro .etapa-linea { stroke: var(--linea-2); stroke-width: 1.2; stroke-dasharray: 4 5; }

.filtro .relleno { transform-origin: center top; transform: scaleY(0); }
.filtro .relleno--sucia { fill: #B6B49C; }
.filtro .relleno--media { fill: #7FC3DC; }
.filtro .relleno--limpia { fill: var(--celeste-claro); }

.filtro .gota-entra { fill: #B6B49C; opacity: 0; }
.filtro .gota-sale { fill: var(--celeste); opacity: 0; }
.filtro .mugre { fill: #7C7658; opacity: 0; }

.filtro .vaso { fill: rgba(2, 132, 199, .05); stroke: var(--celeste-hondo); stroke-width: 2; }
.filtro .vaso-agua { fill: var(--celeste-claro); transform: translateY(64px); }

.filtro-etapas { display: grid; gap: .3rem; padding: 0 1.05rem 1.05rem; }
.filtro-etapas li {
    display: flex; align-items: center; gap: .55rem;
    font-size: .8rem; font-weight: 600;
    color: var(--tinta-3);
    transition: color .45s var(--ease);
}
.filtro-etapas b {
    width: 19px; height: 19px;
    flex-shrink: 0;
    display: grid; place-items: center;
    border-radius: 5px;
    background: var(--celeste-nube);
    color: var(--tinta);
    font-family: var(--font-head);
    font-size: .66rem;
    transition: background .45s var(--ease), color .45s var(--ease);
}

/* El trazo se dibuja solo antes de que entre el agua: primero existe el
   filtro, después funciona. pathLength="1" en el marcado permite hacerlo
   sin medir cada figura. */
.filtro .tubo, .filtro .canio, .filtro .vaso {
    stroke-dasharray: 1;
    stroke-dashoffset: 1;
}
.filtro.is-on .tubo,
.filtro.is-on .canio,
.filtro.is-on .vaso { animation: dibujar 900ms var(--ease) forwards; }
.filtro.is-on .canio { animation-delay: 120ms; }
.filtro.is-on .vaso { animation-delay: 380ms; }
@keyframes dibujar { to { stroke-dashoffset: 0; } }

/* El agua sucia entra desenfocada y turbia; la limpia sale nítida.
   El desenfoque está acotado a dos figuras chicas, no a una capa entera. */
.filtro .relleno--sucia { filter: blur(1.6px); }
.filtro .relleno--media { filter: blur(.7px); }
.filtro.is-on .relleno { animation: llenar .9s var(--ease) forwards; }
.filtro.is-on .relleno--sucia { animation-delay: .35s; }
.filtro.is-on .relleno--media { animation-delay: 1.15s; }
.filtro.is-on .relleno--limpia { animation-delay: 1.95s; }
@keyframes llenar { to { transform: scaleY(1); } }

.filtro.is-on .gota-entra { animation: caer-entrada 1.5s ease-in infinite; }
.filtro.is-on .gota-entra--2 { animation-delay: .5s; }
.filtro.is-on .gota-entra--3 { animation-delay: 1s; }
@keyframes caer-entrada {
    0% { transform: translateY(0); opacity: 0; }
    18% { opacity: 1; } 75% { opacity: 1; }
    100% { transform: translateY(42px); opacity: 0; }
}

.filtro.is-on .mugre { animation: atrapar 1.1s var(--ease) forwards; }
.filtro.is-on .m1 { animation-delay: .6s;  --parada: 62px; }
.filtro.is-on .m2 { animation-delay: .95s; --parada: 58px; }
.filtro.is-on .m3 { animation-delay: 1.3s; --parada: 66px; }
.filtro.is-on .m4 { animation-delay: 1.7s; --parada: 128px; }
.filtro.is-on .m5 { animation-delay: 2s;   --parada: 132px; }
@keyframes atrapar {
    0% { transform: translateY(0); opacity: 0; }
    22% { opacity: .95; }
    100% { transform: translateY(var(--parada)); opacity: .95; }
}

.filtro.is-on .gota-sale { animation: caer-salida 1.4s ease-in infinite 2.6s; }
.filtro.is-on .gs2 { animation-delay: 3.3s; }
@keyframes caer-salida {
    0% { transform: translateY(0); opacity: 0; }
    20% { opacity: 1; } 85% { opacity: 1; }
    100% { transform: translateY(34px); opacity: 0; }
}

.filtro.is-on .vaso-agua { animation: llenar-vaso 2.4s var(--ease) forwards 2.9s; }
@keyframes llenar-vaso { to { transform: translateY(14px); } }

.filtro.is-on .fe1 { animation: encender .45s var(--ease) forwards .5s; }
.filtro.is-on .fe2 { animation: encender .45s var(--ease) forwards 1.3s; }
.filtro.is-on .fe3 { animation: encender .45s var(--ease) forwards 2.1s; }
@keyframes encender { to { color: var(--tinta); } }
.filtro.is-on .fe1 b, .filtro.is-on .fe2 b, .filtro.is-on .fe3 b { background: var(--celeste); color: var(--btn-texto); }

/* --------------------------------------------------------------------------
   7. FRANJA DE GARANTÍAS
   -------------------------------------------------------------------------- */
.franja { background: var(--blanco); border-bottom: 1px solid var(--linea); }
.franja ul { display: grid; grid-template-columns: repeat(3, 1fr); }
/* Cada garantía va CENTRADA en su tercio.
   Antes iban pegadas a la izquierda de la celda, y como los textos son cortos
   sobraban entre 185 y 216px a la derecha de cada una: se veían apelotonadas
   contra las líneas divisorias, con medio hueco vacío al lado. Encima la
   primera empezaba en 0 y las otras dos a 30px, por el padding-left que se le
   quitaba, así que ni siquiera guardaban el mismo ritmo entre ellas.
   Centradas, las tres pesan igual y las líneas quedan a media distancia. */
/* Las cifras dentro de la franja: sin la linea de arriba ni los margenes que
   tenian en "Nosotros", porque ahi separaban de un bloque de texto y aca la
   franja ya es su propio bloque. El cliente ademas pidio hace poco quitar las
   divisorias de esta franja, asi que meter un borde nuevo seria deshacerlo. */
.franja .cifras {
    grid-template-columns: repeat(4, 1fr);
    border-top: none;
    margin-top: 0;
    padding-top: 0;
    gap: clamp(.75rem, 2vw, 2rem);
}
.franja .cifra {
    text-align: center;
    padding-block: clamp(1.5rem, 3vw, 2.25rem);
}
.franja .cifra strong { font-size: clamp(1.9rem, 3.4vw, 2.9rem); }
/* Las cuatro etiquetas reservan DOS renglones aunque usen uno. Sin esto,
   "EQUIPOS INSTALADOS" y "DEL CLORO ELIMINADO" bajaban a dos lineas y
   "AÑOS DE GARANTIA" se quedaba en una: los numeros quedaban a la misma
   altura pero el bloque entero se veia desparejo. */
.franja .cifra span {
    max-width: 18ch;
    margin-inline: auto;
    line-height: 1.35;
    min-height: calc(2 * 1.35em);
}

@media (max-width: 700px) {
    /* Cuatro cifras en una fila no entran: van de a dos. */
    .franja .cifras { grid-template-columns: repeat(2, 1fr); }
    .franja .cifra { padding-block: 1.15rem; }
}

.franja li {
    display: flex; align-items: center; justify-content: center; gap: .75rem;
    padding: 1.5rem clamp(.75rem, 2vw, 2rem);
    font-family: var(--font-head);
    font-weight: 600; font-size: 1rem;
    color: var(--tinta);
    text-align: center;
}
.franja svg { width: 21px; height: 21px; color: var(--accion-texto); flex-shrink: 0; }

/* --------------------------------------------------------------------------
   8. NOSOTROS
   -------------------------------------------------------------------------- */
.sobre {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: clamp(2rem, 6vw, 5rem);
    align-items: start;
}
.sobre-texto p { color: var(--tinta-2); font-size: 1.1rem; max-width: 54ch; }

.pilares { display: grid; margin-top: 2.25rem; }
.pilar {
    display: grid; grid-template-columns: 30px 1fr; gap: 1rem;
    padding: 1.25rem 0;
    border-top: 1px solid var(--linea);
}
.pilar:last-child { border-bottom: 1px solid var(--linea); }
.pilar svg { width: 24px; height: 24px; color: var(--accion-texto); margin-top: 2px; }
.pilar h3 { margin-bottom: .2rem; }
.pilar p { font-size: .98rem; color: var(--tinta-3); }

.sobre-visual { position: relative; }
/* HUECO DE FOTO
   Mientras el cliente no suba la foto real, esto era un rectángulo azul
   oscuro enorme: a ojo se lee como una imagen que no cargó, y es lo que más
   ensuciaba la página. Ahora es un panel claro con filete, del mismo gris
   que las bandas, y con las ondas apenas insinuadas. Cuando llegue la foto
   la tapa entera y este tratamiento desaparece solo. */
.sobre-foto {
    position: relative;
    aspect-ratio: 5 / 4;
    border-radius: var(--r-media);
    overflow: hidden;
    background: var(--celeste-nube);
    border: 1px solid var(--linea);
    display: grid; place-items: center;
}
.sobre-foto:has(img), .sobre-foto:has(video) { border-color: transparent; box-shadow: var(--sh-soft); }
.sobre-foto .ondas span { border-color: rgba(65, 97, 128, .22); }
.sobre-foto img, .sobre-foto video { width: 100%; height: 100%; object-fit: cover; }

/* Ondas concéntricas mientras no haya foto real del equipo */
.ondas { position: absolute; inset: 0; display: grid; place-items: center; }
.ondas span {
    position: absolute;
    width: 22%;
    aspect-ratio: 1;
    border: 1.5px solid rgba(255, 255, 255, .6);
    border-radius: 50%;
    opacity: 0;
    animation: onda-agua 5s var(--ease) infinite;
}
.ondas span:nth-child(2) { animation-delay: 1.25s; }
.ondas span:nth-child(3) { animation-delay: 2.5s; }
.ondas span:nth-child(4) { animation-delay: 3.75s; }
@keyframes onda-agua {
    0% { transform: scale(.2); opacity: 0; border-width: 3px; }
    12% { opacity: .9; }
    100% { transform: scale(3.6); opacity: 0; border-width: .5px; }
}
.fuera-de-vista .ondas span { animation-play-state: paused; }

.pie-foto { margin-top: .75rem; font-size: .88rem; color: var(--tinta-3); }

.cifras {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: clamp(1rem, 3vw, 2.5rem);
    margin-top: clamp(2.5rem, 5vw, 4rem);
    padding-top: 2rem;
    border-top: 2px solid var(--celeste);
}
.cifra strong {
    display: block;
    font-family: var(--font-head);
    font-size: clamp(2rem, 4.2vw, 3rem);
    font-weight: 700;
    letter-spacing: -.03em;
    color: var(--accion-texto);
    line-height: 1.05;
}
.cifra span {
    display: block; margin-top: .5rem;
    font-size: .92rem;
    color: var(--tinta-3);
    max-width: 18ch;
}

/* --------------------------------------------------------------------------
   9. PASOS
   -------------------------------------------------------------------------- */
/* Menos aire entre la bajada y los pasos: con 110px de hueco el bloque se
   iba al fondo de la banda y arriba quedaba un vacio grande. */
.pasos-cab { display: grid; gap: .75rem; margin-bottom: clamp(1.25rem, 2.2vw, 1.75rem); max-width: 46ch; }
.pasos-cab p { color: rgba(255, 255, 255, .8); }
.pasos { display: grid; grid-template-columns: repeat(4, 1fr); counter-reset: paso; }
.pasos li {
    counter-increment: paso;
    padding: 1.25rem clamp(1rem, 2vw, 1.75rem) 0 0;
    border-top: 2px solid rgba(255, 255, 255, .3);
    font-size: 1rem;
    color: rgba(255, 255, 255, .84);
    transition: border-color var(--t-base);
}
.pasos li:hover { border-top-color: #fff; }
.pasos li::before {
    content: counter(paso, decimal-leading-zero);
    display: block;
    font-family: var(--font-head);
    font-size: 1.6rem; font-weight: 700; letter-spacing: -.02em;
    color: var(--celeste-claro);
    margin-bottom: .75rem;
}
.pasos .step-num { display: none; }

/* Cinta de localidades */
/* La cinta va a sangre por la derecha (para eso es una cinta), pero el
   rótulo tiene que arrancar alineado con el resto de la página: cuelga
   fuera del .container, así que se le da el mismo margen a mano. Antes
   quedaba pegado al borde de la ventana y se cortaba. */
.cinta {
    margin-top: clamp(2.5rem, 5vw, 4rem);
    border-top: 1px solid rgba(255, 255, 255, .24);
    padding-top: 1.5rem;
    padding-left: clamp(1.25rem, 5vw, 3rem);
    display: flex; align-items: center; gap: 1.5rem; overflow: hidden;
}
.cinta-rotulo {
    flex-shrink: 0;
    font-family: var(--font-head);
    font-size: .8rem; font-weight: 600;
    letter-spacing: .14em; text-transform: uppercase;
    color: rgba(255, 255, 255, .72);
}
.cinta-riel {
    flex: 1; min-width: 0; overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.cinta-pista {
    display: flex; align-items: center; gap: 2.5rem;
    width: max-content;
    animation: correr 38s linear infinite;
    will-change: transform;
}
.cinta:hover .cinta-pista { animation-play-state: paused; }
.cinta-pista span {
    display: flex; align-items: center; gap: 2.5rem;
    font-family: var(--font-head);
    font-size: clamp(1.1rem, 2vw, 1.5rem);
    font-weight: 600; letter-spacing: -.02em;
    color: var(--sobre-oscuro);
    white-space: nowrap;
}
.cinta-pista span::after {
    content: '';
    width: 7px; height: 7px; border-radius: 50%;
    background: var(--celeste-claro);
    flex-shrink: 0;
}
@keyframes correr { to { transform: translate3d(-50%, 0, 0); } }
.fuera-de-vista .cinta-pista { animation-play-state: paused; }

/* --------------------------------------------------------------------------
   10. SERVICIOS
   -------------------------------------------------------------------------- */
.cards-grid {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1rem;
    grid-auto-flow: dense;
}
/* Tarjeta del MASTER: fondo Background, radio 12px, sombra media, y en
   hover sombra grande más 2px de desplazamiento. */
.service-card {
    background: var(--blanco);
    border: 1px solid var(--linea);
    border-radius: var(--r-lg);
    padding: clamp(1.5rem, 2.6vw, 2.25rem);
    display: flex; flex-direction: column; gap: .6rem;
    transition: transform var(--t-base), box-shadow var(--t-base);
}
.service-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--sh-lift);
}
.cards-grid .service-card:first-child {
    grid-column: span 2; grid-row: span 2;
    justify-content: flex-end;
    background: var(--celeste-hondo);
    color: var(--btn-texto);
    min-height: 300px;
}
.cards-grid .service-card:first-child h3 { color: var(--btn-texto); font-size: clamp(1.5rem, 2.6vw, 2rem); }
.cards-grid .service-card:first-child p { color: var(--btn-texto); opacity: .9; font-size: 1.05rem; max-width: 42ch; }
.cards-grid .service-card:first-child .service-icon { color: var(--btn-texto); }

.service-icon { color: var(--accion-texto); margin-bottom: .6rem; }
.service-icon svg { width: 28px; height: 28px; transition: transform var(--t-base); }
.service-card:hover .service-icon svg { transform: translateY(-3px); }
.cards-grid .service-card:first-child .service-icon svg { width: 40px; height: 40px; }
.service-card p { font-size: .97rem; color: var(--tinta-3); }

/* --------------------------------------------------------------------------
   11. TESTIMONIOS
   -------------------------------------------------------------------------- */
/* Cinco reseñas en una figura fija: la destacada ocupa la columna izquierda
   y DOS filas de alto, así que se lee primero por tamaño, no por color. A su
   derecha entran dos, y otras dos cierran abajo. El reparto lo hace sola la
   colocación automática de la grilla: alcanza con fijar la destacada.

           ┌──────────────┬────────┐
           │              │   2    │
           │      1       ├────────┤
           │  (destacada) │   3    │
           ├──────────────┼────────┤
           │      4       │   5    │
           └──────────────┴────────┘

   Si hay pocas reseñas cargadas, el JS pone .testimonials--pocas y la
   destacada deja de estirarse: una tarjeta azul altísima al lado de una
   sola blanca queda desbalanceada. */
.testimonials {
    display: grid;
    grid-template-columns: minmax(0, 1.12fr) minmax(0, 1fr);
    grid-auto-rows: auto;
    gap: 1.25rem;
    align-items: stretch;
}
.testimonial {
    background: var(--blanco);
    border: 1px solid var(--linea);
    border-radius: var(--r-lg);
    padding: clamp(1.5rem, 2.6vw, 2.25rem);
    display: flex; flex-direction: column; gap: 1rem;
}
/* Doble clase a propósito: más abajo hay un `.testimonial { background }` que
   aclara todas las tarjetas, y con una sola clase le empataba en peso y ganaba
   la de abajo — la destacada salía blanca. */
.testimonial.testimonial--destacado {
    grid-column: 1;
    grid-row: 1 / span 2;
    justify-content: center;
    background: var(--celeste-hondo);
}
.testimonials--pocas .testimonial--destacado { grid-row: auto; }
/* Doble clase otra vez: `.testimonial blockquote` está más abajo y le empata
   en peso, así que sin esto la cita de la destacada salía chica y en gris
   oscuro sobre el azul — ilegible. */
.testimonial.testimonial--destacado blockquote {
    font-family: var(--font-head);
    font-size: clamp(1.3rem, 2.4vw, 1.85rem);
    font-weight: 600; line-height: 1.22; letter-spacing: -.025em;
    color: var(--btn-texto);
    flex: 0 1 auto;
}
.testimonial--destacado .testimonial-author strong { color: var(--btn-texto); }
.testimonial--destacado .testimonial-author span { color: var(--btn-texto); opacity: .82; }
.testimonial--destacado .avatar { background: rgba(255, 255, 255, .22); color: #fff; }
.testimonial--destacado .stars { color: var(--estrella); }
.testimonial--destacado .testimonial-fuente { color: rgba(255, 255, 255, .7); }
.testimonial--destacado .testimonial-fuente::before { background: rgba(255, 255, 255, .28); }

/* Sello de origen. Google pide que se vea de dónde sale la reseña, y de paso
   le dice a quien lee que no la escribió la empresa. */
.testimonial-fuente {
    display: flex; align-items: center; gap: .45rem;
    font-size: .74rem; letter-spacing: .04em; text-transform: uppercase;
    font-family: var(--font-head); color: var(--tinta-3);
}
.testimonial-fuente::before {
    content: ''; flex: 1; height: 1px; background: var(--linea);
}
.testimonial-fuente svg { width: 13px; height: 13px; }

/* Relevo de reseñas: la que se va cae, la que llega sube. Son dos animaciones
   y no una transición, porque el revelado de scroll ya usa transiciones sobre
   el mismo transform y se pisarían. */
@keyframes resenaSale { to { opacity: 0; transform: translateY(-10px); } }
@keyframes resenaEntra { from { opacity: 0; transform: translateY(12px); } }
.testimonial.sale  { animation: resenaSale  calc(300ms * var(--mo)) var(--ease) forwards; }
.testimonial.entra { animation: resenaEntra calc(440ms * var(--mo)) var(--ease); }

.testimonial blockquote { font-size: 1rem; color: var(--tinta-2); flex: 1; }

/* Marco de alto fijo para las reseñas que se relevan.
   Una reseña de dos líneas y otra de seis hacen filas de alto distinto, y como
   la tarjeta azul ocupa DOS filas, cada relevo le cambiaba el alto a ella y a
   media página: medido, saltaba +54px y después -82px. Con las cuatro blancas
   siempre del mismo alto la figura queda quieta y sólo cambia el texto.

   Cuatro líneas: las cortas se rellenan con el mínimo, las largas se cortan
   con puntos suspensivos. Una reseña más larga que eso nadie la lee entera en
   una tarjeta igual. */
.testimonial:not(.testimonial--destacado) blockquote {
    line-height: 1.65;
    min-height: calc(4 * 1.65em);
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
            line-clamp: 4;
    overflow: hidden;
}
/* La destacada tiene letra más grande, así que le entran menos líneas. No
   rota, pero si el cliente pone un testimonio kilométrico igual desbordaría. */
.testimonial.testimonial--destacado blockquote {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 6;
            line-clamp: 6;
    overflow: hidden;
}
.stars { display: flex; gap: 2px; color: var(--estrella); }
.stars svg { width: 16px; height: 16px; }
.testimonial-author { display: flex; align-items: center; gap: .7rem; }
.avatar {
    width: 34px; height: 34px; flex-shrink: 0;
    display: grid; place-items: center;
    border-radius: var(--r-sm);
    background: var(--celeste-hondo); color: var(--btn-texto);
    font-family: var(--font-head); font-weight: 700; font-size: .85rem;
}
.testimonial-author strong { display: block; font-family: var(--font-head); font-size: .93rem; color: var(--tinta); }
.testimonial-author span { font-size: .84rem; color: var(--tinta-3); }

/* --------------------------------------------------------------------------
   11b. DISTRIBUIDORES
   Foto redonda, nombre debajo y contacto abajo. La figura viene del sitio que
   pidio de referencia: circulo de 150px, texto centrado, el contacto en el
   color de acento. Ahi el fondo es oscuro y el aro va en blanco translucido;
   aca el fondo es claro, asi que el aro se hace con la linea de la web y una
   sombra suave, si no el circulo flota sin apoyarse en nada.
   -------------------------------------------------------------------------- */
.distribuidores {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: clamp(1.5rem, 3vw, 2.25rem);
}
.distribuidor { text-align: center; }

.distribuidor-foto {
    width: 150px; height: 150px;
    margin: 0 auto 1rem;
    border-radius: 50%;
    overflow: hidden;
    background: var(--celeste-suave);
    border: 3px solid var(--blanco);
    /* El aro exterior repite el color de acento apagado: separa el circulo del
       fondo sin dibujar un borde duro. */
    box-shadow: 0 10px 26px -10px rgba(29, 45, 61, .35),
                0 0 0 3px var(--linea);
    display: grid; place-items: center;
}
.distribuidor-foto img {
    width: 100%; height: 100%;
    object-fit: cover;
    object-position: center 30%;   /* las caras suelen quedar arriba del centro */
    display: block;
}
/* Silueta de ejemplo mientras no hay foto cargada */
.distribuidor-foto svg {
    width: 58%; height: 58%;
    color: var(--accion-texto);
    opacity: .55;
}

.distribuidor-nombre {
    font-family: var(--font-head);
    font-size: 1.05rem; font-weight: 700;
    color: var(--tinta);
    margin-bottom: .25rem;
}
.distribuidor-zona {
    display: block;
    font-size: .8rem; letter-spacing: .06em; text-transform: uppercase;
    font-family: var(--font-head); font-weight: 600;
    color: var(--tinta-3);
    margin-bottom: .35rem;
}
.distribuidor-contacto {
    display: inline-flex; align-items: center; gap: .4rem;
    font-size: .95rem; font-weight: 600;
    color: var(--accion-texto);
    /* 44px de alto real: es un enlace que se toca con el dedo */
    min-height: 44px; padding-block: .5rem;
}
.distribuidor-contacto svg { width: 17px; height: 17px; flex-shrink: 0; }
.distribuidor-contacto:hover { text-decoration: underline; }

/* La foto se levanta apenas al pasar por encima, como las tarjetas */
.js .distribuidor .distribuidor-foto {
    transition: transform var(--t-base), box-shadow var(--t-base);
}
.js .distribuidor:hover .distribuidor-foto {
    transform: translateY(calc(-4px * var(--mo)));
    box-shadow: 0 16px 34px -12px rgba(29, 45, 61, .42),
                0 0 0 3px var(--linea-2);
}

/* --------------------------------------------------------------------------
   12. FORMULARIO
   -------------------------------------------------------------------------- */
.form-grid {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: start;
}
.form-aside p { color: var(--tinta-2); font-size: 1.08rem; max-width: 46ch; }
.form-reasons { display: grid; margin-top: 1.75rem; }
.form-reasons li {
    display: flex; align-items: center; gap: .7rem;
    padding: .8rem 0;
    border-bottom: 1px solid var(--linea);
    font-family: var(--font-head); font-weight: 600;
    color: var(--tinta);
}
.form-reasons svg { width: 20px; height: 20px; color: var(--accion-texto); flex-shrink: 0; }

.form-aside-contact { margin-top: 2rem; display: grid; gap: .8rem; }
/* Teléfono, mail y dirección son los tres enlaces que más se tocan desde un
   celular, y medían 29px de alto. El mínimo cómodo es 44: se llega con
   relleno vertical, sin mover el texto ni cambiar cómo se ven. */
.form-aside-contact a {
    display: flex; align-items: center; gap: .7rem;
    min-height: 44px;
    font-weight: 600; color: var(--tinta);
    overflow-wrap: anywhere; min-width: 0;
}
.form-aside-contact a:hover { color: var(--celeste-noche); }
.form-aside-contact svg { width: 19px; height: 19px; color: var(--accion-texto); flex-shrink: 0; }

.form-card {
    background: var(--blanco);
    border: 1px solid var(--linea);
    border-radius: var(--r-lg);
    padding: clamp(1.5rem, 3vw, 2.5rem);
}
.field { margin-bottom: 1.1rem; }
.field label {
    display: block;
    font-family: var(--font-head);
    font-size: .87rem; font-weight: 600;
    color: var(--tinta); margin-bottom: .35rem;
}
.field .optional { font-weight: 400; color: var(--tinta-3); }
.field input, .field select, .field textarea {
    width: 100%;
    min-height: 52px;
    padding: .75rem .95rem;
    background: var(--papel);
    border: 1px solid var(--linea);
    border-radius: var(--r-sm);
    font-size: 1rem;
    color: var(--tinta);
    transition: border-color var(--t-fast), background var(--t-fast), box-shadow var(--t-fast);
}
.field textarea { min-height: 108px; resize: vertical; }
.field select {
    cursor: pointer; appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 256 256' fill='%2361707F'%3E%3Cpath d='M213.7 101.7l-80 80a8 8 0 0 1-11.4 0l-80-80A8 8 0 0 1 48 88h160a8 8 0 0 1 5.7 13.7z'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 1rem center; padding-right: 2.75rem;
}
.field input::placeholder, .field textarea::placeholder { color: #94A3B8; }
/* Foco tal cual lo define el MASTER: borde Primary y anillo de 3px al 12,5% */
.field input:focus, .field select:focus, .field textarea:focus {
    outline: none;
    background: var(--blanco);
    border-color: var(--celeste);
    box-shadow: 0 0 0 3px rgba(2, 132, 199, .125);
}
.field-error { display: none; margin-top: .35rem; font-size: .85rem; font-weight: 600; color: var(--error); }
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: var(--error); background: #FEF3F2; }
.field.has-error .field-error { display: block; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }

.form-note { margin-top: .85rem; font-size: .86rem; color: var(--tinta-3); }
.form-status { display: none; margin-top: 1rem; padding: .85rem 1.1rem; border-radius: var(--r-sm); font-weight: 600; font-size: .95rem; }
.form-status.is-visible { display: block; }
.form-status.is-ok { background: #ECFDF3; color: #05603A; box-shadow: inset 0 0 0 1px rgba(21,128,61,.28); }
.form-status.is-error { background: #FEF3F2; color: #912018; box-shadow: inset 0 0 0 1px rgba(180,35,24,.28); }
.hp-field { position: absolute; left: -9999px; opacity: 0; height: 0; }

/* --------------------------------------------------------------------------
   13. PIE
   -------------------------------------------------------------------------- */
.site-footer {
    background: var(--celeste-pie);
    color: rgba(255, 255, 255, .82);
    padding-top: clamp(3.5rem, 7vw, 5rem);
}
.footer-grid {
    display: grid;
    grid-template-columns: 1.6fr 1fr 1fr 1.4fr;
    gap: clamp(2rem, 4vw, 3rem);
    padding-bottom: 3rem;
}
.site-footer h4 {
    font-size: .8rem; font-weight: 600;
    color: var(--sobre-oscuro);
    margin-bottom: 1.15rem;
    letter-spacing: .1em; text-transform: uppercase;
}
.site-footer .brand { color: var(--sobre-oscuro); margin-bottom: 1rem; }
.site-footer .brand-mark { background: rgba(255, 255, 255, .18); }
.footer-about { font-size: .94rem; max-width: 34ch; line-height: 1.65; }

.footer-links li + li { margin-top: .55rem; }
/* Los enlaces del pie medían 18px de alto: imposibles de acertar con el dedo
   sin darle al de al lado. El relleno vertical los lleva a un objetivo
   cómodo sin cambiar cómo se ven, porque el texto no se mueve. */
.footer-links a {
    color: rgba(255, 255, 255, .8);
    font-size: .94rem;
    display: inline-block;
    padding-block: .45rem;
}
.footer-links a:hover { color: var(--sobre-oscuro); }

.footer-contact { display: grid; gap: .85rem; }
.footer-contact a, .footer-contact div {
    display: flex; align-items: flex-start; gap: .65rem;
    color: rgba(255, 255, 255, .84); font-size: .94rem;
}
.footer-contact a:hover { color: var(--sobre-oscuro); }
.footer-contact svg { width: 18px; height: 18px; color: var(--celeste-claro); flex-shrink: 0; margin-top: 3px; }

.socials { display: flex; gap: .5rem; margin-top: 1.5rem; }
.social-btn {
    width: 42px; height: 42px;
    display: grid; place-items: center;
    border-radius: var(--r-sm);
    background: rgba(255, 255, 255, .12);
    color: #fff;
    transition: background var(--t-fast), transform var(--t-fast);
}
.social-btn:hover { background: var(--celeste); color: var(--btn-texto); transform: translateY(-2px); }
.social-btn svg { width: 19px; height: 19px; }

.footer-bottom {
    border-top: 1px solid rgba(255, 255, 255, .18);
    padding-block: 1.5rem 1.75rem;
    display: flex; flex-wrap: wrap; gap: .75rem;
    align-items: center; justify-content: space-between;
    font-size: .84rem; color: rgba(255, 255, 255, .84);
}
.footer-bottom a { color: var(--sobre-oscuro); font-weight: 600; }
.footer-bottom a:hover { color: var(--celeste-claro); }

/* --------------------------------------------------------------------------
   14. BOTÓN FLOTANTE
   -------------------------------------------------------------------------- */
/* Círculo puro, sin el texto "Escribinos": la misma silueta que el botón
   de la barra (.wa-barra), que ya era icono solo. Antes era una píldora de
   128px de ancho; con la gota reventando encima, un círculo de 56px es el
   destino que tiene sentido. El texto sigue vivo para lectores de pantalla
   (.sr-only en el <span>), así que la accesibilidad no se pierde. */
.wa-float {
    position: fixed;
    right: clamp(1rem, 3vw, 1.75rem);
    bottom: clamp(1rem, 3vw, 1.75rem);
    z-index: 850;
    display: inline-flex; align-items: center; justify-content: center;
    width: 56px; height: 56px;
    border-radius: var(--r-pill);
    background: var(--wa);
    color: var(--wa-texto);
    box-shadow: var(--sh-soft);
    opacity: 0; visibility: hidden;
    transform: translateY(14px);
    transition: opacity var(--t-base), transform var(--t-base), visibility var(--t-base), background var(--t-fast);
}
.wa-float.is-visible { opacity: 1; visibility: visible; transform: none; }
.wa-float:hover { background: var(--wa-fuerte); color: var(--wa-texto); }
.wa-float svg { width: 26px; height: 26px; flex-shrink: 0; }

/* --------------------------------------------------------------------------
   15. CATÁLOGO
   -------------------------------------------------------------------------- */
.page-hero {
    position: relative;
    overflow: hidden;
    padding-top: calc(var(--header-h) + clamp(2.5rem, 5vw, 4rem));
    padding-bottom: clamp(2.5rem, 5vw, 4rem);
    background: var(--celeste-noche);
    color: #b7c3cd;
}
.page-hero > .container { position: relative; z-index: 2; }
.page-hero h1 { color: var(--sobre-oscuro); }
.page-hero p { max-width: 58ch; color: rgba(255, 255, 255, .9); margin-top: 1rem; }
.page-hero .eyebrow { margin-bottom: 1rem; }

.breadcrumb {
    display: flex; align-items: center; gap: .45rem;
    font-size: .85rem; color: rgba(255, 255, 255, .8); margin-bottom: 1.25rem;
}
.breadcrumb svg { width: 13px; height: 13px; }
.breadcrumb a { color: var(--sobre-oscuro); font-weight: 600; }
.breadcrumb a:hover { color: var(--sobre-oscuro); }
.detail .breadcrumb { color: var(--tinta-3); }
.detail .breadcrumb a { color: var(--celeste-noche); }

.catalog-toolbar {
    display: flex; flex-wrap: wrap; gap: 1rem;
    align-items: center; justify-content: space-between;
    padding-bottom: 1.25rem; margin-bottom: 2rem;
    border-bottom: 1px solid var(--linea);
}
.filter-tabs { display: flex; flex-wrap: wrap; gap: .4rem; }
/* El borde va declarado en el estado base: antes el hover animaba
   `border-color` sobre un elemento sin borde y no hacía nada. */
.tab {
    min-height: 42px; padding: .5rem 1.05rem;
    border: 1px solid var(--linea);
    border-radius: var(--r-sm);
    background: var(--blanco);
    font-family: var(--font-head); font-size: .9rem; font-weight: 600;
    color: var(--tinta-2);
    transition: background var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.tab:hover { border-color: var(--celeste-hondo); color: var(--celeste-noche); }
.tab.is-active { background: var(--celeste-hondo); border-color: var(--celeste-hondo); color: var(--btn-texto); }

.sort-group { display: flex; align-items: center; gap: .6rem; }
.sort-group label { font-size: .86rem; font-weight: 600; color: var(--tinta-3); white-space: nowrap; }
.sort-group select {
    min-height: 42px; padding: .45rem 2.4rem .45rem .9rem;
    border: 1px solid var(--linea);
    border-radius: var(--r-sm);
    background-color: var(--blanco);
    font-size: .9rem; font-weight: 600; cursor: pointer; appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 256 256' fill='%2361707F'%3E%3Cpath d='M213.7 101.7l-80 80a8 8 0 0 1-11.4 0l-80-80A8 8 0 0 1 48 88h160a8 8 0 0 1 5.7 13.7z'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right .9rem center;
}

.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: clamp(1.25rem, 2.5vw, 2rem); }
.product-card {
    display: flex; flex-direction: column;
    background: var(--blanco);
    border: 1px solid var(--linea);
    border-radius: var(--r-lg);
    overflow: hidden;
    transition: transform var(--t-base), box-shadow var(--t-base);
}
.product-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--sh-lift);
}
.product-media {
    position: relative; aspect-ratio: 4 / 3; overflow: hidden;
    background: var(--celeste-nube);
    display: grid; place-items: center;
}
.product-media img { width: 100%; height: 100%; object-fit: cover; transition: transform 700ms var(--ease); }
.product-card:hover .product-media img { transform: scale(1.04); }
/* La gota de relleno iba al 30% del ancho y en el azul claro: gritaba
   "acá falta la foto". Al 18% y muy bajada de tono se lee como una marca
   de agua discreta, que es lo que corresponde a un hueco. */
.product-media .placeholder-icon { width: 18%; height: auto; color: rgba(65, 97, 128, .3); }
.product-media { border-bottom: 1px solid var(--linea); }

.product-badge {
    position: absolute; top: .7rem; left: .7rem;
    border-radius: var(--r-sm);
    background: var(--celeste-hondo); color: var(--btn-texto);
    font-family: var(--font-head);
    font-size: .7rem; font-weight: 600;
    letter-spacing: .06em; text-transform: uppercase;
    padding: .3rem .7rem; overflow: hidden;
}

.product-body { padding: 1.3rem; display: flex; flex-direction: column; flex: 1; gap: .5rem; }
.product-cat { font-size: .74rem; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: var(--accion-texto); }
.product-body h3 { font-size: 1.15rem; }
.product-body h3 a { color: var(--tinta); }
.product-body h3 a:hover { color: var(--celeste-noche); }
.product-body > p { font-size: .94rem; color: var(--tinta-3); flex: 1; }
.product-price {
    font-family: var(--font-head);
    font-size: 1.2rem; font-weight: 800; color: var(--tinta);
    letter-spacing: -.02em;
    padding-top: .5rem; border-top: 1px solid var(--linea);
}
.product-price small { display: block; font-size: .8rem; font-weight: 400; color: var(--tinta-3); letter-spacing: 0; }
.product-actions { display: flex; gap: .5rem; margin-top: .4rem; }
.product-actions .btn { flex: 1; }

.empty-state {
    grid-column: 1 / -1; text-align: center;
    padding: clamp(2.5rem, 6vw, 4rem) 1.5rem;
    background: var(--blanco);
    border: 1px dashed var(--linea-2);
    border-radius: var(--r-lg);
    color: var(--tinta-3);
}
.empty-state svg { width: 40px; height: 40px; color: var(--linea-2); margin: 0 auto 1rem; }

.skeleton-card { border: 1px solid var(--linea); border-radius: var(--r-lg); background: var(--blanco); overflow: hidden; }
.skeleton-media { aspect-ratio: 4 / 3; }
.skeleton-line { height: 13px; margin: .75rem 1.3rem; }
.skeleton-media, .skeleton-line {
    background: linear-gradient(100deg, var(--celeste-nube) 30%, var(--celeste-suave) 50%, var(--celeste-nube) 70%);
    background-size: 220% 100%;
    animation: shimmer 1.3s infinite linear;
}
@keyframes shimmer { to { background-position: -220% 0; } }

/* --------------------------------------------------------------------------
   16. FICHA DE PRODUCTO
   -------------------------------------------------------------------------- */
.detail { padding-top: calc(var(--header-h) + clamp(1.5rem, 4vw, 2.5rem)); padding-bottom: clamp(3rem, 6vw, 5rem); background: transparent; }
.detail-grid { display: grid; grid-template-columns: minmax(0, 6fr) minmax(0, 5fr); gap: clamp(2rem, 4vw, 3.5rem); align-items: start; }
.gallery-main {
    position: relative; aspect-ratio: 4 / 3; overflow: hidden;
    border-radius: var(--r-lg);
    background: var(--celeste-nube);
    display: grid; place-items: center;
    box-shadow: var(--sh-sm);
}
.gallery-main img { width: 100%; height: 100%; object-fit: cover; }
.gallery-main .placeholder-icon { width: 28%; color: var(--celeste-claro); }
.gallery-thumbs { display: flex; gap: .5rem; margin-top: .6rem; flex-wrap: wrap; }
.gallery-thumb {
    width: 74px; height: 60px; overflow: hidden;
    border: 1px solid var(--linea); border-radius: var(--r-sm);
    background: var(--celeste-nube); padding: 0;
}
.gallery-thumb img { width: 100%; height: 100%; object-fit: cover; }
.gallery-thumb.is-active { border-color: var(--celeste-hondo); border-width: 2px; }

.detail-title { margin-bottom: .4rem; font-size: clamp(2rem, 3.6vw, 2.8rem); }
.detail-subtitle { font-size: 1.05rem; color: var(--tinta-3); margin-bottom: 1.75rem; }
.detail-price-label { font-size: .78rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; color: var(--tinta-3); }
.detail-price { font-family: var(--font-head); font-size: 2.1rem; font-weight: 800; letter-spacing: -.03em; color: var(--celeste-noche); margin-bottom: .35rem; }
.detail-cta { display: flex; flex-wrap: wrap; gap: .7rem; margin: 1.75rem 0; }

.feature-list { display: grid; margin-top: .5rem; }
.feature-list li { display: flex; gap: .65rem; align-items: flex-start; padding: .7rem 0; border-bottom: 1px solid var(--linea); font-size: .98rem; }
.feature-list svg { width: 19px; height: 19px; color: var(--accion-texto); flex-shrink: 0; margin-top: 3px; }

.spec-table { width: 100%; border-collapse: collapse; margin-top: .5rem; }
.spec-table th, .spec-table td { text-align: left; padding: .75rem 0; font-size: .95rem; }
.spec-table tr { border-bottom: 1px solid var(--linea); }
.spec-table th { color: var(--tinta-3); font-weight: 400; width: 48%; }
.spec-table td { color: var(--tinta); font-weight: 600; font-family: var(--font-head); }

.detail-block { margin-top: 2.5rem; }
.detail-block h2 { font-size: 1.35rem; margin-bottom: .6rem; }
.detail-block p { color: var(--tinta-2); max-width: 68ch; }
.detail-extra { display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: clamp(1.5rem, 3vw, 3rem); align-items: start; }

/* ==========================================================================
   17. MOVIMIENTO
   ========================================================================== */
.progreso {
    position: fixed; top: 0; left: 0;
    height: 3px; width: 100%; z-index: 1000;
    background: linear-gradient(90deg, var(--celeste), var(--celeste-claro));
    transform: scaleX(0); transform-origin: left;
    transition: transform 120ms linear, opacity 300ms;
    opacity: 0; pointer-events: none;
}
.progreso.is-visible { opacity: 1; }

/* Olas del borde del hero */
/* El hero del diseño nuevo es blanco: las olas y las burbujas estaban
   pintadas para un bloque de color y sobre blanco quedaban como manchas
   grises. Se ocultan en portada y se conservan en el encabezado interior,
   que sí sigue siendo oscuro. */
.hero .olas, .hero .burbujas { display: none; }
.olas { position: absolute; inset: auto 0 -1px 0; height: 88px; z-index: 3; pointer-events: none; overflow: hidden; }
.ola {
    position: absolute; bottom: 0; left: 0;
    width: 200%; height: 100%;
    background-repeat: repeat-x; background-position: bottom left; background-size: 50% 100%;
    animation: deriva linear infinite;
    will-change: transform;
}
.ola--1 {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 720 88' preserveAspectRatio='none'%3E%3Cpath d='M0 40C120 18 240 18 360 40 480 62 600 62 720 40L720 88 0 88Z' fill='%23FFFFFF' fill-opacity='0.28'/%3E%3C/svg%3E");
    animation-duration: 22s;
}
.ola--2 {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 720 88' preserveAspectRatio='none'%3E%3Cpath d='M0 54C120 76 240 76 360 54 480 32 600 32 720 54L720 88 0 88Z' fill='%23F0F9FF' fill-opacity='0.5'/%3E%3C/svg%3E");
    animation-duration: 15s; animation-direction: reverse;
}
.ola--3 {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 720 88' preserveAspectRatio='none'%3E%3Cpath d='M0 66C90 79 270 79 360 66 450 53 630 53 720 66L720 88 0 88Z' fill='%23FFFFFF'/%3E%3C/svg%3E");
    animation-duration: 11s;
}
@keyframes deriva { from { transform: translate3d(0,0,0); } to { transform: translate3d(-50%,0,0); } }

.olas--claras { height: 56px; }
.olas--claras .ola--2 {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 720 88' preserveAspectRatio='none'%3E%3Cpath d='M0 54C120 76 240 76 360 54 480 32 600 32 720 54L720 88 0 88Z' fill='%23F0F9FF' fill-opacity='0.45'/%3E%3C/svg%3E");
}
.olas--claras .ola--3 {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 720 88' preserveAspectRatio='none'%3E%3Cpath d='M0 66C90 79 270 79 360 66 450 53 630 53 720 66L720 88 0 88Z' fill='%23F0F9FF'/%3E%3C/svg%3E");
}

/* Burbujas */
.burbujas { position: absolute; inset: 0; z-index: 1; pointer-events: none; overflow: hidden; }
.burbujas span {
    position: absolute; bottom: -60px; border-radius: 50%;
    background: radial-gradient(circle at 32% 28%, rgba(255,255,255,.6), rgba(255,255,255,.08) 62%);
    border: 1px solid rgba(255,255,255,.24);
    animation: subir linear infinite;
    will-change: transform, opacity;
    opacity: 0;
}
.burbujas span:nth-child(1) { left: 7%;  width: 15px; height: 15px; animation-duration: 16s; animation-delay: 0s; }
.burbujas span:nth-child(2) { left: 21%; width: 8px;  height: 8px;  animation-duration: 12s; animation-delay: 3.5s; }
.burbujas span:nth-child(3) { left: 34%; width: 21px; height: 21px; animation-duration: 20s; animation-delay: 1.5s; }
.burbujas span:nth-child(4) { left: 47%; width: 10px; height: 10px; animation-duration: 14s; animation-delay: 6s; }
.burbujas span:nth-child(5) { left: 61%; width: 17px; height: 17px; animation-duration: 18s; animation-delay: 2.5s; }
.burbujas span:nth-child(6) { left: 74%; width: 8px;  height: 8px;  animation-duration: 11s; animation-delay: 8s; }
.burbujas span:nth-child(7) { left: 86%; width: 23px; height: 23px; animation-duration: 22s; animation-delay: 4.5s; }
.burbujas span:nth-child(8) { left: 94%; width: 11px; height: 11px; animation-duration: 13s; animation-delay: 9.5s; }
@keyframes subir {
    0% { transform: translate3d(0,0,0) scale(.7); opacity: 0; }
    12% { opacity: .7; }
    50% { transform: translate3d(26px,-48vh,0) scale(1); }
    88% { opacity: .45; }
    100% { transform: translate3d(-14px,-96vh,0) scale(1.15); opacity: 0; }
}

.fuera-de-vista .ola,
.fuera-de-vista .burbujas span { animation-play-state: paused; }

/* ENTRADA DEL HERO
   Coreografía en cascada: primero baja la barra, después entra el texto por
   partes y por último la lámina del filtro, que es la pieza que hay que
   mirar. El orden cuenta una frase; si entraran todos juntos no habría
   jerarquía, sólo un parpadeo. */
.js .hero-copy > *, .js .hero-visual {
    opacity: 0;
    animation: entrar var(--mo-entrada) var(--ease-salida) forwards;
}
.js .hero-copy > .eyebrow  { animation-delay: calc(var(--mo-paso) * 1); }
.js .hero-copy > .hero-sub { animation-delay: calc(var(--mo-paso) * 2.6); }
.js .hero-copy > .hero-cta { animation-delay: calc(var(--mo-paso) * 4); }
.js .hero-visual { animation-delay: calc(var(--mo-paso) * 3.2); animation-name: entrar-foto; }

/* El título no se desvanece: si empieza en opacidad 0 no cuenta como
   pintado y el LCP se dispara casi un segundo. Medido. */
.js .hero-copy > h1 {
    opacity: 1;
    animation-name: deslizar;
    animation-duration: calc(var(--mo-entrada) * .92);
}

/* La barra baja sola al cargar, antes que nada. Anima `top` y no
   `transform` a propósito: un transform sobre la cabecera crea bloque
   contenedor y encierra al menú móvil, que es `fixed`. Está avisado arriba
   en la sección 5 y es exactamente la trampa que hay que no pisar. */
.js .site-header { animation: bajar-barra var(--mo-entrada) var(--ease-salida) both; }
@keyframes bajar-barra { from { top: -96px; } to { top: 14px; } }

@keyframes entrar { from { opacity: 0; transform: translate3d(0, calc(var(--mo-y) * 1.25), 0); } to { opacity: 1; transform: none; } }
@keyframes deslizar { from { transform: translate3d(0, var(--mo-y), 0); } to { transform: none; } }
@keyframes entrar-foto {
    from { opacity: 0; transform: translate3d(0, calc(var(--mo-y) * 1.6), 0) scale(.985); clip-path: inset(12% 0 0 0); }
    to { opacity: 1; transform: none; clip-path: inset(0 0 0 0); }
}

/* Las olas del borde del hero suben a su sitio al cargar, así el corte
   entre el bloque de color y la franja blanca se "dibuja" en vez de estar */
.js .olas { animation: subir-olas calc(var(--mo-entrada) * 1.4) var(--ease-salida) both; }
@keyframes subir-olas { from { transform: translateY(70%); } to { transform: none; } }

/* Micro-interacciones */
.product-badge::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,.5) 50%, transparent 62%);
    transform: translateX(-120%);
}
.product-card:hover .product-badge::after { animation: brillo 900ms var(--ease); }
@keyframes brillo { to { transform: translateX(120%); } }

.js .testimonial.is-visible .stars svg { opacity: 0; animation: estrella 340ms var(--ease) forwards; }
.js .testimonial.is-visible .stars svg:nth-child(1) { animation-delay: 100ms; }
.js .testimonial.is-visible .stars svg:nth-child(2) { animation-delay: 170ms; }
.js .testimonial.is-visible .stars svg:nth-child(3) { animation-delay: 240ms; }
.js .testimonial.is-visible .stars svg:nth-child(4) { animation-delay: 310ms; }
.js .testimonial.is-visible .stars svg:nth-child(5) { animation-delay: 380ms; }
@keyframes estrella {
    from { opacity: 0; transform: scale(.4) rotate(-22deg); }
    60% { transform: scale(1.15) rotate(3deg); }
    to { opacity: 1; transform: none; }
}

.cifra strong.contado { animation: golpe 400ms var(--ease); }
@keyframes golpe { 0% { transform: scale(1); } 45% { transform: scale(1.07); } 100% { transform: scale(1); } }

.btn .onda {
    position: absolute; border-radius: 50%;
    background: rgba(255,255,255,.45);
    transform: scale(0);
    animation: onda 560ms ease-out forwards;
    pointer-events: none;
}
@keyframes onda { to { transform: scale(2.6); opacity: 0; } }

/* La entrada por keyframes se retira: fijaba transform y le ganaba a la
   transicion del viaje, dejando la burbuja plantada a mitad de camino. */
.wa-float.is-visible::before {
    content: '';
    position: absolute; inset: 0;
    border-radius: inherit;
    border: 2px solid var(--wa);
    animation: latido 6s ease-out infinite 2s;
    pointer-events: none;
}
.wa-float:hover::before { animation-play-state: paused; opacity: 0; }
@keyframes aparecer-wa {
    0% { transform: translateY(16px) scale(.82); }
    62% { transform: translateY(-3px) scale(1.04); }
    100% { transform: none; }
}
@keyframes latido {
    0% { transform: scale(1); opacity: .75; }
    18% { transform: scale(1.32); opacity: 0; }
    100% { transform: scale(1.32); opacity: 0; }
}

.form-status.is-visible { animation: entrar 360ms var(--ease); }

.js .product-card.reveal { transition-delay: calc(var(--i, 0) * 55ms); }
.js .service-card.reveal { transition-delay: calc(var(--i, 0) * 60ms); }

/* TESIS DE MOVIMIENTO
   Momento autoral: el corte del filtro. Es lo único que se autoriza a
   contar algo por sí mismo, porque responde la pregunta que trae el
   visitante (qué le hace esto a mi agua).
   Todo lo demás sostiene: aparece contenido, se acusa una acción, se
   explica un cambio de estado. Nada se mueve porque sí.

   Antes había la misma entrada idéntica en cada sección. Eso no es una
   tesis, es deuda: cuando todo entra igual, nada entra. Ahora las listas
   entran como listas (escalonadas, porque son varias cosas hermanas) y
   la prosa simplemente aparece, sin desplazamiento. */
.js .reveal {
    opacity: 0;
    transition: opacity var(--mo-reveal) var(--ease-salida),
                transform var(--mo-reveal) var(--ease-salida);
}
.js .reveal.is-visible { opacity: 1; transform: none; }

/* Variantes de revelado. La regla de la norma es que el desplazamiento sea
   corto (8-16px) para que se lea como un fundido y no como un tobogán; por
   eso todas parten de `--mo-y` y ninguna inventa su propia distancia.
   Se elige variante por el TIPO de contenido, no por gusto:
     · prosa y titulares  → sólo fundido, sin desplazamiento
     · listas y tarjetas  → suben, porque son varias cosas hermanas
     · medios y láminas   → suben y se destapan
     · lo lateral         → entra desde su lado */
/* TODAS llevan :not(.is-visible), por el mismo motivo que las listas de mas
   abajo: sin el empatan en peso (0,3,0) con ".js .reveal.is-visible {
   transform: none }" y, por venir despues en el archivo, ganan. El elemento
   se queda desplazado PARA SIEMPRE con la animacion ya terminada.
   Con "izq" eso dejaba el texto de Nosotros 105px corrido a la izquierda: en
   la web se salia del margen y en la vista previa del panel se veia cortado. */
.js .reveal[data-rev="sube"]:not(.is-visible)   { transform: translateY(var(--mo-y)); }
.js .reveal[data-rev="izq"]:not(.is-visible)    { transform: translateX(calc(var(--mo-x) * -1)); }
.js .reveal[data-rev="der"]:not(.is-visible)    { transform: translateX(var(--mo-x)); }
.js .reveal[data-rev="escala"]:not(.is-visible) { transform: scale(.94); }
/* La lamina separa el estado inicial de la transicion: la transicion tiene que
   seguir declarada cuando ya es visible, o el clip-path llegaria de golpe. */
.js .reveal[data-rev="lamina"] {
    clip-path: inset(14% 0 0 0);
    transition: opacity var(--mo-reveal) var(--ease-salida),
                transform var(--mo-reveal) var(--ease-salida),
                clip-path var(--mo-reveal) var(--ease-salida);
}
.js .reveal[data-rev="lamina"]:not(.is-visible) {
    transform: translateY(calc(var(--mo-y) * 1.3));
}
.js .reveal[data-rev="lamina"].is-visible { clip-path: inset(0 0 0 0); }

/* Las listas siguen subiendo aunque no lleven data-rev: es su comportamiento
   por defecto y así el marcado viejo no se queda quieto.

   EL :not(.is-visible) NO ES ADORNO. Sin el, estas cuatro reglas empataban en
   peso con ".js .reveal.is-visible { transform: none }" (las dos 0,3,0) y,
   por estar mas abajo en el archivo, GANABAN: los pasos, las tarjetas de
   servicio, las de producto y las reseñas se quedaban desplazadas hacia abajo
   --mo-y para siempre, incluso con la animacion terminada. Con el nivel de
   movimiento en "expresivo" eso son 62px de mas en cada bloque. */
.js .service-card.reveal:not(.is-visible),
.js .product-card.reveal:not(.is-visible),
.js .testimonial.reveal:not(.is-visible),
.js .pasos.reveal:not(.is-visible) { transform: translateY(var(--mo-y)); }

/* Escalonado. El tope existe porque más allá de ~8 elementos los últimos
   entran tarde y la página parece lenta, no elegante. */
.js .reveal[data-delay="1"] { transition-delay: calc(var(--mo-paso) * 1); }
.js .reveal[data-delay="2"] { transition-delay: calc(var(--mo-paso) * 2); }
.js .reveal[data-delay="3"] { transition-delay: calc(var(--mo-paso) * 3); }
.js .reveal[data-delay="4"] { transition-delay: calc(var(--mo-paso) * 4); }

/* Cabeceras de sección: el rótulo y el título entran juntos pero el filete
   se dibuja después, que es el gesto que "abre" el capítulo. */
.js .reveal h2 { transition: inherit; }

/* ==========================================================================
   17b. MICRO-INTERACCIONES
   Regla que las gobierna a todas: responden a algo que hizo la persona
   (puntero, foco, pulsación, envío). Ninguna se mueve sola. Por eso siguen
   funcionando aunque el panel baje el movimiento decorativo a cero.
   ========================================================================== */

/* Botones: la pulsación hunde, el ícono acompaña, y el de WhatsApp adelanta
   el globo un pelo al pasar por encima. */
/* Pulsación: escala corta y rápida. Es el detalle que hace que el botón se
   sienta escuchado; 160ms y .97 son el rango donde se percibe sin verse. */
.btn { will-change: transform; }
.btn:active { transform: scale(.97); }
.btn svg { transition: transform var(--t-base); }
.btn--wa:hover svg { transform: scale(1.12) rotate(-6deg); }
.btn--ghost:hover, .btn--light:hover { transform: translateY(-1px); }

/* Flecha de los enlaces de línea: ya se desplazaba; ahora además el subrayado
   entra desde la izquierda y sale por la derecha, que es la dirección de
   lectura y hace que el enlace se sienta "recorrido". */
.enlace-linea { background-position: 0 100%; }
.enlace-linea:hover { background-position: 100% 100%; }

/* Navegación: el subrayado ya existía. Se le suma un levante mínimo del
   texto para que el objetivo se sienta "vivo" antes de hacer clic. */
.site-nav ul a { transition: color var(--t-fast), transform var(--t-fast); }
.site-nav ul a:hover { transform: translateY(-1px); }

/* Tarjetas: además del levante, el ícono de la tarjeta reacciona. Es el
   detalle que separa una tarjeta clicable de un rectángulo con texto. */
.service-card [data-icon] svg,
.pilar [data-icon] svg { transition: transform var(--t-base); }
.service-card:hover [data-icon] svg { transform: scale(1.1) rotate(-4deg); }

/* Campos: el foco ya pinta el anillo; se le añade un levante de 1px para
   que la fila enfocada se despegue de las vecinas. */
.field input, .field select, .field textarea {
    transition: border-color var(--t-fast), box-shadow var(--t-fast),
                background var(--t-fast), transform var(--t-fast);
}
.field input:focus, .field select:focus, .field textarea:focus { transform: translateY(-1px); }

/* El error no aparece: baja. Un texto que se materializa en el sitio pasa
   desapercibido; uno que entra desde arriba se lee como consecuencia.
   Va con keyframes y no con transition porque el error pasa de
   `display:none` a `display:block`, y eso una transición no lo puede tomar.
   Además el campo se sacude una vez: es el aviso que se siente sin leer. */
.field.has-error .field-error { animation: bajar-error 220ms var(--ease-salida) both; }
@keyframes bajar-error {
    from { opacity: 0; transform: translateY(-5px); }
    to { opacity: 1; transform: none; }
}
.field.has-error input,
.field.has-error select,
.field.has-error textarea { animation: sacudir 320ms var(--ease) 1; }
@keyframes sacudir {
    0%, 100% { transform: translateX(0); }
    25% { transform: translateX(-4px); }
    50% { transform: translateX(4px); }
    75% { transform: translateX(-2px); }
}

/* Miniaturas de la galería y filtros del catálogo */
.gallery-thumb { transition: border-color var(--t-fast), transform var(--t-fast); }
.gallery-thumb:hover { transform: translateY(-2px); }
.tab { will-change: transform; }
.tab:active { transform: scale(.96); }

/* Botón flotante: crece apenas al pasar por encima */
.wa-float { transition: transform var(--t-base), box-shadow var(--t-base); }
.wa-float:hover { transform: translateY(-2px) scale(1.03); }

/* Movimiento decorativo apagado. Dos caminos llegan acá:
     · el visitante pidió movimiento reducido en su sistema, o
     · el panel puso el nivel de animación en "mínimo" (html.mo-0).
   En los dos casos lo que se detiene es lo que se mueve solo. El feedback
   de los controles usa --t-fast/--t-base, que no dependen de --mo, así que
   el foco, el hover y la pulsación siguen respondiendo: eso no es adorno.
   Todo lo que anima en bucle tiene que quedar en un estado legible, no
   invisible — de ahí los estados finales de más abajo. */
html.sin-fondo-animado body::before { animation: none; }

html.mo-0 body::before,
html.mo-0 .ola,
html.mo-0 .burbujas span,
html.mo-0 .cinta-pista,
html.mo-0 .wa-float.is-visible::before { animation: none !important; }
html.mo-0 .burbujas span { opacity: .35; }
html.mo-0 .ondas span { opacity: .55; }
html.mo-0 .ondas span:nth-child(1) { transform: scale(1); }
html.mo-0 .ondas span:nth-child(2) { transform: scale(1.8); }
html.mo-0 .ondas span:nth-child(3) { transform: scale(2.6); }
html.mo-0 .ondas span:nth-child(4) { transform: scale(3.4); }

/* MOVIMIENTO REDUCIDO POR PREFERENCIA DEL SISTEMA
   Antes acá había el apagón de siempre: un `*` que ponía todas las
   animaciones y TODAS las transiciones en .01ms. Eso apaga de paso el
   feedback de los botones y los campos, y deja la página como un documento
   muerto. La preferencia pide quitar el movimiento grande —el que marea—,
   no quitar la respuesta de los controles. Así que ahora es quirúrgico:
   se detiene lo que se mueve solo y lo que recorre distancia larga, y se
   conserva el fundido corto y el feedback.

   `html.forzar-motion` deja sin efecto todo este bloque. Lo activa el panel
   cuando el dueño elige "animar siempre". Es una decisión suya y consciente:
   pasa por encima de una preferencia de accesibilidad del visitante. */
@media (prefers-reduced-motion: reduce) {
    html:not(.forzar-motion) { scroll-behavior: auto; }

    /* Lo que se mueve en bucle, quieto */
    html:not(.forzar-motion) body::before,
    html:not(.forzar-motion) .ola,
    html:not(.forzar-motion) .burbujas span,
    html:not(.forzar-motion) .cinta-pista,
    html:not(.forzar-motion) .wa-float.is-visible::before,
    html:not(.forzar-motion) .product-badge::after { animation: none !important; }

    /* Las entradas pierden el recorrido pero conservan un fundido corto */
    html.js:not(.forzar-motion) .reveal {
        transform: none !important;
        clip-path: none !important;
        transition-duration: 220ms !important;
        transition-delay: 0ms !important;
    }
    html.js:not(.forzar-motion) .hero-copy > *,
    html.js:not(.forzar-motion) .hero-visual,
    html.js:not(.forzar-motion) .site-header,
    html.js:not(.forzar-motion) .olas {
        animation-duration: 260ms !important;
        animation-delay: 0ms !important;
    }
    html:not(.forzar-motion) .field.has-error input,
    html:not(.forzar-motion) .field.has-error select,
    html:not(.forzar-motion) .field.has-error textarea { animation: none !important; }

    /* Los anillos terminan en opacidad 0: se congelan en un estado visible,
       si no el bloque de "Nosotros" queda como un rectángulo azul vacío. */
    html:not(.forzar-motion) .ondas span { animation: none !important; opacity: .55; border-width: 1.5px; }
    html:not(.forzar-motion) .ondas span:nth-child(1) { transform: scale(1); }
    html:not(.forzar-motion) .ondas span:nth-child(2) { transform: scale(1.8); }
    html:not(.forzar-motion) .ondas span:nth-child(3) { transform: scale(2.6); }
    html:not(.forzar-motion) .ondas span:nth-child(4) { transform: scale(3.4); }
    html:not(.forzar-motion) .burbujas span { opacity: .35; }

    /* El corte del filtro salta a su estado final: cuenta algo, y ese algo
       tiene que seguir contado aunque no se anime. */
    html:not(.forzar-motion) .rotulo::before { transition: none; transform: translateY(-.3em) scaleX(1); }
    html:not(.forzar-motion) .filtro * { animation: none !important; }
    html:not(.forzar-motion) .filtro .relleno { transform: scaleY(1); }
    html:not(.forzar-motion) .filtro .mugre { opacity: .95; }
    html:not(.forzar-motion) .filtro .vaso-agua { transform: translateY(14px); }
    html:not(.forzar-motion) .filtro-etapas li { color: var(--tinta); }
    html:not(.forzar-motion) .filtro-etapas b { background: var(--celeste); color: #fff; }
    html:not(.forzar-motion) .filtro .tubo,
    html:not(.forzar-motion) .filtro .canio,
    html:not(.forzar-motion) .filtro .vaso { stroke-dashoffset: 0; }
}

/* --------------------------------------------------------------------------
   18. RESPONSIVE
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
    .hero-grid, .sobre, .form-grid, .detail-grid, .detail-extra { grid-template-columns: minmax(0, 1fr); }
    .hero-grid { gap: 2.5rem; }
    .hero-foto { max-width: 460px; aspect-ratio: 4 / 3; }
    .sobre-visual { order: -1; }
    .footer-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .pasos { grid-template-columns: 1fr 1fr; gap: 0 clamp(1rem, 3vw, 2rem); }
    .cards-grid { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .cards-grid .service-card:first-child { grid-column: span 2; grid-row: span 1; }
    /* En una columna la figura no existe: la destacada es sólo la primera. */
    .testimonials { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
    .testimonial--destacado { grid-column: 1; grid-row: auto; }
}

@media (max-width: 860px) {
    .nav-toggle { display: inline-flex; }
    .header-actions .btn { display: none; }

    .site-nav {
        position: fixed; inset: 0;
        background: var(--papel);
        transform: translateX(100%);
        transition: transform 320ms var(--ease);
        z-index: 950;
        padding: calc(var(--header-h) + 1.5rem) 1.5rem 2rem;
        overflow-y: auto;
    }
    .site-nav.is-open { transform: none; }
    .site-nav ul { flex-direction: column; align-items: stretch; gap: 0; }
    .site-nav li { border-bottom: 1px solid var(--linea); }
    /* Acotado a la lista: si no, el display:block pisa el inline-flex del
       botón de WhatsApp y le apila el ícono sobre el texto. */
    .site-nav ul a { display: block; font-size: 1.3rem; padding: 1.05rem .25rem; }
    .site-nav ul a::after { display: none; }
    .site-nav .nav-cta { display: block; margin-top: 1.5rem; }
    .site-nav .nav-cta .btn { width: 100%; }

    .cifras { grid-template-columns: 1fr 1fr; gap: 1.5rem; }
    /* Apiladas vuelven a la izquierda: tres renglones centrados de largos
       distintos quedan desflecados, y así además arrancan a la misma altura
       que el resto del texto de la página. */
    .franja ul { grid-template-columns: 1fr; }
    .franja li {
        padding-inline: 0; padding-block: 1.1rem;
        justify-content: flex-start; text-align: left;
    }
    .wa-float span { display: none; }
    .wa-float { width: 56px; padding-inline: 0; justify-content: center; }
    .olas { height: 52px; }
}

@media (max-width: 620px) {
    :root {
        --header-h: 72px;
        /* Sin recorrido lateral en celular. Con 20px de margen de contenedor,
           cualquier desplazamiento horizontal saca el texto del borde y el
           recorte de la página se lo come: se veían las frases cortadas por
           la izquierda mientras entraban. En vertical no pasa. */
        --mo-x: 0px;
        --mo-y: calc(22px * var(--mo));
    }
    /* Los revelados laterales pasan a verticales: mismo gesto, sin recorte. */
    .js .reveal[data-rev="izq"],
    .js .reveal[data-rev="der"] { transform: translateY(var(--mo-y)); }
    .field-row { grid-template-columns: 1fr; }
    .footer-grid { grid-template-columns: 1fr; }
    .pasos { grid-template-columns: 1fr; }
    .cards-grid, .testimonials { grid-template-columns: minmax(0, 1fr); }
    .cards-grid .service-card:first-child { grid-column: span 1; grid-row: span 1; }
    .catalog-toolbar { flex-direction: column; align-items: stretch; }
    .catalog-toolbar > * { min-width: 0; }
    .filter-tabs { overflow-x: auto; flex-wrap: nowrap; padding-bottom: .35rem; scrollbar-width: none; width: 100%; }
    .filter-tabs::-webkit-scrollbar { display: none; }
    .tab { flex-shrink: 0; }
    .product-actions { flex-direction: column; }
    .hero-cta .btn { width: 100%; }
    .cifras { grid-template-columns: 1fr 1fr; }
}

@supports (padding: env(safe-area-inset-bottom)) {
    .wa-float { bottom: calc(clamp(1rem, 3vw, 1.75rem) + env(safe-area-inset-bottom)); }
}

@media print {
    .site-header, .wa-float, .site-footer, .hero-cta, .detail-cta, .progreso { display: none !important; }
    body { color: #000; background: #fff; }
}

/* ==========================================================================
   19. PRESENCIA
   El diagnóstico del cliente fue "parece muerta y no vende". Con movimiento
   o sin él tenía razón: la página era tímida. Texto chico, gris sobre
   blanco, el azul de marca apareciendo sólo en dos botones y una banda.
   Nada pesaba, así que el ojo no se detenía en ningún lado.

   Esto no cambia la paleta ni la estructura: sube el contraste de tamaño y
   mete el color de marca donde ya había jerarquía pero no se veía.
   ========================================================================== */

/* --- 1. Escala. Lo primero que hace que una página se sienta segura de sí
   misma es que el titular pese. Barlow Condensed aguanta cuerpo grande sin
   ocupar más ancho, así que esto no reflowea nada. -------------------------- */
h1 { font-size: clamp(2.75rem, 6vw, 4.25rem); }
h2 { font-size: clamp(2rem, 4vw, 2.85rem); }
.hero-sub { font-size: clamp(1.1rem, 1.5vw, 1.28rem); max-width: 44ch; }

/* --- 2. Los íconos existían pero eran hilos grises invisibles. Con el azul
   de marca y un poco más de cuerpo pasan a ser puntos de anclaje. --------- */
.pilar [data-icon] svg,
.service-card [data-icon] svg,
.franja svg,
.form-reasons svg {
    width: 22px; height: 22px;
    color: var(--celeste);
}
.pilar [data-icon], .service-card [data-icon] {
    display: inline-grid; place-items: center;
    width: 44px; height: 44px;
    border-radius: var(--r-sm);
    background: var(--celeste-suave);
    margin-bottom: .25rem;
}
.service-card:first-child [data-icon] { background: rgba(255, 255, 255, .12); }
.service-card:first-child [data-icon] svg { color: #fff; }

/* --- 3. Las cifras eran el dato más vendedor de la página y estaban en
   cuerpo de nota al pie. Ahora son el momento visual de la sección. ------- */
.cifras { border-top: 2px solid var(--celeste); padding-top: 2.5rem; }
.cifra strong {
    font-size: clamp(2.6rem, 5vw, 3.9rem);
    color: var(--accion-texto);
    letter-spacing: -.03em;
}
.cifra span {
    font-size: .82rem; font-weight: 600;
    letter-spacing: .06em; text-transform: uppercase;
    color: var(--tinta-3);
}

/* --- 4. La franja de garantías era texto suelto sobre blanco. Con fondo
   propio y separadores se lee como sello de confianza, que es su trabajo. */
.franja { background: var(--celeste-suave); border-bottom: none; }
.franja li { font-weight: 600; }

/* --- 5. Tarjetas: blanco sobre blanco no era jerarquía, era ausencia. Un
   tinte mínimo y un filete que reacciona alcanzan para que se despeguen. -- */
.service-card, .testimonial, .product-card { background: #fcfdfe; }
.service-card:hover, .product-card:hover { border-color: var(--celeste); }

/* --- 6. Movimiento con motivo (los cuatro del marco: continuidad, foco,
   respuesta y estado). Todo en transform y opacity, todo interrumpible,
   y todo colgado de --mo, así que el panel lo sigue regulando. ------------ */

/* El ícono acusa el hover de la tarjeta antes que la tarjeta: el ojo ya está
   ahí, y adelantarlo hace que la respuesta se sienta inmediata. */
.service-card [data-icon] {
    transition: background 200ms ease, transform 150ms cubic-bezier(.22, 1, .36, 1);
}
.service-card:hover [data-icon] { transform: translateY(-2px) scale(1.06); }

/* Las cifras entran contando; el filete de arriba se dibuja primero y las
   presenta. Es el único gesto decorativo que me permito, y dura una vez. */
.cifras { position: relative; }
.cifras::after {
    content: '';
    position: absolute; inset: -2px 0 auto 0;
    height: 2px; background: var(--celeste-hondo);
    transform: scaleX(0); transform-origin: left;
    transition: transform calc(700ms * var(--mo)) cubic-bezier(.25, 1, .5, 1);
}
.cifras.is-visible::after { transform: scaleX(1); }
/* Dentro de la franja no va: esa raya se dibujaba para separar las cifras del
   texto de "Nosotros", y aca la franja ya es su propio bloque. Ademas el
   cliente pidio expresamente quitar las divisorias de esta franja. */
.franja .cifras::after { content: none; }

/* El botón principal gana un barrido de luz al pasar por encima. Va con
   clip-path sobre una capa propia: no toca layout y se interrumpe limpio. */
.btn--wa::after,
.btn:not(.btn--ghost):not(.btn--light)::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(105deg, transparent 40%, rgba(255,255,255,.22) 50%, transparent 60%);
    transform: translateX(-110%);
    transition: transform 620ms cubic-bezier(.25, 1, .5, 1);
    pointer-events: none;
}
.btn--wa:hover::after,
.btn:not(.btn--ghost):not(.btn--light):hover::after { transform: translateX(110%); }

/* Los pasos numerados.
   El medallon estaba en inline-grid, o sea que fluia DENTRO del parrafo: la
   primera linea arrancaba a su derecha y las siguientes se le metian debajo,
   pegadas al borde izquierdo. Se leia torcido y los cuatro pasos quedaban
   desparejos entre si.
   Ahora cada paso es una grilla de dos columnas: el numero en la suya, fija, y
   el texto en la otra. Los cuatro arrancan a la misma altura y el parrafo se
   mantiene en bloque. */
.pasos li {
    display: grid;
    grid-template-columns: 46px minmax(0, 1fr);
    gap: .9rem;
    align-items: start;
}
.pasos li::before {
    display: grid; place-items: center;
    width: 46px; height: 46px;
    border-radius: 50%;
    background: var(--celeste-hondo);
    color: var(--btn-texto);
    font-size: 1.15rem;
    margin-bottom: 0;
}
.section--deep .pasos li::before { background: var(--celeste); }

@media (hover: none) {
    /* Sin puntero no hay hover: en táctil estos gestos nunca se disparan y
       dejarlos declarados sólo confunde al leer el archivo. */
    .service-card:hover [data-icon] { transform: none; }
}

/* ==========================================================================
   20. AJUSTES AL DISEÑO ENTREGADO
   Comparadas la maqueta y la implementación lado a lado, dos diferencias
   salían a la vista antes que ninguna otra. Se corrigen acá.
   ========================================================================== */

/* --- Cabeceras de sección centradas. En la maqueta el rótulo, el título y
   la bajada van centrados en una columna angosta; alineados a la izquierda
   la sección arranca sin ceremonia y se lee como un listado. ------------- */
.section > .container > .reveal:first-child,
.pasos-cab {
    max-width: 40rem;
    margin-left: auto;
    margin-right: auto;
    text-align: center;
}
.section > .container > .reveal:first-child .rotulo,
.pasos-cab .rotulo { justify-content: center; }
/* La sección de nosotros y la del formulario son a dos columnas: ahí el
   centrado rompería la lectura, así que se excluyen. */
.sobre .reveal, .form-grid .reveal { max-width: none; text-align: left; }
.sobre .reveal .rotulo, .form-grid .reveal .rotulo { justify-content: flex-start; }

/* --- La categoría del producto era texto suelto. En chip se lee como
   clasificación y le da color a una tarjeta que era toda gris. ---------- */
.product-cat {
    display: inline-flex;
    align-self: flex-start;
    background: var(--celeste-suave);
    color: var(--celeste);
    padding: .25rem .6rem;
    border-radius: 5px;
    font-size: .68rem;
    letter-spacing: .06em;
}

/* --- Existencias -----------------------------------------------------------
   El stock lo lleva el CRM sobre esta misma tabla `products`. El indicador sólo
   aparece en los productos que tienen el control activado, así que los que se
   cargaron antes de que el CRM existiera se siguen viendo igual que siempre.
   -------------------------------------------------------------------------- */
.product-meta { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }

.stock {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    padding: .25rem .55rem;
    border-radius: 5px;
    font-size: .68rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
}

.stock::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

.stock--si   { background: #e9f6ee; color: #1c7a41; }
.stock--poco { background: #fdf4e6; color: #9a5b00; }
.stock--no   { background: #fbeceb; color: #b3261e; }

/* ==========================================================================
   21. FONDO CON VIDA
   La página se leía demasiado blanca. La capa de ambiente ya existía pero
   estaba al 5% y apagada por defecto: no se veía ni quieta ni en movimiento.

   Criterio: el fondo tiene que dar vida ESTANDO QUIETO. Si toda la presencia
   depende de la animación, quien tenga el movimiento reducido en su sistema
   (o el propio dueño, que lo tiene apagado en Windows) sigue viendo una hoja
   en blanco. El movimiento suma; no sostiene.
   ========================================================================== */

/* Segunda capa, más lenta y en sentido contrario a la primera. Dos capas a
   distinta velocidad es lo que hace que el fondo se lea con profundidad en
   vez de como una mancha que se desplaza. */
body::after {
    content: '';
    position: fixed;
    inset: -25vmax;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(50vmax 42vmax at 88% 68%, var(--fondo-mancha-1), transparent 58%),
        radial-gradient(46vmax 40vmax at 8% 78%, var(--fondo-mancha-3), transparent 60%);
    opacity: .7;
    animation: derivar-fondo-2 64s ease-in-out infinite alternate;
    will-change: transform;
}
@keyframes derivar-fondo-2 {
    from { transform: translate3d(1.5%, 1%, 0) scale(1.05); }
    to   { transform: translate3d(-2%, -1.5%, 0) scale(1); }
}

/* Las bandas claras dejan de ser opacas y pasan a un velo: el ambiente se ve
   a través de ellas en vez de cortarse de golpe en cada sección. El blanco
   sigue leyéndose blanco, pero teñido por lo que hay detrás. */
.section--paper { background: rgba(245, 247, 249, .72); }
/* Sin banda de fondo: las cifras van sobre el fondo de la pagina, como pidio
   el cliente. La franja queda siendo solo el espacio que las contiene. */
.franja { background: transparent; }

/* La cabecera y las tarjetas se quedan opacas a propósito: son superficies
   de lectura, y un fondo que se les transparente les baja el contraste. */
.service-card, .testimonial, .product-card, .form-card { background: #fcfdfe; }

html.sin-fondo-animado body::after { animation: none; }
html.mo-0 body::after { animation: none !important; }

@media (prefers-reduced-motion: reduce) {
    /* Sin movimiento las dos capas se quedan en su posición inicial, que ya
       está compuesta: el degradé sigue ahí, sólo deja de derivar. */
    html:not(.forzar-motion) body::after { animation: none; }
}

/* ==========================================================================
   22. EL BOTÓN DE WHATSAPP VIAJA
   Antes eran dos botones distintos: uno en la barra y otro que aparecía
   abajo al scrollear. Se veían como dos cosas, y el de abajo salía de la
   nada. Ahora es UNA sola pieza que se traslada: el de la barra se apaga
   justo cuando la burbuja sale de esa misma posición hacia la esquina.
   El principio es continuidad sobre teletransporte — el disparador se
   convierte en la superficie en vez de desaparecer y reaparecer.
   ========================================================================== */

/* El de la barra pasa a círculo, con la misma medida y forma que la burbuja:
   si las dos siluetas no coinciden, el traslado se lee como un cambio de
   pieza y no como un movimiento. */
.wa-barra {
    width: 46px;
    min-height: 46px;
    padding: 0;
    border-radius: var(--r-pill);
    justify-content: center;
    transition: opacity 200ms ease, transform 260ms cubic-bezier(.22, 1, .36, 1),
                background 200ms ease;
}
.wa-barra svg { width: 22px; height: 22px; }

/* Al salir la burbuja, el de la barra se retira encogiéndose: refuerza que
   la pieza se fue de acá, no que se apagó. */
.site-header.wa-viajo .wa-barra {
    opacity: 0;
    transform: scale(.6);
    pointer-events: none;
}

/* La burbuja parte de donde está el botón de la barra. El desplazamiento lo
   mide el JS y lo deja en --wa-dx / --wa-dy, porque la posición depende del
   ancho de la ventana y del alto de la página: calcularlo a mano en CSS se
   rompe en cuanto cambia cualquiera de los dos. */
.wa-float {
    --wa-dx: 0px;
    --wa-dy: 0px;
    transform: translate3d(var(--wa-dx), var(--wa-dy), 0) scale(.62);
    opacity: 0;
    visibility: hidden;
    /* Curva de entrada: arranca rápido y frena largo, que es lo que hace que
       el recorrido se lea como un desplazamiento con peso y no como un salto. */
    transition: transform 620ms cubic-bezier(.22, 1, .36, 1),
                opacity 260ms ease,
                visibility 620ms,
                box-shadow var(--t-base),
                background var(--t-fast);
}
.wa-float.is-visible {
    transform: translate3d(0, 0, 0) scale(1);
    opacity: 1;
    visibility: visible;
}
/* El hover sólo se declara cuando ya llegó: si no, pisa el transform del
   viaje y la burbuja se planta a mitad de camino. */
@media (hover: hover) and (pointer: fine) {
    .wa-float.is-visible:hover { transform: translate3d(0, -3px, 0) scale(1.04); }
}

/* Sin movimiento no hay viaje: la burbuja simplemente está o no está. */
@media (prefers-reduced-motion: reduce) {
    html:not(.forzar-motion) .wa-float { transform: none; transition: opacity 200ms ease, visibility 200ms; }
    html:not(.forzar-motion) .wa-float.is-visible { transform: none; }
    html:not(.forzar-motion) .site-header.wa-viajo .wa-barra { transform: none; }
}
html.mo-0 .wa-float { transform: none; }
html.mo-0 .wa-float.is-visible { transform: none; }

/* ==========================================================================
   23. LA LLAMADA AL CATÁLOGO
   Era un enlace de texto fino, alineado a la izquierda y del mismo tono que
   el cuerpo: la única puerta al catálogo desde la portada y no la veía nadie.
   Además chocaba con la última tarjeta, porque las tarjetas entran desplazadas
   62px y el enlace ya estaba en su sitio. Se resuelven las dos cosas: pasa a
   botón centrado y se le da aire suficiente para que ningún recorrido de
   entrada lo alcance.
   ========================================================================== */
.cta-catalogo {
    /* El margen supera el recorrido máximo de entrada (46px x 1.35 = 62px),
       así que una tarjeta a mitad de animación no puede pisarlo. */
    margin-top: clamp(4.5rem, 7vw, 6rem);
    display: flex;
    justify-content: center;
}
.btn--catalogo {
    background: var(--celeste-hondo);
    color: var(--btn-texto);
    min-height: 54px;
    padding-inline: 2.25rem;
    font-size: 1rem;
}
.btn--catalogo:hover { background: var(--celeste-noche); color: var(--sobre-oscuro); }
.btn--catalogo svg {
    width: 18px; height: 18px;
    transition: transform 200ms cubic-bezier(.22, 1, .36, 1);
}
/* La flecha adelanta el gesto: dice hacia dónde lleva antes del clic. */
.btn--catalogo:hover svg { transform: translateX(4px); }

/* ==========================================================================
   24. LA GOTA QUE CAE, Y MEDIA EN LA TARJETA DESTACADA
   ========================================================================== */

/* --- El botón de WhatsApp cae como una gota -----------------------------
   Antes viajaba con curva de salida (rápido y frenando), que es lo contrario
   de caer. Una gota ACELERA: arranca lenta y llega rápido. Y al caer se
   estira en el eje del movimiento, al impactar se achata, y recién ahí se
   redondea. Ese estirar-achatar es lo que la lee como líquido y no como una
   pelota. Va en keyframes porque son cuatro fases con timings distintos;
   una transición sólo sabe ir de A a B. ---------------------------------- */
/* EL VIAJE: UNA GOTA QUE CAE Y REVIENTA
   El botón no se deforma nunca — deformar una caja con un logo adentro daba
   un bulto, no una gota. Acá la gota es un SVG aparte, sin contenido, así que
   SÍ puede tener silueta de gota de verdad. La secuencia es:
     1. la gota se desprende del botón de la barra y cae acelerando
     2. al llegar se aplasta contra el punto de destino y se desvanece
     3. de ahí emerge la burbuja redonda, ya con el logo
   Cada paso arranca cuando termina el anterior, con retardos calculados
   sobre --mo, así que el nivel de movimiento del panel los escala a todos. */
:root {
    --gota-caida: calc(780ms * var(--mo));
    --gota-splash: calc(150ms * var(--mo));
}

/* Una gota sola cayendo recta se veía mecánica. Ahora son cuatro, de
   tamaños decrecientes y con arranques escalonados, como se parte un chorro
   real: la primera es la grande y las de atrás la siguen achicándose.
   Cada una lleva su propia deriva horizontal, así que ninguna cae por la
   misma línea — que era lo que más delataba que era una animación. */
.gotas-viaje {
    position: fixed;
    z-index: 851;
    right: clamp(1rem, 3vw, 1.75rem);
    bottom: clamp(1rem, 3vw, 1.75rem);
    /* Ancho suficiente para que el arco mas abierto (78px) no se recorte */
    width: 56px; height: 56px;
    overflow: visible;   /* los arcos llegan a 86px, no se recortan */
    pointer-events: none;
}
.gotas-viaje .gota {
    position: absolute;
    left: 50%; top: 50%;
    width: var(--t); height: var(--t);
    margin-left: calc(var(--t) / -2);
    margin-top: calc(var(--t) / -2);
    color: var(--wa);
    opacity: 0;
}
.gotas-viaje .gota svg { width: 100%; height: 100%; display: block; fill: currentColor; }

/* Cada gota lleva TRES desvíos horizontales, no uno: dónde nace (--d1),
   cuánto se abre a mitad de camino (--d2) y dónde aterriza (--d3). Con eso
   cada una describe un arco propio y el conjunto se abre como un regado,
   en vez de caer todas por la misma vertical. La grande va casi derecha
   porque es la que marca el destino; las chicas son las que se dispersan. */
.gotas-viaje .gota:nth-child(1) { --t: 52px; --d1:   2px; --d2:   8px; --d3:   0px; --r: 0ms;   }
.gotas-viaje .gota:nth-child(2) { --t: 36px; --d1: -16px; --d2: -52px; --d3: -24px; --r: 110ms; }
.gotas-viaje .gota:nth-child(3) { --t: 28px; --d1:  14px; --d2:  58px; --d3:  28px; --r: 205ms; }
.gotas-viaje .gota:nth-child(4) { --t: 21px; --d1:  -8px; --d2: -80px; --d3: -42px; --r: 300ms; }
.gotas-viaje .gota:nth-child(5) { --t: 16px; --d1:  10px; --d2:  86px; --d3:  46px; --r: 385ms; }

.gotas-viaje.cae .gota {
    animation: gota-cae var(--gota-caida) cubic-bezier(.45, .05, .55, .95) both;
    animation-delay: calc(var(--r) * var(--mo));
}
.gotas-viaje.sube .gota {
    animation: gota-sube var(--gota-caida) cubic-bezier(.45, .05, .55, .95) both;
    animation-delay: calc(var(--r) * var(--mo));
}

/* El arco: sale del botón, se abre hacia afuera a mitad de recorrido y se
   cierra al aterrizar. El desvanecido empieza antes del impacto para que no
   haya corte seco. */
@keyframes gota-cae {
    0% {
        transform: translate3d(calc(var(--wa-dx) + var(--d1)), var(--wa-dy), 0) scale(.35);
        opacity: 0;
    }
    12% { opacity: .95; }
    /* punto más abierto del arco */
    45% {
        transform: translate3d(calc(var(--wa-dx) * .45 + var(--d2)), calc(var(--wa-dy) * .45), 0)
                   scale3d(.78, 1.26, 1);
        opacity: .95;
    }
    72% {
        transform: translate3d(calc(var(--wa-dx) * .12 + var(--d3)), calc(var(--wa-dy) * .12), 0)
                   scale3d(.82, 1.2, 1);
        opacity: .9;
    }
    90% {
        transform: translate3d(calc(var(--d3) * .3), 0, 0) scale3d(1.06, .88, 1);
        opacity: .45;
    }
    100% { transform: translate3d(0, 2px, 0) scale3d(1.2, .62, 1); opacity: 0; }
}
@keyframes gota-sube {
    0%   { transform: translate3d(0, 2px, 0) scale3d(1.18, .68, 1); opacity: 0; }
    14%  { transform: translate3d(calc(var(--d3) * .3), 0, 0) scale3d(.8, 1.24, 1); opacity: .95; }
    52% {
        transform: translate3d(calc(var(--wa-dx) * .45 + var(--d2)), calc(var(--wa-dy) * .45), 0)
                   scale3d(.76, 1.28, 1);
        opacity: .9;
    }
    100% {
        transform: translate3d(calc(var(--wa-dx) + var(--d1)), var(--wa-dy), 0) scale(.35);
        opacity: 0;
    }
}

/* La burbuja ya no viaja: emerge en el sitio donde reventó la gota, con un
   rebote corto. El retardo es exactamente lo que tarda la caída. */
.wa-float.is-visible {
    animation: burbuja-emerge calc(300ms * var(--mo)) cubic-bezier(.34, 1.5, .64, 1)
               calc(var(--gota-caida) * .78) both;
}
.wa-float.subiendo {
    visibility: visible;
    animation: burbuja-recoge calc(200ms * var(--mo)) cubic-bezier(.55, .06, .68, .19) both;
}

@keyframes burbuja-emerge {
    0%   { transform: scale3d(1.35, .5, 1); opacity: 0; }   /* nace achatada, del splash */
    45%  { transform: scale3d(.94, 1.08, 1); opacity: 1; }
    100% { transform: scale3d(1, 1, 1); opacity: 1; }
}
@keyframes burbuja-recoge {
    0%   { transform: scale(1); opacity: 1; }
    100% { transform: scale3d(1.3, .45, 1); opacity: 0; }   /* se aplasta y deja salir la gota */
}

/* Sin movimiento no hay gota ni recorrido: la burbuja está o no está. */
@media (prefers-reduced-motion: reduce) {
    html:not(.forzar-motion) .gotas-viaje { display: none; }
    html:not(.forzar-motion) .wa-float.is-visible,
    html:not(.forzar-motion) .wa-float.subiendo { animation: none !important; transform: none; }
}
html.mo-0 .gotas-viaje { display: none; }
html.mo-0 .wa-float.is-visible,
html.mo-0 .wa-float.subiendo { animation: none !important; transform: none; }

/* --- Foto o video en la tarjeta destacada de servicios ------------------- */
.cards-grid .service-card.con-media { position: relative; overflow: hidden; }
.cards-grid .service-card.con-media > img,
.cards-grid .service-card.con-media > video {
    position: absolute; inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    z-index: 0;
}
/* Velo oscuro: sin esto el texto blanco desaparece sobre una foto clara.
   No es estética, es lo único que garantiza que se siga leyendo con
   cualquier imagen que suba el cliente. */
.cards-grid .service-card.con-media::after {
    content: '';
    position: absolute; inset: 0; z-index: 1;
    background: linear-gradient(180deg,
        rgba(29, 45, 61, .35) 0%,
        rgba(29, 45, 61, .82) 100%);
}
.cards-grid .service-card.con-media > *:not(img):not(video) { position: relative; z-index: 2; }

/* ==========================================================================
   25. TRANSICIÓN DEL CATÁLOGO A LA FICHA
   Navegación entre dos documentos distintos, así que no alcanza con CSS de
   una página: se usa la View Transitions API, que es nativa y no suma
   ninguna librería. El navegador saca una foto de la página que se va, otra
   de la que llega, y las interpola.

   Lo importante no es el fundido sino el elemento COMPARTIDO: la foto del
   producto en la tarjeta y la foto grande de la ficha llevan el mismo
   nombre, así que el navegador entiende que son la misma cosa y la mueve
   de un lugar al otro en vez de cruzarlas. Eso es lo que hace que se sienta
   que abriste ESE producto y no que cambiaste de página.

   Dónde no hay soporte (Firefox hoy) la navegación es la de siempre: no se
   rompe nada, simplemente no hay animación.
   ========================================================================== */
@view-transition { navigation: auto; }

/* LA QUE SE VA DESAPARECE; LA QUE LLEGA ENTRA SOLA.
   Por defecto el navegador FUNDE las dos capturas enteras, y con maquetas tan
   distintas eso se ve roto: quedaban dos migas de pan, el hero oscuro del
   catálogo encima del precio y dos barras superiores desfasadas.

   La primera corrección fue quitarle la animación a las dos. Se acabó el
   fantasma, pero quedó un corte seco: la página cambiaba de golpe y no se
   leía ninguna transición.

   Esto es lo que hay que hacer: la vieja se oculta de una (nunca llega a
   verse encima de la nueva, así que no hay fantasma) y la nueva sube y
   aparece. Detrás no queda un hueco negro: el fondo de la página lo pinta el
   lienzo del navegador, no la captura, así que durante la entrada se ve el
   celeste del sitio. */
::view-transition-old(root) {
    animation: none;
    opacity: 0;
}
::view-transition-new(root) {
    animation: ficha-entra calc(380ms * var(--mo)) cubic-bezier(.22, 1, .36, 1) both;
    mix-blend-mode: normal;
}
/* 22px, sin escalar. La primera versión subía 16px en 340ms y no se veía; la
   segunda subía 34px y además encogía la página, y encoger una página entera
   emborrona el texto mientras dura — se ve barato. Subir y aparecer, nada más. */
@keyframes ficha-entra {
    from { opacity: 0; transform: translateY(22px); }
    to   { opacity: 1; transform: none; }
}

/* La foto viaja de la tarjeta a la ficha.
   La sombra existe SÓLO durante el vuelo: sin ella es un recuadro gris claro
   moviéndose sobre un fondo gris claro, o sea nada. Con la sombra se despega
   del fondo y se entiende que es la tarjeta que tocaste, viajando. Cuando el
   cliente cargue fotos de verdad esto se va a leer solo. */
::view-transition-group(ficha-media) {
    animation-duration: calc(460ms * var(--mo));
    animation-timing-function: cubic-bezier(.22, 1, .36, 1);
    border-radius: var(--r-lg);
    box-shadow: 0 26px 60px -18px rgba(29, 45, 61, .45);
}
/* El cruce entre las dos fotos va corto: cuando es la misma imagen no se nota,
   y cuando la tarjeta y la ficha muestran cosas distintas evita el fantasma.
   `cover` es lo que impide que la foto se deforme mientras cambia de tamaño. */
::view-transition-old(ficha-media),
::view-transition-new(ficha-media) {
    height: 100%;
    object-fit: cover;
    animation-duration: calc(220ms * var(--mo));
}

/* El título NO viaja, a propósito. Se probó y quedaba peor: el nombre salía
   volando en diagonal desde la tarjeta hasta la ficha y, mientras tanto, su
   lugar en la ficha quedaba VACÍO — se leía como contenido que no cargó. Con
   la foto sola el gesto se entiende igual y la página llega entera. */

/* Quien pidió menos movimiento no quiere que la página se deslice: se le
   deja el corte seco de siempre, que es lo que tenía antes. */
@media (prefers-reduced-motion: reduce) {
    html:not(.forzar-motion) ::view-transition-group(*),
    html:not(.forzar-motion) ::view-transition-old(*),
    html:not(.forzar-motion) ::view-transition-new(*) { animation: none !important; }
}
