/* ==========================================================================
   VIM POS — hoja de estilos del sitio.

   FUENTE DE VERDAD DE LA PALETA: vim-pos/packages/ui/tokens.css
   Los colores y las tipografías se copian de ahí sin tocar, para que el sitio y
   el producto se vean como la misma cosa. Si cambian allá, cambian aquí.
   (Ese fue un bug real: el preset de Tailwind se quedó con el color de marca
   anterior y medio producto salió del color viejo durante semanas.)

   Lo que SÍ es propio de la web y no se hereda:
     · escala tipográfica fija y más grande — una web necesita más aire que un POS
     · controles de 40 px en vez de 44 (48 solo en el CTA del hero)
     · el acento es color de marca, no "la acción": UN botón azul lleno por
       pantalla, el resto fantasmas, para que no compita con las capturas
   ========================================================================== */

:root {
  /* ---- Heredado del producto ---- */
  --accent: #0078C9;
  --accent-hover: #0063A8;
  --accent-soft: #EAF3FB;

  --ink: #16161A;
  --ink-2: #5A5A60;
  --ink-3: #6E6E74;

  --success: #2E7D52;
  --warning: #9A6B12;
  --danger: #C0392B;

  --bg: #FFFFFF;
  --surface: #FBFBFA;
  --line: #ECECE9;
  --line-strong: #DDDDD9;
  --hover: #F6F6F4;

  --font: 'Inter Tight', system-ui, -apple-system, sans-serif;
  --font-display: 'Sora', system-ui, sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, monospace;

  --r-sm: 4px;
  --r: 6px;
  --r-lg: 8px;

  /* ---- Escala tipográfica fija, propia de la web ----
     Ocho pasos y ni uno más. El sistema del producto declaraba esto como deuda
     pendiente; aquí se resuelve de entrada para no improvisar tamaños por página. */
  --t-12: 12px;
  --t-13: 13px;
  --t-15: 15px;
  --t-18: 18px;
  --t-24: 24px;
  --t-32: 32px;
  --t-44: 44px;
  --t-60: 60px;

  /* ---- Ritmo vertical ---- */
  --sp-1: 4px;  --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px;  --sp-8: 48px;  --sp-10: 64px;
  --sp-12: 96px; --sp-16: 128px;

  --ancho: 1180px;
  --ancho-texto: 68ch;

  /* ---- Curvas de easing ----
     Las de CSS son demasiado flojas: `ease-out` a secas no tiene el remate que
     hace que una animación se sienta intencionada. Estas son las variantes
     fuertes, y son las únicas que se usan en todo el sitio.

     Y NUNCA `ease-in` en interfaz: empieza lento justo en el instante que el
     usuario está mirando, así que a igual duración se SIENTE más lenta. */
  --sal: cubic-bezier(0.23, 1, 0.32, 1);        /* entra o sale */
  --mueve: cubic-bezier(0.77, 0, 0.175, 1);     /* se desplaza en pantalla */
  --cajon: cubic-bezier(0.32, 0.72, 0, 1);      /* panel lateral, curva de iOS */

  /* ---- Duraciones ----
     Todo por debajo de 300 ms. Un desplegable de 180 ms se siente más
     responsivo que uno de 400, aunque los dos sean "rápidos". */
  --v-toque: 140ms;   /* respuesta al presionar */
  --v-corta: 180ms;   /* hover, tooltips */
  --v-media: 240ms;   /* desplegables, acordeón */
}

/* ==========================================================================
   Base
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;

  /* Red de seguridad contra el scroll horizontal. La causa real de que
     apareciera —el cajón atrapado dentro del header— está arreglada en el
     HTML; esto queda porque un sitio con carruseles y tablas anchas es
     terreno fértil para que vuelva por otro lado, y de lado la página no se
     debe mover nunca.

     `clip` y NO `hidden`: los dos recortan, pero `hidden` convierte al elemento
     en un contenedor de scroll y eso rompe el `position: sticky` del
     encabezado, que dejaría de quedarse arriba al bajar. */
  overflow-x: clip;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  font-size: var(--t-15);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  letter-spacing: -0.01em;
}

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

/* `hidden` tiene que ganar SIEMPRE.
   
   El navegador lo aplica como `[hidden] { display: none }`, que es la
   especificidad de un atributo y por tanto pierde contra cualquier regla de
   clase que ponga `display`. Fue un bug real: en demo.html la confirmación de
   envío lleva `hidden` y también la clase `.pila` (`display: flex`), así que
   ganaba la clase y el mensaje de "ya nos llegó" salía debajo del formulario
   desde que cargaba la página, sin que nadie hubiera enviado nada.
   
   Es de los pocos sitios donde `!important` es lo correcto: no hay ningún caso
   en el que un elemento con `hidden` deba verse, así que no hay nada que se
   pueda romper por encima. */
[hidden] { display: none !important; }

h1, h2, h3 {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
  line-height: 1.1;
  text-wrap: balance;
  margin: 0;
}

h1 { font-size: clamp(var(--t-32), 6vw, var(--t-60)); font-weight: 700; }
h2 { font-size: clamp(var(--t-24), 4vw, var(--t-44)); font-weight: 600; }
h3 { font-size: var(--t-24); font-weight: 600; }

p { margin: 0; color: var(--ink-2); max-width: var(--ancho-texto); }

a { color: var(--ink); text-underline-offset: 3px; }

/* El foco de teclado se ve SIEMPRE. Un sitio donde no se ve por dónde vas es
   inutilizable sin ratón, y quitarlo es la forma más rápida de romperlo. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* ==========================================================================
   Utilidades de composición
   El espaciado lo pone el layout con `gap`, no márgenes por elemento: los
   márgenes se colapsan o se duplican en silencio y luego nadie sabe por qué
   una sección respira distinto que la de al lado.
   ========================================================================== */

.contenedor {
  width: 100%;
  max-width: var(--ancho);
  margin-inline: auto;
  padding-inline: var(--sp-5);
}

.pila { display: flex; flex-direction: column; }
.pila-2 { gap: var(--sp-2); }
.pila-3 { gap: var(--sp-3); }
.pila-4 { gap: var(--sp-4); }
.pila-5 { gap: var(--sp-5); }
.pila-6 { gap: var(--sp-6); }
.pila-8 { gap: var(--sp-8); }
.pila-10 { gap: var(--sp-10); }

.fila { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }

.seccion { padding-block: clamp(var(--sp-10), 10vw, var(--sp-16)); }

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--t-12);
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* Contenido ancho —tablas, código— con su propio scroll, para que el cuerpo de
   la página nunca se desplace de lado. */
.desborda { overflow-x: auto; }

.solo-lectores {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

/* ==========================================================================
   Botones
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  height: 40px;
  padding-inline: var(--sp-5);
  border: 1px solid transparent;
  border-radius: var(--r);
  font-family: var(--font);
  font-size: var(--t-15);
  font-weight: 600;
  letter-spacing: -0.01em;
  text-decoration: none;
  cursor: pointer;
  white-space: nowrap;
  /* Propiedades explícitas, nunca `all`: con `all` acabas animando cosas que no
     querías —incluido el color del foco— y pagando layout en cada cambio. */
  transition:
    transform var(--v-toque) var(--sal),
    background-color var(--v-corta) ease,
    border-color var(--v-corta) ease,
    color var(--v-corta) ease;
}

/* El hover solo donde hay puntero fino. En táctil se dispara al tocar y deja el
   botón "pegado" en estado hover después de soltarlo. */
@media (hover: hover) and (pointer: fine) {
  .btn-primario:hover { background: var(--accent-hover); }
  .btn-fantasma:hover { background: var(--hover); border-color: var(--line-strong); }
}

/* La respuesta al presionar sí va en todos lados, táctil incluido: es lo que
   hace que la interfaz parezca estar escuchando. Sutil — 0.97, no 0.9. */
.btn:active { transform: scale(0.97); }

.btn-primario {
  background: var(--accent);
  color: #fff;
}

.btn-fantasma {
  background: transparent;
  color: var(--ink);
  border-color: var(--line-strong);
}

