/* ---------------------------------------------------------------------------
   Utopie — hoja de estilos del sitio público renderizado por Django.

   La fuente de verdad del diseño es la landing del SPA, no este archivo. Los
   colores están copiados literalmente de src/assets/scss/colors.scss y hay un
   test (backend/tests/test_marketing_design.py) que falla si alguien cambia uno
   de los dos lados y no el otro.

   Por qué no importamos el SCSS del SPA directamente: la identidad de la
   landing no vive en una hoja propia, vive en componentes de bootstrap-vue
   (b-card, b-button pill) más un puñado de variables. Traerla entera
   significaría servir Bootstrap — unos 160 KB — para reusar dos componentes, en
   una página cuyo argumento entero es pesar poco. Lo que sí se comparte, y es
   lo único que puede desincronizarse, son los trece colores.
--------------------------------------------------------------------------- */

:root {
  /* Paleta — src/assets/scss/colors.scss */
  --dark: #393e46;
  --light: #dfe2e2;
  --ultralight: #f3f3f3;
  --omglight: #fafafa;
  --primary: #f8b500;
  --primary-dark: #c28e02;
  --secondary: #00adb5;
  --secondary-dark: #227580;
  --redpie: #e74a57;
  --tertiary: #2789ba;
  --purple: #c6588d;
  --brown: #b76629;
  --pie: #208ac6;

  /* Roles. El acento lo fija cada producto en su plantilla, tomándolo del
     btnVariant que ese mismo producto ya usa en la landing: Corrección de
     Tests es outline-brown, Eureka es outline-primary, y así. Es el color del
     producto, no el de la familia de URL. */
  --accent: var(--primary);
  --accent-ink: var(--primary-dark);
  --ink: var(--dark);
  --ink-soft: #5c636d;
  --ground: #ffffff;
  --rule: var(--light);

  --shell: 1180px;
  --radius: 14px;
  /* Los radios crecen con la superficie: una ficha de lista lleva menos que el
     panel que la contiene, y el cierre oscuro más que ninguno. */
  --radius-md: 16px;
  --radius-lg: 18px;
  --radius-xl: 22px;
  --radius-pill: 999px;

  /* Sombras. Van en rgba del ink de marca y no en negro puro: sobre el gris
     claro del sitio el negro ensucia y el ink tiñe. */
  --sombra-ficha: 0 12px 26px rgba(57, 62, 70, 0.09);
  --sombra-alta: 0 24px 46px rgba(57, 62, 70, 0.15);
  --sombra-boton: 0 14px 30px rgba(57, 62, 70, 0.25);

  /* Ubuntu es la tipografía del SPA: la declara el body de LandingPage.vue.
     La pila de reserva es la del sistema para que el texto se lea antes de que
     llegue la fuente. */
  --font: Ubuntu, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif;
}

/* --- base ---------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font);
  font-size: 1.0625rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3 {
  margin: 0 0 0.5em;
  font-weight: 700;
  line-height: 1.15;
  text-wrap: balance;
}

h1 {
  font-size: clamp(1.9rem, 4vw, 3.1rem);
}

h2 {
  font-size: clamp(1.45rem, 2.5vw, 2.05rem);
}

h3 {
  font-size: 1.15rem;
}

p {
  margin: 0 0 1em;
}

a {
  color: var(--accent-ink);
  text-underline-offset: 0.18em;
}

a:hover {
  color: var(--ink);
}

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

strong {
  font-weight: 700;
}

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

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: 12px 20px;
  background: var(--dark);
  color: #fff;
  border-radius: 0 0 var(--radius) 0;
}

.skip-link:focus {
  left: 0;
  color: #fff;
}

/* --- rejilla ------------------------------------------------------------- */

.shell {
  width: 100%;
  max-width: var(--shell);
  margin-inline: auto;
  padding-inline: 22px;
}

.section {
  padding-block: clamp(2.5rem, 5vw, 4.25rem);
}

.section--tinted {
  background: var(--ultralight);
}

.section__head {
  max-width: 44rem;
  margin-bottom: 2rem;
}

.section__head--centered {
  max-width: none;
  text-align: center;
}

.section__head p {
  color: var(--ink-soft);
  margin-bottom: 0;
}

.eyebrow {
  display: block;
  margin-bottom: 0.5rem;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--accent-ink);
}

/* --- cabecera y pie ------------------------------------------------------ */

/* Fija: en una página de producto larga el enlace a las otras herramientas y
   el botón de entrar tienen que seguir a mano. El desenfoque es para que el
   contenido que pasa por debajo no compita con el texto de la barra. */
.site-header {
  position: sticky;
  top: 0;
  z-index: 60;
  background: rgba(57, 62, 70, 0.96);
  backdrop-filter: blur(12px);
  color: #fff;
}

/* Sin `backdrop-filter` el fondo translúcido deja leer lo de abajo. */
@supports not (backdrop-filter: blur(1px)) {
  .site-header {
    background: var(--dark);
  }
}

.site-header__bar {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  /* 54 del logotipo más 10 de aire arriba y abajo. */
  min-height: 74px;
  /* Sin `wrap`: son dos elementos —logotipo y Entrar— y tienen que ir en la
     misma línea también en el teléfono. Con `wrap` el botón caía a una segunda
     fila y la barra doblaba su alto justo donde menos sitio hay. */
  flex-wrap: nowrap;
}

.site-header__brand {
  display: flex;
  align-items: center;
  font-size: 1.3rem;
  font-weight: 700;
  color: #fff;
  text-decoration: none;
  letter-spacing: -0.01em;
}

/* 3.375rem = 54 px, que es `$headerHeight` del SPA: el logotipo se ve del
   mismo tamaño acá que dentro de la aplicación. Va en rem para que acompañe al
   tipo si alguien cambia la escala, y el ancho en auto para no deformarlo. */
.site-header__brand img {
  display: block;
  height: 3.375rem;
  width: auto;
  transition: opacity 0.2s ease;
}

/* En pantalla angosta la barra ya reparte cabecera, menú y botón en dos filas;
   el logotipo baja para no comerse la primera. */
@media (max-width: 639px) {
  .site-header__brand img {
    height: 2.5rem;
  }
}

/* Con texto el hover cambiaba al ámbar; un logotipo no se recolorea, así que
   responde bajando un punto la opacidad. */
.site-header__brand:hover img,
.site-header__brand:focus-visible img {
  opacity: 0.8;
}

.site-header__brand:hover {
  color: var(--primary);
}

.site-nav {
  display: flex;
  gap: 1.1rem;
  flex-wrap: nowrap;
  margin-left: auto;
  align-items: center;
  flex: none;
}

.site-nav a {
  color: var(--light);
  text-decoration: none;
  font-size: 0.95rem;
  font-weight: 500;
}

