@charset "UTF-8";
/* ==========================================================================
   TarjetaCloud: Landing page
   Sistema de diseño y estilos. Sin dependencias externas.
   ========================================================================== */

/* --------------------------------------------------------------------------
   01 · Reset
   -------------------------------------------------------------------------- */

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

* { margin: 0; padding: 0; }

html {
  -webkit-text-size-adjust: 100%;
  /* Sin scroll-behavior: smooth, entra en conflicto con Lenis.
     El desplazamiento suave lo gestiona el JS. */
  scroll-padding-top: 92px;
}

body {
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg, picture { display: block; max-width: 100%; }

/* Un <svg> sin ancho ni alto declarados no mide 0: cae al tamaño por defecto
   del formato, 300x150 px, y revienta el bloque que lo contiene. Se le da una
   medida ligada al texto para que cualquier icono al que se le olvide una
   regla propia quede, como mucho, del tamaño de su línea. */
svg { width: 1em; height: 1em; }

button, input, select, textarea { font: inherit; color: inherit; }

button { background: none; border: 0; cursor: pointer; }

a { color: inherit; text-decoration: none; }

ul, ol { list-style: none; }

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

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

/* --------------------------------------------------------------------------
   02 · Tokens
   -------------------------------------------------------------------------- */

:root {
  /* Paleta base */
  --ink:        #0a0a12;
  --ink-2:      #101019;
  --ink-3:      #16161f;
  --cream:      #f6f4ef;
  --cream-2:    #efece4;
  --cream-3:    #e5e1d6;

  --accent:     #7c5cff;
  --accent-lo:  #6b48ff;
  --accent-hi:  #a690ff;

  /* Espectro iridiscente de la tarjeta */
  --irid-1: #7c5cff;
  --irid-2: #22d3ee;
  --irid-3: #b48bff;
  --irid-4: #ff7a59;
  --irid-5: #4ade80;

  /* Tipografía */
  --font-display: "Clash Display", "Helvetica Neue", Arial, sans-serif;
  --font-body: "Satoshi", -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif;

  --step--1: clamp(0.82rem, 0.79rem + 0.15vw, 0.9rem);
  --step-0:  clamp(1rem, 0.96rem + 0.2vw, 1.125rem);
  --step-1:  clamp(1.1rem, 1.03rem + 0.35vw, 1.28rem);
  --step-2:  clamp(1.25rem, 1.12rem + 0.62vw, 1.6rem);
  --step-3:  clamp(1.5rem, 1.25rem + 1.2vw, 2.1rem);
  --step-4:  clamp(1.9rem, 1.3rem + 2.2vw, 3rem);
  --step-5:  clamp(2.2rem, 1.2rem + 3.6vw, 4.25rem);
  --step-6:  clamp(2.7rem, 1.1rem + 5.4vw, 5.25rem);

  /* Ritmo vertical */
  --gutter: clamp(1.25rem, 4vw, 2.5rem);
  --section-y: clamp(4.5rem, 8vw, 8rem);
  --maxw: 1240px;
  --maxw-narrow: 900px;

  --radius-s: 10px;
  --radius-m: 18px;
  --radius-l: 28px;
  --radius-xl: 40px;

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-io: cubic-bezier(0.65, 0, 0.35, 1);

  /* Tema por defecto: claro */
  --bg: var(--cream);
  --bg-elev: #fff;
  --fg: var(--ink);
  --fg-muted: #55555f;
  --fg-faint: #8a8a94;
  --line: rgba(10, 10, 18, 0.12);
  --line-soft: rgba(10, 10, 18, 0.07);
}

[data-theme="dark"] {
  --bg: var(--ink);
  --bg-elev: var(--ink-3);
  --fg: #f4f3f7;
  --fg-muted: #a3a2b0;
  --fg-faint: #6e6d7d;
  --line: rgba(255, 255, 255, 0.14);
  --line-soft: rgba(255, 255, 255, 0.07);
}

/* --------------------------------------------------------------------------
   03 · Base
   -------------------------------------------------------------------------- */

body {
  background: var(--ink);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.6;
  font-weight: 400;
}

h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.028em;
  text-wrap: balance;
}

h1 { font-size: var(--step-6); letter-spacing: -0.04em; }
h2 { font-size: var(--step-5); letter-spacing: -0.035em; }
h3 { font-size: var(--step-2); letter-spacing: -0.02em; }

p { text-wrap: pretty; }

strong { font-weight: 700; }

/* Secciones ------------------------------------------------------------- */

.section {
  position: relative;
  background: var(--bg);
  color: var(--fg);
  padding-block: var(--section-y);
  isolation: isolate;
}

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

.shell--narrow { max-width: var(--maxw-narrow); }

/* Grano sobre los bloques oscuros --------------------------------------- */

[data-theme="dark"]::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0.5;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.34'/%3E%3C/svg%3E");
}

[data-theme="dark"] > * { position: relative; z-index: 1; }

/* --------------------------------------------------------------------------
   04 · Piezas reutilizables
   -------------------------------------------------------------------------- */

/* Encabezado de sección.
   El ancho de los titulares lo marcan los saltos de línea del HTML, no un
   max-width: si se limita aquí, el párrafo de apoyo hereda ese ancho y se
   queda en una columna demasiado estrecha. */
.sec-head { max-width: none; }

.sec-lede {
  margin-top: 1.5rem;
  max-width: 52ch;
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--fg-muted);
}

/* Botones */
.btn {
  --btn-bg: var(--accent);
  --btn-fg: #fff;
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.65em;
  padding: 1.05em 1.85em;
  border-radius: 100px;
  background: var(--btn-bg);
  color: var(--btn-fg);
  font-weight: 700;
  font-size: var(--step-0);
  letter-spacing: -0.01em;
  line-height: 1;
  white-space: nowrap;
  transition: transform 0.45s var(--ease-out), box-shadow 0.45s var(--ease-out),
              background-color 0.3s ease, color 0.3s ease;
  /* Este will-change se probo a limitar a los punteros con :hover, con la idea
     de que en una pantalla tactil promueve a capa propia unos botones que nunca
     se van a mover. Medido en un Pixel 5 con el procesador frenado seis veces,
     salio peor: sin el, el rasterizado de recorrer la pagina entera sube de 319
     a 353 ms, porque la capa promovida se rasteriza una vez y se reusa, y sin
     promover se repite cada vez que el boton vuelve a entrar en pantalla. Son
     cinco o diez botones por pagina, no los treinta que parecian: la memoria
     que ahorraba no compensaba. Se queda como estaba. */
  will-change: transform;
}

.btn svg { width: 1.05em; height: 1.05em; flex: none; }

.btn--primary {
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.25) inset,
              0 14px 34px -12px color-mix(in srgb, var(--accent) 75%, transparent);
}

.btn--primary:hover {
  --btn-bg: var(--accent-lo);
  transform: translateY(-2px);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.3) inset,
              0 20px 44px -12px color-mix(in srgb, var(--accent) 85%, transparent);
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--fg);
  border: 1px solid var(--line);
  backdrop-filter: blur(6px);
}

.btn--ghost:hover {
  --btn-bg: color-mix(in srgb, var(--fg) 8%, transparent);
  transform: translateY(-2px);
}

/* Botón arcoíris. Pedido el 16/10/26 para el botón que quedó solo en el hero
   de Fidelidad al sacar el morado (D-41): un borde que no es un color
   plano, es el mismo espectro iridiscente de la tarjeta (--irid-1 a 5, ver
   sección 02) puesto en anillo, y ese anillo gira en sentido horario al
   pasar el cursor.

   El anillo se pinta con el truco de la máscara en dos capas: el
   pseudo-elemento cubre toda la caja con el degradado cónico, y la máscara
   le resta el centro (content-box), así que lo único que queda pintado es
   el grosor que marca el padding. No es un `border` porque un border no
   admite un degradado cónico ni se puede rotar por separado del texto de
   adentro.

   El primer intento rotaba el pseudo-elemento entero con `transform:
   rotate()`, y eso gira la CAJA, no el color: en un botón ancho y bajo (no
   cuadrado) la caja rotada saca las puntas del anillo bien afuera del
   botón, como dos rayas cruzando la pantalla -se vio así y no sirvió-. Lo
   que hay que mover es el ángulo de arranque del degradado cónico
   (`from`), no la caja que lo contiene, para que el anillo se quede quieto,
   pegado al borde del botón, y solo el color le dé la vuelta por dentro.
   Eso pide una custom property registrada como ángulo (`@property`), que es
   lo único que el navegador sabe interpolar en una animación; sin ese
   registro el degradado cónico no admite transición ni keyframes. */
@property --giro-arcoiris {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.btn--arcoiris {
  --btn-bg: color-mix(in srgb, var(--ink-3) 82%, transparent);
  border: none;
}

/* Más delgado que un .btn--lg normal (1,2em de relleno vertical): con el
   anillo de afuera, el mismo relleno que lleva cualquier otro botón grande
   del sitio se veía inflado. Las dos clases juntas pesan más que .btn--lg
   sola, así que esto gana sin importar en qué orden caigan en la hoja de
   estilos. */
.btn--arcoiris.btn--lg { padding: 0.8em 2em; }

.btn--arcoiris::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1.5px;
  background: conic-gradient(from var(--giro-arcoiris),
    var(--irid-1), var(--irid-4), var(--irid-2), var(--irid-5), var(--irid-3), var(--irid-1));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  pointer-events: none;
}

/* La animación (y el `--giro-arcoiris` que anima) van las dos en el mismo
   ::before que pinta el degradado, no en el botón: `inherits: false` de
   arriba significa justo eso, que esta variable no baja sola del padre a un
   pseudo-elemento, así que si la animación corriera en el botón y la
   leyera el ::before, el ::before nunca se enteraría y se quedaría fijo en
   el valor inicial.

   Tampoco es un adorno ambiental que haya que apagar en modo ligero
   (sección 29): solo corre con el cursor encima, es la respuesta a un
   gesto. */
.btn--arcoiris:hover::before {
  animation: btn-arcoiris-giro 2.6s linear infinite;
}

@keyframes btn-arcoiris-giro {
  to { --giro-arcoiris: 360deg; }
}

.btn--lg { padding: 1.2em 2.2em; font-size: var(--step-1); }

/* Enlace con subrayado animado */
.link-u {
  position: relative;
  font-weight: 600;
  color: var(--fg);
  padding-bottom: 2px;
}

.link-u::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform-origin: right;
  transform: scaleX(1);
  transition: transform 0.5s var(--ease-out);
}

.link-u:hover::after { transform: scaleX(0); transform-origin: left; }

/* Tarjeta genérica */
.card {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--bg-elev);
  padding: clamp(1.6rem, 3vw, 2.4rem);
  overflow: hidden;
}

/* Marca de agua numérica */
.ghost-num {
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: 500;
  letter-spacing: -0.04em;
  line-height: 1;
  color: var(--fg-faint);
  opacity: 0.5;
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   05 · Logotipo
   -------------------------------------------------------------------------- */

/* Geometría del bloqueo, idéntica a la de los archivos de branding/:
   símbolo 1.62 em, hueco 0.44 em, caja de línea de 1 em centrada. El hueco
   va en em y no en rem para que escale con el logotipo. */
.wordmark {
  display: inline-flex;
  align-items: center;
  gap: 0.44em;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 1.24rem;
  letter-spacing: -0.035em;
  line-height: 1;
  color: var(--fg);
}

/* El símbolo es ahora un dibujo completo: la caja, el degradado, la luz de
   canto y las ondas van dentro del propio SVG. Antes la caja la ponía el CSS
   y el glifo el SVG, y eso obligaba a mantener dos piezas sincronizadas. */
.wordmark__mark {
  width: 1.62em;
  height: 1.62em;
  flex: none;
  display: block;
  filter: drop-shadow(0 4px 12px rgba(124, 92, 255, 0.32));
}

.wordmark__mark svg { width: 100%; height: 100%; }

/* Las dos palabras van dentro de un mismo elemento. Sueltas en el flex
   serian dos hijos distintos y el gap de arriba, pensado para separar el
   simbolo del nombre, se colaria tambien entre "tarjeta" y "cloud". */
.wordmark__txt { display: block; }

.wordmark em {
  font-style: normal;
  font-weight: 400;
  color: var(--fg-muted);
}

/* --------------------------------------------------------------------------
   06 · Navegación
   -------------------------------------------------------------------------- */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  padding-block: 1.1rem;
  color: #f4f3f7;
  transition: color 0.4s ease, padding 0.5s var(--ease-out);
}

.nav::before {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 72%, transparent);
  backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid rgba(255, 255, 255, 0.09);
  opacity: 0;
  transition: opacity 0.45s ease;
}

.nav.is-stuck { padding-block: 0.75rem; }
.nav.is-stuck::before { opacity: 1; }

.nav.is-light { color: var(--ink); }

.nav.is-light::before {
  background: color-mix(in srgb, var(--cream) 78%, transparent);
  border-bottom-color: rgba(10, 10, 18, 0.1);
}

.nav__inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.nav .wordmark { color: currentColor; }
.nav .wordmark em { color: currentColor; opacity: 0.55; }

.nav__links {
  display: flex;
  align-items: center;
  gap: clamp(1.1rem, 2.4vw, 2.1rem);
  font-size: 0.95rem;
  font-weight: 500;
}

.nav__links a { opacity: 0.72; transition: opacity 0.25s ease; }
.nav__links a:hover { opacity: 1; }

.nav__cta {
  padding: 0.72em 1.35em;
  border-radius: 100px;
  background: var(--accent);
  color: #fff;
  font-weight: 700;
  font-size: 0.95rem;
  line-height: 1;
  transition: background-color 0.3s ease, transform 0.4s var(--ease-out);
}

.nav__cta:hover { background: var(--accent-lo); transform: translateY(-1px); }

.nav__burger { display: none; }

@media (max-width: 860px) {
  .nav__links { display: none; }
  .nav__cta { display: none; }

  .nav__burger {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9rem;
    font-weight: 600;
    padding: 0.6em 1.1em;
    border: 1px solid currentColor;
    border-radius: 100px;
    opacity: 0.85;
  }

  .nav__burger svg { width: 1.15em; height: 1.15em; flex: none; }
}

/* Menú desplegable móvil */
.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 99;
  background: var(--ink);
  color: #f4f3f7;
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* En una pantalla corta (o en horizontal) la lista puede pasar del alto
     disponible, y centrada eso recorta justo por arriba: el primer enlace se
     queda fuera y sin forma de llegar a él. Con safe center el centrado cede
     antes que el contenido, y el desplazamiento recoge lo que sobre. */
  justify-content: safe center;
  overflow-y: auto;
  padding: var(--gutter);
  gap: 0.4rem;
  clip-path: inset(0 0 100% 0);
  transition: clip-path 0.65s var(--ease-out);
  visibility: hidden;
}

/* overscroll-behavior evita que el gesto se propague a la página de detrás,
   que en iOS sigue desplazándose aunque el menú la tape por completo. */
.mobile-menu.is-open {
  clip-path: inset(0 0 0 0);
  visibility: visible;
  overscroll-behavior: contain;
}

.mobile-menu a {
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 500;
  letter-spacing: -0.03em;
  padding-block: 0.4rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}

/* El botón del menú no es un enlace más de la lista: recupera la tipografía y
   el tamaño de botón, y se queda sin la línea que separa a los demás. */
.mobile-menu a.mobile-menu__cta {
  margin-top: 1.8rem;
  border-bottom: 0;
  font-family: var(--font-body);
  font-size: var(--step-0);
  font-weight: 700;
  letter-spacing: -0.01em;
  padding: 1.05em 1.85em;
}

/* --------------------------------------------------------------------------
   07 · Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding-top: clamp(6.5rem, 12vh, 8rem);
  padding-bottom: clamp(3rem, 7vh, 4.5rem);
  overflow: hidden;
}

/* Luz de fondo. La versión anterior era tres círculos sueltos (cada uno su
   propio div, cuadrado, border-radius:50% y un blur encima): eso es
   exactamente lo que delata a un degradado de maqueta, porque el ojo lee el
   borde del blur como el borde de una esfera. Esta va TODA en un solo plano
   de pantalla completa, con varios radial-gradient elípticos superpuestos en
   el mismo background (no cajas separadas, no border-radius), cada uno
   excéntrico y de proporción distinta, así que donde se tocan no hay costura:
   se funden en una sola bruma de luz con dos colores, que es como se ve una
   fuente de luz real y no una lamparita de escritorio.

   El orden importa: el primer gradiente de la lista pinta ENCIMA de los
   siguientes, así que el núcleo brillante de cada color va primero y su halo
   ancho va después, inmediatamente debajo. */
.hero__aurora {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  /* Los núcleos usan el MISMO morado y el MISMO cian de siempre
     (rgba(124,92,255,·) y rgba(34,211,238,·), los dos colores que ya estaban
     en los círculos originales), nunca un lavanda casi blanco: un núcleo más
     claro que eso deja de leerse como "más intenso de ese color" y pasa a
     leerse como blanco, que es justo la queja. Lo único que cambia entre
     núcleo y halo es la opacidad, no el color. */
  background-image:
    /* núcleo morado, arriba a la derecha */
    radial-gradient(ellipse 34% 28% at 82% 4%, rgba(124, 92, 255, 0.6), transparent 68%),
    /* halo morado, mucho más ancho que el núcleo que lo contiene */
    radial-gradient(ellipse 62% 54% at 76% 16%, rgba(124, 92, 255, 0.3), transparent 72%),
    /* núcleo cian, abajo a la izquierda */
    radial-gradient(ellipse 26% 20% at 8% 94%, rgba(34, 211, 238, 0.4), transparent 66%),
    /* halo cian */
    radial-gradient(ellipse 48% 40% at 13% 88%, rgba(34, 211, 238, 0.18), transparent 74%),
    /* puente tenue entre los dos: sin esto el morado y el cian se leen como
       dos luces que no se tocan en vez de una sola con dos colores */
    radial-gradient(ellipse 85% 48% at 48% 22%, rgba(124, 92, 255, 0.1), transparent 76%),
    /* lavado parejo desde arriba, como luz de estudio cayendo sobre la
       escena entera: es lo que evita que el centro de la pantalla se vea
       sin ninguna luz entre los dos halos de las esquinas. Mismo morado,
       muy rebajado, no blanco. */
    linear-gradient(180deg, rgba(124, 92, 255, 0.06), transparent 46%);
  filter: blur(54px);
  animation: hero-aurora-deriva 48s ease-in-out infinite;
}

@keyframes hero-aurora-deriva {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-1.6%, 1.8%) scale(1.035); }
}

@media (prefers-reduced-motion: reduce) {
  .hero__aurora { animation: none; }
}

/* El velo que enfoca. Sin él los dos halos grandes llegan parejos hasta el
   borde de la ventana y el mazo compite con las cuatro esquinas por la
   atención; con él los bordes se apagan un punto y el ojo cae solo en el
   centro, que es donde está el titular y donde empieza el mazo. Va por
   encima de los halos y la rejilla pero por debajo de todo el contenido
   (z-index 2, más arriba). */
.hero__vignette {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(ellipse 76% 62% at 50% 38%,
    transparent 55%,
    rgba(10, 10, 18, 0.5) 100%);
}

/* Rejilla tenue */
.hero__grid {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(rgba(255, 255, 255, 0.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.055) 1px, transparent 1px);
  background-size: 74px 74px;
  mask-image: radial-gradient(ellipse 90% 62% at 50% 34%, #000 20%, transparent 76%);
}

.hero__inner {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1.12fr) minmax(0, 0.88fr);
  align-items: center;
  gap: clamp(2.5rem, 5vw, 4rem);
  width: 100%;
}

.hero__title {
  font-size: var(--step-6);
  line-height: 0.94;
  letter-spacing: -0.045em;
  font-weight: 600;
}

