/*
 * base.css
 * ------------------------------------------------------------
 * Fundaciones globales: design tokens, reset, tipografía.
 * Cargar ANTES que layout, components y admin.
 *
 * Dirección de diseño
 * -------------------
 * Quien llega a este sitio suele estar en medio de un trámite que no entiende:
 * una licencia rechazada, una evaluación de invalidez. Necesita que el sitio
 * se vea serio y le baje la ansiedad, no que lo entretenga.
 *
 * La paleta es de una sola familia: el verde azulado de la marca sobre un
 * blanco con una gota de menta.
 *
 *     crema     → el fondo de la página;
 *     verde     → superficies, chips, dibujos y franjas suaves;
 *     verde profundo → el texto de acento, los iconos y la acción;
 *     arena     → el neutro cálido de las ilustraciones;
 *     verde muy oscuro → el texto y las superficies oscuras.
 *
 * De dónde salen esos colores
 * ---------------------------
 * Del manual de marca, que trae cuatro planos y saturados: #00b09e, #ffa400,
 * #ff427b y #ff802c. Los cuatro sirven impresos en una taza; los cuatro juntos
 * al tamaño de una página web se leen como fiesta infantil, y quien llega acá
 * viene de una licencia rechazada.
 *
 * Así que manda uno solo, el verde azulado del isotipo, y lo hace en tres
 * pasos: claro para superficies, lavado para franjas, profundo para la acción.
 * El #00b09e literal del manual no aparece como tal en ningún lado porque no
 * puede: con texto blanco encima da 2.4:1 y no sirve de botón. El profundo es
 * ese mismo verde bajado hasta que el blanco llega a 6:1.
 *
 * El naranja del manual sí entra, pero lavado, en --sand: es el neutro cálido
 * de los dibujos y es lo único que evita que una paleta enteramente verde
 * azulada se lea fría. El rosa no entra a la paleta: ya es el color de aviso
 * del sitio, y un color que significa dos cosas no significa ninguna.
 *
 * Un solo tono y neutros, sin un segundo color de acento: la jerarquía la dan
 * el relleno y el peso, no la temperatura.
 *
 * Regla que se mantiene: cada color de texto sobre su fondo cumple WCAG AA
 * (4.5:1, o 3:1 en texto grande). Los ratios están anotados abajo. Si cambias
 * un color, verifica el contraste antes de commitear.
 *
 * Por qué el verde claro no se usa como texto: sobre crema da 2.1:1, por
 * debajo del mínimo. Vive como superficie —con texto oscuro encima— y para
 * texto e iconos está su versión profunda, que es el mismo color bajado en
 * luminosidad, no otro color.
 *
 * Nota de nombres: las variables se siguen llamando --sage. Es el nombre del
 * verde salvia con el que nació el sitio y quedó regado por once hojas de
 * estilo; renombrarlo no cambiaría un pixel y sí ensuciaría el historial de
 * todas ellas. --sage es "el color de la marca", no "salvia".
 * ------------------------------------------------------------
 */