.site-nav a:hover,
.site-nav a[aria-current="page"] {
  color: var(--primary);
}

.site-nav .btn {
  font-size: 0.9rem;
  padding: 0.4em 1.2em;
}

.site-footer {
  background: var(--dark);
  color: var(--light);
  padding-block: 2.75rem 2rem;
  margin-top: 0;
  font-size: 0.95rem;
}

.site-footer a {
  color: var(--light);
  text-decoration: none;
}

.site-footer a:hover {
  color: var(--primary);
}

.site-footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1.75rem 2rem;
}

.site-footer h2 {
  font-size: 0.8rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--primary);
  margin-bottom: 0.75rem;
}

.site-footer ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

.site-footer li + li {
  margin-top: 0.4rem;
}

.site-footer__legal {
  margin-top: 2.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  color: #9aa1aa;
  font-size: 0.85rem;
}

.site-footer__legal p {
  margin: 0;
}

/* --- botones ------------------------------------------------------------- */
/* Píldora, como en toda la landing (b-button pill). Sobre un campo de color el
   botón es blanco con el texto del acento — que es exactamente lo que hace
   InfoBanner. Sobre fondo claro se invierte, porque un botón blanco sobre
   blanco no se ve. */

.btn {
  display: inline-block;
  padding: 0.62em 1.7em;
  border: 2px solid transparent;
  border-radius: var(--radius-pill);
  font: inherit;
  font-weight: 700;
  text-align: center;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.15s ease, background-color 0.15s ease,
    color 0.15s ease;
}

/* Los botones que llevan a la demo laten solos.

   No es adorno: son la acción de la página y tienen que pedir el clic sin que
   nadie pase por encima. El latido se DETIENE al apuntarlos —ahí ya tienen la
   atención, y además el `transform` del hover no podría aplicarse mientras una
   animación esté corriendo: una animación gana a una declaración normal—. Por
   eso `animation: none` y no `animation-play-state: paused`, que congelaría el
   botón a media escala.

   Solo los que redirigen: el del banner, el del teaser y el del cierre. Un
   botón latiendo en la cabecera sería un tic.

   OJO con el del teaser, que son DOS botones y solo uno se ve a la vez:
   `.teaser-poster__actions .btn` es el del póster, el HTML que se sirve; en
   cuanto el componente monta, ese póster se esconde y el que queda a la vista
   es el del propio teaser, que tiene su clase y no `.btn`. Latir solo en el
   póster es latir donde casi nadie mira. De ahí la segunda lista, una entrada
   por teaser —los siete tienen clase raíz y clase de botón propias— y colgando
   de `.teaser-slot`, que es el que sobrevive al montaje. */
.hero__text .btn,
.teaser-poster__actions .btn,
.cta .btn,
.teaser-slot .pie-teaser .pt-btn,
.teaser-slot .plan-teaser .plt-btn,
.teaser-slot .eureka-teaser .eut-btn,
.teaser-slot .materials-teaser .mat-btn,
.teaser-slot .mct .mct-btn,
.teaser-slot .tests-teaser .tst-btn,
.teaser-slot .pie-system-teaser .pst-btn {
  animation: latido 1.5s ease-in-out infinite;
}

.hero__text .btn:hover,
.hero__text .btn:focus-visible,
.teaser-poster__actions .btn:hover,
.teaser-poster__actions .btn:focus-visible,
.cta .btn:hover,
.cta .btn:focus-visible,
.teaser-slot .pie-teaser .pt-btn:hover,
.teaser-slot .plan-teaser .plt-btn:hover,
.teaser-slot .eureka-teaser .eut-btn:hover,
.teaser-slot .materials-teaser .mat-btn:hover,
.teaser-slot .mct .mct-btn:hover,
.teaser-slot .tests-teaser .tst-btn:hover,
.teaser-slot .pie-system-teaser .pst-btn:hover,
.teaser-slot .pie-teaser .pt-btn:focus-visible,
.teaser-slot .plan-teaser .plt-btn:focus-visible,
.teaser-slot .eureka-teaser .eut-btn:focus-visible,
.teaser-slot .materials-teaser .mat-btn:focus-visible,
.teaser-slot .mct .mct-btn:focus-visible,
.teaser-slot .tests-teaser .tst-btn:focus-visible,
.teaser-slot .pie-system-teaser .pst-btn:focus-visible {
  animation: none;
}

@keyframes latido {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.07);
  }
}

/* Crece y se levanta. La escala es el gesto que hace que el botón se sienta
   pulsable antes de pulsarlo; el desplazamiento solo, que es lo que había,
   apenas se nota. Se queda en 1.04: más que eso y el texto se ve reescalado en
   vez de acercado. `prefers-reduced-motion` anula la transición más abajo, así
   que quien pidió menos movimiento recibe el estado final sin recorrido. */
.btn:hover,
.btn:focus-visible {
  transform: translateY(-2px) scale(1.08);
  box-shadow: var(--sombra-boton);
}

.btn:active {
  transform: translateY(-1px) scale(1.01);
}

/* El fondo del botón sólido es el ink y no el color base: el base está elegido
   para rellenos y con texto blanco encima no llega a 4,5:1. Con el amarillo la
   diferencia es abismal — 1,81:1 contra 4,70:1. */
.btn--solid {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  color: #fff;
}

/* El hover no cambia el color: lo levanta. Antes el sólido viraba al ink de
   marca y el de campo al amarillo, sin importar de qué producto fuera la
   página — en Corrección de Tests un botón café pasaba a amarillo al pasar el
   ratón. El color del botón es el del producto y se queda. */
.btn--solid:hover,
.btn--solid:focus-visible {
  background: var(--accent-ink);
  border-color: var(--accent-ink);
  color: #fff;
}

.btn--on-field {
  background: #fff;
  border-color: #fff;
  color: var(--accent-ink);
}

.btn--on-field:hover,
.btn--on-field:focus-visible {
  background: #fff;
  border-color: #fff;
  color: var(--accent-ink);
}

/* La pastilla blanca sobre un campo claro no se recorta sola: en el ámbar de
   Eureka da 1,91:1, y la WCAG pide 3:1 para el contorno de un control. El borde
   en el ink la dibuja. Solo pasa en los heroes de titular oscuro —los demás
   tienen el campo bastante más profundo—, así que la regla se limita a ellos. */
.hero--dark .btn--on-field {
  border-color: var(--accent-ink);
}

/* Acentos claros (Eureka, el primary de la marca).

   El ink es tan oscuro que deja de parecerse al producto —Eureka salía café, el
   color de Corrección de Tests—, así que donde el acento es SUPERFICIE se pinta
   el acento de verdad y el texto va oscuro: sobre #f8ac04, negro da 5,58:1 y
   blanco 1,93:1. Donde el acento sería TEXTO sobre blanco no hay treta posible
   —1,93:1— y ahí se sigue usando el ink.

   La pastilla blanca sobre ámbar tampoco se recorta (1,91:1, y la WCAG pide
   3:1 para el contorno de un control), así que lleva borde oscuro. */