/* Solo en el hero. 48 px porque es el único botón de la página que alguien
   busca antes de haber leído nada. */
.btn-hero { height: 48px; font-size: var(--t-18); padding-inline: var(--sp-6); }

@media (prefers-reduced-motion: reduce) {
  .btn { transition-property: background-color, border-color, color; }
  .btn:active { transform: none; }
}

/* ==========================================================================
   Encabezado
   ========================================================================== */

.nav {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in srgb, var(--bg) 88%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}

.nav-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  height: 64px;
}

.nav-logo {
  display: flex; align-items: center; gap: var(--sp-3);
  flex-shrink: 0; text-decoration: none; color: var(--ink);
}
.nav-logo img { height: 30px; width: 30px; }
.nav-logo span {
  font-family: var(--font-display); font-size: var(--t-18); font-weight: 700;
  letter-spacing: -0.02em;
}

/* `stretch` y no `center`: cada entrada mide los 64 px de la barra. Así el
   disparador de un panel toca el borde de abajo y el mouse pasa de él al panel
   sin cruzar un hueco que lo cierre. */
.nav-enlaces { display: none; gap: var(--sp-5); align-items: stretch; align-self: stretch; }
@media (min-width: 900px) { .nav-enlaces { display: flex; } }

.nav-enlace {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 0; border: 0; background: none; cursor: pointer;
  font-family: var(--font); letter-spacing: inherit;
  color: var(--ink-2);
  text-decoration: none;
  font-size: var(--t-15);
  font-weight: 500;
  transition: color var(--v-corta) ease;
}
@media (hover: hover) and (pointer: fine) {
  .nav-enlace:hover { color: var(--ink); }
}
.nav-enlace[aria-current="page"],
.nav-grupo[data-actual] .nav-enlace,
.nav-grupo[data-abierto="true"] .nav-enlace { color: var(--ink); }

/* ---- Paneles del menú (Producto, Recursos) --------------------------------
   Tres enlaces dejaban fuera más de la mitad del sitio: Sin internet, las
   guías, Novedades y las páginas por giro solo se alcanzaban desde el pie.

   El panel cuelga de `.nav` —que es sticky y por eso ya es su bloque
   contenedor— y ocupa todo el ancho: blanco, letra grande y líneas finas, sin
   fotos. Entra en 180 ms y sale en 120: al abrir hay que ver de dónde viene, al
   cerrar ya se decidió.

   Sin JavaScript abre con el mouse encima o con el foco dentro; vim.js marca
   `data-js` y desde ahí manda él (clic, Escape, y el `aria-expanded`). */
.nav-grupo { display: flex; }

.nav-flecha { width: 14px; height: 14px; transition: transform var(--v-corta) var(--sal); }
.nav-grupo[data-abierto="true"] .nav-flecha { transform: rotate(180deg); }

.nav-panel {
  position: absolute;
  inset: 100% 0 auto 0;
  background: var(--bg);
  border-block: 1px solid var(--line);
  box-shadow: 0 24px 40px -28px rgba(22, 22, 26, .22);
  opacity: 0;
  visibility: hidden;
  transform: translateY(-4px);
  transition: opacity 120ms ease, transform 120ms ease, visibility 0s linear 120ms;
}
.nav-grupo[data-abierto="true"] .nav-panel,
.nav-enlaces:not([data-js]) .nav-grupo:is(:hover, :focus-within) .nav-panel {
  opacity: 1;
  visibility: visible;
  transform: none;
  transition: opacity var(--v-corta) var(--sal), transform var(--v-corta) var(--sal), visibility 0s;
}
/* De un panel al de al lado no hay entrada: el segundo ya «estaba abierto». */
.nav-enlaces[data-instante] .nav-panel { transition-duration: 0s; }

.nav-panel-rejilla {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-8);
  padding-block: var(--sp-6) var(--sp-8);
}
.nav-panel-titulo {
  font-family: var(--font-mono);
  font-size: var(--t-12);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0 0 var(--sp-3);
  max-width: none;
}
.nav-panel-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-1); }
.nav-panel-lista a {
  display: block;
  padding: var(--sp-2) var(--sp-3);
  margin-inline: calc(-1 * var(--sp-3));     /* el fondo del hover sobresale; el texto queda alineado con el título */
  border-radius: var(--r);
  text-decoration: none;
  transition: background-color var(--v-corta) ease;
}
.nav-panel-lista b {
  display: block;
  font-family: var(--font-display); font-size: var(--t-18); font-weight: 600;
  letter-spacing: -0.01em; color: var(--ink);
}
.nav-panel-lista span { display: block; font-size: var(--t-13); color: var(--ink-2); }
.nav-panel-lista a[aria-current="page"] b { color: var(--accent-hover); }
@media (hover: hover) and (pointer: fine) {
  .nav-panel-lista a:hover { background: var(--hover); }
}
.nav-panel-destacado {
  align-self: start;
  padding: var(--sp-5);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
}

@media (prefers-reduced-motion: reduce) {
  .nav-panel, .nav-grupo[data-abierto="true"] .nav-panel, .nav-flecha { transition: none; transform: none; }
}

.nav-acciones { display: flex; align-items: center; gap: var(--sp-3); }

/* La prueba gratis es la acción SECUNDARIA: fantasma, a la izquierda de la
   demo, y solo cuando hay ancho de sobra. En el teléfono no cabe junto al logo
   y la hamburguesa, así que vive en el cajón del menú. */
.nav-prueba { display: none; }
@media (min-width: 1024px) { .nav-prueba { display: inline-flex; } }

.nav-hamburguesa {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px; height: 40px;
  background: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  cursor: pointer;
  color: var(--ink);
  transition: transform var(--v-toque) var(--sal), background-color var(--v-corta) ease;
}
.nav-hamburguesa:active { transform: scale(0.97); }
@media (min-width: 900px) { .nav-hamburguesa { display: none; } }

/* ==========================================================================
   Menú móvil — cajón lateral

   Antes se desplegaba hacia abajo empujando la página. Dos problemas: el
   contenido saltaba bajo el dedo justo cuando el usuario iba a tocar algo, y
   con cuatro enlaces el encabezado ocupaba media pantalla.

   Ahora entra desde la derecha —el lado donde está la hamburguesa, así que
   sale de donde lo pediste— y va POR ENCIMA de la página: nada se mueve
   debajo.

   El alto ya no lo escribe el JS. Un `translateX` entre dos valores fijos
   interpola en cualquier navegador y corre fuera del hilo principal, cosa que
   animar `height` nunca hizo. El JS se queda solo con lo que CSS no puede: el
   foco y el bloqueo del scroll.

   EL CAJÓN VIVE FUERA DEL <header>, Y NO SE PUEDE MOVER DENTRO.

   `.nav` lleva `backdrop-filter`, y eso —igual que `transform` o `filter`—
   convierte al elemento en bloque contenedor de sus descendientes `fixed`. Con
   el cajón dentro del header, su `position: fixed` se resolvía contra el
   header: medía 320×64 px en vez de ocupar la pantalla de arriba abajo, y sus
   320 px de ancho fuera de pantalla sumaban al scroll horizontal de la página.

   Si algún día alguien lo devuelve al header "porque es donde va el menú",
   reaparecen las dos cosas a la vez.
   ========================================================================== */

.nav-movil {
  position: fixed;
  inset: 0 0 0 auto;                    /* pegado a la derecha, de arriba abajo */
  width: min(320px, 86vw);
  z-index: 60;                          /* por encima del encabezado (50) */

  background: var(--bg);
  border-left: 1px solid var(--line);
  box-shadow: -24px 0 48px -24px rgba(22, 22, 26, .18);

  /* Fuera de pantalla del todo, y no a `scale(0)` ni a `opacity: 0`: un cajón
     se desliza, no aparece. */
  transform: translateX(100%);
  visibility: hidden;

  overflow-y: auto;
  overscroll-behavior: contain;         /* al llegar al final no arrastra la página */

  transition:
    transform var(--v-media) var(--cajon),
    visibility 0s linear var(--v-media);
}