.hero__title .accent {
  background: linear-gradient(96deg, var(--accent-hi) 8%, var(--irid-2) 58%, var(--accent-hi) 96%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* --------------------------------------------------------------------------
   07b · El titular que se teclea y se cae (portada de fidelidad)

   Lo arma assets/js/hero-titulo.js, letra por letra, dentro de un
   <h1 data-titulo-vivo>. Si ese script no llega a correr, el HTML de abajo
   nunca se reemplaza y el titular se ve como cualquier otro: una frase
   quieta, sin tecleo y sin nada que se caiga.

   z-index entra acá porque, mientras las letras caen, pasan por delante de
   la bajada y los botones que tiene debajo: sin subirlo se verían detrás,
   que es al revés de cómo se ve una letra cayendo sobre un papel.
   -------------------------------------------------------------------------- */

.hero__title[data-titulo-vivo] {
  position: relative;
  z-index: 6;
}

.titulo-vivo__palabra {
  display: inline-block;
  white-space: nowrap;
}

.titulo-vivo__letra {
  display: inline-block;
  white-space: pre;
}

/* Va en una capa de adentro, no en .titulo-vivo__letra: comprobado con
   capturas que si la MISMA letra lleva este degradado y además la opacidad
   en cero del tecleo (más abajo, en hero-titulo.js), Chromium headless la
   pinta igual, entera, pase lo que diga el opacity del computed style. La
   opacidad tiene que caer sobre un envoltorio sin background-clip.

   El mismo degradado de .hero__title .accent, de arriba, pero con
   background-image y no con el atajo `background`: hero-titulo.js le pone a
   cada letra su propio background-size/position por encima, y conviene que
   esta regla no declare esas dos propiedades ni de casualidad (el atajo
   `background` las resetea a su valor inicial si no las lleva escritas). */
.titulo-vivo__letra--acento {
  background-image: linear-gradient(96deg, var(--accent-hi) 8%, var(--irid-2) 58%, var(--accent-hi) 96%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.hero__lede {
  margin-top: clamp(1.5rem, 2.6vw, 2rem);
  max-width: 46ch;
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--fg-muted);
}

.hero__actions {
  margin-top: clamp(2rem, 3.4vw, 2.75rem);
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
}

.hero__trust {
  margin-top: clamp(2.2rem, 4vw, 3rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem 1.4rem;
  font-size: var(--step--1);
  color: var(--fg-faint);
  letter-spacing: 0.01em;
}

.hero__trust li {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.hero__trust svg {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  color: var(--irid-5);
}

/* --------------------------------------------------------------------------
   08 · Tarjeta NFC en 3D
   -------------------------------------------------------------------------- */

/* La escena solo centra. La perspectiva vive en el envoltorio, que es quien se
   la pasa a la tarjeta; ver la nota de .card3d-wrap. */
.card3d-stage {
  position: relative;
  display: grid;
  place-items: center;
}

/* Envoltorio intermedio. La animación de entrada se aplica aquí y la
   inclinación con el cursor a .card3d, para que GSAP no escriba un transform
   en línea sobre el elemento cuyo transform depende de variables CSS.

   La perspectiva se declara aquí y el envoltorio se aplana a propósito. Con
   transform-style: preserve-3d el navegador ordena a los hijos por su posición
   en el espacio y deja de mirar z-index: como la tarjeta se inclina, sus
   esquinas se hunden detrás del plano del pase de fidelidad y los dos se
   atraviesan. Aplanado, la tarjeta se compone como una capa (su relieve lo
   sigue montando ella con su propio preserve-3d) y el orden vuelve a decidirlo
   z-index. La perspectiva tiene que pasar de la escena a aquí porque un
   envoltorio plano ya no se la transmitiría a la tarjeta. */
.card3d-wrap {
  position: relative;
  width: min(100%, 470px);
  perspective: 1400px;
  transform-style: flat;
}

.card3d {
  --rx: 0deg;
  --ry: 0deg;
  --mx: 50%;
  --my: 50%;
  position: relative;
  width: 100%;
  aspect-ratio: 1.586 / 1;   /* proporción ISO de tarjeta */
  border-radius: 22px;
  transform-style: preserve-3d;
  transform: rotateX(var(--rx)) rotateY(var(--ry));
  transition: transform 0.6s var(--ease-out);
  will-change: transform;
}

/* Mientras el cursor manda, la interpolación la hace el JS fotograma a
   fotograma; una transición CSS encima añadiría retraso. */
.card3d.is-live { transition: none; }

/* Con el cursor encima, la tarjeta pasa por delante de todo lo que la rodea:
   el pase de fidelidad y las etiquetas flotantes le mordían las esquinas justo
   cuando está inclinada, que es cuando se la está mirando. */
@media (hover: hover) and (pointer: fine) {
  .card3d:hover { z-index: 10; }
}

.card3d__face {
  position: absolute;
  inset: 0;
  border-radius: 22px;
  overflow: hidden;
  background:
    linear-gradient(148deg, #23233a 0%, #14141f 42%, #0d0d16 100%);
  border: 1px solid rgba(255, 255, 255, 0.13);
  box-shadow:
    0 2px 0 rgba(255, 255, 255, 0.09) inset,
    0 40px 80px -30px rgba(0, 0, 0, 0.9),
    0 10px 30px -12px rgba(124, 92, 255, 0.5);
}

/* Capa iridiscente */
.card3d__irid {
  position: absolute;
  inset: -30%;
  background: conic-gradient(
    from 210deg,
    var(--irid-1), var(--irid-2), var(--irid-3),
    var(--irid-4), var(--irid-5), var(--irid-1)
  );
  opacity: 0.2;
  filter: blur(30px) saturate(1.5);
  mix-blend-mode: screen;
  animation: irid-spin 16s linear infinite;
}

/* Vela sobre la mitad inferior. Sin esto la iridiscencia sube tanto el brillo
   que el nombre grabado pierde contraste y la tarjeta deja de leerse como un
   objeto oscuro. Va en ::after para quedar por encima de las capas hijas. */
.card3d__face::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    202deg,
    rgba(10, 10, 18, 0) 24%,
    rgba(10, 10, 18, 0.5) 74%,
    rgba(10, 10, 18, 0.75) 100%
  );
}

@keyframes irid-spin {
  to { transform: rotate(360deg); }
}

/* Brillo especular que sigue al cursor */
.card3d__sheen {
  position: absolute;
  inset: 0;
  background: radial-gradient(
    440px circle at var(--mx) var(--my),
    rgba(255, 255, 255, 0.3),
    rgba(255, 255, 255, 0.06) 34%,
    transparent 62%
  );
  mix-blend-mode: soft-light;
  transition: opacity 0.4s ease;
}

/* Grano de la tarjeta */
.card3d__grain {
  position: absolute;
  inset: 0;
  opacity: 0.3;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23g)' opacity='0.5'/%3E%3C/svg%3E");
}

.card3d__content {
  position: absolute;
  inset: 0;
  padding: clamp(1.3rem, 3.4vw, 1.9rem);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  color: #fff;
  transform: translateZ(38px);
}

.card3d__top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
}

.card3d__brand {
  font-family: var(--font-display);
  font-size: clamp(1rem, 2.5vw, 1.2rem);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1;
}

.card3d__brand span { opacity: 0.55; font-weight: 400; }

/* Ondas NFC animadas */
.nfc-waves { width: clamp(24px, 5.5vw, 32px); height: auto; color: #fff; }
.nfc-waves path { opacity: 0.35; }
.nfc-waves path:nth-child(1) { animation: nfc-pulse 2.4s ease-in-out infinite; }
.nfc-waves path:nth-child(2) { animation: nfc-pulse 2.4s ease-in-out 0.22s infinite; }
.nfc-waves path:nth-child(3) { animation: nfc-pulse 2.4s ease-in-out 0.44s infinite; }

@keyframes nfc-pulse {
  0%, 100% { opacity: 0.22; }
  45%      { opacity: 1; }
}

/* Aquí iba un contacto dorado de tarjeta bancaria. Se quitó: una NFC no lleva
   contactos a la vista, el chip va dentro del plástico y no se ve. Enseñarlo
   contaba una mentira sobre el producto y encima ensuciaba la cara de la
   tarjeta, que es lo único que el cliente va a mirar. */

.card3d__name {
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 2.9vw, 1.4rem);
  font-weight: 500;
  letter-spacing: -0.03em;
  line-height: 1.1;
}

.card3d__role {
  margin-top: 0.15rem;
  font-size: clamp(0.7rem, 1.8vw, 0.8rem);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.card3d__bottom {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
}

/* QR de respaldo dibujado en CSS */
.card3d__qr {
  width: clamp(38px, 9vw, 50px);
  height: clamp(38px, 9vw, 50px);
  border-radius: 6px;
  background: #fff;
  padding: 4px;
  flex: none;
}

.card3d__qr svg { width: 100%; height: 100%; color: #0a0a12; }

/* Reflejo bajo la tarjeta */
.card3d-wrap::after {
  content: "";
  position: absolute;
  bottom: -26px;
  left: 50%;
  translate: -50% 0;
  width: 78%;
  height: 46px;
  background: radial-gradient(ellipse at center, rgba(124, 92, 255, 0.45), transparent 70%);
  filter: blur(22px);
  z-index: -1;
}

/* Etiqueta flotante junto a la tarjeta */
.float-tag {
  position: absolute;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
  padding: 0.6rem 0.95rem;
  border-radius: 100px;
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: -0.01em;
  color: #f4f3f7;
  background: rgba(22, 22, 31, 0.78);
  border: 1px solid rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(10px);
  box-shadow: 0 18px 40px -20px rgba(0, 0, 0, 0.9);
  white-space: nowrap;
}

/* La etiqueta va en flex y el icono es un hijo flexible: cuando el hueco
   disponible se estrecha, el navegador lo encoge hasta dejarlo en cero ancho y
   desaparece sin avisar. Con el texto no pasa porque lleva nowrap. */
.float-tag svg { flex: none; }

.float-tag svg { width: 1em; height: 1em; color: var(--irid-2); }

/* Adornos: no deben robarle el cursor a la tarjeta. Si lo hicieran, al cruzar
   con el ratón la esquina que pisan, la tarjeta dejaría de estar en :hover y
   volvería a meterse debajo de ellos. */
.card3d-wrap .float-tag { pointer-events: none; }

/* Se posicionan contra .card3d-wrap, es decir, contra el borde real de la
   tarjeta, y no contra la celda de la rejilla. */
.float-tag--1 { top: -15%;   left: -6%;  }
.float-tag--2 { bottom: -12%; right: -6%; }

@media (max-width: 1100px) {
  .float-tag--1 { left: -2%; }
  .float-tag--2 { right: -2%; }
}

/* La etiqueta de precio del hero. Usa la misma posición que .float-tag--2
   (así hereda su ajuste en pantallas angostas) pero no su piel: un precio no
   es un dato que se susurra en una píldora translúcida, así que lleva el
   degradado de la marca, letra más grande y el giro leve de una pegatina. */
.float-tag--precio {
  flex-direction: column;
  align-items: flex-start;
  gap: 0.2rem;
  padding: 0.75rem 1.05rem;
  border-radius: 16px;
  background: linear-gradient(135deg, var(--accent-hi), var(--accent));
  border-color: rgba(255, 255, 255, 0.25);
  box-shadow: 0 18px 40px -16px rgba(124, 92, 255, 0.65);
  rotate: -4deg;
  white-space: normal;
  color: #fff;
}

.float-tag__cifra {
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1;
}

.float-tag__cifra i {
  margin-left: 0.2em;
  font-size: 0.62rem;
  font-style: normal;
  font-weight: 600;
  opacity: 0.75;
}

.float-tag__texto {
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1.25;
  opacity: 0.92;
}

/* --------------------------------------------------------------------------
   09 · Marquesina
   -------------------------------------------------------------------------- */

.marquee {
  /* El paso base de la cinta, y el mínimo: nunca va más lenta que esto.
     `main.js` acelera encima hasta 2,4 veces mientras haces scroll, tocando el
     `playbackRate` de esta misma animación. No cambia la duración: la cambia
     de ritmo sin reiniciarla, así que este número sigue siendo el único sitio
     donde se ajusta la velocidad normal. */
  --speed: 42s;
  position: relative;
  padding-block: clamp(2.2rem, 4vw, 3.2rem);
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
  overflow: hidden;
  display: flex;
  gap: 0;
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.marquee__track {
  display: flex;
  align-items: center;
  gap: 2.6rem;
  padding-right: 2.6rem;
  flex: none;
  animation: marquee var(--speed) linear infinite;
}

.marquee:hover .marquee__track { animation-play-state: paused; }

@keyframes marquee {
  to { transform: translateX(-100%); }
}

.marquee__item {
  display: inline-flex;
  align-items: center;
  gap: 2.6rem;
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 2.2vw, 1.5rem);
  font-weight: 500;
  letter-spacing: -0.025em;
  color: var(--fg-muted);
  white-space: nowrap;
}

.marquee__item::after {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0.65;
  flex: none;
}

/* --------------------------------------------------------------------------
   10 · Cómo funciona
   -------------------------------------------------------------------------- */

.steps {
  margin-top: clamp(3rem, 6vw, 4.5rem);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1rem, 2.4vw, 1.6rem);
}

.step {
  position: relative;
  padding: clamp(1.6rem, 2.8vw, 2.2rem);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--bg-elev);
  overflow: hidden;
  transition: transform 0.55s var(--ease-out), box-shadow 0.55s var(--ease-out);
}

.step:hover {
  transform: translateY(-5px);
  box-shadow: 0 26px 50px -34px rgba(10, 10, 18, 0.45);
}

.step__icon {
  width: 3rem;
  height: 3rem;
  border-radius: 14px;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  margin-bottom: 1.4rem;
}

.step__icon svg { width: 1.45rem; height: 1.45rem; }

.step__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.7rem;
}

.step h3 { font-size: var(--step-2); }

.step p { color: var(--fg-muted); max-width: 34ch; }

@media (max-width: 900px) {
  .steps { grid-template-columns: 1fr; }
  .step p { max-width: none; }
}

/* --------------------------------------------------------------------------
   11 · Producto: tarjetas NFC
   -------------------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2.5rem, 6vw, 5rem);
}

.split--reverse .split__visual { order: -1; }

@media (max-width: 940px) {
  .split { grid-template-columns: 1fr; }
  .split--reverse .split__visual { order: 0; }
}

/* Listado de características */
.features {
  margin-top: clamp(2rem, 3.4vw, 2.75rem);
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.9rem 1.6rem;
}

.features li {
  display: flex;
  align-items: flex-start;
  gap: 0.7rem;
  font-size: var(--step-0);
  line-height: 1.45;
}

.features svg {
  width: 1.15em;
  height: 1.15em;
  flex: none;
  margin-top: 0.2em;
  color: var(--accent);
}

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

/* Muestrario de acabados */
.finishes {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(0.7rem, 1.6vw, 1.1rem);
}

.finish {
  aspect-ratio: 1.586 / 1;
  border-radius: 16px;
  position: relative;
  overflow: hidden;
  border: 1px solid var(--line);
  box-shadow: 0 22px 44px -28px rgba(10, 10, 18, 0.6);
  transition: transform 0.5s var(--ease-out);
}

.finish:hover { transform: translateY(-6px) scale(1.02); }

.finish__label {
  position: absolute;
  left: 0.8rem;
  right: 0.8rem;
  bottom: 0.7rem;
  font-size: clamp(0.72rem, 1.5vw, 0.78rem);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  line-height: 1.2;
}

.finish--matte {
  background: linear-gradient(150deg, #2a2a3d, #14141e 60%, #0d0d15);
}
.finish--matte .finish__label { color: rgba(255, 255, 255, 0.72); }

.finish--white {
  background: linear-gradient(150deg, #ffffff, #ece9e1 70%, #dedacf);
}
.finish--white .finish__label { color: rgba(10, 10, 18, 0.55); }

.finish--metal {
  background:
    linear-gradient(115deg, #b9bcc6 0%, #f2f3f7 18%, #8f93a0 38%,
                    #dfe1e8 58%, #7e8290 78%, #cfd2da 100%);
}
.finish--metal .finish__label { color: rgba(10, 10, 18, 0.6); }

.finish--holo {
  background: conic-gradient(from 140deg, #ff7a59, #b48bff, #22d3ee, #4ade80, #ff7a59);
  filter: saturate(0.85);
}
.finish--holo .finish__label { color: rgba(10, 10, 18, 0.62); }

.finish--wood {
  background: linear-gradient(150deg, #a97b4f, #7d5533 55%, #603f24);
}
.finish--wood .finish__label { color: rgba(255, 255, 255, 0.75); }

.finish--custom {
  display: grid;
  place-items: center;
  background: var(--bg-elev);
  border-style: dashed;
  border-color: var(--line);
}

.finish--custom .finish__label {
  position: static;
  color: var(--fg-faint);
  text-align: center;
}

/* Aclaración bajo el muestrario: metal y madera son impresión, no el material */
.finishes__note {
  margin-top: 1.1rem;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--fg-muted);
}

@media (max-width: 620px) {
  .finishes { grid-template-columns: repeat(2, 1fr); }
}

/* --------------------------------------------------------------------------
   11b · Fotocheck NFC, la tarjeta de pie
   La misma tarjeta ISO girada 90 grados. Reutiliza las capas de textura de
   .card3d (iridiscencia y grano) porque son genéricas de "cara de tarjeta" y
   duplicar el ruido en base64 no compensa. Lo que sí es propio es todo lo que
   cambia al ponerla vertical: la ranura del cordón, la foto y el pie.
   -------------------------------------------------------------------------- */

.fotocheck__eyebrow {
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-muted);
  margin-bottom: 0.9rem;
}

.fotocheck__nota {
  margin-top: clamp(1.4rem, 3vw, 1.9rem);
  font-size: var(--step--1);
  line-height: 1.55;
  color: var(--fg-muted);
  max-width: 46ch;
}

.fotocheck-stage {
  display: grid;
  place-items: center;
  padding-block: clamp(1.5rem, 4vw, 2.5rem);
}

.fotocheck {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1.586;   /* la proporción ISO de siempre, de pie */
  border-radius: 20px;
  overflow: hidden;
  isolation: isolate;
}
/* La postura de reposo y la transición viven en .lanyard .fotocheck, más
   abajo: aquí sobraban y obligaban a cancelarlas dos veces más adelante. */

/* Acabado mate. La tarjeta horizontal del hero lleva una capa iridiscente que
   gira, y aquí no se quiere: un fotocheck se imprime en PVC mate y ese arcoíris
   recorriendo la superficie lo delataba como un render. En su lugar hay un
   plano oscuro casi neutro y dos luces muy tenues: una que entra por arriba a
   la izquierda y un rebote frío en el canto de abajo a la derecha. */
.fotocheck__face {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background:
    radial-gradient(115% 78% at 16% -12%, rgba(255, 255, 255, 0.085), transparent 56%),
    radial-gradient(85% 58% at 112% 110%, rgba(124, 92, 255, 0.12), transparent 60%),
    linear-gradient(168deg, #1b1b24 0%, #15151d 52%, #101016 100%);
  border: 1px solid rgba(255, 255, 255, 0.1);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.07) inset,
    0 44px 84px -38px rgba(0, 0, 0, 0.92),
    0 8px 26px -16px rgba(124, 92, 255, 0.26);
}

/* Reflejo especular: una sola banda en diagonal, al límite de lo visible. Es
   lo que separa un mate premium de un plano plano. */
.fotocheck__sheen {
  position: absolute;
  inset: -25%;
  pointer-events: none;
  background: linear-gradient(
    104deg,
    transparent 30%,
    rgba(255, 255, 255, 0.05) 45%,
    rgba(255, 255, 255, 0.012) 55%,
    transparent 68%
  );
}

/* El grano es lo que de verdad hace que se lea mate: rompe el degradado para
   que la superficie no parezca plástico brillante. Va más fino y más presente
   que el de la tarjeta del hero. */
.fotocheck__grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.42;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3Cfilter id='f'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='1.1' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100' height='100' filter='url(%23f)' opacity='0.55'/%3E%3C/svg%3E");
}

/* Vela inferior, muy suave. Sin iridiscencia que compensar ya no hace falta
   apagar media tarjeta: solo asienta el nombre sobre el fondo. */
.fotocheck__face::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    190deg,
    rgba(10, 10, 18, 0) 38%,
    rgba(10, 10, 18, 0.22) 78%,
    rgba(10, 10, 18, 0.38) 100%
  );
}

/* Ranura troquelada para el cordón */
.fotocheck__slot {
  position: absolute;
  z-index: 2;
  top: clamp(0.75rem, 2.4vw, 1rem);
  left: 50%;
  translate: -50% 0;
  width: 26%;
  height: 7px;
  border-radius: 100px;
  background: #05050a;
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.14),
    0 1px 3px rgba(0, 0, 0, 0.7) inset;
}

.fotocheck__content {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: clamp(0.7rem, 2.2vw, 0.95rem);
  /* El relleno superior deja sitio a la ranura */
  padding: clamp(2rem, 6vw, 2.4rem) clamp(1.1rem, 3.4vw, 1.4rem)
           clamp(1rem, 3vw, 1.25rem);
  color: #fff;
  text-align: center;
}

.fotocheck .nfc-waves { width: 20px; }

/* El retrato va dibujado, como todo lo demás del sitio. Una silueta genérica
   se lee como "aquí va la foto"; una foto de archivo se leería como una
   promesa de que esa persona existe. */