.tema--acento-claro .cta {
  background: var(--accent);
  color: var(--ink);
}

.tema--acento-claro .cta h2 {
  color: var(--ink);
}

.tema--acento-claro .cta p {
  color: var(--ink);
}

.tema--acento-claro .btn--solid {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--ink);
}

.tema--acento-claro .btn--solid:hover,
.tema--acento-claro .btn--solid:focus-visible {
  background: var(--accent);
  border-color: var(--ink);
  color: var(--ink);
}

/* Pastilla blanca de borde blanco, con la letra en el ámbar más claro que se
   puede leer encima (#9d6c00, 4,59:1).

   La sombra en reposo no es adorno y por eso no se quita: blanco sobre el campo
   ámbar del banner da 1,91:1, y la WCAG pide 3:1 para el contorno de un control
   —sin nada que lo dibuje, la pastilla se funde con el fondo—. El borde blanco
   no puede hacer ese trabajo, así que lo hace la sombra: separa el botón del
   campo sin meterle una línea oscura al diseño.

   Es una solución visual, no una que cumpla la letra de 1.4.11: el criterio se
   mide con contraste de color, no con sombras. Se eligió así a propósito. */
.tema--acento-claro .btn--on-field {
  background: #fff;
  border-color: #fff;
  color: var(--accent-ink);
  box-shadow: 0 2px 10px rgba(57, 62, 70, 0.28);
}

.tema--acento-claro .btn--on-field:hover,
.tema--acento-claro .btn--on-field:focus-visible {
  background: #fff;
  border-color: #fff;
  color: var(--accent-ink);
}

.btn--quiet {
  background: transparent;
  border-color: currentColor;
  color: inherit;
}

/* El borde se queda en `currentColor` al pasar el ratón: fijarlo en blanco
   funcionaba mientras este botón solo viviera dentro del cierre, que tiene el
   acento de fondo. Ahora acompaña al de sesión también en la portada y en el
   póster del teaser —fondo claro en Eureka, blanco en el póster—, y ahí un
   borde blanco desaparece. La WCAG pide 3:1 para el contorno de un control
   (1.4.11) y el contorno es lo único que dibuja este botón. */
.btn--quiet:hover,
.btn--quiet:focus-visible {
  background: rgba(255, 255, 255, 0.14);
  color: inherit;
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  align-items: center;
}

/* --- migas --------------------------------------------------------------- */

.breadcrumbs {
  padding-block: 0.9rem 0;
  font-size: 0.87rem;
  color: var(--ink-soft);
}

.breadcrumbs ol {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.breadcrumbs li + li::before {
  content: "›";
  margin-right: 0.4rem;
  color: var(--light);
}

/* --- hero ---------------------------------------------------------------- */
/* El mismo gesto que InfoBanner: el banner ocupa todo el ancho y el texto va
   encima, en blanco, sobre el campo liso que la ilustración deja libre. Va como
   <img> y no como background para que la ilustración tenga alt y Google la
   pueda leer. */

/* La altura la manda el texto, no la imagen. Con la imagen mandando, un titular
   de tres líneas más el botón se salen por abajo del banner — que es justo lo
   que pasaba. El banner se recorta por la derecha, que es campo de color liso:
   no se pierde nada de la ilustración. */
/* La banda toma la proporción del banner, así que la ilustración entra entera:
   con `cover` a una altura menor el recorte se comía el avión de papel, que es
   justo el gesto que amarra la página a la identidad. Es además cómo se
   comporta InfoBanner en el SPA, donde la b-card lleva la imagen a width:100%
   y height:auto.

   aspect-ratio propone la altura, no la impone: si el texto es más alto —
   móvil, tipografía grande, un titular largo — la banda crece y no recorta. */
.hero {
  position: relative;
  overflow: hidden;
  background: var(--accent);
  aspect-ratio: 1920 / 690;
}

@media (max-width: 767px) {
  .hero {
    aspect-ratio: 800 / 1339;
  }
}

/* La ilustración sale del flujo: la altura de la banda la fija el texto y la
   imagen rellena lo que haya. Al revés — con la imagen mandando — un titular de
   tres líneas empuja el botón fuera del campo de color, y como el botón es
   blanco sobre fondo blanco, desaparece sin dejar rastro. */
.hero__media {
  position: absolute;
  inset: 0;
}

.hero__media img {
  display: block;
  width: 100%;
  height: 100%;
  /* Recorta por la derecha, que es campo liso: la ilustración no se toca. */
  object-fit: cover;
  object-position: left center;
}

.hero__body {
  position: relative;
  display: flex;
  align-items: center;
  padding-block: clamp(1.75rem, 4vw, 3rem);
}

/* Velo bajo el texto blanco. El campo cafe de Correccion de Tests da 4,25:1
   con blanco y el minimo AA para texto normal es 4,5: el titular pasa por
   texto grande y le bastaria 3:1, pero la bajada y la nota no.

   El degradado arranca en el 40% y ya esta al maximo en el 55%, que es donde
   empieza el bloque de texto: la ilustracion de la izquierda no se toca y la
   zona del texto queda a opacidad uniforme, que es lo que hace que el test
   pueda medirla. El valor sale de HERO_SCRIM en registry.py — un solo numero
   para el CSS y para el test. */
.hero--light .hero__body::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    to right,
    rgba(0, 0, 0, 0) 40%,
    rgba(0, 0, 0, var(--hero-scrim)) 55%
  );
}

.hero--light.hero--left .hero__body::before {
  background: linear-gradient(
    to left,
    rgba(0, 0, 0, 0) 40%,
    rgba(0, 0, 0, var(--hero-scrim)) 55%
  );
}

.hero--light .hero__body > * {
  position: relative;
}

@media (max-width: 767px) {
  /* En vertical el texto va arriba, asi que el velo tambien. */
  .hero--light .hero__body::before {
    background: linear-gradient(
      to bottom,
      rgba(0, 0, 0, var(--hero-scrim)) 55%,
      rgba(0, 0, 0, 0) 72%
    );
  }
}

.hero h1 {
  font-size: clamp(1.75rem, 3.1vw, 2.65rem);
}

/* De que lado cae el texto. Es el `orientation` de InfoBanner: cada banner
   deja libre un lado distinto y el texto va donde el campo es liso. */
.hero__text {
  width: min(50%, 34rem);
}

.hero--right .hero__text {
  margin-left: auto;
  padding-right: 22px;
}