.nav-movil[data-abierto="true"] {
  transform: translateX(0);
  visibility: visible;
  transition:
    transform var(--v-media) var(--cajon),
    visibility 0s;                      /* al abrir, visible YA; al cerrar, tras la animación */
}

/* El velo cuelga del BODY, no del cajón.
   
   La primera versión lo puso como `::before` del propio cajón para no añadir
   un nodo, y no cubría la pantalla: el cajón lleva `transform`, y un ancestro
   transformado hace que `position: fixed` se resuelva contra ÉL en vez de
   contra la ventana. El velo salía de 320 px de ancho y se movía con el cajón,
   así que tocar fuera no cerraba nada — el toque llegaba al texto de debajo.
   
   El body no está transformado, así que aquí `fixed` sí significa fixed. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  background: rgba(22, 22, 26, .32);
  opacity: 0;
  visibility: hidden;
  z-index: 55;                          /* bajo el cajón (60), sobre el encabezado (50) */
  transition: opacity var(--v-media) ease, visibility 0s linear var(--v-media);
}
body[data-menu-abierto="true"]::before {
  opacity: 1;
  visibility: visible;
  transition: opacity var(--v-media) ease, visibility 0s;
}

.nav-movil-lista {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-10) var(--sp-5) var(--sp-5);   /* arriba, hueco para el botón de cerrar */
}
.nav-movil-lista > a,
.nav-movil-lista summary {
  padding: var(--sp-3) 0;
  color: var(--ink);
  text-decoration: none;
  font-size: var(--t-18);
  font-weight: 500;
  border-bottom: 1px solid var(--line);
}
.nav-movil-lista > a:last-child { border-bottom: 0; }
.nav-movil-lista a[aria-current="page"] { color: var(--accent-hover); }

/* Los grupos del cajón son <details> a secas: abrir y cerrar lo hace el
   navegador, con teclado incluido, y sin una línea de JavaScript. El signo es
   el mismo «+» que gira del acordeón. */
.nav-movil-lista summary {
  display: flex; align-items: center; justify-content: space-between;
  cursor: pointer; list-style: none;
}
.nav-movil-lista summary::-webkit-details-marker { display: none; }
.nav-movil-lista summary::after {
  content: "+";
  font-size: var(--t-24); font-weight: 400; line-height: 1; color: var(--ink-3);
  transition: transform var(--v-media) var(--sal);
}
.nav-movil-lista details[open] > summary::after { transform: rotate(45deg); }
.nav-movil-lista details a {
  display: block;
  padding: var(--sp-3) 0 var(--sp-3) var(--sp-4);
  color: var(--ink-2);
  text-decoration: none;
  font-size: var(--t-15);
}
.nav-movil-lista details[open] { border-bottom: 1px solid var(--line); padding-bottom: var(--sp-2); }

/* El botón de cerrar vive dentro del cajón: con el menú abierto, la
   hamburguesa del encabezado queda debajo del velo y no se puede tocar. */
.nav-cerrar {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  width: 40px; height: 40px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--bg);
  color: var(--ink-2);
  cursor: pointer;
  transition: transform var(--v-toque) var(--sal);
}
.nav-cerrar:active { transform: scale(0.94); }

/* Mientras el cajón está abierto, la página de debajo no se desplaza. */
body[data-menu-abierto="true"] { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .nav-movil, body::before, .nav-cerrar, .nav-movil-lista summary::after { transition: none; }
}

/* El cajón se apaga en el MISMO punto en que desaparece la hamburguesa (900).
   Con 860 quedaba una franja de 40 px donde el botón seguía a la vista y no
   abría nada. */
@media (min-width: 900px) {
  .nav-movil, .nav-movil[data-abierto="true"] {
    display: none;
  }
}

/* ==========================================================================
   Pie
   ========================================================================== */

.pie {
  border-top: 1px solid var(--line);
  padding-block: var(--sp-10) var(--sp-8);
  margin-top: var(--sp-12);
}

.pie-rejilla {
  display: grid;
  gap: var(--sp-8);
  grid-template-columns: 1fr;
}
@media (min-width: 700px) { .pie-rejilla { grid-template-columns: 1.6fr 1fr 1fr 1fr; } }

.pie .pie-titulo {
  /* Es un h2 —el nivel que le toca en el esquema del documento— pero se ve
     como una etiqueta pequeña. El nivel es semántica; el tamaño, diseño. */
  font-family: var(--font-mono);
  font-size: var(--t-12);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
  margin: 0 0 var(--sp-3);
}

.pie-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.pie-lista a { color: var(--ink-2); text-decoration: none; font-size: var(--t-15); transition: color var(--v-corta) ease; }
@media (hover: hover) and (pointer: fine) { .pie-lista a:hover { color: var(--ink); } }
/* Con el dedo, 19 px de alto no se atinan: en pantallas táctiles cada enlace del
   pie ocupa 44 px y el hueco entre ellos lo pone el propio enlace. */
@media (pointer: coarse) {
  .pie-lista { gap: 0; }
  .pie-lista a, .pie-legal a, .pie-cookies { display: inline-flex; align-items: center; min-height: 44px; }
}

.pie-legal {
  margin-top: var(--sp-8);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--line);
  color: var(--ink-2);
  font-size: var(--t-13);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3) var(--sp-5);
  justify-content: space-between;
}

/* ==========================================================================
   Hero
   ========================================================================== */

.hero { padding-block: clamp(var(--sp-10), 9vw, var(--sp-12)) var(--sp-10); }
.hero-rejilla { display: grid; gap: var(--sp-10); align-items: center; }
@media (min-width: 980px) { .hero-rejilla { grid-template-columns: 1fr 1fr; gap: var(--sp-8); } }

.hero p.entradilla { font-size: var(--t-18); max-width: 52ch; }

/* El precio bajo los botones del hero: dato, no anuncio. Mismo cuerpo que la letra
   de apoyo, con la cifra en tinta para que se encuentre de un vistazo. */
.hero-precio { font-size: var(--t-15); color: var(--ink-2); max-width: none; }
.hero-precio strong { color: var(--ink); font-weight: 600; }
.hero-precio a { color: var(--ink-2); text-underline-offset: 3px; white-space: nowrap; }
@media (hover: hover) and (pointer: fine) {
  .hero-precio a:hover { color: var(--accent); }
}

.prueba-social {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  font-size: var(--t-13); color: var(--ink-2);
}
.prueba-social::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: var(--success); flex-shrink: 0;
}

/* El escenario de la captura del hero. El marco blanco con borde gris claro
   sobre fondo blanco se leía como una miniatura; sobre el azul suave se
   despega sin necesitar sombra más fuerte ni un fondo oscuro, que en la web no
   se usa. En pantallas anchas crece hacia el margen derecho —hasta 160 px— y
   es de ahí de donde la captura saca su tamaño. */
.hero-escenario {
  background: var(--accent-soft);
  border-radius: var(--r-lg);
  padding: clamp(var(--sp-4), 2.4vw, var(--sp-6));
  /* Un solo momento de entrada en toda la primera pantalla; lo demás aparece
     quieto. `backwards` sostiene el estado inicial durante el retraso. */
  animation: hero-entra var(--v-media) var(--sal) 80ms backwards;
}
@media (min-width: 980px) {
  .hero-escenario { margin-right: calc(-1 * clamp(0px, (100vw - var(--ancho)) / 2, 160px)); }
}
@keyframes hero-entra { from { opacity: 0; transform: scale(0.98); } }
@keyframes hero-entra-fundido { from { opacity: 0; } }
@media (prefers-reduced-motion: reduce) {
  .hero-escenario { animation-name: hero-entra-fundido; }
}

/* ==========================================================================
   Marco de monitor — dibujado, no imagen
   Mientras no existan las capturas, dentro va un marcador que dice qué irá ahí.
   Es deliberado que se vea como un hueco y no como una captura difuminada: un
   placeholder que parece producto acaba publicándose por accidente.
   ========================================================================== */

.monitor {
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(22,22,26,.04), 0 12px 32px -12px rgba(22,22,26,.18);
}
.monitor-barra {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--line);
  background: var(--bg);
}
.monitor-punto { width: 9px; height: 9px; border-radius: 50%; background: var(--line-strong); }