:root {
    /* ---- Paleta base ----
       Entre paréntesis, el contraste contra el fondo crema. */
    --cream:          #f5f9f8;   /* blanco con una gota de menta: el fondo de la página */
    --sage:           #5cbfb2;   /* verde de marca: superficies, chips y dibujos (2.1:1 — nunca texto) */
    --sage-soft:      #dcf0ec;   /* verde lavado: franjas y fondos suaves */
    --sage-deep:      #0a6f66;   /* verde profundo: texto, iconos y la acción (5.7:1) */
    --sage-hover:     #075a53;   /* 8.1:1 con texto blanco */
    --sand:           #f7e2d2;   /* arena: el naranja del manual, lavado, en los dibujos */
    /* El segundo color de la marca, sin lavar: el rosa del manual tal cual.
       ------------------------------------------------------------
       Es el único color saturado que entra a la paleta, y entra con una regla
       estricta: solo marcas y titulares. Da entre 3.1 y 3.6:1 según el fondo,
       que es el mínimo que WCAG pide para texto grande (24px, o 19px en
       negrita) y no llega al 4.5 que pide el texto corriente. Así que puede
       ser la palabra marcada de un titular, el filete de un antetítulo o la
       barra de progreso de un artículo, y no puede ser una bajada ni un dato.

       El valor no es el #ff427b exacto del manual: es un escalón más abajo por
       la misma recta, sin mover el tono. El del manual da 2.8:1 sobre la
       franja lavada —donde vive el bloque de cierre de cada página, que sí
       lleva titular—, o sea que quedaba corto justo en uno de los cuatro
       fondos sobre los que se usa. A ojo son el mismo rosa; medidos, este pasa
       en los cuatro y el otro no.

       Va acotado a propósito. En el logotipo el rosa es media palabra —"Evalua
       mente"— y funciona porque está una vez; repartido por la página se
       convierte en el color del sitio y se lleva por delante lo serio, que es
       justo lo que este sitio necesita conservar. */
    --accent-alt:     #f73a74;
    --ink:            #1f3a36;   /* verde azulado muy oscuro: el texto (11.5:1) */
    --ink-deep:       #142b28;   /* el mismo, más profundo: titulares y pie (14.1:1) */

    /* ---- Texto ---- */
    --color-text:           var(--ink);
    --color-text-strong:    var(--ink-deep);   /* 14.9:1 sobre blanco */
    --color-text-muted:     #4a6863;   /* 5.7:1 sobre crema */
    --color-text-subtle:    #86a29d;   /* solo placeholders: no es texto que haya que leer */
    --color-text-inverse:   #f0f6f4;

    /* ---- Fondos ----
       La página es crema; las tarjetas siguen siendo blancas, y ese salto de
       blanco sobre crema es lo que las hace flotar sin cargar más sombra. */
    --color-bg:             #ffffff;
    --color-bg-page:        var(--cream);
    --color-bg-alt:         var(--sage-soft);
    --color-bg-hover:       #e9f4f1;
    --color-bg-app:         var(--cream);
    --color-bg-ink:         var(--ink);

    /* ---- Bordes ----
       Templados hacia el fondo: un borde gris neutro sobre un fondo que tira a
       menta se ve sucio. */
    --color-border:         #dde8e5;
    --color-border-strong:  #c0d2ce;
    --color-border-cool:    #c9e4de;

    /* ---- Marca ---- */
    --color-primary:        var(--ink);
    --color-primary-hover:  var(--ink-deep);
    --color-primary-text:   #ffffff;
    --color-accent:         var(--sage);
    --color-accent-text:    var(--sage-deep);
    /* Sobre una superficie oscura el verde profundo desaparece. Para lo que va
       encima de fondo oscuro, el verde aclarado: 6.6:1 sobre --ink. */
    --color-accent-on-dark: #62d0c0;
    --color-text-on-dark:   #b9d6d1;   /* texto secundario sobre oscuro: 7.9:1 */
    --color-accent-soft:    var(--sage-soft);
    /* La acción principal es verde profundo con texto blanco (6:1). El verde
       claro no sirve de botón —con blanco encima da 2.2:1—, así que queda para
       superficies y realces. */
    --color-cta:            var(--sage-deep);
    --color-cta-hover:      var(--sage-hover);
    --color-cta-soft:       var(--sage-soft);
    --color-cta-text:       #ffffff;

    /* ---- Estados ----
       Recalculados contra crema, no contra blanco. */
    --color-success:        #3a6b52;   /* 5.9:1 */
    --color-success-soft:   #e6f0e9;
    --color-warn:           #855a16;   /* 5.5:1 */
    --color-warn-soft:      #f7efdd;
    --color-danger:         #a4402f;   /* 5.7:1 */
    --color-danger-soft:    #f6e8e4;

    /* ---- Tipografía ---- */
    /* Poppins para titulares: geométrica y redonda, que es lo que pide una
       paleta cálida. Inter para el cuerpo, que es lo que se lee con la cabeza
       en otra cosa. Las dos son sans serif. */
    --font-display:       "Poppins", "Inter", -apple-system, BlinkMacSystemFont, sans-serif;
    --font-family:        "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    --font-family-mono:   ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
    --font-size-base:     16px;
    --line-height-base:   1.65;
    --font-feature:       "cv11", "ss01";
    --font-numeric:       tabular-nums;

    /* ---- Escala tipográfica ----
       Había 56 tamaños distintos declarados a mano (0.755rem, 0.925rem,
       0.97rem…): doce de ellos caían entre 13.6 y 15.7px, diferencias que no
       se distinguen una por una pero que juntas hacen que el sitio se sienta
       inestable. Estos son los únicos pasos permitidos.

       Al agregar un componente: elige el paso más cercano, no inventes un
       valor intermedio. Si de verdad hace falta uno nuevo, se agrega acá y se
       revisa que no duplique a un vecino.

       Los títulos h1/h2 no usan estos pasos: son fluidos con clamp() más
       abajo, porque tienen que respirar en desktop sin desbordar el móvil. */
    --text-2xs:   0.6875rem;  /* 11px — cintas y etiquetas en versales */
    --text-xs:    0.75rem;    /* 12px — metadatos, pies de tarjeta */
    --text-sm:    0.8125rem;  /* 13px — interfaz secundaria */
    --text-md:    0.875rem;   /* 14px — interfaz por omisión, navegación */
    --text-base:  0.9375rem;  /* 15px — texto dentro de tarjetas, botones */
    --text-lg:    1rem;       /* 16px — cuerpo */
    --text-xl:    1.125rem;   /* 18px — bajadas, títulos de tarjeta */
    --text-2xl:   1.25rem;    /* 20px */
    --text-3xl:   1.5rem;     /* 24px */
    --text-4xl:   1.75rem;    /* 28px */
    --text-5xl:   2rem;       /* 32px — precios */
    --text-6xl:   2.5rem;     /* 40px — cifras y ornamentos */
    --text-7xl:   3rem;       /* 48px — titular del hero en desktop */

    /* ---- Espaciado ---- */
    --space-xs:  0.25rem;
    --space-sm:  0.5rem;
    --space-md:  1rem;
    --space-lg:  1.5rem;
    --space-xl:  2.5rem;
    --space-2xl: 4rem;
    --space-3xl: 6rem;

    /* ---- Sizing ---- */
    /* El ancho de la página y su aire lateral, en un solo lugar.
       ------------------------------------------------------------
       Antes había dos medidas conviviendo: la cabecera y el pie se armaban con
       1200px y 1.2rem escritos a mano en site_header.css, y el contenido con
       este token en 1120px y --space-md. El resultado se veía: el título de
       cada página empezaba 37px más adentro que la marca de la cabecera y que
       el pie, en todos los anchos de escritorio.

       Ahora los tres consumen estos dos valores. Si mañana hay que mover el
       ancho de la página, se mueve acá y la cabecera y el pie lo siguen. */
    --container-max: 1200px;
    --container-pad: 1.2rem;
    /* Radios de la vuelta actual: lo que hace un par de años se veía moderno
       a 10px hoy se lee duro. Un paso más de curvatura en cada nivel, con la
       proporción intacta. */
    --radius-sm:     8px;
    --radius:        12px;
    --radius-lg:     16px;
    --radius-xl:     20px;
    --radius-2xl:    26px;
    --radius-pill:   999px;

    /* ---- Elevación ----
       Sombras teñidas del verde oscuro del texto en vez de gris, para que no se
       vean sucias sobre el fondo crema. El estilo es plano: la mayoría de las tarjetas se
       define con borde, y la sombra queda para lo que flota de verdad. */
    --shadow-xs: 0 1px 2px rgba(31, 58, 54, 0.04);
    --shadow-sm: 0 1px 2px rgba(31, 58, 54, 0.04), 0 3px 8px -4px rgba(31, 58, 54, 0.05);
    --shadow-md: 0 6px 20px -6px rgba(31, 58, 54, 0.10), 0 2px 8px -3px rgba(31, 58, 54, 0.05);
    --shadow-lg: 0 22px 48px -16px rgba(31, 58, 54, 0.16), 0 8px 20px -8px rgba(31, 58, 54, 0.07);
    --shadow-xl: 0 34px 68px -20px rgba(31, 58, 54, 0.24), 0 12px 28px -12px rgba(31, 58, 54, 0.1);
    --shadow-cta: 0 8px 18px -8px rgba(10, 111, 102, 0.4);

    /* ---- Focus ---- */
    --ring:        0 0 0 3px rgba(31, 58, 54, 0.1);
    --ring-strong: 0 0 0 3px rgba(92, 191, 178, 0.4);
    --ring-cta:    0 0 0 3px rgba(10, 111, 102, 0.32);

    /* ---- Motion ---- */
    --transition:      150ms cubic-bezier(0.4, 0, 0.2, 1);
    --transition-slow: 280ms cubic-bezier(0.4, 0, 0.2, 1);
    --ease-out-quart:  cubic-bezier(0.25, 1, 0.5, 1);
    --ease-spring:     cubic-bezier(0.22, 1.2, 0.36, 1);

    /* ==========================================================
       Capa editorial
       ==========================================================
       Todo lo de acá abajo es aditivo: no reemplaza ningún token de arriba.
       Esa es la regla, y tiene una razón concreta: admin.css tiene 23
       referencias a --radius-* y auth.css otras 21. Cambiarle el valor a un
       token existente para suavizar una tarjeta del sitio público reflota el
       panel de administración en silencio. Por eso hay --radius-organic en vez
       de un --radius-lg más grande.

       Quién consume esto: solo el sitio público. base.css lo cargan también el
       admin, el login y el instalador, pero las reglas que usan estos tokens
       viven en layout.css (que es público y nada más) o van prefijadas
       .sitio-publico. Los tokens declarados y no usados no pesan nada.
       ========================================================== */

    /* ---- Titulares ----
       La tipografía es la misma que en main: Poppins para titular e Inter para
       el cuerpo. El nombre del token se conserva —lo consumen layout.css,
       components.css, agenda.css, tienda.css y site_header.css, unas treinta
       reglas— pero apunta a la display de la marca. Cambiarlo acá y no en cada
       regla es lo que mantiene una sola fuente de verdad; si mañana vuelve un
       serif, vuelve en esta línea.

       El Cormorant Garamond que había acá también salió del <link> de Google
       Fonts en lib/layout.php: una familia que ya no se usa igual se descarga. */
    --font-serif:      var(--font-display);
    /* Tracking y leading de sans, no de serif: Poppins es geométrica y ancha, y
       con el +0.012em que pedía el serif los titulares quedaban sueltos. */
    --tracking-serif:  -0.022em;
    --leading-serif:   1.1;

    /* ---- Ornamento botánico ----
       Los dibujos de línea se pintan con currentColor y se gradúan con estas
       opacidades, así una sola regla los tematiza: cambiar cuánto pesan es
       mover dos números, no repasar cada SVG. */
    --ornament-color:   var(--sage);
    --ornament-opacity: 0.34;   /* trazo sobre crema */
    --ornament-strong:  0.52;   /* trazo sobre franja salvia, que come contraste */

    /* ---- Lavados y filete ----
       Los halos del hero estaban escritos en rgba() dentro de cada regla: para
       mover el tono de un lavado había que encontrar los seis lugares donde
       estaba copiado. Como tokens derivados de --sage y --sand, se mueve acá. */
    --wash-sage: color-mix(in srgb, var(--sage) 26%, transparent);
    --wash-sand: color-mix(in srgb, var(--sand) 55%, transparent);
    /* Más liviano que --color-border: es para separar sin dibujar una caja. */
    --hairline:  color-mix(in srgb, var(--sage-deep) 18%, transparent);

    /* ---- Formas orgánicas ----
       border-radius con ocho valores: el óvalo irregular de las fotos de la
       referencia. Es CSS puro —sin imagen ni máscara detrás— así que escala sin
       perder nitidez y anima si alguna vez hace falta. */
    --blob-1: 58% 42% 47% 53% / 43% 39% 61% 57%;
    --blob-2: 44% 56% 62% 38% / 55% 47% 53% 45%;
    --blob-3: 63% 37% 39% 61% / 41% 58% 42% 59%;

    /* ---- Superficie y elevación del público ----
       Un escalón más suave que --color-bg blanco, para lo que tiene que
       distinguirse del fondo sin llegar a flotar. */
    /* ---- Elementos que acompañan al scroll ----
       Lo único que se queda pegado arriba es la barra de navegación: la franja
       de contacto se va con el scroll (vive fuera del <header>, ver la nota en
       components/site_header.php). Así que este es su alto, borde incluido, y
       es lo que hay que descontar para cualquier otra cosa que se pegue.

       Antes acá había 7.5rem, calculados sobre una cabecera de 108px —franja
       más navegación— que se pegaba entera. Con la franja fuera sobraban 40px
       y todo lo pegajoso arrancaba con un hueco visible por arriba.

       Es un valor fijo y no una medida real porque no hay forma en CSS de leer
       el alto de otro elemento. La barra tiene `min-height` declarado, así que
       este número solo cambia si cambia aquel. */
    --header-alto: 4.3rem;
    /* El resto de lo pegajoso arranca debajo de la barra, con algo de aire: sin
       ese respiro el panel queda lamiendo el borde de la navegación. */
    --sticky-top: calc(var(--header-alto) + 0.75rem);

    --surface-quiet:  color-mix(in srgb, var(--cream) 70%, #ffffff);
    /* Más difusa y más baja que --shadow-lg: sugiere el borde en vez de
       recortarlo. */
    --shadow-soft:    0 18px 40px -28px rgba(31, 58, 54, 0.28);
    --radius-organic: 28px;
}

/* En el teléfono el aire lateral baja. 1.2rem a cada lado de una pantalla de
   390px son 38px que le faltan al contenido, y ahí sí se notan.

   Lo baja el token y no cada componente: así la cabecera, el contenido y el pie
   siguen alineados también en el teléfono, que es donde más se nota una
   diferencia de cuatro píxeles porque no hay margen que la disimule. */
@media (max-width: 540px) {
    :root { --container-pad: .9rem; }
}

/* ==========================================================
   Modo oscuro: ya no existe
   ==========================================================
   Acá vivía la paleta oscura del panel, el ingreso y el instalador, colgada de
   `prefers-color-scheme` y de las clases .admin-body / .auth-body. El sitio
   público nunca entró: la portada siempre fue clara.

   Se fue entera, y no por gusto. Mantener dos paletas significa que cada
   componente nuevo hay que mirarlo dos veces, y el que no se mira se rompe en
   silencio: el panel de inicio salió con las cifras invisibles sobre su propia
   tarjeta porque `.stat` traía un `#fff` escrito a mano que en oscuro no se
   daba vuelta. Eso no se ve en claro y nadie lo reporta —quien tiene el sistema
   en oscuro casi nunca eligió tenerlo así—.

   Con una sola paleta, un `#hex` mal puesto se ve el día que se escribe.

   Si alguna vez vuelve: que sea con un interruptor propio en el panel y una
   clase en <body>, no con la preferencia del sistema. Así se puede probar sin
   cambiar los ajustes del computador, que es lo que hacía que nadie lo probara.
   ========================================================== */
/* Acá vivía un ajuste de brillo para .cover-ilustrada. Se fue con el modo
   oscuro del sitio público: esa ilustración solo se usa en components/public,
   así que con la portada siempre en claro la regla no se aplicaba nunca —y si
   se hubiera quedado, habría apagado la ilustración sobre un fondo crema cada
   vez que el visitante tuviera el sistema en oscuro. */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ==========================================================
   Reset
   ========================================================== */
*, *::before, *::after { box-sizing: border-box; }

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    /* El menú móvil vive fuera de pantalla a la derecha y estiraba el área del
       documento hasta ~700px. La página no llegaba a desplazarse, pero los
       elementos `fixed` se dimensionan contra esa área y salían más anchos que
       la pantalla: al banner de cookies se le cortaba el texto en el teléfono.
       Va en `html` porque un `fixed` escapa del recorte del `body`.
       `clip` y no `hidden`: `hidden` crearía un contenedor de scroll y
       rompería cualquier `position: sticky`. */
    overflow-x: clip;
}