.hero--left .hero__text {
  margin-right: auto;
  padding-left: 0;
}

/* Dos variantes, igual que el txtColor de InfoBanner en la landing. El campo
   de color del banner de producto es saturado y pide texto blanco; el del
   banner principal es crema y pide texto oscuro. Con la variante equivocada el
   titular no se lee, y es un error que no salta al mirar el codigo: hay que
   mirar la imagen. Por eso lo mide un test. */
.hero--light .hero__text,
.hero--light .hero__text h1 {
  color: #fff;
}

.hero--light .hero__note {
  color: rgba(255, 255, 255, 0.93);
}

.hero--dark .hero__text,
.hero--dark .hero__text h1 {
  color: var(--ink);
}

.hero--dark .hero__note {
  color: var(--ink-soft);
}

.hero__lede {
  font-size: clamp(1rem, 1.35vw, 1.28rem);
  margin-bottom: 1.4rem;
  max-width: 30rem;
}

.hero__note {
  margin: 0.9rem 0 0;
  font-size: 0.92rem;
}

/* En móvil el banner vertical deja el campo de color arriba, así que el texto
   se apila sobre esa mitad en vez de flotar al centro. */
/* En móvil el banner vertical deja el campo de color arriba, así que el texto
   se apila sobre esa mitad en vez de centrarse. El campo ocupa el 61% superior
   de la imagen y el bloque tiene que caber ahí: pasado ese punto el texto se
   monta sobre la taza de la ilustración y no se lee. De ahí que todo el ritmo
   se apriete en este tramo. */
@media (max-width: 767px) {
  .hero__body {
    align-items: flex-start;
    padding-block: 1rem 0;
  }

  /* En vertical el campo de color siempre queda arriba, asi que el lado deja
     de importar y el texto ocupa el ancho completo. */
  .hero--left .hero__text,
  .hero--right .hero__text {
    width: 100%;
    margin: 0;
    padding: 3vw 22px 0;
  }

  .hero h1 {
    font-size: clamp(1.55rem, 6.4vw, 2rem);
  }

  .hero__lede {
    max-width: none;
    margin-bottom: 1rem;
  }

  .hero__note {
    margin-top: 0.6rem;
    font-size: 0.85rem;
  }
}

/* Por debajo de 384 px el bloque de texto ya no cabe en el campo de color del
   banner vertical, y el titular se monta sobre la taza de la ilustración. Ahí
   el hero deja de ser una superposición y se desapila: primero el texto sobre
   el color, después la ilustración recortada a su parte útil.

   El recorte importa. La imagen de móvil es campo de color arriba e ilustración
   abajo; puesta entera debajo del texto, se ve el campo vacío dos veces. Con
   `object-position: bottom` y la proporción de la mitad inferior, se muestra
   solo la ilustración.

   OJO: .hero__media es position:absolute en el modo superpuesto y hay que
   devolverla al flujo, no solo reordenarla. Un elemento posicionado se pinta
   por encima de uno que no lo está: dejarla absolute con el texto en static
   borra el hero entero. Ya pasó. */
@media (max-width: 383px) {
  .hero {
    aspect-ratio: auto;
    display: flex;
    flex-direction: column;
  }

  .hero__body {
    order: 1;
    padding-block: 1.5rem;
  }

  .hero__media {
    order: 2;
    position: relative;
    inset: auto;
    aspect-ratio: 800 / 645;
  }

  /* La imagen va absolute dentro del recorte y no con height:100% a secas: un
     porcentaje contra una altura que sale de aspect-ratio no resuelve, la
     imagen se desborda y lo que se ve es el campo de color vacío de arriba en
     vez de la ilustración. */
  .hero__media img {
    position: absolute;
    inset: 0;
    object-position: center bottom;
  }

  .hero__text {
    padding-top: 0;
  }
}

/* Aquí vivía una regla que ponía .hero__body en position:static por debajo de
   430 px. Borraba el texto del hero entero: .hero__media es absolute, y un
   elemento posicionado se pinta por encima de uno que no lo está, así que la
   ilustración tapaba el titular, la bajada y el botón. El body se queda
   relative siempre. El banner vertical deja el campo de color arriba y ahí cabe
   todo, que es justo para lo que existe la variante de móvil. */

/* --- la lista de tests --------------------------------------------------- */
/* Es el contenido que rankea: nombres propios, no adjetivos. Se lee como una
   ficha técnica — un renglón por test, con su área y lo que se ingresa. La
   regla punteada es el eco de la trayectoria del avión de papel de los
   banners. */

.tests-group + .tests-group {
  margin-top: 2.25rem;
}

.tests-group__head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  margin-bottom: 0.35rem;
}

.tests-group__head h3 {
  margin: 0;
  font-size: 1.05rem;
}

.tests-group__count {
  font-size: 0.85rem;
  color: var(--ink-soft);
}

.tests-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Rejilla y no flex: con flex, "Perfil sensorial · Padres" desborda su columna
   y arrastra la descripción, que deja de alinearse con las de arriba. La ficha
   se lee escaneando la segunda columna, así que esa alineación es el punto. */
.tests-list li {
  display: grid;
  grid-template-columns: minmax(11rem, 15rem) 1fr;
  align-items: baseline;
  gap: 0.25rem 1.2rem;
  padding: 0.72rem 0;
  border-bottom: 1px dotted var(--light);
}

.tests-list b {
  font-size: 1.02rem;
}

.tests-list span {
  color: var(--ink-soft);
  font-size: 0.95rem;
}

@media (max-width: 559px) {
  .tests-list li {
    grid-template-columns: 1fr;
  }
}

/* --- preguntas ----------------------------------------------------------- */
/* <details> nativo: el desplegable funciona sin JavaScript. */

.faq {
  max-width: 48rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.faq details {
  border-bottom: 1px solid var(--rule);
}

.faq details:last-child {
  border-bottom: 0;
}

.faq summary {
  padding: 1.1rem 3.5rem 1.1rem 1.4rem;
  font-weight: 700;
  cursor: pointer;
  list-style: none;
  position: relative;
  transition: background-color 0.25s ease;
}

.faq summary:hover {
  background: var(--ultralight);
}

.faq summary::-webkit-details-marker {
  display: none;
}

.faq summary::after {
  content: "+";
  position: absolute;
  right: 1.1rem;
  top: 50%;
  width: 1.7rem;
  height: 1.7rem;
  display: grid;
  place-items: center;
  transform: translateY(-50%);
  border-radius: 50%;
  background: var(--ultralight);
  font-size: 1.15rem;
  font-weight: 700;
  line-height: 1;
  color: var(--accent-ink);
  transition: background-color 0.3s ease;
}

.faq details[open] summary::after {
  content: "–";
  background: var(--accent);
  color: #fff;
}

.faq details > div {
  padding: 0 1.4rem 1.25rem;
  color: var(--ink-soft);
}

.faq details > div > :last-child {
  margin-bottom: 0;
}

/* --- el puente entre páginas emparentadas --------------------------------- */
/* Corrección de Tests y los informes de muestra son la misma historia contada
   desde los dos lados: uno vende el corrector, los otros enseñan lo que sale de
   él. Antes solo se alcanzaban por la cabecera, y la cabecera ya no lleva
   secciones. Este puente los une por el cuerpo, que es donde el enlace además
   significa algo para quien lo lee.

   Una franja, no una tarjeta: va entre dos secciones y no compite con ninguna. */
.puente {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 0.75rem;
  margin: 0;
  padding: 1.1rem 1.35rem;
  background: var(--ultralight);
  border: 1px solid var(--rule);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius);
  font-size: 0.97rem;
  color: var(--ink-soft);
}