.hueco {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: var(--sp-2); min-height: 300px; padding: var(--sp-6);
  background:
    repeating-linear-gradient(45deg,
      var(--hover) 0 10px, transparent 10px 20px);
  text-align: center;
}
.hueco strong { font-size: var(--t-15); color: var(--ink-2); font-weight: 600; }
.hueco span { font-size: var(--t-13); color: var(--ink-2); }

/* El chip que flota sobre la captura del hero. Es el argumento del producto en
   cuatro palabras, así que va encima de la imagen y no debajo. */
.chip-offline {
  position: absolute; left: var(--sp-5); bottom: var(--sp-5);
  display: inline-flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-4);
  background: var(--ink); color: #fff;
  border-radius: 999px;
  font-size: var(--t-13); font-weight: 600;
  box-shadow: 0 8px 24px -8px rgba(22,22,26,.5);
}
.chip-offline::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--warning);
}
.monitor-envoltura { position: relative; }

/* ==========================================================================
   Franja de hechos
   ========================================================================== */

.hechos {
  display: grid; gap: 1px;
  grid-template-columns: repeat(2, 1fr);
  background: var(--line);
  border-block: 1px solid var(--line);
}
@media (min-width: 980px) { .hechos { grid-template-columns: repeat(4, 1fr); } }
.hecho { background: var(--bg); padding: var(--sp-6) var(--sp-5); }
.hecho p { font-size: var(--t-15); color: var(--ink); font-weight: 500; max-width: none; }

/* ==========================================================================
   Rejillas de contenido
   ========================================================================== */

.tres { display: grid; gap: var(--sp-6); }
@media (min-width: 800px) { .tres { grid-template-columns: repeat(3, 1fr); } }

.tarjeta {
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
  background: var(--bg);
}

/* Bloques alternados de imagen y texto. El `order` invierte el par en pantallas
   anchas; en móvil siempre va la imagen primero, porque es lo que engancha. */
.alterna { display: grid; gap: var(--sp-6); align-items: center; }
@media (min-width: 900px) {
  .alterna { grid-template-columns: 1fr 1fr; gap: var(--sp-10); }
  .alterna.invertido .alterna-texto { order: 2; }
}

/* ==========================================================================
   Acordeón — giros y preguntas frecuentes
   La altura la escribe el JS, igual que el menú, y por la misma razón.
   ========================================================================== */

.acordeon { border-top: 1px solid var(--line); }
.acordeon-item { border-bottom: 1px solid var(--line); }

.acordeon-boton {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  width: 100%; padding: var(--sp-5) 0;
  background: none; border: 0; cursor: pointer;
  font-family: var(--font-display); font-size: var(--t-18); font-weight: 600;
  color: var(--ink); text-align: left; letter-spacing: -0.01em;
  transition: color var(--v-corta) ease;
}
@media (hover: hover) and (pointer: fine) {
  .acordeon-boton:hover { color: var(--accent); }
}

.acordeon-signo {
  flex-shrink: 0; width: 20px; height: 20px;
  color: var(--ink-3);
  transition: transform var(--v-media) var(--sal);
}
.acordeon-item[data-abierto="true"] .acordeon-signo { transform: rotate(45deg); }

.acordeon-panel {
  height: 0; overflow: hidden;
  transition: height var(--v-media) var(--sal);
}
.acordeon-panel > div { padding-bottom: var(--sp-5); }
/* Una fila que llega abierta desde el HTML: su alto es el de su contenido.
   Cuando vim.js la abre o la cierra escribe el alto en línea, que gana a esto. */
.acordeon-item[data-abierto="true"] > .acordeon-panel { height: auto; }

@media (prefers-reduced-motion: reduce) {
  .acordeon-signo, .acordeon-panel { transition: none; }
}

/* ==========================================================================
   Precios resumidos
   ========================================================================== */

.planes { display: grid; gap: var(--sp-5); }
@media (min-width: 860px) { .planes { grid-template-columns: repeat(3, 1fr); } }

.plan {
  background: var(--bg);
  display: flex; flex-direction: column; gap: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  padding: var(--sp-6);
}
.plan.destacado { border-color: var(--accent); border-width: 2px; }
.plan-precio { font-family: var(--font-display); font-size: var(--t-32); font-weight: 700; letter-spacing: -0.02em; }
.plan-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.plan-lista li { font-size: var(--t-15); color: var(--ink-2); }

/* ==========================================================================
   Cierre
   ========================================================================== */

.cierre {
  background: var(--ink);
  border-radius: var(--r-lg);
  padding: clamp(var(--sp-8), 7vw, var(--sp-12)) var(--sp-6);
  text-align: center;
}
.cierre h2 { color: #fff; }
.cierre p { color: #B8B8B4; margin-inline: auto; }
.cierre .btn-fantasma { color: #fff; border-color: rgba(255,255,255,.28); }
@media (hover: hover) and (pointer: fine) {
  .cierre .btn-fantasma:hover { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.4); }
}

/* Fondo suave para la sección de sin internet, que es la de más peso. */
.destacada-suave { background: var(--hover); border-block: 1px solid var(--line); }

/* ==========================================================================
   Rejilla de dos
   ========================================================================== */

.dos { display: grid; gap: var(--sp-6); }
@media (min-width: 800px) { .dos { grid-template-columns: repeat(2, 1fr); } }

/* ==========================================================================
   Tablas comparativas

   Son el argumento entero de dos páginas, así que no se dejan al estilo por
   omisión del navegador. Dos decisiones que cargan el peso:

     · La columna de VIM POS lleva fondo propio y se marca con `scope="col"`.
       El lector no debería tener que contar columnas para saber cuál es la
       nuestra, y quien usa lector de pantalla tampoco.
     · En el teléfono NO se parte por columnas (una tarjeta por plan): una
       comparativa se lee comparando, y eso lo destruiría. Se apila por RENGLÓN:
       el concepto arriba y las tres columnas lado a lado debajo, cada una con su
       nombre (`.tabla-apilada`, abajo). Antes se desplazaba de lado y en 350 px
       solo se veía Esencial: Negocio y Cadena quedaban fuera de pantalla.
   ========================================================================== */

.tabla-envoltura { overflow-x: auto; border: 1px solid var(--line); border-radius: var(--r-lg); }

.tabla {
  width: 100%;
  min-width: 620px;
  border-collapse: collapse;
  font-size: var(--t-15);
  font-variant-numeric: tabular-nums;
}

.tabla th, .tabla td {
  padding: var(--sp-4) var(--sp-5);
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--line);
}
.tabla tbody tr:last-child th,
.tabla tbody tr:last-child td { border-bottom: 0; }

.tabla thead th {
  font-family: var(--font-display);
  font-size: var(--t-15);
  font-weight: 600;
  color: var(--ink);
  background: var(--surface);
  white-space: nowrap;
}

/* La primera columna dice de qué trata la fila: se queda fija al desplazar. */
.tabla tbody th {
  font-weight: 500;
  color: var(--ink);
  position: sticky;
  left: 0;
  background: var(--bg);
  min-width: 190px;
}
.tabla thead th:first-child { position: sticky; left: 0; z-index: 1; }

.tabla td { color: var(--ink-2); }

.tabla .col-vim { background: var(--accent-soft); color: var(--ink); }
.tabla thead .col-vim { color: var(--accent-hover); }

/* Marcas de sí / no. El color no va solo: lleva glifo, porque el 8 % de los
   hombres no distingue el verde del rojo y una tabla ilegible para ellos es
   una tabla que no cumple su función. */
.marca { display: inline-flex; align-items: center; gap: var(--sp-2); }
.marca::before { font-weight: 700; font-size: var(--t-18); line-height: 1; }
.marca-si::before  { content: "✓"; color: var(--success); }
.marca-no::before  { content: "✕"; color: var(--danger); }
.marca-mas::before { content: "~"; color: var(--warning); }

/* Tabla apilada por renglón, solo en el teléfono. Cada <td> trae en data-plan el
   nombre de su columna (se escribe en el HTML, no lo pone el JS) y la tabla lleva
   roles ARIA explícitos: cambiar el display de una tabla le quita la semántica en
   algunos navegadores, y un lector de pantalla tiene que seguir leyéndola como
   tabla. `.tabla-apilada-lista` es para textos largos: una columna debajo de otra. */
@media (max-width: 619px) {
  .tabla-envoltura:has(.tabla-apilada) { overflow: visible; }
  .tabla.tabla-apilada { min-width: 0; }
  .tabla-apilada thead {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip-path: inset(50%); white-space: nowrap;
  }
  .tabla-apilada tbody tr {
    display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
    border-bottom: 1px solid var(--line);
  }
  .tabla-apilada tbody tr:last-child { border-bottom: 0; }
  .tabla-apilada tbody th {
    grid-column: 1 / -1; position: static; min-width: 0;
    padding: var(--sp-4) var(--sp-4) var(--sp-2); border-bottom: 0;
  }
  .tabla-apilada td {
    display: flex; flex-direction: column; gap: var(--sp-1);
    padding: var(--sp-2) var(--sp-4) var(--sp-4); border-bottom: 0;
    font-size: var(--t-13); line-height: 1.4;
  }
  .tabla-apilada td::before {
    content: attr(data-plan);
    font-family: var(--font-mono); font-size: 10.5px; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase; color: var(--ink-3);
  }
  /* Fondo propio: en una banda gris, las celdas tomaban el gris y el concepto quedaba en blanco. */
  .tabla-apilada td:not(.col-vim) { background: var(--bg); }
  .tabla-apilada td.col-vim::before { color: var(--accent-hover); }
  .tabla-apilada .marca::before { font-size: var(--t-15); }

  .tabla-apilada-lista tbody tr { grid-template-columns: 1fr; }
  .tabla-apilada-lista td { padding-block: var(--sp-2); }
  .tabla-apilada-lista td:last-child { padding-bottom: var(--sp-4); }
}

/* ==========================================================================
   Pasos numerados

   El número aquí no es decoración: la secuencia es real —vuelve la señal,
   sube lo del día, se revisa, queda— y leerla en desorden la vuelve falsa.
   ========================================================================== */

.pasos { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--sp-5); counter-reset: paso; }
@media (min-width: 860px) { .pasos { grid-template-columns: repeat(4, 1fr); } }

