/* ==========================================================================
   base.css — Cimiento compartido de las landings
   --------------------------------------------------------------------------
   Contiene: reset moderno, escala tipográfica fluida, tokens de movimiento,
   utilidades mínimas, foco visible, estados de scroll-reveal, keyframes
   compartidos, scrollbar y la neutralización de movimiento.

   REGLA DE COLOR: este archivo NO define ninguna paleta. Cada propiedad lee
   una custom property pública (--color-*, --fuente-*, --radio-*, --sombra-*)
   con un fallback neutro. La landing define los valores reales en su propio
   CSS. Si la landing no define nada, todo sigue siendo legible en gris.

   CRITERIO DE MOVIMIENTO (Emil Kowalski):
   - Los easings nativos de CSS son débiles; aquí se publican curvas custom.
   - `ease-in` está prohibido en UI: arranca lento justo cuando el usuario
     está más atento y hace sentir lenta la interfaz.
   - Entrada/salida => ease-out. Movimiento en pantalla => ease-in-out.
     Movimiento constante (marquee/progreso) => linear.
   - La salida SIEMPRE es más rápida que la entrada: el usuario ya decidió.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens que provee la librería (movimiento, tipografía, capas)
   -------------------------------------------------------------------------- */
:root {
  /* Curvas. Justificación de cada una:
     --ease-out    : arranca al 100% de velocidad => feedback instantáneo.
                     Para todo lo que ENTRA o SALE (modales, toasts, reveals).
     --ease-in-out : simétrica y suave, para algo que ya está en pantalla y se
                     MUEVE de un punto a otro (indicador de tabs, acordeón).
     --ease-drawer : curva del sistema iOS (vía Ionic). Casi un spring: sale
                     disparada y se asienta sin rebote. Para drawers y paneles
                     grandes que cubren pantalla.
     --ease-spring : con overshoot mínimo (~2%). Solo para elementos que deben
                     sentirse "vivos": badge del carrito, chips de selección.
                     Nunca para superficies grandes: marea. */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --ease-drawer: cubic-bezier(0.32, 0.72, 0, 1);
  --ease-spring: cubic-bezier(0.34, 1.42, 0.64, 1);

  /* Duraciones por tipo de interacción. La regla es: mientras más seguido lo
     ve el usuario, más corto (o inexistente) debe ser. Nada de UI >300ms. */
  --dur-press: 140ms;    /* feedback de :active en botones */
  --dur-hover: 180ms;    /* color / borde en hover */
  --dur-tooltip: 160ms;  /* tooltips y popovers chicos */
  --dur-menu: 220ms;     /* dropdowns, selects, chips */
  --dur-modal: 260ms;    /* modales: entrada */
  --dur-modal-salida: 180ms; /* salida más rápida que la entrada */
  --dur-drawer: 420ms;   /* drawer lateral: recorrido largo, curva iOS */
  --dur-drawer-salida: 260ms;
  --dur-reveal: 620ms;   /* scroll reveal: es narrativo, no es UI operativa,
                            por eso se permite pasar de 300ms */

  /* Escala tipográfica fluida. clamp(min, preferido, max) evita media queries
     y evita saltos de tamaño al redimensionar. Base 16px -> 20px. */
  --txt-xs:   clamp(0.75rem, 0.73rem + 0.10vw, 0.8125rem);
  --txt-sm:   clamp(0.875rem, 0.85rem + 0.12vw, 0.9375rem);
  --txt-base: clamp(1rem, 0.96rem + 0.20vw, 1.0625rem);
  --txt-md:   clamp(1.125rem, 1.06rem + 0.32vw, 1.25rem);
  --txt-lg:   clamp(1.375rem, 1.24rem + 0.65vw, 1.75rem);
  --txt-xl:   clamp(1.75rem, 1.48rem + 1.35vw, 2.5rem);
  --txt-2xl:  clamp(2.25rem, 1.75rem + 2.50vw, 3.5rem);
  --txt-3xl:  clamp(2.75rem, 1.90rem + 4.25vw, 5rem);

  /* Espaciado fluido mínimo */
  --esp-1: 0.25rem;
  --esp-2: 0.5rem;
  --esp-3: 0.75rem;
  --esp-4: 1rem;
  --esp-5: 1.5rem;
  --esp-6: 2rem;
  --esp-7: clamp(2.5rem, 1.8rem + 3vw, 4rem);
  --esp-8: clamp(4rem, 2.5rem + 6vw, 7rem);

  /* Capas. Un solo lugar donde se decide qué tapa a qué. */
  --z-sticky: 100;
  --z-wa: 400;
  --z-drawer: 500;
  --z-modal: 600;
  --z-lightbox: 700;
  --z-toast: 800;

  /* Ancho de la barra de scroll, lo calcula JS al bloquear el scroll para
     compensar el salto de layout. Default 0 para que no afecte si no hay JS. */
  --ancho-scrollbar: 0px;

  color-scheme: light;
}