.puente strong {
  color: var(--ink);
  font-weight: 700;
}

.puente a {
  font-weight: 700;
}

/* --- llamada de cierre --------------------------------------------------- */

/* El color del producto, no el gris de la marca: es el último bloque de la
   página y cierra en el mismo tono con el que abrió el banner. Va en el ink y
   no en el color base porque encima hay texto blanco, y varios de los base no
   llegan a 4,5:1 — el amarillo se queda en 1,81:1. */
.cta {
  background: var(--accent-ink);
  color: #fff;
  border-radius: var(--radius-xl);
  padding: clamp(1.75rem, 4vw, 2.75rem);
  text-align: center;
}

.cta h2 {
  color: #fff;
  max-width: 30rem;
  margin-inline: auto;
}

.cta p {
  color: var(--light);
  max-width: 34rem;
  margin-inline: auto 1.5rem;
  margin-inline: auto;
  margin-bottom: 1.5rem;
}

.cta .btn-row {
  justify-content: center;
}

/* --- hueco del teaser ---------------------------------------------------- */
/* El póster está en el HTML servido desde el primer byte; el componente real
   lo reemplaza después de la primera pintura. Si el JavaScript no llega, el
   póster se queda y sigue llevando a la demo. */

.teaser-slot {
  position: relative;
}

.teaser-poster {
  background: var(--ultralight);
  border: 1px solid var(--rule);
  border-radius: var(--radius-xl);
  padding: clamp(1.5rem, 3.5vw, 2.5rem);
}

.teaser-poster__actions {
  margin-top: 1.6rem;
  justify-content: center;
}

/* Los tres pasos del flujo. La trayectoria punteada entre ellos es el gesto
   que repiten los banners de la landing — el avión de papel y su estela — y
   acá gana su sitio porque de verdad codifica una secuencia: los puntajes
   entran, el test se corrige, sale el informe. Es el único sitio de la página
   donde aparece. */
.flow {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: 1.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Cada paso en su ficha, sobre el panel. Antes eran tres bloques sueltos
   unidos por la estela punteada del avión; el panel ya encierra la secuencia,
   y con la estela dentro de fichas quedaban dos marcos discutiendo. El número
   sigue diciendo el orden, que es lo que la estela decía. */
.flow__step {
  position: relative;
  display: flex;
  gap: 0.85rem;
  align-items: flex-start;
  padding: 1.1rem;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1),
    box-shadow 0.35s ease;
}

.flow__step:hover {
  transform: translateY(-3px);
  box-shadow: var(--sombra-ficha);
}

/* Círculo hueco con el aro del acento, no relleno: dentro de una ficha blanca
   tres discos macizos pesan más que el texto que numeran. */
.flow__n {
  flex: none;
  width: 2.1rem;
  height: 2.1rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--ground);
  border: 2px solid var(--accent);
  color: var(--accent-ink);
  font-weight: 700;
  font-size: 0.95rem;
}

.flow__label b {
  display: block;
  line-height: 1.3;
}

.flow__label em {
  display: block;
  margin-top: 0.2rem;
  font-style: normal;
  font-size: 0.92rem;
  color: var(--ink-soft);
}

@media (max-width: 719px) {
  .flow {
    grid-template-columns: 1fr;
    gap: 1rem;
  }
}

/* Los teasers de la landing traen dos acciones: la principal abre la demo y la
   secundaria manda al login del SPA. Acá sobra la segunda.

   Quien llega a esta página vino buscando ESTE servicio, y las dos acciones
   ahora terminan en el mismo sitio —la demo—, así que serían dos botones
   idénticos con distinto texto. En la landing el par sí tiene sentido: ahí el
   visitante está mirando siete productos y puede que ya tenga cuenta.

   Se apaga desde acá y no tocando el componente: es el mismo archivo que usa
   la landing, y allá el botón de sesión tiene que seguir. */
/* OJO con el selector, que tiene dos trampas y caí en las dos:

   1. NO se puede colgar de `.teaser-mount`. Vue 2 REEMPLAZA el elemento que
      recibe `$mount()`, así que en cuanto el teaser monta ese div deja de
      existir y la regla no matchea nada. El que sobrevive es `.teaser-slot`,
      que es el padre.
   2. Hay que ganarle en especificidad al componente. Sus reglas son
      `.plan-teaser .plt-btn-ghost` y `.tests-teaser .tst-login-link` —las
      mismas dos clases que usaría uno—, y su hoja la inyecta _teaser_boot.html
      DESPUÉS de esta, así que en un empate gana el componente. De ahí las tres
      clases: 0,3,0 contra 0,2,0, sin `!important`.

   `.tst-login-link` además trae `display: inline` propio, o sea que sin esto
   se pintaría igual. */
.teaser-slot .plan-teaser .plt-btn-ghost,
.teaser-slot .tests-teaser .tst-login-link {
  display: none;
}

/* Cuando el componente monta, se anuncia con este atributo y el póster se
   retira. Un solo interruptor, para que no puedan verse los dos a la vez. */
.teaser-slot[data-teaser="live"] .teaser-poster {
  display: none;
}

.teaser-mount:empty {
  display: none;
}

/* --- el índice del sistema ------------------------------------------------ */
/* Cierra cada página de producto con las otras seis. Existe por dos razones que
   apuntan al mismo sitio: el visitante que llegó buscando una cosa descubre que
   hay seis más, y el rastreador deja de encontrarse un callejón sin salida —
   antes de esto las siete páginas solo se enlazaban por la cabecera y el pie.

   Cada ficha lleva SU acento en el canto izquierdo, el mismo que el producto
   usa en su banner y en la landing. Es el único lugar del sitio donde los siete
   se ven juntos, y es lo que sostiene en imagen lo que la página de Plataforma
   PIE afirma en palabras: que esto es un sistema y no seis productos sueltos.

   No van numeradas: los siete son un conjunto, no una secuencia. El único sitio
   de la página donde el orden significa algo es .flow, y ahí sí van numerados. */