.paso { counter-increment: paso; display: flex; flex-direction: column; gap: var(--sp-3); }
.paso::before {
  content: counter(paso);
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; flex-shrink: 0;
  border-radius: 999px;
  background: var(--accent-soft); color: var(--accent-hover);
  font-family: var(--font-mono); font-size: var(--t-13); font-weight: 600;
}
.paso h3 { font-size: var(--t-18); }
.paso p { font-size: var(--t-15); }

/* ==========================================================================
   Pastilla de estado y letra chica
   ========================================================================== */

.pastilla {
  display: inline-flex; align-items: center;
  padding: 2px var(--sp-3);
  border-radius: 999px;
  background: var(--hover); border: 1px solid var(--line-strong);
  font-size: var(--t-12); font-weight: 600; color: var(--ink-2);
  letter-spacing: 0; white-space: nowrap;
}
/* --warning (#9A6B12) sobre este crema da 4.35:1 y AA pide 4.5. El token no
   se cambia —es del producto—: se oscurece solo aquí, que es el único sitio
   del sitio donde ese color lleva texto encima. */
.pastilla-pronto { background: #FDF6E7; border-color: #EBD9AE; color: #7A5410; }

/* Lo que hay que decir aunque no venda. Se marca como aparte —no como nota al
   pie diminuta— porque esconder la letra chica es exactamente lo que hace que
   el prospecto deje de creerte el resto de la página. */
.letra-chica {
  border-left: 2px solid var(--line-strong);
  padding-left: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-3);
}
.letra-chica p { font-size: var(--t-13); color: var(--ink-2); }

/* ==========================================================================
   Conmutador mensual / anual

   Dos botones, no un interruptor: un interruptor no dice cuál de los dos
   estados está activo sin que el usuario ya sepa qué significa cada lado.
   ========================================================================== */

.conmutador {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--hover); border: 1px solid var(--line);
  border-radius: 999px;
}
.conmutador button {
  padding: var(--sp-2) var(--sp-5);
  border: 0; border-radius: 999px; background: none; cursor: pointer;
  font-family: var(--font); font-size: var(--t-13); font-weight: 600;
  color: var(--ink-2);
  transition: background var(--v-corta) ease, color var(--v-corta) ease, transform var(--v-toque) var(--sal);
}
.conmutador button[aria-pressed="true"] {
  background: var(--bg); color: var(--ink);
  box-shadow: 0 1px 2px rgba(22,22,26,.08);
}
.conmutador button:active { transform: scale(0.97); }
@media (prefers-reduced-motion: reduce) {
  .conmutador button { transition: none; }
  .conmutador button:active { transform: none; }
}

.plan-periodo { font-size: var(--t-13); color: var(--ink-2); }

/* ==========================================================================
   Formulario

   Siete campos y ni uno más. Cada campo extra cuesta conversión, así que los
   que están se ganaron el sitio: nombre y WhatsApp para poder contestar,
   negocio para saber con quién hablamos, y cajas + sucursales porque son las
   dos que deciden el plan — sin ellas la llamada empieza a ciegas.
   ========================================================================== */

.formulario { max-width: 560px; }

.campos { display: grid; gap: var(--sp-5); }
@media (min-width: 560px) { .campos { grid-template-columns: 1fr 1fr; } }
.campo-ancho { grid-column: 1 / -1; }

.campo { display: flex; flex-direction: column; gap: var(--sp-2); }

.campo label {
  font-size: var(--t-13); font-weight: 600; color: var(--ink);
  letter-spacing: 0;
}

.campo input, .campo select {
  width: 100%;
  padding: 0 var(--sp-4);
  height: 44px;                     /* 44 y no 40: se llena con el pulgar */
  border: 1px solid var(--line-strong);
  border-radius: var(--r);
  background: var(--bg);
  color: var(--ink);
  /* 16 px y no 15: por debajo de 16, Safari de iPhone hace zoom al tocar el campo
     y la página se queda desencuadrada. Única excepción a la escala de ocho pasos. */
  font-family: var(--font); font-size: 16px;
  letter-spacing: -0.01em;
  transition: border-color var(--v-corta) ease, box-shadow var(--v-corta) ease;
}
.campo select { appearance: none; padding-right: var(--sp-8); cursor: pointer;
  /* La flecha va como fondo para no meter un elemento más que posicionar. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%235A5A60' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--sp-4) center;
}

.campo input::placeholder { color: var(--ink-3); }

.campo input:focus-visible, .campo select:focus-visible {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}

/* El error se marca al soltar el campo, no mientras se escribe: avisar a la
   tercera letra de que el nombre es inválido es regañar a quien todavía no
   termina de escribirlo. */
.campo[data-mal] input, .campo[data-mal] select { border-color: var(--danger); }
.campo-error { font-size: var(--t-13); color: var(--danger); }

.campo-ayuda { font-size: var(--t-13); color: var(--ink-2); max-width: none; }

/* La trampa para robots. `display: none` la saltan los bots que leen el CSS,
   así que se saca de la pantalla en vez de ocultarla. */
.trampa {
  position: absolute; left: -9999px;
  width: 1px; height: 1px; overflow: hidden;
}

.form-error {
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--danger);
  border-radius: var(--r);
  background: #FDF1EF;
  font-size: var(--t-15); color: var(--danger);
  max-width: none;
}

.form-listo {
  padding: var(--sp-6);
  border: 1px solid var(--success);
  border-radius: var(--r-lg);
  background: #F1F8F4;
}
.form-listo h2 { font-size: var(--t-24); }

/* Mientras se envía. Sin spinner: el botón cambia de texto y se desactiva, que
   dice lo mismo con una pieza menos y no parpadea si la respuesta es rápida. */
.btn[aria-busy="true"] { opacity: .7; pointer-events: none; }

/* Los pasos en columna, para la barra lateral de la página de demo. */
.pasos-columna { grid-template-columns: 1fr; gap: var(--sp-5); }
@media (min-width: 860px) { .pasos-columna { grid-template-columns: 1fr; } }