.fotocheck__photo {
  position: relative;
  width: 72%;
  aspect-ratio: 1 / 1.22;
  margin-inline: auto;
  border-radius: 12px;
  overflow: hidden;
  /* Neutro a propósito: el marco de la foto no debe teñir el retrato. */
  background: linear-gradient(168deg, #24242e, #17171e);
  border: 1px solid rgba(255, 255, 255, 0.11);
}

.fotocheck__figure { position: absolute; inset: 0; }

/* El viewBox es 100x122, la misma proporción que el marco, así que la silueta
   lo llena sin deformarse. Hay que declarar la medida porque la regla global
   deja cualquier <svg> suelto en 1em. */
.fotocheck__figure {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.fotocheck__id { margin-top: auto; }

.fotocheck__name {
  font-family: var(--font-display);
  font-size: clamp(1rem, 2.8vw, 1.14rem);
  font-weight: 600;
  letter-spacing: -0.03em;
  line-height: 1.15;
}

.fotocheck__role {
  margin-top: 0.22rem;
  font-size: 0.68rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
}

.fotocheck__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding-top: clamp(0.6rem, 2.2vw, 0.8rem);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}

.fotocheck__code {
  font-size: 0.66rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  color: rgba(255, 255, 255, 0.45);
}

.fotocheck__qr {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: 5px;
  background: #fff;
  padding: 3px;
}

.fotocheck__qr svg { width: 100%; height: 100%; color: #0a0a12; }

/* --------------------------------------------------------------------------
   11c · El cordón del que cuelga el fotocheck
   La cuerda la dibuja main.js nudo a nudo; aquí solo está el escenario, el
   aspecto de la cinta y el estado de reposo. Ese estado de reposo no es un
   adorno: es lo que se ve sin JS y con "reducir movimiento" activado, así que
   tiene que sostenerse solo.
   -------------------------------------------------------------------------- */

.lanyard {
  --card-w: min(62%, 252px);
  --rope: 132px;

  position: relative;
  width: 100%;
  height: clamp(470px, 68vw, 610px);
  /* El gafete se balancea fuera de la caja y no se debe recortar. */
  overflow: visible;
  /* El dedo sigue desplazando la página en vertical; el gesto horizontal es
     el que mueve el gafete. Sin esto, agarrarlo en el móvil deja la página
     clavada, que es justo el fallo que se acaba de arreglar en el resto. */
  touch-action: pan-y;
}

/* Cinta de reposo. Se apaga en cuanto main.js toma el mando y pasa a dibujar
   la cuerda de verdad en el SVG. */
.lanyard__static {
  position: absolute;
  top: -18px;
  left: 50%;
  translate: -50% 0;
  width: 24px;
  height: calc(var(--rope) + 18px);
  border-radius: 2px;
  background:
    linear-gradient(90deg, rgba(255, 255, 255, 0) 32%, rgba(255, 255, 255, 0.13) 40%,
                    rgba(255, 255, 255, 0.13) 60%, rgba(255, 255, 255, 0) 68%),
    #bf1a22;
  /* Mismo desvanecido que la cinta viva, para que el relevo no se note. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 46px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 46px);
}

.lanyard.is-live .lanyard__static { display: none; }

/* El desvanecido de arriba es lo que evita que el cordón parezca colgado de un
   punto en el aire: se difumina contra el fondo y se lee como que sigue fuera
   del encuadre, que es lo que hace un cordón cuando alguien lo lleva puesto. */
.lanyard__band {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 46px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 46px);
}

.lanyard__strap {
  stroke: #bf1a22;
  stroke-width: 24;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* El filo claro por el centro. Es lo que separa una cinta tejida de una raya. */
.lanyard__shine {
  stroke: rgba(255, 255, 255, 0.13);
  stroke-width: 8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* El nombre tejido en la cinta. Va en un peso alto y muy pequeño porque a ese
   tamaño lo que se lee es la textura, no el texto: exactamente igual que en un
   cordón impreso de verdad. */
.lanyard__word {
  font-family: var(--font-display);
  font-size: 8.5px;
  font-weight: 600;
  letter-spacing: 0.3em;
  fill: rgba(255, 255, 255, 0.62);
}

/* Capa dura: la pinza metálica, por delante del gafete.
   Queda oculta hasta que main.js toma el mando, porque es él quien la coloca:
   sin transform los rectángulos se dibujan en sus coordenadas de origen y la
   pinza aparece suelta en una esquina. */
.lanyard__hard {
  position: absolute;
  inset: 0;
  z-index: 4;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  display: none;
}

.lanyard.is-live .lanyard__hard { display: block; }

/* main.js la coloca y la gira con la ranura. */
.lanyard__clip {
  fill: #b9bdc7;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 1;
}

/* El hueco por donde muerde, en oscuro: sin él la pinza es una pastilla gris. */
.lanyard__clip-boca {
  fill: #17171c;
  stroke: rgba(0, 0, 0, 0.5);
  stroke-width: 0.8;
}

.lanyard .fotocheck {
  position: absolute;
  top: calc(var(--rope) - 16px);
  left: 50%;
  width: var(--card-w);
  /* En reposo cuelga casi recto, con la caída mínima de un cordón. */
  transform: translate(-50%, 0) rotate(-1.5deg);
  cursor: grab;
  /* La transición solo sirve para el estado de reposo: main.js la quita al
     tomar el mando, o la tarjeta iría medio fotograma por detrás del cursor. */
  transition: transform 0.7s var(--ease-out);
}

.lanyard.is-live .fotocheck {
  top: 0;
  left: 0;
  transform: none;
  transition: none;
  transform-origin: 50% 50%;
  will-change: transform;
  /* El gafete se queda con el gesto entero, en las dos direcciones. El
     contenedor está en pan-y para que el resto de la zona siga desplazando la
     página, pero encima del gafete eso convertía cualquier tirón hacia abajo
     en scroll: el navegador se quedaba el gesto y mandaba un pointercancel.
     Tirar hacia abajo es justo lo que uno hace con un gafete colgado. */
  touch-action: none;
}

.lanyard.is-held { cursor: grabbing; }
.lanyard.is-held .fotocheck { cursor: grabbing; }

/* Se queda a media altura y bien separada del borde del gafete, porque el
   gafete se mueve: lo que en reposo es un dedo de aire, en pleno balanceo es
   la diferencia entre una etiqueta y un tachón. Anclarla al gafete tampoco
   sirve: viajaría con él y el texto sería ilegible justo cuando hay algo que
   mirar. */
.lanyard__tag {
  top: calc(var(--rope) + 30%);
  left: 50%;
  margin-left: calc(var(--card-w) / 2 + 0.4rem);
}

.lanyard__hint {
  margin-top: -1.4rem;
  text-align: center;
  font-size: var(--step--1);
  color: var(--fg-faint);
}

/* Sin JS no hay nada que arrastrar, así que la instrucción sobra. */
.no-js .lanyard__hint,
.reduced-motion .lanyard__hint { display: none; }

@media (prefers-reduced-motion: reduce) {
  .lanyard__hint { display: none; }
}

/* En el teléfono no hay aire a los lados del gafete, así que la etiqueta pasa
   debajo. Tiene que quedar por debajo del canto del gafete, no sobre él: el
   gafete es más alto que su caja (cuelga fuera a propósito), y una etiqueta
   centrada al ras de la caja le cae justo encima del QR y del nombre. El
   margen inferior es el hueco que la etiqueta ya no tiene dentro. */
@media (max-width: 620px) {
  .lanyard {
    --card-w: min(74%, 232px);
    margin-bottom: 4.5rem;
  }

  .lanyard__tag {
    top: auto;
    bottom: -76px;
    left: 50%;
    margin-left: 0;
    translate: -50% 0;
  }
}

/* --------------------------------------------------------------------------
   11d · El gafete de ejemplo, con el diseño de PeruVet Media
   Es el mismo componente .fotocheck con la marca del cliente encima. Todo lo
   que cambia vive en este bloque, así que cambiar de ejemplo es cambiar de
   modificador y no tocar el componente.
   -------------------------------------------------------------------------- */

.fotocheck--pv {
  --pv-rojo: #e4232b;
}

/* Negro pleno, como la tarjeta impresa: el violeta de la casa aquí estorba.
   Pero negro sobre una sección casi negra desaparece, así que el plano sube
   un par de puntos y el canto se marca con un filo claro: es el contorno, y no
   el relleno, lo que despega la tarjeta del fondo. */
.fotocheck--pv .fotocheck__face {
  background:
    radial-gradient(115% 76% at 20% -12%, rgba(255, 255, 255, 0.1), transparent 56%),
    radial-gradient(80% 54% at 110% 108%, rgba(228, 35, 43, 0.14), transparent 62%),
    linear-gradient(168deg, #24242b 0%, #191920 54%, #121217 100%);
  border-color: rgba(255, 255, 255, 0.17);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.1) inset,
    0 48px 90px -34px rgba(0, 0, 0, 1),
    0 10px 30px -14px rgba(228, 35, 43, 0.34);
}

.fotocheck--pv .fotocheck__content {
  gap: clamp(0.5rem, 1.7vw, 0.7rem);
}

.pv-logo { display: grid; justify-items: center; }

.pv-logo__mark {
  width: 26px;
  height: 29px;
}

.pv-logo__word {
  margin-top: 0.34rem;
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  /* El tracking abre un hueco a la derecha que descentra el bloque. */
  text-indent: 0.2em;
  color: #fff;
}

.pv-logo__word span { color: var(--pv-rojo); }

/* MEDIA entre dos filetes, como en la tarjeta impresa. */
.pv-logo__sub {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  margin-top: 0.2rem;
  font-size: 0.46rem;
  font-weight: 600;
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  color: rgba(255, 255, 255, 0.72);
}

.pv-logo__sub i {
  width: 16px;
  height: 1px;
  background: rgba(255, 255, 255, 0.45);
}

.fotocheck--pv .fotocheck__photo {
  width: 56%;
  aspect-ratio: 1 / 1.1;
  border-radius: 10px;
}

.fotocheck--pv .fotocheck__id { margin-top: 0; }

.fotocheck--pv .fotocheck__name {
  font-size: clamp(0.92rem, 2.6vw, 1.04rem);
  font-weight: 400;
}

.fotocheck--pv .fotocheck__name strong { font-weight: 700; }

.fotocheck--pv .fotocheck__role {
  font-size: 0.58rem;
  letter-spacing: 0.1em;
  color: rgba(255, 255, 255, 0.58);
}

.pv-contact {
  display: grid;
  gap: 0.24rem;
  margin-top: auto;
  text-align: left;
  font-size: 0.58rem;
  line-height: 1.3;
  color: rgba(255, 255, 255, 0.74);
}

.pv-contact li {
  display: flex;
  align-items: center;
  gap: 0.42rem;
  /* Los datos largos no deben partir el gafete. */
  min-width: 0;
}

.pv-contact svg {
  width: 11px;
  height: 11px;
  flex: none;
  color: var(--pv-rojo);
}

.fotocheck--pv .fotocheck__foot { padding-top: 0.55rem; }

.fotocheck--pv .fotocheck__code {
  font-size: 0.56rem;
  letter-spacing: 0.12em;
}

.fotocheck--pv .nfc-waves {
  width: 15px;
  margin-left: auto;
  color: rgba(255, 255, 255, 0.5);
}

.fotocheck--pv .fotocheck__qr {
  width: 30px;
  height: 30px;
}

/* --------------------------------------------------------------------------
   12 · Producto: fidelidad digital
   -------------------------------------------------------------------------- */

.stamps {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0.35rem;
  margin-top: 0.85rem;
}

.stamp {
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1.5px dashed rgba(255, 255, 255, 0.4);
  display: grid;
  place-items: center;
  font-size: 0.62rem;
}

.stamp svg { width: 62%; height: 62%; opacity: 0.85; }

.stamp.is-filled {
  background: rgba(255, 255, 255, 0.95);
  border-style: solid;
  border-color: transparent;
  color: #5a37e0;
}

/* --------------------------------------------------------------------------
   La notificación, copiada de una de verdad

   Esto no es una pastilla con un icono: es el aviso que iOS pone sobre la
   pantalla bloqueada, y tiene que pasar por uno. Cuatro cosas lo deciden, y
   ninguna es el color:

   1 · LA TIPOGRAFÍA ES LA DEL SISTEMA, no la de la marca. Un aviso del
       teléfono no está escrito en Satoshi: está en la letra del aparato. En
       un Mac o un iPhone esta pila resuelve a SF Pro de verdad, que es la
       misma que el visitante tiene delante veinte veces al día. Es lo que más
       hace de las cuatro, y es gratis.

   2 · EL FONDO ES MATERIAL, no pintura. iOS no pone un rectángulo de color:
       pone un cristal que desenfoca y satura lo que hay detrás. Por eso el
       aviso de referencia sale verde azulado: no es verde, es la foto de
       detrás pasada por el cristal.

   3 · LA HORA VA EN EL RENGLÓN DEL TÍTULO, y el texto corre por debajo de
       ella a todo el ancho. Estaba en una tercera columna, que estrechaba el
       mensaje entero para dejarle sitio a una palabra de cinco letras.

   4 · EL DISTINTIVO EN LA ESQUINA DEL ICONO. Cuando un aviso llega desde un
       pase, iOS le pega encima el icono de la cartera. Es el detalle que
       nadie sabría nombrar y que todos reconocen.

   SOBRE LA OPACIDAD, QUE AQUÍ TIENE HISTORIA. El aviso del mazo se dejó casi
   opaco el 13/10/26 porque, translúcido y claro, el de la izquierda salía
   gris sucio sobre el fondo negro y el de la derecha blanco sobre una tarjeta
   clara: dos avisos del mismo sistema de dos colores distintos. El tinte
   ahora es oscuro y pesa 0,74, así que lo de detrás lo tiñe sin llegar a
   mandarlo nunca. Se gana el material y no se pierde la coherencia.

   EL DISTINTIVO NO ES EL ICONO DE APPLE WALLET. Es un montón de tarjetas con
   NUESTRA paleta, el mismo criterio de las pastillas «Añadir a Apple Wallet»:
   el icono de Apple es suyo y no se redibuja.
   -------------------------------------------------------------------------- */

.push {
  position: absolute;
  z-index: 4;
  left: 50%;
  translate: -50% 0;
  bottom: 13%;
  /* El ancho de un aviso de verdad: en un iPhone ocupa casi la pantalla
     entera. A 300 px se leía como una pastilla de la web y no como algo que
     hubiera puesto ahí el sistema. */
  width: min(94%, 364px);

  /* Rejilla y no fila: el cuerpo y la hora comparten celda. La hora se va al
     extremo derecho del primer renglón y el texto pasa por debajo a todo el
     ancho, que es como se reparte un aviso de verdad. Lo que impide que
     choquen es el hueco a la derecha del título, aquí abajo. */
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: 0.7rem;

  /* Medido sobre la captura de un aviso real: el aire de los lados es el 6%
     del ancho y el redondeo el 4%. Con 364 px de ancho salen estos dos. */
  padding: 0.95rem 1.3rem;
  border-radius: 24px;

  font-family: -apple-system, "SF Pro Text", BlinkMacSystemFont, "Segoe UI",
               system-ui, "Helvetica Neue", Arial, sans-serif;
  letter-spacing: -0.012em;

  background: rgba(28, 28, 33, 0.74);
  color: #fff;
  -webkit-backdrop-filter: blur(30px) saturate(1.9);
  backdrop-filter: blur(30px) saturate(1.9);

  /* El canto claro de un cristal de iOS, por dentro, y la sombra corta y
     profunda de algo que flota a un dedo de la pantalla. */
  box-shadow:
    0 0 0 0.5px rgba(255, 255, 255, 0.16) inset,
    0 18px 34px -16px rgba(0, 0, 0, 0.78);
}

.push__icon {
  position: relative;
  grid-area: 1 / 1 / 2 / 2;
  /* Y el icono, el 9,7% del ancho. */
  width: 2.3rem;
  height: 2.3rem;
  flex: none;
  /* La esquina de un icono de app, que es casi un cuarto de su lado. Con los
     8px de antes parecía un botón. */
  border-radius: 24%;

  /* PLANO Y OSCURO, NO UN DEGRADADO.

     Un logo de negocio es un color sólido con una marca encima. El degradado
     violeta que había aquí es un recurso de página web: les ponía a todos los
     negocios el mismo icono, y además el nuestro, así que los avisos parecían
     salir de la misma app en vez de cada uno de su local. Cada variante le
     pone debajo el color del suyo. */
  background: var(--push-marca, #2f3338);
  display: grid;
  place-items: center;
  color: #fff;
}

/* La marca, más gruesa. Los iconos de esta hoja están dibujados con trazo de
   1,9 para leerse a tamaño de texto; dentro de un icono de app, a ese grosor
   se ven como un alambre. Un logo tiene peso. */
.push__icon svg {
  width: 50%;
  height: 50%;
  stroke-width: 2.6;
}

/* EL COLOR DE CADA NEGOCIO.

   Va aquí y no en el HTML porque cada hueco ya sabe qué aviso le toca, y
   porque el HTML de /fidelidad está a medias en otra mano. El día que un
   aviso cambie de negocio, se cambia su color en una línea.

   El verde del gimnasio es el mismo de su tarjeta en /demo. El marrón del
   café no sale de su tarjeta, que es violeta: lo que esto imita es el LOGO
   del negocio, no el color de su pase, y el logo de una cafetería es marrón.

   El de /soporte-nfc es nuestro de verdad (lo dispara una tarjeta NFC
   nuestra y el título dice tarjetacloud.com), así que lleva el violeta de la
   marca, plano. */
.push--mazo-izq { --push-marca: #4a3225; }
.push--mazo-der { --push-marca: #1f4f4a; }
.push--ios     { --push-marca: #4a3225; }
.push--suelto  { --push-marca: #5b3fd1; }

/* El montón de tarjetas en la esquina, SOLO donde el aviso sale de un pase.

   En iOS ese distintivo quiere decir «esto te lo manda una tarjeta de tu
   cartera», así que va en los avisos de fidelidad y no en el de /soporte-nfc,
   que lo dispara una tarjeta NFC al acercarla y no tiene pase ninguno
   detrás. Ponerlo ahí sería un detalle bonito diciendo algo falso.

   Todo en porcentajes del propio icono, que es lo único que lo mantiene en su
   sitio en las dos variantes: cada una le da al icono un tamaño distinto y
   ninguna tiene que acordarse de esto. */
.push--mazo .push__icon::after,
.push--ios .push__icon::after {
  content: "";
  position: absolute;
  /* Abajo a la derecha y desbordando un poco, como en el aviso de verdad:
     el distintivo se monta sobre la esquina del icono, no se mete dentro. */
  right: -13%;
  bottom: -9%;
  width: 48%;
  aspect-ratio: 1;
  border-radius: 27%;
  background: #fff
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Crect x='4.5' y='3.2' width='15' height='3.6' rx='1.5' fill='%23ff7a59'/%3E%3Crect x='3.4' y='7' width='17.2' height='3.6' rx='1.5' fill='%234ade80'/%3E%3Crect x='2.6' y='10.8' width='18.8' height='3.6' rx='1.5' fill='%2322d3ee'/%3E%3Crect x='2' y='14.4' width='20' height='6.4' rx='2.1' fill='%23a690ff'/%3E%3C/svg%3E")
    center / 74% no-repeat;
  box-shadow: 0 0.05em 0.18em rgba(0, 0, 0, 0.4);
}

.push__body {
  grid-area: 1 / 2 / 2 / 3;
  /* A la izquierda, y escrito aunque parezca obvio: el aviso de /soporte-nfc
     vive dentro de una figura centrada y heredaba ese centrado. Un aviso del
     sistema no se centra nunca, ni aunque esté centrado lo que lo rodea. */
  text-align: left;
  /* El 3,9% del ancho, que es lo que mide la letra en el aviso de verdad, y
     el renglón corto de iOS: 1,25 y no 1,33, que es aire de página web. */
  font-size: 0.89rem;
  line-height: 1.25;
  color: rgba(255, 255, 255, 0.94);
}

/* Mismo tamaño que el mensaje y más peso, como en iOS: el título no es un
   encabezado, es el nombre de quien escribe. El hueco de la derecha es el
   que le deja sitio a la hora sin estrechar el mensaje de abajo. */
.push__body b {
  display: block;
  font-size: 1em;
  font-weight: 600;
  letter-spacing: -0.018em;
  margin-bottom: 0.06em;
  padding-right: 3.1em;
  color: #fff;
}

.push__time {
  grid-area: 1 / 2 / 2 / 3;
  justify-self: end;
  align-self: start;
  margin-left: 0;
  font-size: 0.8rem;
  line-height: 1.25;
  color: rgba(255, 255, 255, 0.52);
}

/* --------------------------------------------------------------------------
   12a · Foto del hero (portada de fidelidad)

   Reemplaza a la baraja de teléfonos el 10/10/26. La foto original traía
   fondo negro sólido de borde a borde; el mismo día se le quitó ese fondo
   (clave de negro con pluma en el pelo y en el aro, no un recorte de un solo
   objeto, para no perder los teléfonos ni los íconos) y quedó en PNG/WebP
   con alpha. Por eso no lleva caja ni borde: flota directo sobre el halo del
   hero con un drop-shadow, como una pieza suelta y no como una foto pegada.
   -------------------------------------------------------------------------- */

.hero__foto {
  position: relative;
  filter:
    drop-shadow(0 1.6em 2.2em rgba(0, 0, 0, 0.55))
    drop-shadow(0 0 2.4em rgba(124, 92, 255, 0.28));
}

.hero__foto img {
  display: block;
  width: 100%;
  height: auto;
}

/* Un poco más grande que su columna a partir de aquí: desborda hacia la
   derecha, que es donde el hero tiene overflow:hidden para recortarla sin
   que empuje el texto ni se note el corte contra el glow. En una sola
   columna (hero__inner a 1fr) no hay sitio de sobra, así que se queda al
   100%. 112% y no más: a 130/155% (probado el 10/10/26) se veía demasiado
   grande para el resto del hero. */
@media (min-width: 1001px) {
  /* max-width: none porque el reset global (sección 1, `img { max-width:
     100% }`) si no, le gana al ancho de abajo y la imagen se queda del
     mismo tamaño que antes sin avisar de nada. */
  .hero__foto img { width: 112%; max-width: none; }
}

/* --------------------------------------------------------------------------
   12b · Baraja de teléfonos (portada de fidelidad)   [ARCHIVADA el 10/10/26]

   Ya no se dibuja: desde el 10/10/26 el hero usa una foto (`.hero__foto`, acá
   arriba) y este bloque quedó entero dentro de un <template> en fidelidad.html
   (id="baraja-fidelidad-archivada"), inerte y sin que nada lo pinte. La regla
   de abajo sigue aquí tal cual porque `.pase-wallet` la comparte con /demo, y
   porque el día que la baraja vuelva a usarse —otra sección, otro proyecto, o
   un simple reviertan esto— no hay que reescribir ni una línea.

   Tres teléfonos con la hoja del pase abierta, tal como la ve el cliente, y
   las dos pastillas de billetera cruzadas por delante.

   Todo lo de dentro mide en em sobre una sola variable, --tel, que es el ancho
   de un teléfono. Cambiando esa cifra la baraja entera crece o encoge sin que
   se descuadre nada: por eso aquí abajo no hay ni un rem ni un píxel suelto.
   -------------------------------------------------------------------------- */

.baraja {
  /* El ancho de un teléfono, y con él el de todo lo demás. El tope es lo que
     cabe en la columna del visual sin robarle sitio al título: por encima de
     esto, "ya no se pierde." se parte en dos líneas. */
  --tel: clamp(150px, 16.3vw, 235px);
  position: relative;
  /* Los de los lados se escalonan con translate, que no ocupa sitio en la
     maquetación: el hueco por arriba y por abajo hay que reservarlo aquí. */
  padding-block: calc(var(--tel) * 0.17);
}

.baraja__fila {
  display: flex;
  justify-content: center;
}

.baraja__tel {
  position: relative;
  width: var(--tel);
  flex: none;
}

/* El orden de apilado sube de izquierda a derecha, como una baraja abierta con
   la mano: cada teléfono tapa un trozo del anterior y el último se ve entero.
   Por eso el pase de sellos es el tercero y no el del medio. */
.baraja__tel--1 { z-index: 1; translate: 0 -7%; }
.baraja__tel--2 { z-index: 2; margin-inline: calc(var(--tel) * -0.4); }
.baraja__tel--3 { z-index: 3; translate: 0 7%; }

/* El aparato.

   Son tres capas, y son las tres las que hacen que se lea como un teléfono y
   no como un rectángulo con una pantalla dentro:

   1. El canto de titanio. Es una banda clara que recorre los dos lados, porque
      el metal del borde está curvado y devuelve la luz justo ahí. Un marco de
      un solo gris se ve plano por mucho brillo que le pongas.
   2. El marco negro de la pantalla, que lo pone la propia hoja con una sombra
      sin desenfoque. Va aparte del canto a propósito: en el aparato de verdad
      son dos piezas distintas y se nota.
   3. El cristal, con su reflejo en diagonal.

   Y por fuera asoman los botones. Con el solape de la baraja solo se ven los
   del primer teléfono por la izquierda y el de encendido del tercero por la
   derecha, que es exactamente lo que se ve de un teléfono apoyado en otro. */
.tel {
  position: relative;
  aspect-ratio: 9 / 18.6;
  border-radius: calc(var(--tel) * 0.158);
  padding: calc(var(--tel) * 0.036);
  /* La medida de la que cuelga toda la letra de dentro. */
  font-size: calc(var(--tel) * 0.062);
  /* El brillo del canto es una línea, no una banda: ocupa el 1,2% del ancho y
     a partir de ahí cae a oscuro antes de llegar al marco negro. Más ancho y
     el aparato deja de parecer titanio y empieza a parecer plástico claro. */
  background:
    linear-gradient(176deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0) 12%),
    linear-gradient(90deg,
      #c2c2d4 0%, #74748c 1.2%, #2d2d39 3.4%, #1e1e27 22%,
      #17171e 50%,
      #1e1e27 78%, #2d2d39 96.6%, #74748c 98.8%, #c2c2d4 100%);
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.2),
    0 40px 60px -32px rgba(0, 0, 0, 0.95),
    0 0 70px -28px rgba(124, 92, 255, 0.5);
}

/* Izquierda: el botón de acción arriba y los dos de volumen debajo, dibujados
   con tres capas de fondo en vez de con tres elementos vacíos. El degradado va
   oscuro por fuera y claro donde toca el canto, que es como los ve la luz: al
   revés se abre una costura negra entre el botón y el aparato. */
.tel::before {
  content: "";
  position: absolute;
  left: calc(var(--tel) * -0.012);
  top: 17%;
  width: calc(var(--tel) * 0.019);
  height: 27%;
  border-radius: calc(var(--tel) * 0.005);
  background:
    linear-gradient(90deg, #3f3f52, #6e6e88 45%, #a9a9c2 100%) no-repeat 0 0 / 100% 16%,
    linear-gradient(90deg, #3f3f52, #6e6e88 45%, #a9a9c2 100%) no-repeat 0 50% / 100% 25%,
    linear-gradient(90deg, #3f3f52, #6e6e88 45%, #a9a9c2 100%) no-repeat 0 100% / 100% 25%;
}

/* Derecha: encendido. */
.tel::after {
  content: "";
  position: absolute;
  right: calc(var(--tel) * -0.012);
  top: 24%;
  width: calc(var(--tel) * 0.019);
  height: 12%;
  border-radius: calc(var(--tel) * 0.005);
  background: linear-gradient(270deg, #3f3f52, #6e6e88 45%, #a9a9c2 100%);
}

/* Un botón solo se dibuja si su canto se ve. Con el solape de la baraja, el
   costado izquierdo de los dos últimos y el derecho de los dos primeros caen
   debajo del teléfono vecino: dibujarlos ahí no los enseña, los pega encima de
   la pantalla de al lado. */
.baraja__tel--2 .tel::before,
.baraja__tel--3 .tel::before,
.baraja__tel--1 .tel::after,
.baraja__tel--2 .tel::after { content: none; }

.tel__isla {
  position: absolute;
  z-index: 3;
  top: 1.08em;
  left: 50%;
  translate: -50% 0;
  width: 30%;
  height: 1.3em;
  border-radius: 100px;
  background: #07070c;
}

/* La lente, que es lo que delata a una isla dibujada sin ella: en el aparato
   de verdad no es una pastilla lisa, tiene el ojo pegado a la derecha. */
.tel__isla::after {
  content: "";
  position: absolute;
  right: 11%;
  top: 50%;
  translate: 0 -50%;
  width: 0.62em;
  height: 0.62em;
  border-radius: 50%;
  background: radial-gradient(circle at 34% 30%, #33486b 0%, #111722 55%, #05070c 100%);
  box-shadow: 0 0 0 0.5px rgba(120, 155, 210, 0.3);
}

/* La pantalla es la hoja que iOS abre al tocar un pase: fondo claro, y el pase
   flotando encima. Va en claro a propósito, porque contra el fondo oscuro de
   la portada es lo único que se lee de lejos.

   El box-shadow sin desenfoque es el marco negro del panel. Se dibuja desde
   aquí y no con más relleno en .tel para que el canto de titanio quede por
   fuera y el negro por dentro, que es el orden que tienen de verdad. */
.tel__hoja {
  position: relative;
  height: 100%;
  border-radius: calc(var(--tel) * 0.12);
  overflow: hidden;
  background: #f1f0f4;
  color: var(--ink);
  box-shadow: 0 0 0 calc(var(--tel) * 0.019) #07070c;
  padding: 0.3em 0.72em 1.3em;
  display: flex;
  flex-direction: column;
}

/* El reflejo del cristal. Muy suave y en diagonal: lo justo para que la
   pantalla no parezca papel pegado al marco. */
.tel__hoja::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: linear-gradient(112deg,
    rgba(255, 255, 255, 0.3) 0%,
    rgba(255, 255, 255, 0.07) 11%,
    rgba(255, 255, 255, 0) 22%,
    rgba(255, 255, 255, 0) 100%);
}

/* La barra de inicio. */
.tel__hoja::before {
  content: "";
  position: absolute;
  z-index: 4;
  bottom: 0.45em;
  left: 50%;
  translate: -50% 0;
  width: 31%;
  height: 0.22em;
  border-radius: 100px;
  background: rgba(10, 10, 18, 0.4);
}

.tel__barra {
  height: 1.7em;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-size: 0.58em;
  font-weight: 700;
  letter-spacing: 0.01em;
  padding-inline: 0.75em;
}

.tel__estado {
  width: 4.6em;
  height: 1.2em;
  opacity: 0.88;
}

.tel__acciones {
  display: flex;
  gap: 0.45em;
  margin-top: 0.6em;
}

/* La de cerrar se va sola a la izquierda; compartir y los tres puntos se
   quedan juntos a la derecha, como en la hoja de verdad. */
.tel__btn:first-child { margin-right: auto; }

.tel__btn {
  width: 1.75em;
  height: 1.75em;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: rgba(10, 10, 18, 0.07);
  color: rgba(10, 10, 18, 0.62);
  box-shadow: 0 0.06em 0.12em rgba(10, 10, 18, 0.06);
}

.tel__btn svg { width: 0.82em; height: 0.82em; }

/* Los puntos del carrusel de pases, dibujados con un degradado que se repite
   en vez de con nueve elementos vacíos. El del medio, más oscuro, es el pase
   que está abierto. */
.tel__puntos {
  position: relative;
  margin: auto auto 0;
  width: 4.5em;
  height: 0.36em;
  color: rgba(10, 10, 18, 0.22);
  background-image: radial-gradient(circle at 50% 50%, currentColor 44%, transparent 46%);
  background-size: 0.5em 0.36em;
  background-repeat: repeat-x;
}

.tel__puntos::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 0;
  translate: -50% 0;
  width: 0.36em;
  height: 0.36em;
  border-radius: 50%;
  background: rgba(10, 10, 18, 0.5);
}

/* El pase dentro de la hoja. Es el único que queda: hasta el 5/10/26 había
   otro, `.pass`, estilizado y sobre fondo oscuro, para la sección de abajo.
   Ahora esa sección usa este mismo teléfono y aquel se borró entero, con su
   `.phone`. Lo único que compartían eran los sellos, y esos siguen vivos. */
.pase-wallet {
  margin-top: 0.9em;
  border-radius: 0.95em;
  overflow: hidden;
  background: var(--pase-bg);
  color: var(--pase-fg);
  box-shadow: 0 0.55em 1.3em -0.6em rgba(10, 10, 18, 0.5);
}

.pase-wallet--morado {
  --pase-bg: linear-gradient(158deg, #8a6cff, #5a37e0 58%, #3d2199);
  --pase-fg: #fff;
  --pase-tira: rgba(255, 255, 255, 0.1);
  --pase-linea: rgba(255, 255, 255, 0.22);
}

.pase-wallet--grafito {
  --pase-bg: linear-gradient(158deg, #33333f, #191922 60%, #101018);
  --pase-fg: #fff;
  --pase-tira: rgba(255, 255, 255, 0.07);
  --pase-linea: rgba(255, 255, 255, 0.16);
}

/* El único claro de los tres, y el único que necesita un canto: sobre la hoja
   gris del teléfono, sin esa línea el pase no se sabe dónde empieza. */
.pase-wallet--crema {
  --pase-bg: linear-gradient(158deg, #fbf9f4, #efece4);
  --pase-fg: var(--ink);
  --pase-tira: rgba(10, 10, 18, 0.05);
  --pase-linea: rgba(10, 10, 18, 0.12);
  box-shadow:
    0 0 0 1px rgba(10, 10, 18, 0.08) inset,
    0 0.55em 1.3em -0.6em rgba(10, 10, 18, 0.5);
}

.pase-wallet__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5em;
  padding: 0.7em 0.75em 0.6em;
}

.pase-wallet__marca {
  font-family: var(--font-display);
  font-size: 0.78em;
  font-weight: 600;
  letter-spacing: -0.025em;
  line-height: 1.1;
}

.pase-wallet__valor {
  flex: none;
  text-align: right;
  line-height: 1.1;
}

.pase-wallet__valor i {
  display: block;
  font-style: normal;
  font-size: 0.42em;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  opacity: 0.6;
}

.pase-wallet__valor b {
  font-family: var(--font-display);
  font-size: 0.7em;
  font-weight: 600;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* La franja del medio: es la parte que cada negocio usa a su manera, y por eso
   lleva una cosa distinta en cada uno de los tres. */
.pase-wallet__tira {
  padding: 0.6em 0.75em 0.75em;
  background: var(--pase-tira);
}

.pase-wallet__cifra {
  font-family: var(--font-display);
  font-size: 1.5em;
  font-weight: 600;
  line-height: 0.95;
  letter-spacing: -0.04em;
}

.pase-wallet__nota {
  margin-top: 0.35em;
  font-size: 0.5em;
  line-height: 1.3;
  opacity: 0.72;
}

.barra-pase {
  height: 0.42em;
  border-radius: 100px;
  background: var(--pase-linea);
  overflow: hidden;
}

.barra-pase span {
  display: block;
  height: 100%;
  border-radius: inherit;
  background: currentColor;
  opacity: 0.85;
}

/* Los sellos son los mismos del pase oscuro, solo que aquí miden en em para
   seguir a --tel. */
.pase-wallet .stamps {
  margin-top: 0;
  gap: 0.3em;
}

.pase-wallet .stamp { border-width: 1px; }

.pase-wallet__campos {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.6em;
  padding: 0.65em 0.75em 0;
}

.pase-wallet__campo:last-child { text-align: right; }

.pase-wallet__campo i {
  display: block;
  font-style: normal;
  font-size: 0.42em;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  opacity: 0.55;
  margin-bottom: 0.25em;
}

.pase-wallet__campo b {
  font-size: 0.56em;
  font-weight: 600;
  line-height: 1.15;
}

/* El QR va sobre blanco aunque el pase sea oscuro: es lo que hace cualquier
   billetera, porque un lector necesita el contraste. */
.pase-wallet__qr {
  width: 62%;
  margin: 0.75em auto 0.85em;
  padding: 0.4em 0.3em 0.28em;
  border-radius: 0.3em;
  background: #fff;
  display: grid;
  justify-items: center;
  gap: 0.22em;
}

.pase-wallet__qr svg {
  width: 74%;
  height: auto;
  aspect-ratio: 1;
  color: #0a0a12;
}

.pase-wallet__qr span {
  font-size: 0.4em;
  color: rgba(10, 10, 18, 0.5);
}

/* Las dos pastillas de billetera.
   No son el badge oficial de Apple ni el de Google: esos son suyos, tienen sus
   propias normas de uso y no se redibujan. Esto es una pastilla nuestra, con
   nuestra paleta, que nombra a cada billetera por su nombre. */
/* Ocupa todo el ancho y centra por dentro, en vez de colgar del 50%: un
   absoluto que arranca en la mitad solo puede medir la otra mitad, y las dos
   pastillas se partían en dos líneas para caber. */
.baraja__wallets {
  position: absolute;
  z-index: 5;
  left: 0;
  right: 0;
  bottom: calc(var(--tel) * 0.1);
  display: flex;
  justify-content: center;
  gap: calc(var(--tel) * 0.05);
}

.pastilla-wallet {
  display: inline-flex;
  align-items: center;
  gap: 0.55em;
  padding: 0.55em 0.8em;
  border-radius: 0.6em;
  /* Sigue a --tel, pero con suelo: es lo único de la baraja que hay que poder
     leer de verdad, y por debajo de 11px "Añadir a" deja de ser una palabra. */
  font-size: clamp(11px, calc(var(--tel) * 0.082), 17px);
  line-height: 1;
  background: #08080e;
  color: #fff;
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, 0.09),
    0 0.7em 1.6em -0.5em rgba(0, 0, 0, 0.9);
}

.pastilla-wallet svg {
  width: 1.55em;
  height: 1.15em;
  flex: none;
}

.pastilla-wallet i {
  display: block;
  font-style: normal;
  font-size: 0.52em;
  opacity: 0.82;
}

.pastilla-wallet b {
  display: block;
  margin-top: 0.15em;
  font-family: var(--font-display);
  font-size: 0.88em;
  font-weight: 600;
  letter-spacing: -0.025em;
}

.pastilla-wallet--apple  { --c1: #ff7a59; --c2: #4ade80; --c3: #22d3ee; --c4: #e7e3d8; }
.pastilla-wallet--google { --c1: #22d3ee; --c2: #ff7a59; --c3: #4ade80; --c4: #a690ff; }

/* El mismo aparato, pero solo.

   La sección «Todo lo que hace que un cliente vuelva» llevaba su propio
   teléfono dibujado aparte: un rectángulo con muesca, sin barra de estado y
   sin los botones del canto. Al lado del de la portada se notaba que eran dos
   dibujos distintos del mismo objeto, y el de abajo era el pobre. Ahora es
   este, que es el bueno.

   `.baraja` reparte su `--tel` entre tres teléfonos en fila; aquí cabe uno, y
   puede ser bastante más grande. Lo único que hace falta es el ancho y el
   `position: relative`, que es de lo que cuelga el aviso flotante. La relación
   de lados se repite para que la altura sea firme antes de maquetar: el aviso
   se coloca con un `bottom` en tanto por ciento y necesita contra qué medirlo. */
.tel-solo {
  --tel: clamp(200px, 23vw, 296px);
  position: relative;
  width: var(--tel);
  aspect-ratio: 9 / 18.6;
  margin-inline: auto;
}

/* El aviso, como lo pinta iOS: una banda que baja por debajo de la isla, no
   una tarjeta pegada al borde de abajo. Dos motivos, y los dos importan.

   Dónde. Abajo se comía el QR del pase. Arriba tapa la fila de botones de la
   hoja, que es exactamente lo que tapa un aviso en el teléfono de verdad, y
   deja el pase entero a la vista.

   De qué tamaño. El `.push` suelto mide en rem, o sea en la letra de la
   página, así que dentro de un teléfono de 296 px salía gigante al lado de la
   letra del pase. Aquí cuelga de `--tel`, igual que todo lo demás del
   aparato, y crece y encoge con él. */
/* Esta vive DENTRO del teléfono, sobre una hoja clara, y por eso es la única
   clara de las cuatro. No es una excepción nuestra: iOS pinta el aviso del
   color del tema que tenga el aparato, y una notificación oscura sobre una
   pantalla blanca se vería pegada encima, no dentro. */
.push--ios {
  top: 7.4%;
  bottom: auto;
  /* Casi de borde a borde, como en la pantalla bloqueada de verdad. */
  width: 92%;
  font-size: calc(var(--tel) * 0.062);
  gap: 0.55em;
  padding: 0.6em 0.68em;
  border-radius: 1.15em;
  background: rgba(250, 250, 252, 0.78);
  color: var(--ink);
  box-shadow:
    0 0 0 0.5px rgba(10, 10, 18, 0.07) inset,
    0 0.5em 1.1em -0.45em rgba(10, 10, 18, 0.6);
}

.push--ios .push__icon {
  width: 1.9em;
  height: 1.9em;
  border-radius: 24%;
}

.push--ios .push__body { font-size: 0.62em; line-height: 1.3; color: rgba(10, 10, 18, 0.9); }
.push--ios .push__body b { color: var(--ink); margin-bottom: 0.04em; }
.push--ios .push__time { font-size: 0.56em; color: rgba(10, 10, 18, 0.42); }

/* El pase arranca más abajo que en la portada, para que el aviso no le caiga
   encima de la cabecera: con el número de sellos tapado, la imagen deja de
   contar lo único que tiene que contar. De paso llena el hueco blanco que
   quedaba entre el pase y la raya de inicio. */
.tel-solo .pase-wallet { margin-top: 2.5em; }

/* En una sola columna la baraja deja de competir por el ancho con el texto y
   puede crecer: el ancho lo manda la pantalla, no la mitad de la pantalla. */
@media (max-width: 1000px) {
  .baraja { --tel: clamp(130px, 40vw, 250px); }
  /* El suelto va al revés: en una columna se queda con el texto encima y
     debajo, y a 23vw se iría de madre. */
  .tel-solo { --tel: clamp(200px, 56vw, 270px); }
}

/* --------------------------------------------------------------------------
   12c · Mazo de pases (portada de fidelidad)   [EN USO desde el 13/10/26]

   Reemplaza a la foto de 12a. El `.hero__copy` pasa a ir centrado y solo, y
   el mazo va de hermano del `.shell`, no de hijo: así desborda el ancho del
   texto y lo recorta el overflow:hidden de `.hero`, que es lo que le da el
   aire de "sigue hacia los bordes" en vez de quedarse metido en la columna.

   Todo cuelga de una sola variable, --pase-ancho, que hereda `.pase-demo`
   de assets/css/style.css sección 23 (la demo de /demo): por eso aquí no
   hace falta repetir ni el ancho ni el tamaño de letra del pase, solo el
   valor de la variable. La segunda, --mazo-ancho, es lo ancho que le queda
   al abanico una vez montado (3,32 pases: cada ranura solapa a la siguiente
   un 44% de su ancho), y es de la que se cuelgan los dos avisos.

   TODO LO QUE SEPARA UN BLOQUE DEL SIGUIENTE MIDE EN VH, NO EN VW, Y ES A
   PROPÓSITO. Esta portada es la única de la casa que tiene que entrar de una
   sola vez (arriba el texto, abajo las tarjetas). Midiendo en ancho, en una
   pantalla de portátil de 900 px de alto el mazo se quedaba entero por
   debajo del borde: se veía el titular, los botones y un dedo de tarjeta, y
   había que bajar para enterarse de qué va el producto.
   -------------------------------------------------------------------------- */

.hero--mazo {
  flex-direction: column;
  justify-content: center;
  /* Más que antes (era 4,75rem/8,5vh/7,5rem, después 6rem/11vh/9rem): con el
     titular centrado en vertical junto con todo lo demás, el piso que pone
     el padding es lo único que garantiza aire sobre el titular cuando el
     bloque entero casi no entra en el alto de la pantalla. Sin este piso,
     apenas carga la página el titular queda pegado a la barra de
     navegación. */
  padding-top: clamp(6.75rem, 13vh, 10rem);
  padding-bottom: clamp(1rem, 3vh, 2.5rem);
}

.hero--mazo .hero__inner { display: block; width: 100%; }

/* El ancho lo pone cada bloque por su cuenta y no la columna entera: así la
   fila de garantías puede ser más ancha que el párrafo y entrar en una sola
   línea, que es lo que arregla el «Sin permanencia» descolgado y solo en un
   segundo renglón. */
.hero--mazo .hero__copy {
  max-width: 48rem;
  margin-inline: auto;
  text-align: center;
}

/* El titular mide también en alto. Con el tamaño de siempre (hasta 5,25rem)
   en una pantalla de 900 px el titular solo se comía la mitad de la portada
   y empujaba el mazo fuera de la vista. */
.hero--mazo .hero__title {
  margin-inline: auto;
  font-size: min(var(--step-6), 6.8vh);
}

.hero--mazo .hero__lede {
  margin-inline: auto;
  margin-top: clamp(1rem, 2.2vh, 2rem);
}

.hero--mazo .hero__actions {
  margin-top: clamp(1.4rem, 3.2vh, 2.75rem);
  justify-content: center;
}

.hero--mazo .hero__trust {
  margin-top: clamp(1.2rem, 2.8vh, 2.4rem);
  justify-content: center;
}

/* --------------------------------------------------------------------------
   12c-ter · Fondo del hero de portada   [EN USO desde el 17/10/26]

   D-46 quitó la tarjeta 3D y las dos columnas; D-47 quitó la rejilla y la
   aurora de dos colores (.hero__grid/.hero__aurora, sección 07 más arriba),
   que siguen en /fidelidad y /tarjetas-nfc pero acá se reemplazaron a
   pedido directo: "muy ai slop" fue la frase exacta.

   D-48 le puso en su lugar un fondo propio: GradientWaves, un fondo de olas
   en WebGL2 (assets/js/vendor/gradient-waves.js, port a JS plano de un
   componente de React que trajo el cliente). .hero__waves es el contenedor
   que ese script monta con un <canvas>; acá solo se fija su posición
   (detrás de todo, a pantalla completa) y el color de los dos textos
   genéricos que trae el propio componente
   (.gradient-waves-container/.gradient-waves-canvas). Los colores de las
   olas en sí (horizonte, cuerpo, cresta) los pone la llamada a
   `new GradientWaves(...)` en main.js, no CSS: son uniforms del shader. */
.gradient-waves-container {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
}

.gradient-waves-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* Pisa el position:relative de .gradient-waves-container de arriba (misma
   especificidad, una sola clase cada regla: sin esto el orden del archivo
   decide y el div se queda en flujo normal, sin alto, y el canvas de abajo
   mide 0 x 0). Dos clases en el selector le ganan a una sola a propósito:
   que el contenedor del hero vaya absoluto y a pantalla completa no es el
   comportamiento genérico del componente, es lo que pide ESTE hero. */
.hero__waves.gradient-waves-container {
  position: absolute;
  inset: 0;
  width: auto;
  height: auto;
  z-index: 0;
}

/* El velo de lectura. Con las olas animadas de fondo, el titular necesita
   más respaldo que la vieja viñeta radial (que oscurecía los BORDES y
   dejaba el centro, justo donde va el texto, más claro: al revés de lo que
   hace falta acá). Este es un degradado de arriba hacia abajo: oscuro
   donde está el titular, se abre en la franja donde más se nota el agua, y
   vuelve a cerrar antes del teléfono para que no compita con su pantalla
   blanca. */
.hero--portada .hero__vignette {
  background: linear-gradient(180deg,
    rgba(10, 10, 18, 0.88) 0%,
    rgba(10, 10, 18, 0.6) 28%,
    rgba(10, 10, 18, 0.22) 52%,
    rgba(10, 10, 18, 0.5) 100%);
}

/* --------------------------------------------------------------------------
   12c-bis · Teléfono del hero de portada   [EN USO desde el 17/10/26]

   El hero de la portada (ver D-46 en branding/decisiones-de-marca.md) usa
   el mismo .tel-solo de Fidelidad, sin tocar su CSS: acá solo se fija
   cuánto mide (--tel). Ya no lleva un halo de color propio debajo (D-48):
   con las olas de fondo, un segundo foco de color encima solo competía por
   la mirada; la sombra lisa de abajo alcanza para asentarlo en el piso. */
.hero__visual--tel {
  --tel: clamp(220px, 22vw, 300px);
  position: relative;
  z-index: 2;
  margin-top: clamp(2.5rem, 6vh, 3.75rem);
}

.hero__visual--tel::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: 50%;
  bottom: 6%;
  translate: -50% 0;
  width: calc(var(--tel) * 1.3);
  height: calc(var(--tel) * 0.3);
  border-radius: 50%;
  background: radial-gradient(ellipse at center,
    rgba(0, 0, 0, 0.55) 0%,
    transparent 72%);
  filter: blur(18px);
  pointer-events: none;
}

/* El precio cuelga del teléfono, no de la pantalla: con bottom/right en
   porcentaje contra .tel-solo (que ya es position:relative) se achica y
   agranda junto con --tel sin ningún media query aparte. */
.hero__visual--tel .float-tag--precio {
  bottom: 9%;
  right: -6%;
}

@media (max-width: 480px) {
  .hero__visual--tel .float-tag--precio { right: -1%; }
}

.hero__mazo {
  position: relative;
  z-index: 2;
  width: 100%;
  /* Este hueco es más grande de lo que parece y tiene que serlo: la tarjeta
     del medio va escalada 1,16 y subida un 6%, así que su borde de arriba
     asoma unos 70 px por encima de donde empieza la fila. Con el hueco justo,
     la fila de garantías termina apoyada en el canto de la tarjeta. */
  margin-top: clamp(4rem, 8.6vh, 5.5rem);
  --pase-ancho: min(clamp(172px, 19vw, 330px), 27.5vh);
  --mazo-ancho: calc(var(--pase-ancho) * 3.32);
}

/* El halo bajo el mazo. Sin él las tarjetas flotan sobre un fondo liso y el
   abanico se lee como cinco recortes pegados; con él tienen suelo. Va detrás
   de las tarjetas pero dentro del propio mazo, así que no toca a los dos
   halos grandes del hero. Lleva una parada intermedia (antes era un solo
   color a transparente) para que tenga un núcleo cálido debajo de la tarjeta
   del medio y no un charco de color parejo. */
.hero__mazo::before {
  content: "";
  position: absolute;
  z-index: -1;
  left: 50%;
  bottom: 2%;
  translate: -50% 0;
  width: min(calc(var(--mazo-ancho) * 1.2), 104%);
  height: calc(var(--pase-ancho) * 1.05);
  border-radius: 50%;
  background: radial-gradient(ellipse at center,
    rgba(166, 144, 255, 0.4) 0%,
    rgba(124, 92, 255, 0.22) 38%,
    transparent 72%);
  filter: blur(36px);
  pointer-events: none;
}

.mazo__fila {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  padding-block: clamp(1.2rem, 2.4vh, 2.6rem);
}

.mazo__ranura {
  position: relative;
  margin-inline: calc(var(--pase-ancho) * -0.22);
  --dur: calc(12s + var(--i, 0) * 1.6s);
  filter: drop-shadow(0 1.3em 2.1em rgba(0, 0, 0, 0.55));
  /* La hondura para que el volteo de abajo se lea como una tarjeta que gira
     en el aire y no como un pase que se achica de golpe. */
  perspective: 1400px;
}

/* El de en medio va delante y más grande, igual que el pase de sellos en la
   baraja archivada de 12b: es el que la gente viene buscando (D-21). Los de
   los costados se van haciendo más chicos y girando más, como un mazo que se
   abre en abanico sobre la mesa, pero sin achicarse demasiado: el pedido fue
   que el mazo entero se vea grande. */
.mazo__ranura:nth-child(1) { z-index: 1; transform: rotate(-16deg) translateY(9%) scale(0.86); }
.mazo__ranura:nth-child(2) { z-index: 2; transform: rotate(-8deg) translateY(3%) scale(0.95); }
.mazo__ranura:nth-child(3) { z-index: 5; transform: rotate(0deg) translateY(-6%) scale(1.16); }
.mazo__ranura:nth-child(4) { z-index: 3; transform: rotate(8deg) translateY(3%) scale(0.95); }
.mazo__ranura:nth-child(5) { z-index: 2; transform: rotate(16deg) translateY(9%) scale(0.86); }

/* Cada ranura guarda dos pases montados uno encima del otro. El primero
   manda el tamaño de la ranura (va en flujo normal); el segundo se superpone
   entero y es quien se cruza con el primero vía `mazo-cambia`. */
.mazo__ranura .pase-demo:nth-child(2) {
  position: absolute;
  top: 0;
  left: 0;
}

/* El cambio de diseño. No es un cruce de opacidades (eso se probó y se veía
   borroso, como una doble exposición): es un volteo, igual que pasar una
   tarjeta de una mano a la otra.

   Ojo con esto si se toca: no alcanza con que cada pase gire y vuelva solo.
   Un pase que pasa el 84% del ciclo plano y visible, y el otro pase corriendo
   la misma regla medio ciclo adelantado, están los dos planos y visibles casi
   todo el tiempo a la vez (se probó, y ahí estaba la doble exposición). Lo
   que hace falta es que cada pase esté plano exactamente la MITAD del ciclo y
   de canto (oculto del todo) la otra mitad, para que el medio ciclo de
   adelanto del segundo caiga justo en la mitad en la que el primero está
   oculto, y nunca en la que está plano. Por eso el tramo de abajo, de 50% a
   94%, repite el mismo valor en los dos extremos: no es un error, es lo que
   mantiene al pase de canto (con la opacidad en cero, da igual el ángulo) en
   vez de dejarlo girar de vuelta antes de tiempo.

   Los dos pases de la ranura comparten exactamente esta misma animación y la
   misma duración; lo único que cambia es el signo del retraso del segundo,
   adelantado medio ciclo entero. --dur varía con --i para que las cinco
   ranuras no volteen todas a la vez. */
@keyframes mazo-cambia {
  0%, 44%   { opacity: 1; transform: rotateY(0deg); }
  50%, 94%  { opacity: 0; transform: rotateY(90deg); }
  100%      { opacity: 1; transform: rotateY(0deg); }
}

.mazo__ranura .pase-demo {
  animation: mazo-cambia var(--dur) ease-in-out infinite;
}

.mazo__ranura .pase-demo:nth-child(2) {
  animation-delay: calc(var(--dur) / -2);
}

/* Los diez pases del mazo miden todos lo mismo de alto, aunque por dentro
   lleven cosas distintas. Dejados a su aire medían entre 1,21 y 1,47 veces su
   propio ancho (el de diez sellos es el más alto, el de una barra el más
   bajo), y eso se notaba en dos sitios: el abanico salía cojo (la punta
   izquierda, más corta, y la derecha, más larga) y sobre todo la tarjeta
   cambiaba de tamaño en pleno volteo, que es justo lo que delata que son dos
   tarjetas y no una que gira.

   La altura es la del pase más alto, así que nadie queda recortado: lo que
   hace el `flex` es repartir el hueco que sobra, y quien lo absorbe es la
   franja del medio (la que cada negocio usa a su manera), con su contenido
   centrado. La cabecera, los campos y el QR se quedan donde estaban. */
.mazo__ranura .pase-wallet {
  height: calc(var(--pase-ancho) * 1.48);
  display: flex;
  flex-direction: column;
}

.mazo__ranura .pase-wallet__tira {
  flex: 1;
  display: grid;
  align-content: center;
}

/* Los dos avisos flotantes. Parten de `.push` (sección 12, la pastilla de
   "Qué incluye") y le cambian dos cosas: de dónde cuelgan y de qué miden.

   DE DÓNDE. Antes se pegaban al borde de la ventana (left/right), y eso solo
   se ve bien en una ventana estrecha: en una pantalla ancha, o con la página
   al 67%, el mazo se queda en el medio y los dos avisos se van a vivir a los
   extremos, a medio metro de las tarjetas, como dos papeles sueltos. Ahora
   cuelgan del centro y se apartan media anchura de mazo (--mazo-ancho), así
   que se posan siempre sobre el hombro de la tarjeta de la punta, mida lo
   que mida la ventana.

   DE QUÉ. El `.push` suelto mide en rem, o sea en la letra de la página, y al
   lado de un pase de 330 px salía enano. Aquí mide en --pase-ancho, igual que
   el pase, y los dos crecen y encogen juntos. Mismo criterio que `.push--ios`
   con --tel.

   OJO CON EL CENTRADO. El `.push` de siempre se centra con `translate`, y
   aquí eso no sirve: GSAP escribe `translate: none; rotate: none; scale:
   none` en línea sobre todo lo que lleva [data-float] (normaliza las tres
   propiedades sueltas para mandar él solo desde `transform`), así que
   cualquier `translate` o `rotate` de esta hoja se lo lleva por delante y el
   aviso vuelve al medio. Por eso el centrado va en `margin-left`, que GSAP no
   toca: media pastilla hacia atrás (su ancho es 1,02 pases) más el apartado.
   Y por eso tampoco hay inclinación: no habría forma de defenderla. */
.push--mazo {
  position: absolute;
  z-index: 6;
  left: 50%;
  right: auto;
  bottom: auto;
  /* El -50% de `.push` se va: aquí centra el margen. Hay que apagarlo de
     verdad y no solo ignorarlo, porque GSAP se lleva el valor que encuentra
     al arrancar y lo mete dentro de su propio transform, así que si se queda
     puesto el aviso termina media pastilla más a la izquierda de donde toca. */
  translate: 0 0;
  /* Ancho y letra siguen al pase, pero los dos con suelo: por debajo de unos
     13,5rem el texto se parte en cuatro renglones, y por debajo de 11,5px no
     hay quien lo lea. En esta hoja el tamaño de letra del aviso ES el de su
     texto (no el de un contenedor del que luego todo baja): así el suelo se
     escribe una sola vez y lo demás (el icono, el aire, el redondeo) crece y
     encoge con la letra. */
  /* Vale metro y medio del pase. Atado al 1,02 del pase se veía como una
     etiqueta pegada a la tarjeta; un aviso del sistema es más ancho que lo
     que avisa, porque no sale de la tarjeta, sale del teléfono. */
  --aviso-ancho: max(20.5rem, calc(var(--pase-ancho) * 1.52));
  margin-left: calc(var(--x, 0px) - var(--aviso-ancho) / 2);
  width: var(--aviso-ancho);
  font-size: max(0.78rem, calc(var(--aviso-ancho) * 0.039));
  gap: 0.95em;
  padding: 1.35em 1.52em;
  border-radius: 1.05em;
  /* El tinte lo hereda de `.push` y aquí solo se le sube el peso: 0,74 deja
     pasar lo de detrás sin que llegue a mandar, que es lo que arregla el
     problema que tenía este par cuando era claro y translúcido (el de la
     izquierda salía gris sucio sobre el negro y el de la derecha blanco
     sobre una tarjeta clara). Siendo oscuros, los dos se quedan oscuros
     pase lo que pase detrás.

     La sombra sí es suya: cuelga de un mazo que flota, no de una pantalla. */
  box-shadow:
    0 2.1em 3.9em -1.6em rgba(0, 0, 0, 0.78),
    0 0 0 0.5px rgba(255, 255, 255, 0.16) inset;
}

.push--mazo .push__icon {
  width: 2.5em;
  height: 3.15em;
  border-radius: 24%;
}

.push--mazo .push__body { font-size: 1em; line-height: 1.25; }
.push--mazo .push__body b { margin-bottom: 0.06em; }
.push--mazo .push__time { font-size: 0.87em; }

/* Uno sobre cada hombro del abanico, y a distinta altura: puestos a la misma
   los dos, el mazo parece una balanza.

   El `max()` (y el `min()` del otro) es el freno para las ventanas angostas:
   entre 620 y 1000 px el mazo llega a ser más ancho que la ventana, y un
   aviso colgado de su hombro se saldría por el costado. Lo que dice la
   segunda mitad es «y nunca más allá de media pastilla más 1,5rem del borde»;
   1,5rem y no 1rem porque en Windows la barra de desplazamiento se cobra sus
   15 px de 50vw. */
.push--mazo-izq {
  --x: max(calc(var(--mazo-ancho) * -0.5), calc(var(--aviso-ancho) / 2 + 1.5rem - 50vw));
  top: 12%;
}

.push--mazo-der {
  --x: min(calc(var(--mazo-ancho) * 0.5), calc(50vw - var(--aviso-ancho) / 2 - 1.5rem));
  top: 22%;
}

/* Movimiento reducido y modo ligero: el mazo se queda quieto en el primer
   diseño de cada ranura. Apagar solo la animación no alcanza, porque el
   recorte global de duración (sección 19) la acorta a nada y la dos pases
   quedan los dos a la vista un instante: el segundo se oculta entero. */
@media (prefers-reduced-motion: reduce) {
  .mazo__ranura .pase-demo { animation: none; }
  .mazo__ranura .pase-demo:nth-child(2) { display: none; }
}

html.tc-ligero .mazo__ranura .pase-demo { animation: none; }
html.tc-ligero .mazo__ranura .pase-demo:nth-child(2) { display: none; }

/* --------------------------------------------------------------------------
   13 · Comparativa
   -------------------------------------------------------------------------- */

.compare {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1rem, 2.4vw, 1.5rem);
}

.compare__col {
  border-radius: var(--radius-l);
  padding: clamp(1.5rem, 3vw, 2.2rem);
  border: 1px solid var(--line);
}

.compare__col--old {
  background: transparent;
  color: var(--fg-muted);
}

.compare__col--new {
  background: var(--ink);
  color: #f4f3f7;
  border-color: transparent;
  box-shadow: 0 40px 70px -44px rgba(10, 10, 18, 0.8);
  position: relative;
  overflow: hidden;
}

.compare__col--new::before {
  content: "";
  position: absolute;
  width: 320px;
  height: 320px;
  right: -110px;
  top: -130px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(124, 92, 255, 0.45), transparent 68%);
  filter: blur(34px);
}

.compare__col--new > * { position: relative; }

.compare__title {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: -0.025em;
  margin-bottom: 1.5rem;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

.compare__title svg { width: 1.1em; height: 1.1em; flex: none; }

.compare__col--old .compare__title svg { color: #b9483f; }
.compare__col--new .compare__title svg { color: var(--irid-5); }

.compare__list li {
  display: flex;
  gap: 0.75rem;
  padding-block: 0.85rem;
  border-top: 1px solid var(--line);
  line-height: 1.45;
}

.compare__col--new .compare__list li { border-top-color: rgba(255, 255, 255, 0.13); }

.compare__list li:first-child { border-top: 0; padding-top: 0; }

.compare__list svg { width: 1.15em; height: 1.15em; flex: none; margin-top: 0.22em; }

.compare__col--old .compare__list svg { color: #b9483f; opacity: 0.75; }
.compare__col--new .compare__list svg { color: var(--irid-5); }

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

/* --------------------------------------------------------------------------
   14 · Precios
   -------------------------------------------------------------------------- */

.plans {
  margin-top: clamp(3rem, 5.5vw, 4rem);
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1rem, 2vw, 1.4rem);
  align-items: start;
}

.plan {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--bg-elev);
  padding: clamp(1.7rem, 3vw, 2.3rem);
  display: flex;
  flex-direction: column;
  height: 100%;
  transition: transform 0.55s var(--ease-out), box-shadow 0.55s var(--ease-out);
}

.plan:hover { transform: translateY(-5px); }

.plan--featured {
  background: var(--ink);
  color: #f4f3f7;
  border-color: transparent;
  box-shadow: 0 50px 80px -46px rgba(10, 10, 18, 0.85);
  overflow: hidden;
}

.plan--featured::before {
  content: "";
  position: absolute;
  width: 340px;
  height: 340px;
  left: -120px;
  bottom: -170px;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(124, 92, 255, 0.5), transparent 66%);
  filter: blur(36px);
}

.plan--featured > * { position: relative; }

.plan__badge {
  position: absolute;
  top: 1.15rem;
  right: 1.15rem;
  z-index: 2;
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: 0.45em 0.85em;
  border-radius: 100px;
  background: var(--accent);
  color: #fff;
}

.plan__name {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 600;
  letter-spacing: -0.025em;
}

.plan__for {
  margin-top: 0.4rem;
  font-size: var(--step--1);
  color: var(--fg-muted);
}

.plan--featured .plan__for { color: #a3a2b0; }

.plan__list {
  margin-top: 1.7rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--line);
  display: grid;
  gap: 0.8rem;
  font-size: 0.96rem;
  line-height: 1.45;
  flex: 1;
}

.plan--featured .plan__list { border-top-color: rgba(255, 255, 255, 0.14); }

.plan__list li { display: flex; gap: 0.65rem; align-items: flex-start; }

.plan__list svg {
  width: 1.1em;
  height: 1.1em;
  flex: none;
  margin-top: 0.22em;
  color: var(--accent);
}

.plan--featured .plan__list svg { color: var(--accent-hi); }

.plan .btn { margin-top: 1.9rem; width: 100%; }

.plan__note {
  margin-top: 0.85rem;
  font-size: 0.78rem;
  text-align: center;
  color: var(--fg-faint);
}

.plan--featured .plan__note { color: #7d7c8d; }

/* Variante de dos planes. Con tres columnas la rejilla llena el ancho sola;
   con dos, cada tarjeta se estiraría hasta quedar más ancha que alta. */
.plans--par {
  grid-template-columns: repeat(2, 1fr);
  max-width: 880px;
  margin-inline: auto;
}

/* Precio sin cifra. Donde no hay importe publicado va el modelo de cobro: qué
   lo hace subir o bajar y cada cuánto se paga. Sin esto la tarjeta de plan se
   queda sin ancla y el visitante no sabe si le están vendiendo un objeto o una
   suscripción.
   Esto lo llevaban todos los planes cuando ninguno publicaba cifra. Hoy solo
   queda en los dos que de verdad se cotizan: el Enterprise de fidelidad y la
   tarjeta NFC «Con perfil digital», a la que todavía no se le ha fijado piso.
   Los planes con importe usan .precio, que está justo debajo. */
/* Sin línea propia: la lista de características que viene justo debajo ya trae
   su border-top, y las dos juntas dibujaban un doble filete. */
.cotiza {
  margin-top: clamp(1.4rem, 2.8vw, 1.9rem);
}

.cotiza__valor {
  display: block;
  font-family: var(--font-display);
  font-size: var(--step-3);
  font-weight: 600;
  letter-spacing: -0.035em;
  line-height: 1;
}

/* La línea gris de debajo del importe la comparten los dos bloques, el que
   lleva cifra y el que no, y por eso van juntas en la misma regla: en una
   página donde conviven «A medida» y «Desde S/ 70» (las tarjetas NFC), dos
   notas con cuerpos distintos se leerían como dos cosas distintas, y son la
   misma frase diciendo de qué depende el precio. */
.cotiza__nota,
.precio__nota {
  display: block;
  margin-top: 0.55rem;
  font-size: 0.84rem;
  line-height: 1.45;
  color: var(--fg-muted);
}

.plan--featured .cotiza__nota,
.plan--featured .precio__nota { color: #a3a2b0; }

/* Precio con cifra, para los planes que sí lo publican.
   No reutiliza .cotiza porque .cotiza__valor está dimensionado para la palabra
   «A medida»: un importe de tres dígitos puesto en ese tamaño se leía como una
   nota al margen, cuando es lo primero que el visitante viene a buscar. Aquí
   la cifra se lleva el tamaño de titular y lo que la rodea («Desde» y la
   unidad) queda en cuerpo de texto, que es exactamente el orden en que se lee.
   La unidad la pone cada página en el texto, no el CSS, y no es la misma en
   las dos: en fidelidad se cobra una cuota, así que dice «al mes»; en las
   tarjetas NFC se vende un objeto por unidades, así que dice «por tarjeta».
   Las tres piezas van en una línea con las bases alineadas, y no «Desde» en un
   renglón aparte, porque así la cifra de los dos planes cae a la misma altura
   aunque solo uno diga «Desde». Si el ancho no alcanza, se parte: el gap
   vertical es 0 a propósito para que al partirse no se abra un hueco. */
.precio {
  margin-top: clamp(1.4rem, 2.8vw, 1.9rem);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 0.45rem;
}

/* El letter-spacing es menos negativo que en el resto de titulares (-0.015em
   frente a -0.035em) por una razón concreta: con el apretado de titular, el
   espacio de «S/ 70» se cierra tanto que se lee «S/70». El símbolo de la
   moneda y la cifra son dos cosas y tienen que verse como dos. */
.precio__cifra {
  font-family: var(--font-display);
  font-size: var(--step-4);
  font-weight: 600;
  letter-spacing: -0.015em;
  line-height: 1;
}

/* «Desde» no es decoración: dice que ese importe es el piso del plan y no su
   precio cerrado. Por eso se lee, no se insinúa, y va al mismo cuerpo que «al
   mes». */
.precio__desde,
.precio__periodo {
  font-size: 0.95rem;
  color: var(--fg-muted);
}

.plan--featured .precio__desde,
.plan--featured .precio__periodo { color: #a3a2b0; }

/* La nota se lleva la fila entera: dentro de un flex con las bases alineadas
   se habría colocado detrás de «por tarjeta», como si fuera parte del mismo
   renglón. Es la única regla suya; todo lo demás lo hereda de .cotiza__nota,
   que está unas líneas más arriba. */
.precio__nota { flex: 0 0 100%; }

/* CON QUÉ SE PAGA LA CUOTA.
   Las marcas de tarjeta van escritas, no con sus logotipos. No es una decisión
   de gusto: los logotipos habría que traerlos de un servidor ajeno, y este
   sitio no pide ni un archivo a terceros (ni fuentes, ni imágenes, ni
   scripts). El único dibujo es el icono de la tarjeta, que vive en el
   <symbol> de la propia página como todos los demás.
   La caja es la misma receta que .plan (borde, radio y fondo elevado) pero en
   horizontal, para que se lea como una nota de los planes y no como un cuarto
   plan. */
.pagos {
  margin-top: clamp(1.6rem, 3vw, 2.2rem);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.9rem clamp(1rem, 2.5vw, 2rem);
  padding: clamp(1rem, 2vw, 1.35rem) clamp(1.1rem, 2.5vw, 1.6rem);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--bg-elev);
}

.pagos__texto {
  flex: 1 1 34ch;
  font-size: var(--step--1);
  line-height: 1.55;
  color: var(--fg-muted);
}

/* inline-block y no el display: block del reset: este icono va dentro de la
   frase, no encima de ella, y en bloque se llevaría el renglón para él solo. */
.pagos__icono {
  display: inline-block;
  width: 1.2em;
  height: 1.2em;
  margin-right: 0.45rem;
  vertical-align: -0.25em;
  color: var(--accent);
}

.pagos__marcas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.45rem;
}

/* Pastillas, y no una enumeración con comas, porque así se reconocen de un
   barrido sin pedirle a nadie que lea la frase entera. */
.pagos__marcas li {
  padding: 0.42em 0.8em;
  border: 1px solid var(--line);
  border-radius: 100px;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  white-space: nowrap;
  color: var(--fg-muted);
}

/* Lo que el plan NO incluye. Decirlo en la propia lista, y no en letra pequeña
   al final, evita que el cliente lo descubra cuando ya tiene la tarjeta. */
.plan__list li.no { color: var(--fg-muted); }
.plan__list li.no svg { color: var(--fg-faint); }

.plans__foot {
  margin-top: clamp(1.6rem, 3vw, 2.2rem);
  text-align: center;
  font-size: var(--step--1);
  color: var(--fg-muted);
}

@media (max-width: 980px) {
  /* .plans--par va nombrada aparte: tiene más especificidad que .plans y si no
     se repite aquí, se quedaría en dos columnas en el móvil. */
  .plans,
  .plans--par { grid-template-columns: 1fr; max-width: 560px; margin-inline: auto; }
}

/* --------------------------------------------------------------------------
   15 · Preguntas frecuentes
   -------------------------------------------------------------------------- */

.faq {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  border-top: 1px solid var(--line);
}

.faq__item { border-bottom: 1px solid var(--line); }

.faq__q {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
  text-align: left;
  padding-block: clamp(1.2rem, 2.2vw, 1.6rem);
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 500;
  letter-spacing: -0.025em;
  line-height: 1.2;
  color: var(--fg);
  transition: color 0.3s ease;
}

.faq__q:hover { color: var(--accent); }

.faq__sign {
  position: relative;
  width: 1.5rem;
  height: 1.5rem;
  flex: none;
  border-radius: 50%;
  border: 1px solid var(--line);
  transition: background-color 0.35s ease, border-color 0.35s ease, rotate 0.5s var(--ease-out);
}

.faq__sign::before,
.faq__sign::after {
  content: "";
  position: absolute;
  inset: 50% 27% auto 27%;
  height: 1.5px;
  background: currentColor;
  translate: 0 -50%;
  transition: opacity 0.3s ease;
}

.faq__sign::after { rotate: 90deg; }

.faq__item.is-open .faq__sign { rotate: 45deg; background: var(--accent); border-color: var(--accent); color: #fff; }
.faq__item.is-open .faq__q { color: var(--fg); }

.faq__a {
  overflow: hidden;
  height: 0;
}

.faq__a > div {
  padding-bottom: clamp(1.2rem, 2.2vw, 1.6rem);
  max-width: 62ch;
  color: var(--fg-muted);
  line-height: 1.65;
}

/* --------------------------------------------------------------------------
   16 · Cierre y pie
   -------------------------------------------------------------------------- */

.closer {
  position: relative;
  text-align: center;
  overflow: hidden;
}

.closer__glow {
  position: absolute;
  width: min(70vw, 760px);
  height: min(70vw, 760px);
  left: 50%;
  top: 20%;
  translate: -50% 0;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(124, 92, 255, 0.4), transparent 64%);
  filter: blur(80px);
  pointer-events: none;
}

.closer h2 { margin-inline: auto; max-width: 16ch; }

.closer .sec-lede { margin-inline: auto; text-align: center; }

.closer__actions {
  margin-top: clamp(2rem, 3.5vw, 2.8rem);
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  justify-content: center;
}

.footer {
  padding-block: clamp(3rem, 5vw, 4rem) clamp(2rem, 3vw, 2.5rem);
  border-top: 1px solid var(--line);
}

.footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) repeat(3, minmax(0, 1fr));
  gap: clamp(2rem, 4vw, 3rem);
  align-items: start;
}

.footer__about {
  margin-top: 1.1rem;
  max-width: 34ch;
  font-size: 0.95rem;
  color: var(--fg-muted);
}

/* QUIÉN VENDE, CON NOMBRE Y DOMICILIO.
   El pie solo decía «© TarjetaCloud», y TarjetaCloud es la marca: la empresa
   que firma, factura y responde es otra cosa, y quien paga una cuota mensual
   tiene derecho a saber a quién le paga sin tener que escribir para
   preguntarlo. Va dentro de la columna de la marca, debajo de la frase de qué
   vendemos, y no en letra diminuta al final de todo: leído ahí se entiende
   como la presentación del negocio y no como un aviso pegado a última hora.
   El ancho es el mismo de .footer__about (34ch) para que las dos piezas de la
   columna caigan sobre la misma vertical; la dirección ocupa dos líneas y así
   está bien, porque no se puede acortar.
   <address> es el elemento que el HTML tiene para los datos de contacto de
   quien publica la página. Lo único que hay que deshacerle es la cursiva que
   trae de fábrica. */
.footer__ident {
  margin-top: 1.5rem;
  display: grid;
  gap: 0.3rem;
  max-width: 34ch;
  font-style: normal;
  font-size: 0.84rem;
  line-height: 1.5;
  color: var(--fg-muted);
}

/* En --fg-faint, que es el tono del resto de la letra pequeña del pie, esto se
   quedaba en 3,9 de contraste sobre el fondo oscuro: por debajo del 4,5 que
   pide la norma para un cuerpo de 13px. Una dirección que hay que acercarse a
   leer no cumple su único trabajo, así que sube a --fg-muted y lo que la
   mantiene discreta es el tamaño, no la falta de luz.
   La razón social se separa por el peso y no por el color: más brillo la
   habría puesto por encima de la frase de qué vendemos, que es la que manda en
   esta columna. */
.footer__ident strong { font-weight: 600; }

/* La 404 no tiene pie: tiene una columna centrada. El bloque es el mismo, así
   que solo cambia de eje. */
.footer__ident--centro {
  margin-inline: auto;
  max-width: 72ch;
  text-align: center;
}

/* h3 y no h4: los títulos de las columnas del pie venían detrás de un h2 y se
   saltaban un nivel, que para quien navega con lector de pantalla es un hueco
   en el índice de la página. */
.footer h3 {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-faint);
  margin-bottom: 1.1rem;
}

.footer__list { display: grid; gap: 0.65rem; font-size: 0.95rem; }
.footer__list a { color: var(--fg-muted); transition: color 0.25s ease; }
.footer__list a:hover { color: var(--fg); }

.footer__bottom {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding-top: 1.6rem;
  border-top: 1px solid var(--line-soft);
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.5rem;
  justify-content: space-between;
  align-items: center;
  font-size: 0.82rem;
  color: var(--fg-faint);
}

/* Los enlaces legales del pie, y el Libro de Reclamaciones.
   El libro llevo recuadro y un icono durante un tiempo, buscando el aspecto de
   cajita con el que la gente lo reconoce en otras webs peruanas. Fuera de esas
   webs no se sostiene: aqui era la unica pieza del pie con borde propio, y lo
   que en las demas es una convencion aqui parecia un boton pegado. Lo que pide
   la Ley 32495 es un enlace permanente, visible y de facil acceso, y eso lo
   cumple igual siendo el ultimo enlace de esta misma linea, con el mismo
   aspecto que sus tres vecinos. */
.footer__legal {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.1rem;
}

.footer__legal a { color: var(--fg-muted); transition: color 0.25s ease; }
.footer__legal a:hover { color: var(--fg); }

@media (max-width: 820px) {
  .footer__grid { grid-template-columns: 1fr 1fr; }
  .footer__brand { grid-column: 1 / -1; }
}

/* Con el dedo, una línea de texto de 19px de alto no es un enlace: es una
   apuesta. El relleno vertical no mueve nada de sitio (la separación de la
   lista baja lo mismo que crece el enlace) y deja cada destino en un objetivo
   de unos 44px, que es lo que ocupa la yema. */
@media (max-width: 620px) {
  .footer__list { gap: 0.15rem; }

  .footer__list a {
    display: inline-block;
    padding-block: 0.5rem;
  }

  /* Lo mismo para los enlaces legales, por lo que les pasa al ser cuatro: en
     un teléfono la línea ya no entra de una y se parte en dos renglones. Dos
     filas de 19px separadas por medio espacio son dos destinos que el dedo
     confunde, y entre ellos está el Libro de Reclamaciones, que es justo el
     que la ley pide que sea de fácil acceso. El relleno se come la separación
     de fila, así que el bloque no crece ni empuja al «©» de arriba. */
  .footer__legal { gap: 0 1.1rem; }

  .footer__legal a {
    display: inline-block;
    padding-block: 0.5rem;
  }
}

/* --------------------------------------------------------------------------
   17 · Animación de entrada
   -------------------------------------------------------------------------- */

/* Máscara para el revelado de líneas de texto */
.split-line { overflow: hidden; padding-bottom: 0.06em; }

/* Estado inicial de los elementos que aparecen al hacer scroll.
   El revelado de emergencia es el seguro: si por lo que sea el JS tarda o no
   llega, a los 2,5 s el contenido aparece solo. Antes esto dependía de un
   temporizador en el <head>, y en un móvil con red lenta la página se quedaba
   en blanco hasta que saltaba. Ahora el seguro es CSS puro, no depende de que
   haya descargado nada, y main.js lo desactiva en cuanto arranca. */
[data-reveal] { opacity: 0; animation: tc-rescate 0.45s ease-out 2.5s both; }

.no-js [data-reveal] { opacity: 1; animation: none; }

/* main.js pone .tc-anim en cuanto se ejecuta: manda GSAP, fuera el seguro. */
.tc-anim [data-reveal] { animation: none; }

/* reduced-motion solo lo pone la red de seguridad, y significa que el JS de
   animación no llegó a hacer su trabajo. Va con !important porque para
   entonces GSAP puede haber dejado un opacity: 0 en línea, y una regla normal
   no lo levantaría: el contenido se quedaría invisible justo en el caso que
   esta clase existe para rescatar. */
.reduced-motion [data-reveal] {
  opacity: 1 !important;
  transform: none !important;
  animation: none !important;
}

/* El mismo seguro de arriba, pero para TODO titular partido en líneas por
   SplitText (D-51, ampliado a todo [data-split] en D-52). Ninguno de
   estos lleva [data-reveal]: a estos los revela SplitText, no el lote
   genérico. Sin este seguro, en una conexión lenta (fuentes y scripts
   tardando en llegar) cualquier titular queda escrito en texto plano y
   totalmente visible durante los segundos que main.js tarda en arrancar:
   para el de la portada eso es justo lo contrario de "espera al fondo de
   olas"; para el resto, un parpadeo de texto sin partir que después se
   esconde y vuelve a aparecer partido. main.js hace opacity:1 a mano
   apenas SplitText parte cada titular en líneas (ver buildHeadlines en
   main.js), en el mismo instante en que esas líneas ya nacen ocultas por
   su propio transform: nunca hay un fotograma con el titular entero y sin
   partir a la vista. */
[data-split] { opacity: 0; animation: tc-rescate 0.45s ease-out 2.5s both; }

.no-js [data-split] { opacity: 1; animation: none; }

.tc-anim [data-split] { animation: none; }

.reduced-motion [data-split] {
  opacity: 1 !important;
  transform: none !important;
  animation: none !important;
}

@keyframes tc-rescate {
  from { opacity: 0; transform: translateY(28px); }
  to   { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   17b · Lenis
   Hoja de estilos que pide la librería. Sin ella el scroll suave choca con el
   del navegador. Lenis solo se activa en escritorio, pero las reglas viven
   aquí porque las aplica la propia librería al poner sus clases en <html>.
   -------------------------------------------------------------------------- */

html.lenis,
html.lenis body { height: auto; }

.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }
.lenis.lenis-smooth iframe { pointer-events: none; }

/* Bloqueo de la página con el menú móvil abierto. Lo pone main.js. */
.tc-lock,
.tc-lock body { overflow: hidden; touch-action: none; }

/* --------------------------------------------------------------------------
   18 · Responsive del hero
   -------------------------------------------------------------------------- */

@media (max-width: 1000px) {
  .hero__inner { grid-template-columns: 1fr; gap: clamp(3rem, 9vw, 4.5rem); }
  .hero { min-height: auto; }
  .card3d-wrap { width: min(100%, 440px); }

  /* En una sola columna el hero deja de sobrarle alto y el pase, que cuelga por
     debajo de la tarjeta, se quedaba cortado por el borde de la sección. */
  .card3d-stage { padding-bottom: 40px; }

  /* El tope por alto se repite aquí porque esta línea pisa entera a la de
     arriba: sin él, una ventana baja y ancha de tableta vuelve a dejar el
     mazo fuera de la vista. */
  .hero__mazo { --pase-ancho: min(clamp(160px, 22vw, 240px), 27.5vh); }
}

@media (max-width: 620px) {
  /* Con la tarjeta a todo el ancho ya no queda margen lateral donde apoyar
     las etiquetas: se sacan arriba y abajo, y el escenario reserva el hueco. */
  .card3d-stage { padding-block: 30px; }

  .float-tag { font-size: 0.76rem; padding: 0.5rem 0.85rem; }
  .float-tag--1 { top: -26px;    left: 0;  }
  .float-tag--2 { bottom: -26px; right: 0; }

  /* Cinco pases en un teléfono angosto se aplastan contra el borde: las dos
     ranuras de las puntas se sueltan y quedan tres, el mismo reparto que la
     baraja de teléfonos tenía antes del 10/10/26. */
  .mazo__ranura:first-child,
  .mazo__ranura:last-child { display: none; }

  /* Con tres ranuras el abanico ya no mide 3,32 pases sino 2,2: si no se
     corrige, los avisos se cuelgan de un mazo que no existe y se van fuera de
     la pantalla. */
  .hero__mazo {
    --pase-ancho: clamp(160px, 44vw, 220px);
    --mazo-ancho: calc(var(--pase-ancho) * 2.2);
  }

  /* En el teléfono los avisos dejan de colgarse del mazo. Colgados de él se
     salían de la pantalla por los dos lados (el mazo es más ancho que el
     teléfono), y atados al pase quedaban de 150 px, con una letra de 6 px que
     no se puede leer. Aquí miden en pantalla, casi tan anchos como ella, y se
     escalonan uno encima del otro como dos avisos que acaban de entrar: que
     tapen un poco las tarjetas es parte de la idea. */
  .push--mazo { --aviso-ancho: min(88vw, 360px); }

  .push--mazo-izq { --x: -1.2rem; top: -4%; }
  .push--mazo-der { --x:  1.2rem; top: 20%; }
}

/* Los dos botones de la portada, del mismo ancho y uno debajo del otro. En el
   teléfono, dos botones de anchos distintos en la misma columna se leen como
   un error de maquetación, no como una jerarquía. */
@media (max-width: 520px) {
  .hero__actions,
  .closer__actions { flex-direction: column; align-items: stretch; }
  .hero__actions .btn,
  .closer__actions .btn { width: 100%; }

  /* Una promesa por línea: en dos columnas desiguales se leían como una lista
     rota. */
  .hero__trust {
    flex-direction: column;
    align-items: flex-start;
    gap: 0.6rem;
  }
}

/* --------------------------------------------------------------------------
   19 · Preferencia de movimiento reducido
   -------------------------------------------------------------------------- */

@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;
  }

  [data-reveal] { opacity: 1 !important; transform: none !important; }
  [data-split] { opacity: 1 !important; transform: none !important; }
  .marquee__track { animation: none; }
  .card3d__irid { animation: none; }
  .wa::before { animation: none; opacity: 0; }
}

/* --------------------------------------------------------------------------
   20 · Bifurcación de la portada
   -------------------------------------------------------------------------- */

.fork {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1rem, 2.4vw, 1.6rem);
}

.door {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: clamp(1.5rem, 3vw, 2.2rem);
  padding: clamp(1.7rem, 3.2vw, 2.6rem);
  border-radius: var(--radius-xl);
  background: var(--ink);
  color: #f4f3f7;
  overflow: hidden;
  isolation: isolate;
  transition: transform 0.6s var(--ease-out), box-shadow 0.6s var(--ease-out);
}

.door::before {
  content: "";
  position: absolute;
  z-index: -1;
  width: 420px;
  height: 420px;
  right: -140px;
  top: -180px;
  border-radius: 50%;
  filter: blur(40px);
  background: radial-gradient(circle, rgba(124, 92, 255, 0.45), transparent 66%);
  transition: opacity 0.6s ease;
}

.door--loyalty::before {
  background: radial-gradient(circle, rgba(34, 211, 238, 0.34), transparent 66%);
}

.door:hover {
  transform: translateY(-7px);
  box-shadow: 0 50px 80px -46px rgba(10, 10, 18, 0.75);
}

/* Las puertas llevaban encima un "Producto 01" y un "Producto 02". Numerarlos
   ponía un techo donde no lo hay: en cuanto entre un tercero habría que
   renumerar la portada entera, y mientras tanto el numerito le decía al
   visitante que esto es todo lo que se hace. El titular basta. */
.door h3 {
  font-size: var(--step-3);
  letter-spacing: -0.03em;
}

.door__desc {
  margin-top: 0.9rem;
  max-width: 34ch;
  color: #a3a2b0;
  line-height: 1.55;
}

.door__for {
  margin-top: 1.3rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
}

.door__for span {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0.4em 0.8em;
  border-radius: 100px;
  border: 1px solid rgba(255, 255, 255, 0.16);
  color: #cfced8;
}

.door__go {
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 700;
  color: #fff;
}

.door__go svg {
  width: 1.05em;
  height: 1.05em;
  transition: transform 0.5s var(--ease-out);
}

.door:hover .door__go svg { transform: translateX(5px); }

/* La tarjeta entera es un enlace: el <a> se estira sobre todo el bloque. */
.door__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
}

/* Miniaturas de producto dentro de cada puerta ------------------------- */

.door__visual {
  position: relative;
  height: clamp(150px, 20vw, 200px);
  display: grid;
  place-items: center;
}

.mini-card {
  position: relative;
  width: min(100%, 260px);
  aspect-ratio: 1.586 / 1;
  border-radius: 15px;
  overflow: hidden;
  padding: 0.9rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  background: linear-gradient(148deg, #23233a, #14141f 45%, #0d0d16);
  border: 1px solid rgba(255, 255, 255, 0.14);
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.9);
  rotate: -6deg;
  transition: rotate 0.6s var(--ease-out);
}

.door:hover .mini-card { rotate: -2deg; }

.mini-card::before {
  content: "";
  position: absolute;
  inset: -30%;
  background: conic-gradient(from 210deg, var(--irid-1), var(--irid-2), var(--irid-3), var(--irid-4), var(--irid-5), var(--irid-1));
  opacity: 0.22;
  filter: blur(22px) saturate(1.4);
  mix-blend-mode: screen;
}

.mini-card::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(202deg, rgba(10, 10, 18, 0) 26%, rgba(10, 10, 18, 0.72) 100%);
}

.mini-card > * { position: relative; z-index: 1; }

.mini-card__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.mini-card__brand {
  font-family: var(--font-display);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: -0.03em;
}

.mini-card__brand span { opacity: 0.55; font-weight: 400; }

.mini-card__name {
  font-family: var(--font-display);
  font-size: 0.95rem;
  font-weight: 500;
  letter-spacing: -0.025em;
}

/* La tarjeta tal como se vende de verdad: PVC blanco holográfico. Es el
   soporte que se compra y sobre el que se imprime, así que el tornasol es del
   material y no un adorno añadido. Enseñar lo que se fabrica y no otra cosa
   es la regla de D-05, la misma por la que no hay tarjetas metálicas en
   ninguna imagen.

   La versión oscura de arriba sigue en pie: es una tarjeta impresa en negro,
   que también se hace. Esta es el soporte en blanco. */
.mini-card--holo {
  background: #f6f7f5;
  /* El canto y una sombra corta de apoyo. Sin las dos, una tarjeta blanca
     sobre el panel blanco de esta sección se queda sin silueta. */
  border-color: rgba(10, 10, 18, 0.17);
  box-shadow:
    0 26px 48px -24px rgba(10, 10, 18, 0.45),
    0 2px 7px -3px rgba(10, 10, 18, 0.16);
  /* el texto va en tinta: sobre blanco, el claro de la versión oscura
     desaparecería */
  color: var(--ink);
}

/* El tornasol. En el material el color aparece y desaparece al girar la
   tarjeta, nunca se ve el arcoíris entero a la vez. Por eso son manchas
   anchas y desaturadas y no una franja de colores: lo que se dibuja es la
   huella que deja esa luz, que es lo que se ve en una foto quieta. */
.mini-card--holo::before {
  inset: -14%;
  opacity: 1;
  /* Poco desenfoque: con más, las manchas se van por el canto y la tarjeta
     se queda blanca del todo, que es lo que no es. */
  filter: blur(18px);
  mix-blend-mode: normal;
  /* El orden importa: en la lámina real el tornasol cae en banda diagonal,
     del verde amarillento de abajo a la izquierda al lila azulado del centro
     derecha, con el núcleo blanco arriba. No son manchas repartidas por las
     cuatro esquinas. */
  background:
    radial-gradient(42% 54% at 22% 70%, #d9efd2 0%, rgba(217, 239, 210, 0) 72%),
    radial-gradient(38% 48% at 42% 86%, #f8f0c9 0%, rgba(248, 240, 201, 0) 70%),
    radial-gradient(44% 56% at 70% 52%, #d6ddf8 0%, rgba(214, 221, 248, 0) 74%),
    radial-gradient(34% 44% at 82% 20%, #f7dfee 0%, rgba(247, 223, 238, 0) 70%),
    radial-gradient(42% 52% at 40% 30%, #ffffff 0%, rgba(255, 255, 255, 0) 74%);
}

/* El brillo del plástico. Una barrida diagonal ancha y un apoyo oscuro abajo:
   sin eso la tarjeta se lee como papel, y el PVC no es papel. */
.mini-card--holo::after {
  background:
    linear-gradient(118deg,
      rgba(255, 255, 255, 0) 14%,
      rgba(255, 255, 255, 0.8) 33%,
      rgba(255, 255, 255, 0.24) 46%,
      rgba(255, 255, 255, 0) 62%),
    linear-gradient(200deg,
      rgba(10, 10, 18, 0) 58%,
      rgba(10, 10, 18, 0.09) 100%);
}

.mini-pass {
  width: min(100%, 250px);
  border-radius: 15px;
  padding: 0.85rem;
  color: #fff;
  background: linear-gradient(155deg, #7c5cff, #5a37e0 55%, #3d2199);
  box-shadow: 0 26px 50px -26px rgba(0, 0, 0, 0.9),
              0 1px 0 rgba(255, 255, 255, 0.22) inset;
  rotate: 5deg;
  transition: rotate 0.6s var(--ease-out);
}

.door:hover .mini-pass { rotate: 2deg; }

.mini-pass__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.7rem;
}

.mini-pass__brand {
  font-family: var(--font-display);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: -0.025em;
}

.mini-pass__count {
  font-size: 0.68rem;
  font-weight: 600;
  opacity: 0.8;
  font-variant-numeric: tabular-nums;
}

.mini-pass .stamps { grid-template-columns: repeat(5, 1fr); margin-top: 0; }

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

/* --------------------------------------------------------------------------
   21 · Lo que incluye la tarjeta, sea cual sea la version
   -------------------------------------------------------------------------- */

/* Lo que incluye cualquier tarjeta, sea cual sea el tramo */
.included {
  margin-top: clamp(2rem, 3.6vw, 2.8rem);
  padding: clamp(1.5rem, 3vw, 2.2rem);
  border: 1px solid var(--line);
  border-radius: var(--radius-l);
  background: var(--bg-elev);
}

.included h3 {
  font-size: var(--step-2);
  margin-bottom: 1.4rem;
}

.included .features { margin-top: 0; grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 820px) {
  .included .features { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 540px) {
  .included .features { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   22 · Acabado aún no disponible
   -------------------------------------------------------------------------- */

.finish--soon { opacity: 0.62; }
.finish--soon:hover { transform: none; }

.finish__soon {
  position: absolute;
  top: 0.55rem;
  right: 0.55rem;
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  padding: 0.34em 0.7em;
  border-radius: 100px;
  background: rgba(10, 10, 18, 0.82);
  color: #f4f3f7;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   23 · Enlace cruzado a otro producto
   -------------------------------------------------------------------------- */

.crosssell {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 3rem);
  padding: clamp(1.8rem, 3.5vw, 2.8rem);
  border-radius: var(--radius-xl);
  background: var(--bg-elev);
  border: 1px solid var(--line);
  overflow: hidden;
}

.crosssell__eyebrow {
  font-size: var(--step--1);
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--accent);
}

.crosssell h2 {
  margin-top: 0.8rem;
  font-size: var(--step-3);
  letter-spacing: -0.03em;
}

.crosssell p {
  margin-top: 0.9rem;
  max-width: 52ch;
  color: var(--fg-muted);
  line-height: 1.6;
}

.crosssell .btn { margin-top: 1.6rem; }

@media (max-width: 820px) {
  .crosssell { grid-template-columns: 1fr; }
  .crosssell__visual { display: none; }
}

/* --------------------------------------------------------------------------
   24 · Razones de la portada
   -------------------------------------------------------------------------- */

.reasons {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1rem, 2.2vw, 1.6rem);
}

.reason__icon {
  width: 2.6rem;
  height: 2.6rem;
  border-radius: 12px;
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent);
  margin-bottom: 1.1rem;
}

.reason__icon svg { width: 1.25rem; height: 1.25rem; }

.reason h3 {
  font-size: var(--step-1);
  letter-spacing: -0.02em;
  margin-bottom: 0.55rem;
}

.reason p {
  color: var(--fg-muted);
  font-size: 0.96rem;
  line-height: 1.55;
}

@media (max-width: 900px) { .reasons { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .reasons { grid-template-columns: 1fr; } }

/* Enlace activo en la barra de navegación */
.nav__links a[aria-current="page"] { opacity: 1; font-weight: 700; }

/* Etiqueta de precio del hero: a 1180px .float-tag--2 ya se acerca al borde
   (ver la regla de la sección 08), y en el teléfono el giro de -4deg la saca
   un poco por fuera de la tarjeta si no se endereza. */
@media (max-width: 620px) {
  .float-tag--precio { rotate: 0deg; }
}

/* --------------------------------------------------------------------------
   25 · Página 404
   La sirve el servidor para cualquier dirección que no exista. No tiene barra
   ni pie: quien llega aquí llegó por error y lo único que necesita es una
   salida clara.
   -------------------------------------------------------------------------- */

.notfound {
  min-height: 100svh;
  display: grid;
  place-items: center;
  text-align: center;
}

.notfound__inner { display: grid; justify-items: center; }

.notfound__logo { width: min(230px, 62vw); height: auto; }

.notfound__code {
  margin-top: clamp(2rem, 5vw, 3rem);
  font-size: var(--step--1);
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--fg-faint);
}

.notfound__title {
  margin-top: 0.8rem;
  font-size: var(--step-5);
  line-height: 0.98;
  letter-spacing: -0.04em;
}

.notfound__lede {
  margin-top: clamp(1.2rem, 2.4vw, 1.6rem);
  max-width: 44ch;
  font-size: var(--step-1);
  line-height: 1.55;
  color: var(--fg-muted);
}

.notfound__actions {
  margin-top: clamp(2rem, 3.4vw, 2.6rem);
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  justify-content: center;
}

.notfound__foot {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  font-size: var(--step--1);
  color: var(--fg-faint);
}

.notfound__foot a {
  color: var(--fg-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* Los enlaces legales de la 404. El aspecto lo pone .footer__legal, que es el
   mismo de las otras cinco páginas; esto solo los centra y los separa del
   bloque de identificación que llevan encima. */
.notfound__legal {
  margin-top: clamp(1.4rem, 3vw, 2rem);
  justify-content: center;
  font-size: 0.82rem;
}

@media (max-width: 520px) {
  .notfound__actions { flex-direction: column; align-items: stretch; width: 100%; }
  .notfound__actions .btn { width: 100%; }
}

/* --------------------------------------------------------------------------
   26 · Página de soporte
   Reutiliza casi todo lo que ya existe: los pasos, el acordeón, la lista de
   checks y el cierre. Aquí solo vive lo que no había: una cabecera corta
   (esta página no vende, así que no le toca un hero de pantalla entera), los
   accesos rápidos y la tabla de qué funciona en cada teléfono.
   -------------------------------------------------------------------------- */

.support-head {
  padding-top: clamp(7.5rem, 13vh, 9.5rem);
  padding-bottom: clamp(3rem, 6vh, 4.5rem);
  overflow: hidden;
}

.support-head h1 { font-size: var(--step-5); letter-spacing: -0.035em; }
.support-head .sec-lede { margin-top: 1.4rem; }

/* Accesos rápidos. Quien llega a soporte llega con un problema concreto y no
   debería tener que leer la página entera para encontrarlo. */
.topics {
  margin-top: clamp(2.5rem, 5vw, 3.4rem);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.9rem, 1.6vw, 1.15rem);
}

.topic {
  display: grid;
  align-content: start;
  gap: 0.6rem;
  padding: 1.4rem 1.45rem 1.55rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: rgba(255, 255, 255, 0.03);
  transition: border-color 0.3s ease, background 0.3s ease,
              transform 0.4s var(--ease-out);
}

.topic:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--accent) 55%, transparent);
  background: rgba(255, 255, 255, 0.06);
}

.topic__icon {
  width: 2.35rem;
  height: 2.35rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: var(--accent-lo);
  color: #fff;
}

.topic__icon svg { width: 1.1rem; height: 1.1rem; }

.topic__title {
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.15;
}

.topic p {
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--fg-muted);
}

@media (max-width: 900px) { .topics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 520px) { .topics { grid-template-columns: 1fr; } }

/* Dos accesos y no tres o seis: sin esto el grid de tres columnas deja un
   hueco vacío al lado del segundo. */
.topics--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 640px; margin-inline: auto; }
@media (max-width: 520px) { .topics--2 { grid-template-columns: 1fr; } }

/* Ilustraciones. Van dibujadas en SVG dentro del propio HTML y no como
   imágenes sueltas, por lo mismo que el resto del sitio: la carpeta se tiene
   que poder abrir con doble clic, sin internet y sin archivos que se pierdan
   por el camino. Además así heredan los colores del tema. */
.figs {
  margin-top: clamp(2.5rem, 5vw, 3.4rem);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(1rem, 2vw, 1.5rem);
}

/* La de la tarjeta va a lo ancho y en horizontal: es un dibujo apaisado con
   una leyenda de tres puntos, y apilarlos dejaba media caja vacía al lado.
   Con las dos clases en el selector para que gane a .fig, que se declara
   más abajo y si no le pisaría la alineación del texto. */
.fig.fig--ancha {
  grid-column: 1 / -1;
  grid-template-columns: minmax(180px, 300px) minmax(0, 1fr);
  justify-items: start;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 3.2rem);
  text-align: left;
}

.fig.fig--ancha figcaption { max-width: 62ch; text-align: left; }

.fig {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 1.3rem;
  padding: clamp(1.6rem, 3vw, 2.3rem);
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--bg-elev);
  text-align: center;
}

.fig svg { width: 100%; height: auto; }

.fig__telefono { max-width: 148px; }
.fig__tarjeta { max-width: 340px; }

.fig figcaption {
  font-size: var(--step--1);
  line-height: 1.55;
  color: var(--fg-muted);
  max-width: 38ch;
}

/* Hijo directo: el titulillo de la figura va en su propia línea, pero los
   <b> que viven dentro de la leyenda tienen que seguir en línea con su
   frase. */
.fig figcaption > b {
  display: block;
  margin-bottom: 0.35rem;
  font-family: var(--font-display);
  font-size: var(--step-1);
  font-weight: 600;
  letter-spacing: -0.02em;
  color: var(--fg);
}

/* Partes del dibujo. El cuerpo va tenue y la zona que importa es lo único
   con color: si todo destaca, no destaca nada. */
.dib-cuerpo { fill: none; stroke: currentColor; stroke-width: 2; opacity: 0.4; }
.dib-detalle { fill: none; stroke: currentColor; stroke-width: 1.6; opacity: 0.26; }

.dib-zona {
  fill: color-mix(in srgb, var(--accent) 24%, transparent);
  stroke: var(--accent);
  stroke-width: 2;
  stroke-dasharray: 6 5;
}

.dib-mitad { stroke: currentColor; stroke-width: 1.4; stroke-dasharray: 4 5; opacity: 0.4; }
.dib-ondas { fill: none; stroke: var(--accent-hi); stroke-width: 1.7; stroke-linecap: round; }
.dib-antena { fill: none; stroke: var(--accent); stroke-width: 1.6; opacity: 0.85; }
.dib-chip { fill: var(--accent); }
.dib-qr { fill: currentColor; opacity: 0.75; }

/* Leyenda de la tarjeta: más claro que meter textos dentro del dibujo, que
   al escalar se descuadran. */
.leyenda {
  display: grid;
  gap: 0.6rem;
  text-align: left;
  font-size: var(--step--1);
  color: var(--fg-muted);
}

.leyenda li { display: flex; gap: 0.65rem; align-items: baseline; }

.leyenda i {
  flex: none;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 3px;
  translate: 0 0.1rem;
}

.leyenda b { color: var(--fg); font-weight: 600; }

/* El aviso del sistema, sacado del teléfono de fidelidad para poder enseñarlo
   suelto: aquí no ilustra una pantalla, es la cosa de la que se habla. */
.push--suelto {
  position: static;
  translate: none;
  width: min(100%, 364px);
  box-shadow: 0 18px 34px -22px rgba(0, 0, 0, 0.75);
}

@media (max-width: 1040px) {
  .figs { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 760px) {
  .figs { grid-template-columns: 1fr; }
  .fig.fig--ancha { grid-template-columns: 1fr; justify-items: center; }
  .fig.fig--ancha .fig__tarjeta { max-width: 300px; }
}

/* Tabla de compatibilidad. En pantalla estrecha no se reorganiza en bloques:
   se desplaza dentro de su propia caja. Una tabla de tres columnas apilada
   pierde justo lo que la hace legible, que es comparar la misma fila de un
   lado y del otro. */
.compat {
  margin-top: clamp(2.2rem, 4vw, 3rem);
  overflow-x: auto;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: var(--bg-elev);
}

.compat table {
  width: 100%;
  min-width: 640px;
  border-collapse: collapse;
  text-align: left;
}

.compat th,
.compat td {
  padding: 1.05rem 1.3rem;
  border-bottom: 1px solid var(--line-soft);
  vertical-align: top;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--fg-muted);
}

.compat thead th {
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--fg);
  border-bottom-color: var(--line);
}

.compat tbody th {
  width: 30%;
  font-weight: 600;
  color: var(--fg);
}

.compat tbody tr:last-child th,
.compat tbody tr:last-child td { border-bottom: 0; }

.compat b { color: var(--fg); font-weight: 600; }

/* Marca de la celda. Verde cuando funciona en los dos sistemas y naranja
   cuando hay letra chica: no es que falle, es que pide algo más, y pintarlo
   de rojo diría otra cosa. */
.compat td svg {
  width: 1.05em;
  height: 1.05em;
  flex: none;
  margin-right: 0.45rem;
  translate: 0 0.16em;
  display: inline-block;
}

.compat .si { color: var(--irid-5); }
.compat .ojo { color: var(--irid-4); }

/* Comparativa de planes. Hereda de .compat porque es el mismo problema: tres
   columnas que solo sirven si se ven juntas. Lo que cambia es que aquí las
   celdas son cifras y no frases, así que van centradas, y la columna del plan
   destacado se pinta para que se corresponda con la tarjeta de arriba. */
.compat--planes table { min-width: 520px; }

.compat--planes caption {
  caption-side: top;
  padding: 1.15rem 1.3rem 0;
  text-align: left;
  font-family: var(--font-display);
  font-size: var(--step-0);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--fg);
}

.compat--planes thead th:not(:first-child),
.compat--planes tbody td {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* El nombre de la fila es el dato secundario: lo que se compara son las
   columnas. Por eso aquí no va en negrita, al revés que en .compat. */
.compat--planes tbody th {
  width: 34%;
  font-weight: 400;
  color: var(--fg-muted);
}

.compat--planes tbody td { color: var(--fg); }

.compat--planes .destacada { background: rgba(124, 92, 255, 0.07); }
.compat--planes thead .destacada { color: var(--accent); }
.compat--planes .no { color: var(--fg-faint); }

/* En el teléfono la tercera columna queda fuera de vista y nada indica que
   esté ahí. La pista solo aparece donde la tabla de verdad se desplaza. */
.compat__hint { display: none; }

@media (max-width: 760px) {
  .compat__hint {
    display: block;
    margin-top: 0.85rem;
    font-size: var(--step--1);
    color: var(--fg-faint);
  }
  /* La de planes es más estrecha (520 y no 640), así que a 760 todavía entera
     y la pista sobraría. Tiene su propio punto de corte más abajo. */
  .compat__hint--planes { display: none; }
}

@media (max-width: 600px) {
  .compat__hint--planes {
    display: block;
    margin-top: 0.85rem;
    font-size: var(--step--1);
    color: var(--fg-faint);
  }
}

/* Aviso. En una página de soporte lo que hay que leer sí o sí casi nunca es
   una ventaja, así que tiene su propia caja y no se mezcla con el cuerpo. */
.note {
  margin-top: 1.9rem;
  display: flex;
  gap: 0.9rem;
  padding: 1.15rem 1.35rem;
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-s);
  background: var(--bg-elev);
  font-size: var(--step--1);
  line-height: 1.55;
  color: var(--fg-muted);
}

.note svg {
  width: 1.15rem;
  height: 1.15rem;
  flex: none;
  margin-top: 0.2rem;
  color: var(--accent);
}

.note b { color: var(--fg); font-weight: 600; }

/* Grupos de preguntas. El acordeón deja una sola abierta en toda la página,
   así que los grupos se separan con un titulillo y no con cajas. */
.faq-group { margin-top: clamp(2.4rem, 4.5vw, 3.2rem); }

.faq-group__title {
  margin-bottom: 0.4rem;
  font-size: var(--step--1);
  font-family: var(--font-body);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--accent);
}

/* Canales de soporte del cierre. */
.channels {
  margin-top: clamp(2.2rem, 4vw, 2.8rem);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: clamp(0.9rem, 1.6vw, 1.15rem);
  text-align: left;
}

.channel {
  padding: 1.35rem 1.4rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-m);
  background: rgba(255, 255, 255, 0.03);
}

.channel h3 {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-size: var(--step-1);
}

.channel h3 svg { width: 1.05em; height: 1.05em; color: var(--accent-hi); }

.channel p {
  margin-top: 0.5rem;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--fg-muted);
}