.sistema {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: 1rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.sistema__item {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  padding: 1.15rem 1.25rem;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius-md);
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1),
    box-shadow 0.35s ease;
}

/* El icono del producto en vez del canto de color. Dice lo mismo —de cuál de
   los siete se trata— y lo dice antes: es el mismo icono que la landing usa
   para ese servicio, así que quien ya usó la plataforma lo reconoce sin leer.
   El canto de color, además, obligaba a un `style` en línea por ficha. */
.sistema__icono {
  width: 2.75rem;
  height: 2.75rem;
  /* Centrado sobre el texto, que se queda alineado a la izquierda: el icono
     hace de rótulo de la ficha y el texto se sigue leyendo como texto. */
  margin: 0 auto 0.6rem;
  object-fit: contain;
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.sistema__item:hover .sistema__icono {
  transform: scale(1.08) rotate(-4deg);
}

.sistema__item:hover,
.sistema__item:focus-within {
  transform: translateY(-4px);
  box-shadow: var(--sombra-ficha);
}

.sistema__nombre {
  font-weight: 700;
  font-size: 1.03rem;
  line-height: 1.25;
  color: var(--ink);
  text-decoration: none;
}

/* El enlace cubre la ficha entera: el objetivo táctil es la tarjeta, no dos
   palabras. El texto de abajo se deja por encima para poder seleccionarlo. */
.sistema__nombre::after {
  content: "";
  position: absolute;
  inset: 0;
}

.sistema__item {
  position: relative;
}

.sistema__nombre:hover,
.sistema__nombre:focus-visible {
  color: var(--ink);
  text-decoration: underline;
}

.sistema__que {
  position: relative;
  margin: 0;
  font-size: 0.93rem;
  line-height: 1.45;
  color: var(--ink-soft);
}

/* --- lo que trae el producto ---------------------------------------------- */
/* La ficha técnica del producto: lo que rankea. Cada renglón en su tarjeta, con
   el canto del acento a lo alto. El acento sale de la página —no de la fila—,
   así que va en CSS y no en un `style` en línea. */

.incluye {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
  gap: 0.9rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.incluye li {
  display: grid;
  grid-template-columns: 8px 1fr;
  align-items: stretch;
  align-content: start;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 0.35s cubic-bezier(0.2, 0.7, 0.2, 1),
    box-shadow 0.35s ease;
}

.incluye li:hover {
  transform: translateX(4px);
  box-shadow: var(--sombra-ficha);
}

.incluye li::before {
  content: "";
  background: var(--accent);
}

.incluye__texto {
  padding: 1rem 1.15rem 1rem 0;
}

.incluye b {
  display: block;
  font-size: 1.02rem;
  line-height: 1.3;
}

.incluye__texto span {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.93rem;
  line-height: 1.55;
  color: var(--ink-soft);
}

/* La lámina del chatbot, debajo del texto y dentro de la misma ficha. Ocupa la
   columna del texto —no la del canto de color— y va contenida, no recortada:
   son ilustraciones con aire propio y `cover` les come el margen. */
.incluye__medio {
  grid-column: 2;
  display: block;
  padding: 0 1.15rem 1.15rem 0;
}

.incluye__medio img {
  display: block;
  width: 100%;
  max-height: 11rem;
  object-fit: contain;
  object-position: left center;
}

/* El vídeo sí llena su caja: es una grabación, no una ilustración. */
.incluye__medio--video {
  padding-right: 1.15rem;
}

.incluye__medio--video iframe {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 0;
  border-radius: var(--radius);
  background: var(--ultralight);
}

/* --- las fichas del hub --------------------------------------------------- */
/* En /producto/ cada herramienta se presenta con su banner, que es la misma
   ilustración que encabeza su página: quien llega al hub reconoce después la
   página a la que entró. El recorte se va al lado de la ilustración —el
   contrario al que ocupa el texto en el hero— para que no se vea campo de
   color vacío.

   Cada ficha declara su propio acento en línea (`--a` / `--ai`), igual que
   base.html hace con el de la página. */

.tarjetas {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr));
  gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.tarjeta {
  position: relative;
  display: flex;
  flex-direction: column;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform 0.4s cubic-bezier(0.2, 0.7, 0.2, 1),
    box-shadow 0.4s ease, border-color 0.4s ease, opacity 0.35s ease;
}

.tarjeta:hover,
.tarjeta:focus-within {
  transform: translateY(-6px);
  box-shadow: var(--sombra-alta);
  border-color: var(--a, var(--rule));
}

.tarjeta__media {
  position: relative;
  aspect-ratio: 20 / 9;
  background: var(--a, var(--ultralight));
  overflow: hidden;
}

.tarjeta__media img {
  display: block;
  width: 100%;
  height: 128%;
  object-fit: cover;
  transition: transform 0.8s cubic-bezier(0.2, 0.7, 0.2, 1);
}

.tarjeta:hover .tarjeta__media img {
  transform: scale(1.06);
}

/* El texto del hero cae de un lado; la ilustración está en el otro. La ficha
   recorta hacia la ilustración. */
.tarjeta--hero-right .tarjeta__media img {
  object-position: left top;
}

.tarjeta--hero-left .tarjeta__media img {
  object-position: right top;
}

.tarjeta__tarea {
  position: absolute;
  left: 0.9rem;
  top: 0.9rem;
  padding: 0.3em 0.85em;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.94);
  color: var(--ai, var(--ink));
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.tarjeta__cuerpo {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  flex: 1;
  padding: 1.25rem 1.35rem 1.35rem;
}

.tarjeta__cuerpo h3 {
  margin: 0;
  font-size: 1.18rem;
  line-height: 1.25;
}

.tarjeta__cuerpo h3 a {
  color: var(--ink);
  text-decoration: none;
}

.tarjeta__cuerpo h3 a::after {
  content: "";
  position: absolute;
  inset: 0;
}

.tarjeta__cuerpo h3 a:hover,
.tarjeta__cuerpo h3 a:focus-visible {
  text-decoration: underline;
}

.tarjeta__que {
  margin: 0;
  flex: 1;
  color: var(--ink-soft);
  font-size: 0.93rem;
  line-height: 1.55;
}

.tarjeta__ir {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  margin-top: 0.2rem;
  color: var(--ai, var(--accent-ink));
  font-weight: 700;
  font-size: 0.9rem;
  transition: gap 0.3s ease;
}

.tarjeta:hover .tarjeta__ir {
  gap: 1rem;
}

/* --- el hub por tarea ----------------------------------------------------- */
/* Las siete no son una lista plana: resuelven cuatro cosas distintas, y en el
   orden del año PIE —se evalúa, se documenta, se hace clase, se gestiona—. El
   hub las agrupa así porque es la pregunta con la que llega quien nunca nos ha
   visto: no «cuáles hay» sino «cuál es para lo que tengo que hacer hoy».

   El filtro son anclas y `:target`, sin una línea de JavaScript: al pulsar una
   tarea el navegador marca esa sección y esta regla apaga las otras tres. Se
   puede compartir el enlace, funciona con el teclado y sin scripts se queda en
   un índice con saltos, que es exactamente lo que sería de todos modos. */

/* Se queda a la vista al bajar: el hub tiene cuatro grupos y sin esto hay que
   subir para cambiar de tarea. `top` es la altura de la cabecera fija. */
.filtro {
  position: sticky;
  top: 74px;
  z-index: 40;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-bottom: 2rem;
  padding-block: 0.75rem;
  background: rgba(255, 255, 255, 0.93);
  backdrop-filter: blur(8px);
}

@supports not (backdrop-filter: blur(1px)) {
  .filtro {
    background: var(--ground);
  }
}

.filtro__chip {
  padding: 0.35em 1.1em;
  border: 1px solid var(--rule);
  border-radius: var(--radius-pill);
  font-size: 0.92rem;
  font-weight: 500;
  color: var(--ink);
  text-decoration: none;
  transition: background-color 0.18s ease, border-color 0.18s ease;
}

.filtro__chip:hover,
.filtro__chip:focus-visible {
  background: var(--ultralight);
  border-color: var(--ink-soft);
  color: var(--ink);
}

.tarea {
  /* El salto del ancla tiene que dejar libre la cabecera fija y la barra de
     filtro, o el título del grupo queda debajo de las dos. */
  scroll-margin-top: 8.5rem;
}

.tarea + .tarea {
  margin-top: 2.5rem;
}

.tarea__titulo {
  margin: 0 0 1rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--rule);
  font-size: 0.85rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* Una sola regla para las cuatro tareas: apaga las que no son el `:target`.
   Se atenúan y no se ocultan a propósito — quien filtra sigue viendo que hay
   siete herramientas y no siente que la página se le vació, y nada salta de
   sitio. `:has()` no llega a todos los navegadores; donde falta, el ancla
   sigue saltando al grupo y no se apaga nada, que es una degradación honesta. */
.hub:has(.tarea:target) .tarea:not(:target) {
  opacity: 0.32;
  transition: opacity 0.35s ease;
}

/* --- paso de una página a otra -------------------------------------------- */
/* Las siete páginas de producto comparten cabecera, pie y estructura: entre
   ellas el cambio es de contenido, no de sitio, y un parpadeo en blanco lo
   cuenta al revés. Con esto el navegador funde una en la otra.

   Nativo y de una línea: no hay router ni bundle detrás, y el navegador que no
   lo soporta navega como siempre. `prefers-reduced-motion` lo desactiva por su
   cuenta, sin que haya que declararlo. */
@view-transition {
  navigation: auto;
}

/* --- movimiento ---------------------------------------------------------- */

/* Las fichas del hub entran escalonadas. Es lo único que se anima al cargar, y
   está donde hace falta: el hub es la primera página que ve mucha gente y una
   rejilla de siete fichas apareciendo de golpe se lee como un volantazo.

   El escalón va por CSS y no con un observador en JavaScript: sin scripts la
   página no puede quedarse con las fichas invisibles. Y con
   `prefers-reduced-motion` la regla de más abajo deja la duración en nada, así
   que el estado final se aplica igual. */
@keyframes fichaEntra {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
}

.tarjeta {
  animation: fichaEntra 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both;
}

.tarjeta:nth-child(2) { animation-delay: 0.06s; }
.tarjeta:nth-child(3) { animation-delay: 0.12s; }
.tarjeta:nth-child(4) { animation-delay: 0.18s; }


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

  .btn:hover,
  .btn:focus-visible,
  .btn:active {
    transform: none;
  }
}