/* ==========================================================================
   Nota de contraste (medida el 25-ago-2026, `--ink-3` al día el 12-oct-2026)

   `--ink-3` era #8E8E94 y sobre blanco daba 3.26:1, que no pasa AA. El núcleo
   lo cambió a #6E6E74 en septiembre (5.07:1) y aquí se quedó el viejo un mes:
   el token se copia a mano de `packages/ui/tokens.css`, y nada avisa cuando
   allá cambia. Si vuelve a moverse, este archivo se revisa en el mismo cambio.

   La regla de USO se queda: `--ink-3` es para lo de apoyo —marcadores de
   lista, el placeholder de un campo, lo inactivo— y el texto que se lee va en
   `--ink-2` (6.85:1).

   El resto de los pares del sistema están medidos y pasan; el más justo es el
   azul de los enlaces y de los botones, a 4.64.
   ========================================================================== */

/* ==========================================================================
   Páginas legales

   Medida de lectura estrecha y nada más. Un aviso de privacidad a todo el
   ancho de la pantalla no se lee, y no leerlo es exactamente lo que hace que
   la gente firme cosas que no entiende.
   ========================================================================== */

.texto-legal { max-width: 72ch; }
.texto-legal h2 { font-size: var(--t-24); }
.texto-legal p, .texto-legal li { max-width: none; }
.texto-legal a { color: var(--accent); }

.lista-legal {
  margin: 0; padding-left: var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-3);
  color: var(--ink-2); font-size: var(--t-15);
}
.lista-legal li::marker { color: var(--ink-3); }

/* ==========================================================================
   Figura de captura

   El hueco del home servía para una imagen suelta dentro del marco de
   monitor. Esto es la versión con pie: la captura sola no dice qué está
   mirando el visitante, y en una página que no es el home —donde ya no hay
   contexto alrededor— el pie es la mitad del trabajo.

   Se escribe como <figure> de verdad y no como un div con un párrafo debajo:
   el pie queda asociado a la imagen para quien usa lector de pantalla, y el
   `alt` puede entonces describir lo que se ve en vez de repetir el pie.
   ========================================================================== */