body {
    margin: 0;
    font-family: var(--font-family);
    font-size: var(--font-size-base);
    line-height: var(--line-height-base);
    font-feature-settings: var(--font-feature);
    color: var(--color-text);
    background: var(--color-bg-page);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}

img, video, svg { max-width: 100%; height: auto; }

::selection {
    background: var(--sage-deep);
    color: #fff;
}

:focus-visible {
    outline: 2px solid var(--sage-deep);
    outline-offset: 2px;
}

/* ==========================================================
   Tipografía
   ========================================================== */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    line-height: 1.12;
    margin: 0 0 var(--space-md);
    font-weight: 600;
    letter-spacing: -0.022em;
    color: var(--color-text);
    text-wrap: balance;
}

/* Escala fluida: el titular respira en desktop sin desbordar en un teléfono. */
h1 { font-size: clamp(var(--text-5xl), 1.5rem + 2.2vw, var(--text-7xl)); letter-spacing: -0.032em; font-weight: 650; }
h2 { font-size: clamp(var(--text-3xl), 1.25rem + 0.9vw, var(--text-5xl));  letter-spacing: -0.026em; }
h3 { font-size: var(--text-xl); letter-spacing: -0.015em; }
h4 { font-size: var(--text-lg); }

p { margin: 0 0 var(--space-md); text-wrap: pretty; }