/* --- impresión ----------------------------------------------------------- */

@media print {
  .site-header,
  .site-footer,
  .cta,
  .teaser-slot,
  .btn {
    display: none !important;
  }

  body {
    font-size: 11pt;
  }

  .faq details {
    /* En papel no hay dónde hacer clic: todo abierto. */
    display: block;
  }

  .faq details > div {
    display: block !important;
  }
}

/* --- informes de muestra -------------------------------------------------- */
/* Estas páginas no llevan banner ilustrado. Lo característico del tema es el
   documento, no un escritorio con una tableta, así que la portada es tipográfica
   y el peso visual se gasta entero en la hoja del informe (.informe-hoja). */

/* Los aviones de papel.

   Es el gesto de la marca: el avión y su estela punteada están en los banners
   de la landing, y estas dos portadas —el hub y el índice de muestras— son las
   únicas del sitio sin banner ilustrado. Los aviones se lo devuelven.

   Van en SVG y no como imagen: pesan nada, escalan, toman los colores de la
   paleta, y sobre todo VUELAN. Cada avión recorre su propia estela con
   `animateMotion` sobre un `mpath`, y la estela es un trazo discontinuo cuyo
   `stroke-dashoffset` corre en sentido contrario. El marcado está en
   _aviones.html; acá van los colores y el movimiento.

   El halo de color de las esquinas va en los pseudoelementos: son dos manchas
   decorativas y no tienen por qué existir en el HTML. */
.hero-plano {
  position: relative;
  overflow: hidden;
  padding-block: clamp(3rem, 7vw, 5.5rem) clamp(2rem, 4vw, 3.5rem);
  /* La veladura de la portada: el frío de tertiary arriba y el cálido de
     primary abajo, los dos a muy baja opacidad sobre el gris del sitio. Salen
     en rgba de la paleta y no como tintes propios: un tinte nuevo es un color
     de marca nuevo, y de eso ya avisa un test. */
  background:
    linear-gradient(
      160deg,
      rgba(39, 137, 186, 0.07) 0%,
      rgba(255, 255, 255, 0) 52%,
      rgba(248, 181, 0, 0.11) 100%
    ),
    var(--ultralight);
  border-bottom: 1px solid var(--rule);
}

.hero-plano::before,
.hero-plano::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  pointer-events: none;
}

.hero-plano::before {
  right: -90px;
  top: -90px;
  width: 340px;
  height: 340px;
  background: radial-gradient(
    circle,
    rgba(39, 137, 186, 0.18),
    rgba(39, 137, 186, 0) 70%
  );
  animation: flota 9s ease-in-out infinite;
}

.hero-plano::after {
  left: -120px;
  bottom: -160px;
  width: 400px;
  height: 400px;
  background: radial-gradient(
    circle,
    rgba(248, 181, 0, 0.2),
    rgba(248, 181, 0, 0) 70%
  );
  animation: flota 13s ease-in-out infinite;
}