.figura { margin: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.figura figcaption { font-size: var(--t-13); color: var(--ink-2); max-width: 60ch; }
.figura .monitor { margin: 0; }

/* Una tira de dos o tres capturas, para cuando la sección enseña un recorrido
   y no una sola pantalla. */
.tira { display: grid; gap: var(--sp-5); }
@media (min-width: 760px) { .tira { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1040px) { .tira-3 { grid-template-columns: repeat(3, 1fr); } }
.tira .hueco { min-height: 200px; }

/* El ticket y el corte Z son rollo de papel, no pantalla: se capturan a
   640×1400 y dentro de un marco de monitor de 1100 px de ancho el papel se
   queda pegado a un lado, con media ventana vacía al otro. El marco se ajusta
   al ancho del rollo, se centra en la columna y pierde la barra de ventana:
   un ticket no sale de un navegador. */
.figura-papel { align-items: center; }
.figura-papel .monitor { max-width: min(480px, 100%); background: var(--bg); }
.figura-papel .monitor-barra { display: none; }
.figura-papel figcaption { text-align: center; }
.figura-papel .hueco { min-height: 380px; }

/* ==========================================================================
   Franja de hechos — centrada

   Las cuatro frases se alineaban a la izquierda dentro de celdas que ocupan un
   cuarto de pantalla cada una, así que cada una empezaba en un sitio distinto y
   la franja se leía como cuatro cosas sueltas en vez de como una fila. Van
   centradas en su propia celda.
   ========================================================================== */

.hecho { text-align: center; }
.hecho p { margin-inline: auto; }

/* ==========================================================================
   Franja de hechos en el teléfono — rejilla de dos por dos, quieta

   Fue una marquesina de 26 s que no se podía pausar (WCAG 2.2.2): texto que se
   lee no se mueve solo. Dos por dos cabe en una pantalla y se lee de un vistazo.
   ========================================================================== */

@media (max-width: 619px) {
  .hecho { padding: var(--sp-5) var(--sp-4); }
  .hecho p { font-size: var(--t-13); line-height: 1.45; }
}

/* ==========================================================================
   Carrusel en móvil

   Cualquier rejilla de tarjetas se convierte en una tira que se desliza de
   lado por debajo de 700 px. Apiladas, tres tarjetas son tres pantallazos de
   scroll vertical antes de llegar a lo siguiente; de lado se recorren con el
   pulgar y se ve de un vistazo que hay más de una.

   La pista de que se desliza es que la siguiente tarjeta asoma —por eso el
   ancho es 82% y no 100%—. Es más honesto que unos puntitos debajo: el corte
   se ve sin tener que interpretarlo.

   El margen negativo saca la tira del padding del contenedor para que llegue
   al borde de la pantalla, y el padding se lo devuelve por dentro: así la
   primera tarjeta sigue alineada con el texto de arriba y la última no queda
   pegada al canto.
   ========================================================================== */

/* Cada rejilla deja de ser carrusel EXACTAMENTE donde empieza a tener columnas
   propias, y no todas en el mismo punto: `.tira` a 760, `.tres` y `.dos` a 800,
   `.planes` a 860.

   Con un corte único a 700 quedaba una franja entre 700 y 800 —el iPad en
   vertical— donde ya no había carrusel pero tampoco columnas, así que las
   tarjetas se apilaban a lo ancho de toda la pantalla.

   Se escriben tres bloques casi iguales en vez de uno con reglas de reseteo
   encima. Es más largo, pero cada rejilla se activa y se apaga en su propio
   bloque y no hay que seguir la cascada para saber qué gana. */

@media (max-width: 759px) {
  .tira {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 82%;

    overflow-x: auto;
    overscroll-behavior-x: contain;    /* no arrastra la página al llegar al final */
    scroll-snap-type: x mandatory;
    scroll-padding-inline-start: var(--sp-5);

    margin-inline: calc(-1 * var(--sp-5));
    padding-inline: var(--sp-5);
    padding-block: 2px;                /* que no se recorte la sombra ni el borde de 2 px */

    scrollbar-width: none;
  }
  .tira::-webkit-scrollbar { display: none; }
  .tira > * { scroll-snap-align: start; }

  @media (prefers-reduced-motion: reduce) {
    .tira { scroll-snap-type: none; }   /* sin el tirón al soltar */
  }
}

@media (max-width: 799px) {
  .tres, .dos {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 82%;

    overflow-x: auto;
    overscroll-behavior-x: contain;    /* no arrastra la página al llegar al final */
    scroll-snap-type: x mandatory;
    scroll-padding-inline-start: var(--sp-5);

    margin-inline: calc(-1 * var(--sp-5));
    padding-inline: var(--sp-5);
    padding-block: 2px;                /* que no se recorte la sombra ni el borde de 2 px */

    scrollbar-width: none;
  }
  .tres::-webkit-scrollbar, .dos::-webkit-scrollbar { display: none; }
  .tres > *, .dos > * { scroll-snap-align: start; }

  @media (prefers-reduced-motion: reduce) {
    .tres, .dos { scroll-snap-type: none; }   /* sin el tirón al soltar */
  }
}

/* `.planes` NO es carrusel: apilados, los tres precios se comparan bajando, y
   en carrusel el plan que recomendamos quedaba fuera de pantalla. */

/* ==========================================================================
   Puntos del carrusel

   La tarjeta cortada por el borde ya dice que hay más, pero solo si te fijas.
   Los puntos lo dicen antes de deslizar y además sirven de mapa: cuántas hay y
   en cuál vas.

   Los pinta el JS, no el HTML, y a propósito: el número de tarjetas cambia por
   sección y duplicarlo a mano en seis páginas es garantía de que un día habrá
   cuatro puntos para tres tarjetas. Si el JS no carga, el carrusel se sigue
   deslizando igual — los puntos son un extra, no el mecanismo.
   ========================================================================== */

.puntos {
  display: none;                       /* solo cuando su carrusel está activo */
  justify-content: center;
  gap: var(--sp-2);
  margin-top: var(--sp-5);
}

/* Cada tira de puntos aparece en el mismo corte que su rejilla. */
@media (max-width: 759px) { .puntos[data-hasta="759"] { display: flex; } }
@media (max-width: 799px) { .puntos[data-hasta="799"] { display: flex; } }

.punto {
  /* El botón mide 24 px aunque el punto se vea de 7: por debajo de 24 el dedo
     falla más de lo que acierta, y un control que hay que apuntar con cuidado
     es peor que no tenerlo. */
  width: 24px; height: 24px;
  padding: 0; border: 0; background: none; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.punto::before {
  content: "";
  width: 7px; height: 7px;
  border-radius: 999px;
  background: var(--line-strong);
  transition: background var(--v-corta) ease, width var(--v-corta) var(--sal);
}
.punto[aria-current="true"]::before {
  width: 20px;                          /* se alarga en vez de crecer: dice
                                           "estás aquí" sin empujar a los demás */
  background: var(--ink);
}
.punto:active::before { background: var(--ink-2); }

@media (prefers-reduced-motion: reduce) {
  .punto::before { transition: none; }
}

/* Un enlace con almohadilla (#cookies en el aviso de privacidad, por ejemplo)
   deja su destino pegado al borde de arriba, que es justo donde está el
   encabezado fijo de 64 px: la persona aterriza en el párrafo equivocado. Esto
   le reserva el hueco del encabezado y un respiro. */
:target { scroll-margin-top: calc(64px + var(--sp-5)); }

/* ═══ Aviso de cookies ══════════════════════════════════════════════════════
   Tarjeta abajo a la izquierda, no una franja que cruce la pantalla. Una
   franja tapa el pie en móvil y obliga a resolverla para seguir leyendo; la
   tarjeta se queda a un lado y se puede ignorar mientras se lee la página,
   que es lo que casi todo el mundo va a hacer.

   El marcado lo construye assets/js/consentimiento.js, no el HTML. El motivo
   está explicado en ese archivo: si el script no carga, Google Analytics
   tampoco arranca, así que un banner que sobreviviera sin él estaría avisando
   de una medición que no ocurre. */

.cookies {
  position: fixed;
  left: var(--sp-5);
  bottom: var(--sp-5);
  z-index: 60;                          /* encima del pie, debajo del menú */
  width: min(420px, calc(100vw - var(--sp-5) * 2));
  padding: var(--sp-5);
  background: var(--bg);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-lg);
  box-shadow: 0 1px 2px rgba(22,22,26,.04), 0 12px 32px -12px rgba(22,22,26,.18);

  /* Estado de partida y de salida. Nunca desde `scale(0)` ni desde la nada:
     el desplazamiento es su propia altura más el hueco al borde, así que sale
     limpio por abajo sin que haya que saber cuánto mide. */
  opacity: 0;
  transform: translateY(calc(100% + var(--sp-5)));
  transition:
    opacity var(--v-corta) var(--sal),
    transform var(--v-corta) var(--sal);
}

/* Entra un poco más lento de lo que sale: al aparecer hay que notar de dónde
   viene, al irse ya se decidió y estorbaría. */
.cookies[data-visible="si"] {
  opacity: 1;
  transform: translateY(0);
  transition-duration: var(--v-media);
}

.cookies-texto {
  margin: 0 0 var(--sp-4);
  font-size: var(--t-13);
  line-height: 1.55;
  color: var(--ink-2);
  text-wrap: pretty;
}
.cookies-texto strong { color: var(--ink); }
.cookies-texto a { color: var(--ink); text-underline-offset: 2px; }

/* Los dos botones pesan lo mismo y miden lo mismo. Esconder el «no» detrás de
   un enlace gris es el truco de siempre, y este sitio vende no tener letra
   chica: el botón de rechazar tiene que verse tan bien como el otro. */
.cookies-acciones {
  display: flex;
  gap: var(--sp-2);
}
.cookies-acciones .btn {
  flex: 1;
  height: 36px;
  padding-inline: var(--sp-3);
  font-size: var(--t-13);
}

/* En móvil ocupa el ancho con su margen y se sube por encima del área del
   gesto de inicio del iPhone. */
@media (max-width: 559px) {
  .cookies {
    left: var(--sp-3);
    right: var(--sp-3);
    bottom: calc(var(--sp-3) + env(safe-area-inset-bottom, 0px));
    width: auto;
    padding: var(--sp-4);
  }
}

/* Con movimiento reducido se queda el fundido, que es el que explica que algo
   apareció; se va el desplazamiento, que es el que marea. */
@media (prefers-reduced-motion: reduce) {
  .cookies { transform: none; }
  .cookies[data-visible="si"] { transform: none; }
}

/* El botón del pie que reabre el aviso. Llega con `hidden` en el HTML y lo
   destapa consentimiento.js: sin JavaScript no hay nada que abrir. */
.pie-cookies {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
  text-decoration: underline;
  text-underline-offset: 2px;
}

/* ==========================================================================
   Diagrama de arquitectura: ancho en pantalla grande, vertical en el teléfono
   El ancho a 350 px dejaba la letra en 5 px. Son dos dibujos con las mismas
   clases y el botón corta los dos (vim.js).
   ========================================================================== */

.diagrama-alto { display: none; }
@media (max-width: 619px) {
  .diagrama-ancho { display: none; }
  .diagrama-alto { display: block; width: 100%; max-width: 420px; }
}

/* ==========================================================================
   Capturas en el teléfono
   Por debajo de 620 px, `<picture>` cambia a la versión del teléfono: el panel
   capturado a 390 px de verdad y, de la caja, el ticket recortado. Son más
   altas que anchas, así que se enmarcan a 4:5 para no ocupar pantalla y media:
   el panel se ve desde arriba (saludo y ventas); el ticket, desde abajo (el
   total y el botón de cobrar, que es lo que cuenta).
   ========================================================================== */

@media (max-width: 619px) {
  .monitor img[src*="capturas/admin-dashboard."],
  .monitor img[src*="capturas/admin-inventario."],
  .monitor img[src*="capturas/pos-home."] {
    aspect-ratio: 4 / 5;
    width: 100%;
    object-fit: cover;
    object-position: top;
  }
  .monitor img[src*="capturas/pos-home."] { object-position: bottom; }
}

/* Resumen de la columna de tranquilidad de /demo, adelantado sobre el formulario
   solo cuando las columnas se apilan (la rejilla del hero pasa a una a 980 px). */
.demo-resumen { display: none; }
@media (max-width: 979px) {
  .demo-resumen {
    display: flex; flex-direction: column; gap: var(--sp-2);
    list-style: none; margin: 0; padding: 0;
  }
  .demo-resumen li {
    display: flex; align-items: baseline; gap: var(--sp-2);
    font-size: var(--t-15); color: var(--ink-2);
  }
  .demo-resumen li::before {
    content: ""; width: 6px; height: 6px; border-radius: 50%;
    background: var(--success); flex-shrink: 0; transform: translateY(-2px);
  }
}

/* Lo que va para agentes (contacto, 404): plegado y en letra de apoyo. */
.para-robots { font-size: var(--t-13); color: var(--ink-2); }
.para-robots summary { cursor: pointer; font-weight: 600; width: fit-content; }
.para-robots p { margin-top: var(--sp-3); font-size: var(--t-13); }

/* ==========================================================================
   Oferta del piloto (precios)
   Un solo recuadro con borde de marca, no una banda de colores: es una oferta
   con fecha de caducidad y no debe pesar más que los planes que tiene arriba.
   ========================================================================== */

.oferta-piloto {
  display: grid; gap: var(--sp-5); align-items: center;
  padding: var(--sp-6);
  border: 2px solid var(--accent);
  border-radius: var(--r-lg);
  background: var(--bg);
}
@media (min-width: 860px) {
  .oferta-piloto { grid-template-columns: 1fr auto; gap: var(--sp-8); }
}
.oferta-titulo { font-size: var(--t-24); }
.oferta-piloto p { max-width: 64ch; }

/* ==========================================================================
   Novedades

   Una lista con fecha, no una línea de tiempo con puntos y rayas: quien llega
   aquí busca «qué cambió y cuándo», y el adorno le estorba la lectura. La
   fecha y la versión van en una columna aparte para que se puedan recorrer de
   arriba abajo sin leer el contenido; en teléfono suben encima de la entrada.

   «Nuevo» lleva el color de marca y las otras dos etiquetas no: es lo único
   que alguien que todavía no es cliente viene a buscar.
   ========================================================================== */

.novedades-mes > h2 {
  font-size: clamp(var(--t-24), 4vw, var(--t-32));
  padding-bottom: var(--sp-5);
}

.novedad {
  display: grid; gap: var(--sp-4);
  padding-block: var(--sp-6);
  border-top: 1px solid var(--line);
}
@media (min-width: 860px) {
  .novedad { grid-template-columns: 220px minmax(0, 1fr); gap: var(--sp-8); }
}

.novedad-meta {
  display: flex; flex-direction: column; gap: var(--sp-1);
  font-family: var(--font-mono); font-size: var(--t-13); color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.novedad-meta time { color: var(--ink); font-weight: 600; }

.novedad-cuerpo { max-width: 72ch; }
.novedad-cuerpo h3 { font-size: var(--t-24); }
.novedad-cuerpo li { max-width: none; }
.novedad-cuerpo a { color: var(--accent); }
.novedad-cuerpo h4 { margin: 0; }
.novedad-cuerpo h4[data-tipo="nuevo"] { color: var(--accent); }

/* ==========================================================================
   Cuatro cosas, con una sola captura (portada)

   A la izquierda, el acordeón de siempre con las cuatro funciones; a la
   derecha, la captura de la fila abierta. Se emparejan por posición con
   `:has()`: la fila 2 abierta enseña la captura 2. No hay JavaScript propio.

   En pantalla ancha las cuatro capturas ocupan la misma celda y se relevan con
   un fundido: miden lo mismo, así que deslizarlas sería mover la misma caja
   para nada. En el teléfono miden distinto —dos van recortadas a 4:5—, y
   apiladas dejarían un hueco bajo las cortas; ahí se enseña una y las demás no
   ocupan sitio.
   ========================================================================== */

.cosas { display: grid; gap: var(--sp-6); align-items: start; }

.cosas-capturas > .monitor { display: none; }
.cosas:has(.acordeon-item:nth-child(1)[data-abierto="true"]) .cosas-capturas > :nth-child(1),
.cosas:has(.acordeon-item:nth-child(2)[data-abierto="true"]) .cosas-capturas > :nth-child(2),
.cosas:has(.acordeon-item:nth-child(3)[data-abierto="true"]) .cosas-capturas > :nth-child(3),
.cosas:has(.acordeon-item:nth-child(4)[data-abierto="true"]) .cosas-capturas > :nth-child(4) { display: block; }

@media (min-width: 900px) {
  .cosas { grid-template-columns: 5fr 7fr; gap: var(--sp-10); }
  .cosas-capturas { display: grid; }
  .cosas-capturas > .monitor {
    display: block;
    grid-area: 1 / 1;
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--v-corta) ease, visibility 0s linear var(--v-corta);
  }
  .cosas:has(.acordeon-item:nth-child(1)[data-abierto="true"]) .cosas-capturas > :nth-child(1),
  .cosas:has(.acordeon-item:nth-child(2)[data-abierto="true"]) .cosas-capturas > :nth-child(2),
  .cosas:has(.acordeon-item:nth-child(3)[data-abierto="true"]) .cosas-capturas > :nth-child(3),
  .cosas:has(.acordeon-item:nth-child(4)[data-abierto="true"]) .cosas-capturas > :nth-child(4) {
    opacity: 1;
    visibility: visible;
    transition: opacity var(--v-corta) ease, visibility 0s;
  }
}

/* Lo que el producto ya hace y la portada no enseñaba. Lista y no tarjetas: son
   seis renglones de apoyo debajo de las cuatro cosas, no otra sección. Dos
   columnas en el teléfono para que quepan sin carrusel. */
.ademas {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: var(--sp-5) var(--sp-6);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}
@media (min-width: 800px) { .ademas { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.ademas li { display: flex; flex-direction: column; gap: var(--sp-1); }
.ademas a {
  font-family: var(--font-display); font-size: var(--t-18); font-weight: 600;
  letter-spacing: -0.01em; text-decoration: none; width: fit-content;
  transition: color var(--v-corta) ease;
}
@media (hover: hover) and (pointer: fine) { .ademas a:hover { color: var(--accent); } }
.ademas p { font-size: var(--t-15); }

/* ==========================================================================
   Tarjeta que es enlace

   El enlace vive en el título —así su nombre para un lector de pantalla es
   «Negocio» y no la tarjeta entera— y su `::after` cubre la tarjeta. El borde
   responde al mouse: sin eso nada decía que se podía entrar.
   ========================================================================== */

.plan { position: relative; transition: border-color var(--v-corta) ease; }
.enlace-tarjeta { color: inherit; text-decoration: none; }
.enlace-tarjeta::after { content: ""; position: absolute; inset: 0; }
.enlace-tarjeta:focus-visible { outline: none; }
.plan:has(.enlace-tarjeta:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (hover: hover) and (pointer: fine) {
  .plan:has(.enlace-tarjeta):hover { border-color: var(--accent); }
}

/* ==========================================================================
   Entrada al bajar

   Cada bloque de una sección sube 12 px y aparece, una sola vez. Las clases las
   pone vim.js y SOLO a lo que está por debajo de la pantalla al cargar: sin
   script todo se ve, y lo que ya estaba a la vista no parpadea. Al terminar se
   las quita, para que el `transition` de cada pieza vuelva a ser el suyo.

   En las rejillas de tarjetas entra cada una 60 ms después de la anterior.
   ========================================================================== */

.por-entrar, .por-entrar-grupo > * { opacity: 0; transform: translateY(12px); }
.por-entrar.entro, .por-entrar-grupo.entro > * {
  opacity: 1;
  transform: none;
  transition: opacity var(--v-media) var(--sal), transform var(--v-media) var(--sal);
}
.por-entrar-grupo.entro > :nth-child(2) { transition-delay: 60ms; }
.por-entrar-grupo.entro > :nth-child(3) { transition-delay: 120ms; }
.por-entrar-grupo.entro > :nth-child(4) { transition-delay: 180ms; }
.por-entrar-grupo.entro > :nth-child(5) { transition-delay: 240ms; }
.por-entrar-grupo.entro > :nth-child(n+6) { transition-delay: 300ms; }

/* Con movimiento reducido queda el fundido y se va el desplazamiento. */
@media (prefers-reduced-motion: reduce) {
  .por-entrar, .por-entrar-grupo > * { transform: none; }
}
/* En papel no hay scroll que lo destape. */
@media print {
  .por-entrar, .por-entrar-grupo > * { opacity: 1; transform: none; }
}

/* ==========================================================================
   Barra de secciones (Funciones, Precios)

   Son las dos páginas más largas del sitio. La barra se queda pegada bajo el
   encabezado —65 px: sus 64 y la línea— con las secciones de la página. En el
   teléfono se desliza de lado.
   ========================================================================== */

.subnav {
  position: sticky;
  top: 65px;
  z-index: 40;                             /* bajo el encabezado (50) y sus paneles */
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--line);
}
.subnav-fila {
  display: flex; gap: var(--sp-5);
  overflow-x: auto; overscroll-behavior-x: contain;
  scrollbar-width: none;
}
.subnav-fila::-webkit-scrollbar { display: none; }
.subnav a {
  flex-shrink: 0;
  display: inline-flex; align-items: center; height: 44px;
  color: var(--ink-2); text-decoration: none; white-space: nowrap;
  font-size: var(--t-13); font-weight: 500;
  transition: color var(--v-corta) ease;
}
@media (hover: hover) and (pointer: fine) { .subnav a:hover { color: var(--ink); } }
/* El destino de un ancla tiene ahora dos barras encima, no una. */
body:has(.subnav) :target { scroll-margin-top: calc(65px + 44px + var(--sp-5)); }