.channel a { color: var(--fg); border-bottom: 1px solid var(--line); }
.channel a:hover { border-bottom-color: var(--accent); }

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

/* --------------------------------------------------------------------------
   27 · Botón flotante de WhatsApp
   -------------------------------------------------------------------------- */

/* Cuelga del final del <body>, fuera de toda sección, para que no herede ni el
   tema ni el grano de los bloques oscuros: es el mismo botón desde la primera
   pantalla hasta el pie.

   Es HTML y CSS y nada más. Ningún script lo monta, lo muestra ni lo esconde,
   así que sigue ahí aunque la cadena de gsap no llegue a cargar, que es
   justamente la situación en la que un visitante querría preguntar algo.

   El z-index lo deja por debajo del menú móvil (99) y de la navegación (100) a
   propósito: con el menú abierto la pantalla es del menú. */
.wa {
  position: fixed;
  /* Las dos primeras son la red: un navegador que no entienda env() tira la
     linea siguiente entera y se queda con esta, en vez de soltar el boton en
     mitad del texto. Las de abajo apartan el boton de la barra del iPhone. */
  right: 1.1rem;
  bottom: 1.1rem;
  right: max(1.1rem, env(safe-area-inset-right));
  bottom: max(1.1rem, env(safe-area-inset-bottom));
  z-index: 90;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3.5rem;
  height: 3.5rem;
  border-radius: 100px;
  /* El verde de WhatsApp, no el morado de la marca. Este botón se reconoce
     antes de leerlo, y eso solo funciona si lleva el color de siempre. */
  background: #25d366;
  color: #fff;
  box-shadow: 0 6px 18px rgba(10, 10, 18, 0.22), 0 2px 6px rgba(10, 10, 18, 0.14);
  transition: transform 0.4s var(--ease-out), opacity 0.35s ease,
              background-color 0.3s ease, box-shadow 0.4s ease;
}