/* El contenido por encima de los adornos. */
.hero-plano .shell {
  position: relative;
  z-index: 1;
}

@keyframes flota {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-9px);
  }
}

.aviones {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.aviones svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Los tres colores son de la paleta —el ink de tertiary, el de brown y el
   secondary oscuro—, así que el test que vigila los colores de esta hoja los
   cubre. Por eso van acá y no sueltos en el marcado del SVG. */
.avion-a { stroke: #247eab; }
.avion-b { stroke: #b06227; }
.avion-c { stroke: #227580; }

@keyframes estelaCorre {
  to {
    stroke-dashoffset: -160;
  }
}

.estela {
  fill: none;
  animation: estelaCorre linear infinite;
}

/* Los aviones se mueven con SMIL (`animateMotion`), y a SMIL no lo para la
   regla de `prefers-reduced-motion` de más abajo: esa solo alcanza a las
   animaciones CSS. Quien pidió menos movimiento se queda sin la capa entera,
   que es decorativa y no se pierde nada. */
@media (prefers-reduced-motion: reduce) {
  .aviones {
    display: none;
  }
}

/* --- índice de muestras --------------------------------------------------- */

.muestras {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
  gap: 1.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Columna y no bloque suelto: el botón se ancla abajo con margin-top:auto, así
   las tres fichas terminan a la misma altura aunque sus índices tengan distinto
   número de secciones. Sin eso, la ficha con el informe más corto deja el botón
   flotando a media altura y las tres dejan de leerse como una fila. */
.muestra-ficha {
  display: flex;
  flex-direction: column;
  padding: 1.6rem;
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.muestra-ficha__profesion {
  margin: 0 0 0.4rem;
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink);
}

.muestra-ficha__caso {
  margin: 0 0 0.5rem;
  font-size: 1.1rem;
  line-height: 1.35;
}

.muestra-ficha__caso a {
  color: inherit;
  text-decoration: none;
}

.muestra-ficha__caso a:hover,
.muestra-ficha__caso a:focus-visible {
  text-decoration: underline;
}

.muestra-ficha__instrumentos {
  margin: 0 0 1.25rem;
  font-size: 0.95rem;
  color: var(--ink-soft);
}

.muestra-ficha__rotulo {
  margin: 0 0 0.5rem;
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* El índice real del informe, leído del documento generado. Va numerado porque
   el orden es información: es el orden en que la profesional lee el informe. */
.muestra-indice {
  margin: 0 0 1.5rem;
  padding-left: 1.4rem;
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--ink-soft);
}

.muestra-indice li {
  padding: 0.15rem 0;
}

.muestra-ficha .btn-row {
  margin-top: auto;
  margin-bottom: 0;
}

/* --- caso e instrumentos -------------------------------------------------- */

.prosa {
  max-width: 62ch;
}

.prosa p {
  margin: 0 0 1rem;
}

.prosa p:last-child {
  margin-bottom: 0;
}

.caso {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}

@media (max-width: 767px) {
  .caso {
    grid-template-columns: 1fr;
  }
}

.caso__ficha {
  margin: 0;
  padding: 1.25rem 1.4rem;
  background: var(--ultralight);
  border-radius: var(--radius);
}

.caso__dato + .caso__dato {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px dotted var(--light);
}

.caso__dato dt {
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.caso__dato dd {
  margin: 0.15rem 0 0;
  font-weight: 500;
}

.instrumentos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 1.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* La regla de acento arriba y no un recuadro: son tres apuntes cortos, y
   encerrarlos les daría más peso del que tienen frente a la hoja de abajo. */
.instrumento {
  padding-top: 0.9rem;
  border-top: 3px solid var(--accent);
}

.instrumento__nombre {
  margin: 0 0 0.35rem;
  font-weight: 700;
  font-size: 1.05rem;
}

.instrumento__aporte {
  margin: 0;
  font-size: 0.95rem;
  color: var(--ink-soft);
}

/* --- la hoja del informe -------------------------------------------------- */
/* El elemento con el que se recuerda la página. Es el documento convertido del
   .docx, puesto como una hoja sobre el fondo tramado de la sección.

   No imita a Word tipografía por tipografía —el documento fiel es el .docx que
   se descarga al lado—; lo que hace es leerse como documento y no como página
   web: medida de lectura corta, interlínea de texto impreso, tablas regladas y
   márgenes de hoja. */

.documento__nota {
  max-width: 60ch;
  margin: 0 0 1.5rem;
  color: var(--ink-soft);
}

.informe-hoja {
  padding: clamp(1.5rem, 4vw, 3.25rem);
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  box-shadow: 0 1px 2px rgba(57, 62, 70, 0.05),
              0 12px 32px rgba(57, 62, 70, 0.07);
  font-size: 0.97rem;
  line-height: 1.6;
}

.informe-hoja > :first-child {
  margin-top: 0;
}

.informe-hoja > :last-child {
  margin-bottom: 0;
}

/* Los títulos del informe, no los de la página: por eso no heredan la escala de
   h2 del sitio, que los dejaría del porte del titular de la sección. */
.informe-hoja h3 {
  margin: 2rem 0 0.6rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
  font-size: 1.02rem;
  font-weight: 700;
  line-height: 1.3;
  color: var(--accent-ink);
}

.informe-hoja h3:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
  font-size: 1.25rem;
  color: var(--ink);
}

.informe-hoja p {
  max-width: 68ch;
  margin: 0 0 0.85rem;
}

/* Las tablas del informe pueden ser anchas —el desglose del TEPROSIF-R trae una
   columna por familia de proceso—. Se desplazan dentro de su caja para que la
   página nunca lo haga: un sitio que se mueve de lado en el teléfono se siente
   roto aunque el contenido esté bien. */
.doc-tabla-scroll {
  overflow-x: auto;
  margin: 0 0 1.25rem;
  -webkit-overflow-scrolling: touch;
}

.doc-tabla {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9rem;
}

.doc-tabla th,
.doc-tabla td {
  padding: 0.42rem 0.7rem;
  border: 1px solid var(--rule);
  text-align: left;
  vertical-align: top;
}

.doc-tabla thead th {
  background: var(--ultralight);
  font-weight: 700;
  white-space: nowrap;
}

.doc-tabla tbody th {
  font-weight: 500;
}

.btn-row--centrada {
  justify-content: center;
}

.documento__pie {
  margin: 0.75rem 0 0;
  font-size: 0.9rem;
  text-align: center;
  color: var(--ink-soft);
}

.muestra-ficha__resto {
  margin: -1rem 0 1.5rem;
  padding-left: 1.4rem;
  font-size: 0.9rem;
  color: var(--ink-soft);
}