/* Los enlaces en línea van en celeste profundo, no en coral: el coral es la
   acción principal y pierde fuerza si además es el color de cada enlace. */
a {
    color: var(--sage-deep);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-decoration-color: color-mix(in srgb, var(--sage-deep) 45%, transparent);
    text-underline-offset: 0.22em;
    transition: color var(--transition), text-decoration-color var(--transition);
}
a:hover {
    color: var(--sage-hover);
    text-decoration-color: var(--sage-hover);
}

/* `small` y `code` van en `em`, no en la escala: tienen que encoger respecto
   del texto que los rodea, no respecto de la raíz. Comparten el mismo factor
   (antes eran 0.85 y 0.86, indistinguibles entre sí). */
small {
    font-size: 0.875em;
    color: var(--color-text-muted);
}

code {
    font-family: var(--font-family-mono);
    font-size: 0.875em;
    background: var(--sage-soft);
    padding: 0.14em 0.42em;
    border-radius: 5px;
    color: var(--sage-deep);
}

pre {
    font-family: var(--font-family-mono);
    font-size: var(--text-md);
    background: var(--color-bg-alt);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    padding: var(--space-md);
    overflow-x: auto;
    margin: 0 0 var(--space-md);
    white-space: pre-wrap;
    line-height: 1.5;
}