/* Al llegar al pie se retira. Lo pone y lo quita main.js mirando el pie con un
   IntersectionObserver; sin ese script el boton se queda donde esta, visible,
   que es el fallo bueno de los dos. */
.wa--fuera {
  opacity: 0;
  transform: translateY(14px) scale(0.86);
  pointer-events: none;
}

.wa--fuera::before { animation: none; opacity: 0; }

.wa:hover {
  background: #1eb355;
  transform: translateY(-2px) scale(1.04);
  box-shadow: 0 12px 28px rgba(10, 10, 18, 0.3), 0 3px 8px rgba(10, 10, 18, 0.16);
}

.wa:active { transform: translateY(0) scale(0.97); }

.wa svg { width: 1.7rem; height: 1.7rem; }

/* El aro que late. En una página que ya se mueve sola, un círculo quieto en
   una esquina se confunde con el fondo; el latido es lento y se apaga entero
   entre pulso y pulso para llamar sin dar la lata. */
.wa::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 2px solid #25d366;
  border-radius: inherit;
  pointer-events: none;
  animation: wa-latido 3.4s var(--ease-out) infinite;
}

@keyframes wa-latido {
  0%        { transform: scale(1); opacity: 0.6; }
  60%, 100% { transform: scale(1.7); opacity: 0; }
}