/* --------------------------------------------------------------------------
   2. Reset moderno
   -------------------------------------------------------------------------- */
*,
*::before,
*::after { box-sizing: border-box; }

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  -moz-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* scroll-behavior se activa solo si el usuario no pidió menos movimiento
     (ver bloque de reduced-motion más abajo). */
  scroll-behavior: smooth;
  /* Evita que un ancla quede debajo de un header sticky. */
  scroll-padding-top: var(--offset-ancla, 5rem);
}

body {
  min-height: 100svh;
  font-family: var(--fuente-texto, ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif);
  font-size: var(--txt-base);
  line-height: 1.6;
  color: var(--color-texto, #17171a);
  background-color: var(--color-fondo, #ffffff);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--fuente-titulos, var(--fuente-texto, ui-sans-serif, system-ui, sans-serif));
  line-height: 1.1;
  /* Evita palabras huérfanas y cortes feos en titulares grandes. */
  text-wrap: balance;
  font-weight: 600;
}

p, li, figcaption { text-wrap: pretty; }

h1 { font-size: var(--txt-2xl); }
h2 { font-size: var(--txt-xl); }
h3 { font-size: var(--txt-lg); }
h4 { font-size: var(--txt-md); }

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

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

button {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  /* Sin resaltado azul al tocar en móvil: se siente a "página", no a app. */
  -webkit-tap-highlight-color: transparent;
  /* Un texto que se selecciona al hacer doble tap en un botón se siente roto. */
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;
}

/* Todo lo presionable responde a la presión. Escala sutil (0.97): más abajo
   se siente elástico, menos no se percibe. ease-out y 140ms porque este
   feedback lo ve el usuario decenas de veces y debe ser instantáneo. */
button:active:not(:disabled),
[role="button"]:active:not([aria-disabled="true"]),
.presionable:active:not(:disabled) {
  transform: scale(0.97);
}
button,
[role="button"],
.presionable {
  transition: transform var(--dur-press) var(--ease-out);
}

a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
}

ul[role="list"], ol[role="list"] { list-style: none; padding: 0; }

table { border-collapse: collapse; }

:where(hr) {
  border: 0;
  border-top: 1px solid var(--color-borde, #e6e6ea);
}

/* --------------------------------------------------------------------------
   3. Foco visible consistente
   El foco NO se anima: aparecer con transición hace que el usuario de teclado
   perciba lag en una acción que repite cientos de veces al día.
   -------------------------------------------------------------------------- */
:focus { outline: none; }

:focus-visible {
  outline: 2px solid var(--color-acento, #17171a);
  outline-offset: 2px;
  border-radius: var(--radio-sm, 6px);
}

/* Fallback para navegadores sin :focus-visible */
@supports not selector(:focus-visible) {
  :focus { outline: 2px solid var(--color-acento, #17171a); outline-offset: 2px; }
}

::selection {
  background-color: var(--color-acento, #17171a);
  color: var(--color-sobre-acento, #ffffff);
}

/* --------------------------------------------------------------------------
   4. Scrollbar
   -------------------------------------------------------------------------- */
html {
  scrollbar-width: thin;
  scrollbar-color: var(--color-borde, #d9d9e0) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background-color: var(--color-borde, #d9d9e0);
  border-radius: 999px;
  border: 3px solid var(--color-fondo, #ffffff);
}
::-webkit-scrollbar-thumb:hover { background-color: var(--color-texto-suave, #8a8a94); }

/* --------------------------------------------------------------------------
   5. Utilidades mínimas
   -------------------------------------------------------------------------- */
.contenedor {
  width: min(100% - 2.5rem, var(--ancho-contenedor, 1180px));
  margin-inline: auto;
}
.contenedor-angosto {
  width: min(100% - 2.5rem, var(--ancho-contenedor-angosto, 760px));
  margin-inline: auto;
}

.seccion { padding-block: var(--esp-8); }

/* Oculto visualmente pero anunciado por lectores de pantalla. */
.visualmente-oculto {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Skip link: se esconde hasta recibir foco de teclado. */
.saltar-al-contenido {
  position: fixed;
  top: var(--esp-3);
  left: var(--esp-3);
  z-index: calc(var(--z-toast) + 1);
  padding: 0.6rem 1rem;
  background: var(--color-superficie, #ffffff);
  color: var(--color-texto, #17171a);
  border: 1px solid var(--color-borde, #e6e6ea);
  border-radius: var(--radio-md, 10px);
  box-shadow: var(--sombra-media, 0 8px 24px rgba(0, 0, 0, 0.12));
  transform: translateY(-160%);
  transition: transform var(--dur-menu) var(--ease-out);
}
.saltar-al-contenido:focus-visible { transform: translateY(0); }

.tabular { font-variant-numeric: tabular-nums; }
.sin-quiebre { white-space: nowrap; }
.gpu { transform: translateZ(0); }
.pila { display: flex; flex-direction: column; }
.fila { display: flex; align-items: center; }
.crece { flex: 1 1 auto; min-width: 0; }

/* Estados vacíos / de carga compartidos */
.esqueleto {
  background: linear-gradient(
    90deg,
    var(--color-borde, #ececf0) 25%,
    var(--color-fondo-alt, #f6f6f8) 50%,
    var(--color-borde, #ececf0) 75%
  );
  background-size: 200% 100%;
  /* linear porque es movimiento constante, no una entrada. */
  animation: lib-shimmer 1.4s linear infinite;
  border-radius: var(--radio-sm, 6px);
}

/* --------------------------------------------------------------------------
   6. Scroll reveal — estados
   El JS agrega la clase `reveal-listo` en <html> apenas se ejecuta (por eso
   reveal.js va en el <head>): así el contenido nace oculto sin parpadeo, y si
   el JS falla o está desactivado, el contenido queda visible por default.
   Solo se animan `transform` y `opacity` (saltan layout y paint, corren en
   GPU). Excepciones justificadas: la variante `clip` usa clip-path y `blur`
   usa filter — ninguna dispara layout, y ambas se compositan.
   -------------------------------------------------------------------------- */
html.reveal-listo [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-desplazamiento, 20px), 0);
  transition-property: opacity, transform;
  transition-duration: var(--dur-reveal);
  transition-timing-function: var(--ease-out);
  /* El delay lo pone reveal.js inline (no como variable heredable: una
     custom property en el padre recalcula estilos de todos los hijos). */
}

html.reveal-listo [data-reveal="fade"] { transform: none; }

html.reveal-listo [data-reveal="scale"] {
  /* Nunca desde scale(0): nada en el mundo real aparece de la nada. */
  transform: scale(0.96);
}

html.reveal-listo [data-reveal="clip"] {
  opacity: 1;
  transform: none;
  clip-path: inset(0 0 101% 0);
  transition-property: clip-path;
}

html.reveal-listo [data-reveal="blur"] {
  transform: translate3d(0, 10px, 0);
  /* <20px: por encima de eso el blur es caro, sobre todo en Safari. */
  filter: blur(10px);
  transition-property: opacity, transform, filter;
}

html.reveal-listo [data-reveal].reveal-visible {
  opacity: 1;
  transform: none;
  filter: none;
  clip-path: inset(0 0 0 0);
}

/* --------------------------------------------------------------------------
   7. Keyframes compartidos
   Regla: keyframes solo para movimiento que se reproduce una vez y no puede
   ser interrumpido (shimmer, shake, pop). Todo lo que el usuario puede
   disparar rápido usa transiciones, porque las transiciones se redirigen a
   media animación y los keyframes reinician desde cero.
   -------------------------------------------------------------------------- */
@keyframes lib-shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

/* Error de formulario: 3 rebotes cortos, amplitud decreciente. Es corto
   (400ms) porque el objetivo es dirigir la vista, no entretener. */
@keyframes lib-shake {
  0%, 100% { transform: translate3d(0, 0, 0); }
  15%  { transform: translate3d(-6px, 0, 0); }
  32%  { transform: translate3d(5px, 0, 0); }
  50%  { transform: translate3d(-3px, 0, 0); }
  68%  { transform: translate3d(2px, 0, 0); }
  84%  { transform: translate3d(-1px, 0, 0); }
}
.lib-shake { animation: lib-shake 400ms var(--ease-out); }

/* Pop del badge: no empieza en 0, arranca comprimido y se pasa apenas. */
@keyframes lib-pop {
  0%   { transform: scale(0.86); }
  55%  { transform: scale(1.14); }
  100% { transform: scale(1); }
}

@keyframes lib-girar {
  to { transform: rotate(360deg); }
}

.lib-cargando {
  width: 1em; height: 1em;
  border-radius: 999px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  /* linear: un spinner con easing se ve descompuesto. */
  animation: lib-girar 640ms linear infinite;
}

/* --------------------------------------------------------------------------
   8. prefers-reduced-motion
   No se apaga TODO: movimiento reducido significa menos movimiento, no cero
   feedback. Se conservan las transiciones de opacidad y color (ayudan a
   entender qué cambió) y se elimina todo desplazamiento, escala y keyframe.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-delay: 0ms !important;
    animation-iteration-count: 1 !important;
    /* Al restringir transition-property a propiedades sin movimiento, el
       resto (transform, translate, clip-path, filter) deja de animarse. */
    transition-property: opacity, color, background-color, border-color, box-shadow, fill, stroke !important;
    transition-duration: 140ms !important;
    transition-delay: 0ms !important;
  }

  html.reveal-listo [data-reveal] {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    clip-path: none !important;
  }

  button:active:not(:disabled),
  [role="button"]:active,
  .presionable:active { transform: none !important; }
}

/* --------------------------------------------------------------------------
   9. Hover solo en dispositivos con puntero fino
   En táctil, :hover se queda "pegado" después del tap y produce falsos
   positivos. Las landings deben envolver sus hovers en este media query.
   -------------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  .hover-elevar { transition: transform var(--dur-hover) var(--ease-out), box-shadow var(--dur-hover) var(--ease-out); }
  .hover-elevar:hover { transform: translate3d(0, -3px, 0); box-shadow: var(--sombra-alta, 0 18px 40px rgba(0, 0, 0, 0.14)); }
}

/* Impresión: que la demo se pueda imprimir sin basura flotante. */
@media print {
  .lib-toasts, .lib-wa, .lib-drawer, .lib-backdrop, .saltar-al-contenido { display: none !important; }
  html.reveal-listo [data-reveal] { opacity: 1 !important; transform: none !important; clip-path: none !important; filter: none !important; }
}