hr {
    border: 0;
    border-top: 1px solid var(--color-border);
    margin: var(--space-lg) 0;
}

/* ==========================================================
   Utilidades
   ========================================================== */
.text-muted    { color: var(--color-text-muted); }
.text-subtle   { color: var(--color-text-subtle); }
.text-tabular  { font-variant-numeric: tabular-nums; }

/* Etiqueta breve sobre un titular. Dice de qué se trata la sección antes de
   que se lea el titular completo. */
.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: var(--text-xs);
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--color-accent-text);
    margin: 0 0 0.9rem;
}
/* El filete va en el resalte de la marca y no en el color del texto. Es una
   marca, no texto, así que no le corre el mínimo de contraste; y es el lugar
   más barato donde el segundo color de la marca aparece: se repite en cada
   sección del sitio y no le quita seriedad a ninguna, porque mide 18 píxeles. */
.eyebrow::before {
    content: '';
    width: 18px;
    height: 1.5px;
    background: var(--accent-alt);
    border-radius: 2px;
}

.visually-hidden {
    position: absolute; width: 1px; height: 1px;
    padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* Entrada escalonada del contenido principal al cargar. Sutil: el objetivo es
   que la página se sienta viva, no que se anuncie. */
@keyframes rise-in {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}

.rise {
    animation: rise-in 620ms var(--ease-out-quart) both;
}

/* ---------- Entrada al hacer scroll ----------
   Los bloques aparecen cuando entran en pantalla, no todos de golpe al cargar:
   acompaña la lectura en una portada larga en vez de animar cosas que la
   persona todavía no está mirando.

   `.js-revelar` la pone el script; sin JavaScript la clase no existe y el
   contenido se ve normal, nunca invisible. Y con `prefers-reduced-motion` el
   script directamente no se engancha. */
.js-revelar [data-revelar] {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 640ms var(--ease-out-quart), transform 640ms var(--ease-out-quart);
}
.js-revelar [data-revelar].es-visible {
    opacity: 1;
    transform: none;
}
/* Escalonado dentro de una grilla: cada tarjeta entra un pelo después que la
   anterior, no las seis a la vez. */
.js-revelar [data-revelar]:nth-child(2) { transition-delay: 80ms; }
.js-revelar [data-revelar]:nth-child(3) { transition-delay: 160ms; }
.js-revelar [data-revelar]:nth-child(4) { transition-delay: 240ms; }
.rise-1 { animation-delay: 60ms; }
.rise-2 { animation-delay: 140ms; }
.rise-3 { animation-delay: 220ms; }
.rise-4 { animation-delay: 300ms; }