/* La etiqueta solo asoma con el ratón encima o al llegar con el tabulador, y
   va en posición absoluta para que aparecer no le mueva el sitio al botón. En
   una pantalla táctil no hay ratón: ahí el icono se explica solo. */
.wa__txt {
  position: absolute;
  right: calc(100% + 0.6rem);
  padding: 0.6em 0.95em;
  /* Oscura con filo claro: el relleno la separa de las secciones de color
     crema y el filo la separa de las oscuras, que si no se la tragan. */
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 100px;
  background: var(--ink);
  color: #f4f3f7;
  box-shadow: 0 6px 18px rgba(10, 10, 18, 0.28);
  font-size: var(--step--1);
  font-weight: 600;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(8px);
  pointer-events: none;
  transition: opacity 0.35s ease, transform 0.35s var(--ease-out);
}

.wa:hover .wa__txt,
.wa:focus-visible .wa__txt { opacity: 1; transform: translateX(0); }

@media (max-width: 480px) {
  .wa { width: 3.2rem; height: 3.2rem; }
  .wa svg { width: 1.55rem; height: 1.55rem; }
  .wa__txt { display: none; }
}

/* El QR de WhatsApp. Reemplaza al globo verde cuando quien mira la pantalla
   no tiene dedo con el que tocarla: una computadora, una laptop o un
   televisor no abren wa.me, pero casi siempre tienen un celular al lado con
   el que apuntar.

   Por defecto esta escondido y el globo es el que manda: si el navegador no
   entiende la consulta de `pointer` (rarisimo a estas alturas) se queda el
   camino que siempre funciono, que es el mismo criterio del botón de abajo:
   "fallar enseñando el contacto es mejor que fallar escondiéndolo".

   `pointer: fine` es el mouse o el trackpad de una computadora o laptop;
   `pointer: none` es un televisor manejado con control remoto, sin puntero
   de ningun tipo. Ninguno de los dos es un dedo, que es `pointer: coarse` y
   es justo lo unico que no entra aqui. */
@media (pointer: fine), (pointer: none) {
  .wa { display: none; }

  .wa-qr {
    display: flex;
  }
}

.wa-qr {
  display: none;
  position: fixed;
  right: 1.1rem;
  bottom: 1.1rem;
  right: max(1.1rem, env(safe-area-inset-right));
  bottom: max(1.1rem, env(safe-area-inset-bottom));
  z-index: 90;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  width: 9.5rem;
  padding: 0.9rem 0.9rem 0.75rem;
  border-radius: 20px;
  /* La franja verde arriba, no el morado de la marca: se reconoce antes de
     leerla, igual que el botón que reemplaza. */
  border-top: 5px solid #25d366;
  background: #fff;
  color: #15161b;
  text-decoration: none;
  box-shadow: 0 6px 18px rgba(10, 10, 18, 0.22), 0 2px 6px rgba(10, 10, 18, 0.14);
  transition: transform 0.4s var(--ease-out), box-shadow 0.4s ease, opacity 0.35s ease;
}

.wa-qr:hover {
  transform: translateY(-2px);
  box-shadow: 0 12px 28px rgba(10, 10, 18, 0.3), 0 3px 8px rgba(10, 10, 18, 0.16);
}

.wa-qr:active { transform: translateY(0) scale(0.98); }

.wa-qr__codigo {
  width: 100%;
  height: auto;
  display: block;
  color: #15161b;
}

/* Se retira al llegar al pie con la misma clase y el mismo gesto que el
   botón: lo pone y lo quita el mismo ScrollTrigger en main.js. */
.wa-qr--fuera {
  opacity: 0;
  transform: translateY(14px) scale(0.9);
  pointer-events: none;
}

.wa-qr__pie {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--step--1);
  font-weight: 700;
  line-height: 1;
  color: #1eb355;
}

.wa-qr__pie svg {
  width: 1rem;
  height: 1rem;
  /* El glifo trae `fill="currentColor"` desde el símbolo, así que lo que lo
     pinta es `color`, no `fill`. */
  color: #25d366;
  flex: none;
}

/* --------------------------------------------------------------------------
   28 · Impresión
   -------------------------------------------------------------------------- */

@media print {
  .nav, .mobile-menu, .hero__aurora, .marquee, .wa, .wa-qr { display: none; }
  .section { padding-block: 1.5rem; }
  [data-reveal] { opacity: 1 !important; }
  [data-split] { opacity: 1 !important; }
}

/* --------------------------------------------------------------------------
   29 · Modo ligero
   --------------------------------------------------------------------------
   La clase `tc-ligero` la pone el script en linea del <head> de cada pagina,
   antes del primer pintado, y tambien `main.js` si mide los fotogramas y no
   dan. Lo que se apaga aqui es exactamente lo que cuesta caro de pintar, y
   nada mas: la estructura, los colores, el texto y las apariciones al hacer
   scroll se quedan igual.

   Que cuesta caro, y por que:

   · `filter: blur()`. El navegador tiene que copiar el elemento a una textura
     aparte, convolucionarla y volver a componerla. El coste va con el area y
     con el radio, y aqui habia halos de 46vw con 90 px de radio. Son los
     adornos mas caros de todo el sitio por bastante margen.
   · `backdrop-filter`. El mismo trabajo, pero sobre lo que haya detras, y la
     barra de navegacion esta fija encima de una pagina que se mueve: se
     recalcula en cada fotograma del scroll.
   · `mix-blend-mode`. Obliga a leer de vuelta lo que ya estaba pintado para
     mezclarlo, y eso rompe el camino rapido del compositor. El grano cubre
     secciones enteras, asi que la lectura es del tamano de la pantalla.
   · Las animaciones que no paran nunca. La iridiscencia giraba a 60 fps toda
     la visita, y detras lleva un desenfoque y una mezcla, asi que cada vuelta
     repite las tres cosas de arriba.

   En su lugar van degradados planos, que el navegador pinta de una pasada y
   le salen gratis. El aspecto cambia poco: un degradado radial que termina en
   transparente ya se ve suave sin desenfocarlo.
   -------------------------------------------------------------------------- */

/* El grano y el brillo especular: textura pura, y lo primero que sobra cuando
   hay que elegir. En una pantalla de telefono barato no se distinguen. */
html.tc-ligero [data-theme="dark"]::after,
html.tc-ligero .card3d__grain,
html.tc-ligero .card3d__sheen,
html.tc-ligero .fotocheck__grain { display: none; }

/* Los halos de fondo pierden el desenfoque. Los grandes se compensan con una
   parada intermedia en el degradado, que es lo que devuelve la caida suave;
   los del tamano de una tarjeta no la necesitan. */
html.tc-ligero .hero__aurora,
html.tc-ligero .closer__glow,
html.tc-ligero .card3d-wrap::after,
html.tc-ligero .compare__col--new::before,
html.tc-ligero .plan--featured::before,
html.tc-ligero .door::before { filter: none; }

/* Sin el blur de 54px encima, cada radial-gradient de la lista tiene que
   quedar suave por su cuenta: mismos centros y proporciones que la versión
   de escritorio, con paradas intermedias algo más generosas para que la
   caida no se note. */
html.tc-ligero .hero__aurora {
  background-image:
    radial-gradient(ellipse 34% 28% at 82% 4%, rgba(124, 92, 255, 0.4) 0%, rgba(124, 92, 255, 0.12) 55%, transparent 85%),
    radial-gradient(ellipse 62% 54% at 76% 16%, rgba(124, 92, 255, 0.2) 0%, rgba(124, 92, 255, 0.07) 55%, transparent 86%),
    radial-gradient(ellipse 26% 20% at 8% 94%, rgba(34, 211, 238, 0.28) 0%, rgba(34, 211, 238, 0.08) 55%, transparent 82%),
    radial-gradient(ellipse 48% 40% at 13% 88%, rgba(34, 211, 238, 0.12) 0%, rgba(34, 211, 238, 0.04) 55%, transparent 85%),
    radial-gradient(ellipse 85% 48% at 48% 22%, rgba(124, 92, 255, 0.08) 0%, transparent 80%),
    linear-gradient(180deg, rgba(124, 92, 255, 0.05), transparent 46%);
}

/* La deriva (sección 7) es solo transform, no repite ni el desenfoque ni la
   mezcla, pero sigue siendo una animación sin fin y esas son justo las que
   este modo apaga primero. */
html.tc-ligero .hero__aurora { animation: none; }

html.tc-ligero .closer__glow {
  background: radial-gradient(circle,
    rgba(124, 92, 255, 0.34) 0%,
    rgba(124, 92, 255, 0.13) 42%,
    transparent 78%);
}

/* El vidrio esmerilado pasa a ser vidrio opaco. El fondo se sube de opacidad
   para que siga separando: sin desenfoque, un 72 % deja leer el texto de
   detras a traves de la barra. */
html.tc-ligero .nav::before {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: color-mix(in srgb, var(--ink) 95%, transparent);
}

html.tc-ligero .btn--ghost,
html.tc-ligero .push {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* Sin el desenfoque, un tinte al 74% deja ver lo de detrás tal cual y el
   aviso se ensucia. Aquí pasa a sólido: pierde el cristal, que es lo que el
   modo ligero existe para no pagar, y conserva el color. */
html.tc-ligero .push { background: #202026; }
html.tc-ligero .push--ios { background: #f7f7fa; }

html.tc-ligero .float-tag {
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
  background: rgba(22, 22, 31, 0.95);
}

/* La iridiscencia se queda, pero quieta y plana: un degradado lineal con los
   mismos cinco colores, sin girar, sin desenfocar y sin mezclar. Sigue siendo
   un reflejo de color sobre la tarjeta, y no cuesta nada. */
html.tc-ligero .card3d__irid,
html.tc-ligero .mini-card::before {
  animation: none;
  filter: none;
  mix-blend-mode: normal;
  background: linear-gradient(132deg,
    var(--irid-1), var(--irid-2) 26%, var(--irid-3) 50%,
    var(--irid-4) 74%, var(--irid-5));
  opacity: 0.13;
}

/* La blanca holográfica no puede heredar ese lavado: sobre blanco se vería
   un arcoíris de verdad, que es justo lo que el material no hace. Se queda
   su propio nácar, plano y sin desenfoque. Va detrás de la regla de arriba
   a propósito: tienen la misma especificidad y gana la última. */
html.tc-ligero .mini-card--holo::before {
  filter: none;
  mix-blend-mode: normal;
  opacity: 1;
  background: linear-gradient(132deg,
    #ecf4ea, #f5e9f1 26%, #eceff9 52%, #f9f3e3 78%, #f3f5f2);
}

/* Lo que late sin parar. El aro del boton de WhatsApp es un transform, que es
   barato, pero mantiene despierto al compositor durante toda la visita: en un
   telefono eso es bateria y calor, y el calor se convierte en lentitud.
   Las ondas del chip animan el opacity de un SVG, que no va por el compositor
   sino repintando: se quedan fijas a media luz, que es como se leen igual. */
html.tc-ligero .wa::before { animation: none; opacity: 0; }
html.tc-ligero .nfc-waves path { animation: none; opacity: 0.5; }

/* Sin fisica del cordon no hay nada de lo que tirar, asi que la instruccion
   sobra. El cordon estatico del CSS ocupa su sitio; es el mismo relevo que ya
   existe para cuando no hay JS. */
html.tc-ligero .lanyard__hint { display: none; }

/* Y el gafete se promueve a capa propia aunque aqui no se mueva nunca. Suena
   al contrario de lo que pide el modo ligero, y la medicion dice que no lo es:
   quieto le cuesta al navegador 557 ms de rasterizado por recorrer la pagina,
   y promovido 214. La razon es que `.lanyard.is-live .fotocheck` ya lo
   promovia mientras corria la fisica, y al quitar la fisica se quedaba sin
   promover: una tarjeta grande, con degradados, texto y un QR, metida en la
   capa que se desplaza y rasterizada otra vez cada vez que vuelve a entrar en
   pantalla. Promovida se rasteriza una vez y se reusa. Es una capa, y es la
   unica de todo el modo ligero que vale lo que ocupa. */
html.tc-ligero .lanyard .fotocheck { will-change: transform; }

/* La inclinacion con el cursor no se monta en modo ligero, asi que la tarjeta
   no tiene transform que anticipar ni transicion que suavizar.

   Al gafete de mas arriba le pasa lo contrario, y la diferencia no es un
   capricho: el gafete esta a media pagina y entra y sale de pantalla, asi que
   promovido se rasteriza una vez en vez de muchas. La tarjeta del hero esta
   arriba del todo y se rasteriza una sola vez de todas formas, asi que
   promoverla no ahorra nada y en cambio anade su composicion a cada fotograma:
   medido en un Pixel 5 frenado, 726 ms de trabajo sin promover contra 804
   promovida, con el rasterizado igual en los dos casos. */
html.tc-ligero .card3d { will-change: auto; transition: none; }

/* --------------------------------------------------------------------------
   30 · La demo (/demo)
   --------------------------------------------------------------------------
   Tres pasos en una sola pantalla: elegir una tarjeta, dejar el WhatsApp y
   escanear el QR. No son tres paginas ni tres secciones una debajo de otra,
   es el mismo sitio cambiando de contenido, porque lo que hay a la derecha
   (la tarjeta elegida) tiene que seguir visible durante los tres.

   Lo que esta seccion NO inventa: el pase es `.pase-wallet`, el mismo de la
   baraja de /fidelidad, con sus mismas medidas en em. Aqui solo se le quita
   el telefono de alrededor, se le pone una medida propia y se le anaden tres
   colores mas. Si algun dia cambia el pase, cambia en los dos sitios a la vez,
   que es justo lo que se buscaba.
   -------------------------------------------------------------------------- */

.demo { overflow: hidden; }

/* El halo de fondo, el mismo recurso del hero: un degradado radial que muere
   en transparente, sin desenfoque. */
.demo__glow {
  position: absolute;
  z-index: 0;
  top: -18%;
  right: -12%;
  width: min(760px, 80vw);
  aspect-ratio: 1;
  pointer-events: none;
  background: radial-gradient(circle, rgba(124, 92, 255, 0.4), rgba(124, 92, 255, 0) 62%);
}

/* Los tres pasos ------------------------------------------------------- */

/* Es una lista y no una botonera: no se puede saltar al 3 sin pasar por el 2,
   asi que ensenarlos como pulsables seria mentir con el cursor. */
.pasos {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(0.9rem, 3vw, 2.2rem);
  margin: 0 0 clamp(1.8rem, 4vw, 3rem);
  padding: 0;
  list-style: none;
  font-size: var(--step--1);
  letter-spacing: 0.02em;
}

.paso {
  display: flex;
  align-items: center;
  gap: 0.6em;
  color: var(--fg-faint);
  transition: color 0.3s var(--ease-out);
}

.paso__n {
  display: grid;
  place-items: center;
  width: 2em;
  height: 2em;
  border-radius: 50%;
  border: 1px solid var(--line);
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.95em;
  line-height: 1;
  transition: background 0.3s var(--ease-out), color 0.3s var(--ease-out),
              border-color 0.3s var(--ease-out);
}

.paso.is-now { color: var(--fg); }

.paso.is-now .paso__n {
  background: linear-gradient(135deg, var(--accent), var(--irid-2));
  border-color: transparent;
  color: #0a0a12;
}

/* El paso ya pasado no se apaga del todo: es la unica senal de por donde va
   quien lleva dos minutos en la pagina. */
.paso.is-done { color: var(--fg-muted); }

.paso.is-done .paso__n {
  background: rgba(124, 92, 255, 0.18);
  border-color: rgba(124, 92, 255, 0.45);
  color: var(--accent-hi);
}

/* El reparto de la pantalla ---------------------------------------------- */

.demo__grid {
  display: grid;
  gap: clamp(2.5rem, 6vw, 4.5rem);
  align-items: center;
}

@media (min-width: 900px) {
  .demo__grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.9fr);
  }
}

/* En el telefono la tarjeta va PRIMERO y el texto despues. Es lo contrario de
   lo que sale solo con el orden del HTML, y es a proposito: quien entra a una
   demo entra a ver la tarjeta, no a leer. En escritorio no hace falta porque
   las dos cosas caben en la misma mirada. */
@media (max-width: 899px) {
  .demo__visual { order: -1; }
}

.demo__title {
  font-family: var(--font-display);
  font-size: var(--step-5);
  font-weight: 600;
  line-height: 1.02;
  letter-spacing: -0.035em;
  margin: 0;
}

.demo__lede {
  margin: 1.1rem 0 0;
  max-width: 46ch;
  color: var(--fg-muted);
  font-size: var(--step-1);
  line-height: 1.55;
}

.demo__acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.8rem;
  margin-top: 1.8rem;
}

.demo__nota {
  margin: 1.6rem 0 0;
  max-width: 48ch;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--fg-faint);
}

.demo__nota a { color: var(--accent-hi); }

/* Los paneles. El que no toca lleva `hidden`, que ya lo saca del arbol de
   accesibilidad: un lector de pantalla no lee tres pasos a la vez. */
.demo__panel[hidden] { display: none; }

/* EL ATRIBUTO `hidden` NO BASTA EN UN BOTON DE ESTA HOJA, Y COSTO CARO.
   El navegador lo aplica con `[hidden] { display: none }`, que es una regla de
   su propia hoja y pierde contra cualquier clase nuestra; `.btn` declara
   `display: inline-flex`, asi que gana y el boton se ve igual.

   Donde dolia: `#seguir-igual` es la salida de emergencia del paso 2, la que
   solo debe aparecer si el aviso no pudo salir. Visible desde el principio,
   dejaba pasar al paso 3 sin escribir el WhatsApp, que es lo unico que esta
   pagina pide a cambio de la tarjeta. Comprobado en el navegador: hidden a
   true, display flex, y un clic bastaba.

   Vale para los dos botones que se esconden (`#seguir-igual` y
   `#ver-la-nuestra`) y para cualquiera que se anada despues. */
.btn[hidden] { display: none; }

.demo__panel.is-now {
  animation: panel-entra 0.5s var(--ease-out) both;
}

@keyframes panel-entra {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}

/* Sin JavaScript no hay pasos, ni QR, ni formulario que llegue a ningun
   sitio. Lo que queda es el camino que nunca se rompe. */
.demo__sinjs { display: none; margin: 1.8rem 0 0; }
html.no-js .demo__sinjs { display: block; }
html.no-js .demo__acciones { display: none; }

/* Las fichas de negocio --------------------------------------------------- */

.fichas {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 1.8rem 0 0;
  padding: 0;
  list-style: none;
}

/* NO SON PASTILLAS, Y ES A PROPOSITO.

   Las pastillas de esta hoja son los botones: cosas que al pulsarlas pasa
   algo y te llevan a otro sitio. Una ficha no hace eso, elige cual de las
   siete tarjetas esta delante y te deja donde estabas. Dandoles la misma
   forma que a un boton se les prometia una accion que no tienen, y de paso
   siete pastillas seguidas eran la parte mas generica de la pagina.

   Cuatro pixeles, no cero: a cero el canto se ve roto al lado de una tarjeta
   con 0.95em de radio. Lo justo para que se lea como una decision. */
.ficha {
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.5em 0.8em;
  border-radius: 4px;
  border: 1px solid var(--line);
  background: transparent;
  color: var(--fg-muted);
  font: inherit;
  /* Un punto por debajo de --step--1, que es el escalon mas chico de la
     tipografia. No baja mas: por debajo de 12px el nombre de un negocio deja
     de leerse de un vistazo, y leerlos de un vistazo es para lo que estan. */
  font-size: clamp(0.76rem, 0.73rem + 0.14vw, 0.84rem);
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.2s var(--ease-out), color 0.2s var(--ease-out),
              background 0.2s var(--ease-out);
}

.ficha:hover { border-color: var(--line); color: var(--fg); }

/* El punto es el color de su tarjeta. Es lo que hace que la lista se lea de un
   vistazo sin ponerle a cada negocio un icono que habria que dibujar. */
.ficha__punto {
  width: 0.72em;
  height: 0.72em;
  border-radius: 50%;
  background: var(--punto, var(--accent));
  flex: none;
}

/* La etiqueta de las tarjetas que son solo diseno. Decirlo en la ficha y no
   al final evita la unica sorpresa fea que podia dar esta pagina: dejar el
   WhatsApp y descubrir recien ahi que esa tarjeta no se puede guardar. */
.ficha__tag {
  margin-left: 0.15em;
  padding: 0.22em 0.42em;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.08);
  font-size: 0.78em;
  letter-spacing: 0.04em;
  color: var(--fg-faint);
}

.ficha[aria-pressed="true"] {
  border-color: var(--accent);
  background: rgba(124, 92, 255, 0.14);
  color: var(--fg);
}

/* LAS QUE TODAVIA NO TIENEN QR, EN GRIS.

   Siguen pulsandose a proposito: la pagina existe para ensenar como se ve una
   tarjeta, y esos seis disenos cumplen esa mitad aunque no se puedan guardar
   todavia. Apagarlas del todo seria esconder justo lo que se vino a ver.

   El gris dice «aun no», no «prohibido», y el color se lo guarda cada una en
   su `data-color`: el dia que su negocio tenga programa, la clase deja de
   ponerse sola y la ficha vuelve a encenderse sin tocar el CSS.

   El punto se pinta aqui y no con `--punto` porque esa variable la escribe el
   script en el atributo style, y un inline gana a cualquier regla. */
.ficha--ejemplo {
  border-color: var(--line-soft);
  color: var(--fg-faint);
}

.ficha--ejemplo .ficha__punto {
  background: var(--fg-faint);
  opacity: 0.5;
}

/* Ni cursor de mano ni reaccion al pasar por encima: las dos cosas prometen
   un clic que no va a pasar. El `not-allowed` es la unica senal que queda
   para quien ya llevo el raton hasta ahi sin fijarse en el gris. */
.ficha:disabled {
  cursor: not-allowed;
}

.ficha:disabled:hover {
  border-color: var(--line-soft);
  color: var(--fg-faint);
  background: transparent;
}

/* La baraja --------------------------------------------------------------- */

.baraja-demo {
  position: relative;
  display: flex;
  justify-content: center;
  /* La medida de la que cuelga todo el pase, igual que --tel en /fidelidad. */
  --pase-ancho: clamp(224px, 27vw, 330px);
}

/* El pase, sin telefono alrededor.

   El 0.067 no es un numero al azar: dentro del telefono el pase mide 0.93 del
   ancho del aparato y la letra cuelga de 0.062 de ese mismo ancho, asi que
   0.062 / 0.93 es la proporcion que hay que repetir aqui para que el pase se
   vea exactamente igual, solo que mas grande. */
.pase-demo {
  width: var(--pase-ancho);
  font-size: calc(var(--pase-ancho) * 0.067);
  transform-style: preserve-3d;
  transition: transform 0.55s var(--ease-out), opacity 0.45s var(--ease-out);
}

.pase-demo .pase-wallet {
  margin-top: 0;
  border-radius: 0.95em;
  box-shadow:
    0 1.4em 2.6em -1em rgba(0, 0, 0, 0.75),
    0 0 0 1px rgba(255, 255, 255, 0.06) inset;
}

/* Las tres posiciones de la baraja. El `data-d` lo escribe demo.js: 0 el que
   esta elegido, -1 el anterior y 1 el siguiente. Los demas se quedan fuera.

   Se eligio atributo y no una variable con calc() porque `abs()` en CSS es
   demasiado nuevo para darlo por supuesto, y aqui son tres casos contados. */
.pase-demo[data-d="0"] {
  position: relative;
  z-index: 3;
}

.pase-demo[data-d="-1"],
.pase-demo[data-d="1"] {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 1;
  opacity: 0.55;
  pointer-events: none;
}

/* El -50% de la izquierda es lo que centra, y el resto es el desplazamiento:
   -80% son -50% de centrado y -30% de apartarse. Se escribe en una sola
   funcion porque los dos porcentajes son del ancho de la propia tarjeta. */
.pase-demo[data-d="-1"] { transform: translate(-80%, -50%) rotate(-8deg) scale(0.88); }
.pase-demo[data-d="1"]  { transform: translate(-20%, -50%) rotate(8deg) scale(0.88); }

/* Las que no estan ni delante ni al lado siguen en el HTML (Google las lee, y
   sin JavaScript se ven las seis), pero no se dibujan. */
.pase-demo[data-d="fuera"] {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 0;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.8);
  pointer-events: none;
}

/* Sin JavaScript nadie escribe `data-d`, asi que no hay baraja: las seis se
   ponen en rejilla y se ven todas. Es menos vistoso y es lo correcto: la
   pagina ensena su contenido entero cuando no puede ensenarlo bonito.

   Se le quita de paso el reparto en dos columnas, porque seis tarjetas una
   encima de otra en la mitad derecha dejan la mitad izquierda vacia y la
   pagina midiendo cinco pantallas. */
html.no-js .demo__grid {
  grid-template-columns: 1fr;
  align-items: start;
}

/* La columna mide mas que la tarjeta a proposito: si la tarjeta fuera mas
   ancha que su celda se montaria encima de la vecina, porque su ancho es una
   medida fija y no un porcentaje (la letra de dentro cuelga de esa medida, y
   un tamano de letra en porcentaje no significa lo mismo). */
html.no-js .baraja-demo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(246px, 1fr));
  gap: 1.4rem;
  justify-items: center;
  --pase-ancho: 230px;
}

/* La inclinacion con el cursor. Las dos variables las escribe demo.js, y solo
   en escritorio, sin movimiento reducido y fuera del modo ligero. */
.pase-demo[data-d="0"] {
  transform: perspective(1100px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
}

/* Mientras el raton esta encima no hay transicion. Las dos suavizan lo mismo y
   se estorban: demo.js ya interpola la inclinacion fotograma a fotograma, asi
   que una transicion de medio segundo encima solo anade retardo. La de arriba
   sigue haciendo falta para cuando la baraja cambia de orden, que es un salto
   de una posicion a otra y ahi si hay que acompanarlo. */
.pase-demo.is-live { transition: none; }

/* El reflejo de luz.

   Son dos y hacen cosas distintas. El primero es una banda diagonal que cruza
   la tarjeta sola, cada cierto tiempo, y es lo que hace que el pase parezca
   una lamina con brillo en vez de un rectangulo de color: funciona igual en un
   telefono, donde no hay cursor que seguir. El segundo es el punto de luz que
   sigue al raton, y ese solo existe en escritorio. */
.pase-demo__brillo {
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: 0.95em;
  overflow: hidden;
  pointer-events: none;
}

.pase-demo__brillo::before {
  content: "";
  position: absolute;
  top: -60%;
  bottom: -60%;
  left: -40%;
  width: 28%;
  background: linear-gradient(
    100deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.38) 50%,
    rgba(255, 255, 255, 0) 100%);
  transform: rotate(14deg);
  /* Ocho segundos de vuelta para un barrido de uno y medio: el brillo tiene
     que leerse como un reflejo que pasa, no como una animacion encendida. */
  animation: pase-destello 8s var(--ease-io) infinite;
}

@keyframes pase-destello {
  0%, 82%   { left: -40%; opacity: 0; }
  84%       { opacity: 1; }
  98%, 100% { left: 112%; opacity: 0; }
}

/* El punto de luz del cursor. Vive en el mismo elemento que el barrido para no
   anadir una capa mas, y se apaga solo cuando el raton se va. */
.pase-demo__brillo::after {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0;
  background: radial-gradient(
    circle at var(--mx, 50%) var(--my, 50%),
    rgba(255, 255, 255, 0.3),
    rgba(255, 255, 255, 0) 55%);
  transition: opacity 0.3s var(--ease-out);
}

.pase-demo.is-live .pase-demo__brillo::after { opacity: 1; }

/* Los tres colores que la baraja de /fidelidad no tenia ------------------- */

.pase-wallet--brasa {
  --pase-bg: linear-gradient(158deg, #6a2f1c, #4a1d10 58%, #2c1009);
  --pase-fg: #fff;
  --pase-tira: rgba(255, 255, 255, 0.07);
  --pase-linea: rgba(255, 255, 255, 0.18);
  --pase-sello: #8c3a1e;
}

.pase-wallet--petunia {
  --pase-bg: linear-gradient(158deg, #f0a6c8, #d4679b 58%, #a53c74);
  --pase-fg: #fff;
  --pase-tira: rgba(255, 255, 255, 0.14);
  --pase-linea: rgba(255, 255, 255, 0.3);
  --pase-sello: #a53c74;
}

.pase-wallet--hierro {
  --pase-bg: linear-gradient(158deg, #1f4f4a, #14332f 58%, #0c1f1d);
  --pase-fg: #fff;
  --pase-tira: rgba(255, 255, 255, 0.07);
  --pase-linea: rgba(255, 255, 255, 0.18);
  --pase-sello: #1f4f4a;
}

/* EL PASE DE TARJETACLOUD. No es una paleta elegida aquí: es la del pase que
   emite la aplicación, mirada en un iPhone. Por eso no sigue la gama de las
   otras seis, que sí son invento nuestro.

   La diferencia que importa: esta tarjeta es la única de la baraja que existe
   de verdad, así que tiene que verse igual que lo que el visitante va a
   encontrarse dentro de su billetera treinta segundos después. Una tarjeta de
   demo que no se parece a la que llega es una promesa incumplida en el único
   momento en el que el visitante estaba comprobando si le decimos la verdad. */
.pase-wallet--tarjetacloud {
  --pase-bg: linear-gradient(158deg, #4634b5, #35259b 58%, #2a1c82);
  --pase-fg: #fff;
  --pase-tira: rgba(255, 255, 255, 0.05);
  --pase-linea: rgba(255, 255, 255, 0.22);
  --pase-sello: #2a1c82;
}

/* El sello lleno es un círculo lila con la estrella oscura encima, y el vacío
   es el mismo círculo sin rellenar. En el pase de verdad los dos llevan
   estrella: el vacío no está hueco, está apagado. */
.pase-wallet--tarjetacloud .stamp.is-filled { background: #9b8ae8; border-color: transparent; }
.pase-wallet--tarjetacloud .stamp:not(.is-filled) {
  border-style: solid;
  border-color: rgba(255, 255, 255, 0.42);
  color: rgba(255, 255, 255, 0.42);
}

/* Seis sellos, no diez. La rejilla de `.stamps` son cinco columnas, que es lo
   que necesitaban las dos tarjetas de sellos inventadas; esta necesita seis en
   una sola fila, y con cinco quedaría 5 + 1, que se lee como un error. */
.stamps--seis {
  grid-template-columns: repeat(6, 1fr);
  gap: 0.26em;
}

/* Tres campos en vez de dos. El pase de verdad pone Próximo premio y
   Recompensas listas arriba y Nivel debajo, todos alineados a la izquierda;
   el reparto de dos columnas con la segunda a la derecha no sirve aquí. */
.pase-wallet__campos--tres {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.55em 0.6em;
}

.pase-wallet__campos--tres .pase-wallet__campo:last-child { text-align: left; }

/* El icono de la aplicación, pegado al nombre, como en el pase real. */
.pase-wallet__marca--logo {
  display: flex;
  align-items: center;
  gap: 0.45em;
}

.pase-wallet__icono {
  width: 1.5em;
  height: 1.5em;
  flex: none;
  border-radius: 0.34em;
  overflow: hidden;
  display: block;
}

.pase-wallet__icono svg { width: 100%; height: 100%; display: block; }

/* El sello relleno lleva el color de su propia tarjeta. En /fidelidad estaba
   escrito a mano en morado porque alli el unico pase con sellos era morado;
   aqui hay dos y no son del mismo color, asi que el valor pasa a variable y el
   morado se queda de respaldo. */
.pase-demo .stamp.is-filled { color: var(--pase-sello, #5a37e0); }

.baraja-demo__wallets {
  /* `.pastilla-wallet` mide sobre --tel, que aqui no existe: se le da el ancho
     del pase para que el tamano de letra salga de la misma cuenta. */
  --tel: var(--pase-ancho, 280px);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem;
  margin-top: clamp(1.6rem, 4vw, 2.4rem);
}

/* El formulario ----------------------------------------------------------- */

.demo__form { margin-top: 1.9rem; max-width: 26rem; }

.campo label {
  display: block;
  margin-bottom: 0.5rem;
  font-size: var(--step--1);
  font-weight: 600;
  letter-spacing: 0.01em;
}

/* El +51 no es un campo: es una etiqueta pegada al borde izquierdo. Un selector
   de pais con una sola opcion es una pregunta que no se hace. */
.campo__tel {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: var(--radius-s);
  background: rgba(255, 255, 255, 0.04);
  transition: border-color 0.2s var(--ease-out), background 0.2s var(--ease-out);
}

.campo__tel:focus-within {
  border-color: var(--accent);
  background: rgba(124, 92, 255, 0.08);
}

.campo__pais {
  display: grid;
  place-items: center;
  padding-inline: 0.9rem;
  border-right: 1px solid var(--line);
  color: var(--fg-faint);
  font-variant-numeric: tabular-nums;
}

.campo__tel input {
  flex: 1;
  min-width: 0;
  padding: 0.85rem 1rem;
  border: 0;
  background: transparent;
  color: var(--fg);
  font: inherit;
  font-size: var(--step-0);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

.campo__tel input:focus { outline: none; }

.campo__tel input::placeholder { color: var(--fg-faint); opacity: 0.7; }

.campo__ayuda {
  margin: 0.55rem 0 0;
  font-size: var(--step--1);
  color: var(--fg-faint);
}

.campo.is-mal .campo__tel { border-color: #ff7a59; }

/* El aviso de privacidad, debajo del boton.

   Hasta el 13/10/26 esto era una casilla obligatoria (`.check`, borrada con
   este cambio): un toque mas antes del unico boton que importa, y el toque
   que mas gente dejaba a medias. Ahora el consentimiento se da al pulsar y el
   texto esta pegado a la accion, que es donde se lee.

   Debajo y no encima a proposito: encima es letra pequena que se salta, y
   debajo del boton es lo ultimo que queda en pantalla. */
.demo__legal {
  margin: 1.1rem 0 0;
  max-width: 34rem;
  font-size: calc(var(--step--1) * 0.92);
  line-height: 1.5;
  color: var(--fg-faint);
}

.demo__legal a {
  color: var(--fg-muted);
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.demo__legal a:hover { color: var(--accent-hi); }

/* Escondido fuera de pantalla y sin tabulacion: una persona no lo ve y los
   robots que rellenan todo, si. El mismo de /pedido. */
.trampa { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.demo__estado {
  margin: 1.2rem 0 0;
  min-height: 1.4em;
  font-size: var(--step--1);
  line-height: 1.45;
  color: #ff9d85;
}

.demo__estado:empty { margin: 0; min-height: 0; }

/* El QR ------------------------------------------------------------------- */

.qr {
  display: flex;
  align-items: center;
  gap: 1.3rem;
  margin-top: 1.9rem;
}

/* Sobre blanco aunque la pagina sea oscura, y con su margen de silencio
   alrededor: un lector necesita el contraste y necesita el borde. */
.qr__marco {
  flex: none;
  width: clamp(168px, 34vw, 212px);
  padding: 0.9rem;
  border-radius: var(--radius-m);
  background: #fff;
  box-shadow: 0 1.2rem 2.4rem -1rem rgba(0, 0, 0, 0.7);
}

.qr__marco svg {
  display: block;
  width: 100%;
  height: auto;
  color: #0a0a12;
}

.qr__pie {
  margin: 0;
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--fg-faint);
  word-break: break-word;
}

/* En un telefono el QR de la pantalla no se puede escanear con la camara de
   ese mismo telefono. Ahi el codigo pasa a ser lo secundario (vale para
   pasarselo a otra persona) y el boton es el camino. */
@media (pointer: coarse) {
  .qr { flex-direction: column; align-items: flex-start; }
  .qr__marco { width: clamp(140px, 42vw, 180px); }
}

/* Movimiento reducido y modo ligero --------------------------------------- */

/* Lo que se apaga aqui es solo lo que se mueve. La tarjeta, los colores y los
   tres pasos se quedan exactamente igual. */
@media (prefers-reduced-motion: reduce) {
  .pase-demo__brillo::before { animation: none; opacity: 0; }
  .demo__panel.is-now { animation: none; }
  .pase-demo { transition: none; }
}

/* El barrido corre toda la visita, y detras lleva un degradado del tamano de
   la tarjeta: es justo el tipo de adorno que el modo ligero existe para
   quitar. El punto de luz del cursor no hace falta apagarlo porque en modo
   ligero demo.js ni siquiera monta la inclinacion. */
html.tc-ligero .pase-demo__brillo { display: none; }
html.tc-ligero .demo__glow {
  background: radial-gradient(circle, rgba(124, 92, 255, 0.28), rgba(124, 92, 255, 0) 58%);
}
