/* ============================================================================
   CYC FACTURACION — base.css
   Reset, tipografia base, fondo aurora, utilidades de layout y TODOS los
   componentes compartidos entre login.html y app.html.

   Requiere css/tokens.css cargado ANTES (todo color/medida sale de var()).
   MOBILE-FIRST: el CSS base es celular; solo se sube con
   @media (min-width:768px) y (min-width:1200px). Nunca max-width.
   ========================================================================= */

/* ===========================================================================
   1. RESET MODERNO
   ======================================================================== */
*,*::before,*::after{ box-sizing:border-box; }

html,body,h1,h2,h3,h4,p,figure,blockquote,dl,dd,ul,ol{ margin:0; padding:0; }
ul[class],ol[class]{ list-style:none; }

html{
  -webkit-text-size-adjust:100%;   /* iOS no reescala al girar el telefono */
  text-size-adjust:100%;
  scroll-behavior:smooth;
  height:100%;
}

body{
  min-height:100vh;
  min-height:100dvh;               /* dvh evita el salto al ocultarse la barra del navegador */
  font-family:var(--font-sans);
  font-size:var(--fs-md);
  font-weight:var(--fw-regular);
  line-height:var(--lh-base);
  color:var(--text);
  background:var(--bg);
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
  overflow-x:hidden;               /* las manchas del aurora nunca generan scroll lateral */
  position:relative;
  /* Transicion de tema: solo color, para que el switch se sienta continuo */
  transition:background-color var(--dur) var(--ease-in-out),
             color var(--dur) var(--ease-in-out);
}

img,svg,video,canvas{ display:block; max-width:100%; }
svg{ fill:none; }

input,button,textarea,select{ font:inherit; color:inherit; }
button{ background:none; border:0; cursor:pointer; }
a{ color:inherit; text-decoration:none; }

/* Quitar el aspecto nativo de iOS en inputs */
input,textarea,select{
  -webkit-appearance:none;
  appearance:none;
  border-radius:0;
}

/* Autofill de Chrome: pinta el input de amarillo y rompe el tema.
   El truco del inset shadow gigante repinta el fondo con nuestro token. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus{
  -webkit-text-fill-color:var(--text);
  -webkit-box-shadow:0 0 0 1000px var(--surface-2) inset;
  transition:background-color 9999s ease-in-out 0s;
  caret-color:var(--text);
}

::selection{ background:var(--primary-soft); color:var(--text); }
::placeholder{ color:var(--text-soft); opacity:1; }

/* ===========================================================================
   2. TIPOGRAFIA BASE
   ======================================================================== */
h1,h2,h3,h4{
  font-weight:var(--fw-bold);
  line-height:var(--lh-tight);
  letter-spacing:var(--ls-tight);
  color:var(--text);
  text-wrap:balance;
}
h1{ font-size:var(--fs-3xl); font-weight:var(--fw-black); letter-spacing:var(--ls-title); }
h2{ font-size:var(--fs-2xl); }
h3{ font-size:var(--fs-xl); }
h4{ font-size:var(--fs-lg); }

.title-xl{ font-size:var(--fs-3xl); font-weight:var(--fw-black); letter-spacing:var(--ls-title); line-height:var(--lh-tight); }
.title{    font-size:var(--fs-2xl); font-weight:var(--fw-bold);  letter-spacing:var(--ls-tight); line-height:var(--lh-tight); }
.subtitle{ font-size:var(--fs-md);  font-weight:var(--fw-regular); color:var(--text-muted); line-height:var(--lh-snug); }
.text-muted{ color:var(--text-muted); }
.text-soft{  color:var(--text-soft); }
.text-sm{ font-size:var(--fs-sm); }
.text-xs{ font-size:var(--fs-xs); }
.text-center{ text-align:center; }
.eyebrow{
  font-size:var(--fs-xs);
  font-weight:var(--fw-semi);
  letter-spacing:var(--ls-wide);
  text-transform:uppercase;
  color:var(--text-soft);
}

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

/* ===========================================================================
   3. FOCO ACCESIBLE
      :focus-visible (no :focus) para no marcar al hacer clic con el mouse.
   ======================================================================== */
/* POR QUE :focus:not(:focus-visible) y no :focus a secas: en navegadores SIN
   soporte de :focus-visible (Safari <=15.3, WebViews viejos) el reset SI se
   aplicaba pero el reemplazo NO, y el teclado se quedaba sin ningun indicador
   de foco. Con el selector combinado, si el navegador no entiende
   :focus-visible descarta la regla ENTERA y conserva el outline nativo. */
:focus:not(:focus-visible){ outline:none; }
:focus-visible{
  outline:3px solid var(--ring);
  outline-offset:2px;
  border-radius:var(--r-xs);
}

/* ===========================================================================
   4. FONDO AURORA
      Dos manchas difusas que derivan MUY lento detras de todo el contenido.
      Puro CSS sobre body::before/::after; z-index negativo para no interceptar
      clics. El elemento opcional .aurora agrega una tercera mancha.
   ======================================================================== */
body::before,
body::after{
  content:"";
  position:fixed;
  z-index:var(--z-aurora);
  border-radius:50%;
  filter:blur(var(--aurora-blur));
  pointer-events:none;
  will-change:transform;
}

/* Mancha 1: arriba a la izquierda, tono primary */
body::before{
  top:-22vh; left:-24vw;
  width:78vw; height:78vw;
  max-width:760px; max-height:760px;
  background:radial-gradient(circle at 50% 50%, var(--primary) 0%, transparent 68%);
  opacity:var(--aurora-op-1);
  animation:aurora-a var(--dur-aurora) var(--ease-in-out) infinite alternate;
}

/* Mancha 2: abajo a la derecha, tono primary-2 */
body::after{
  bottom:-28vh; right:-26vw;
  width:86vw; height:86vw;
  max-width:840px; max-height:840px;
  background:radial-gradient(circle at 50% 50%, var(--primary-2) 0%, transparent 70%);
  opacity:var(--aurora-op-2);
  animation:aurora-b calc(var(--dur-aurora) * 1.35) var(--ease-in-out) infinite alternate;
}

/* Tercera mancha opcional: <span class="aurora" aria-hidden="true"></span> */
.aurora{
  position:fixed;
  z-index:var(--z-aurora);
  top:38vh; right:-18vw;
  width:56vw; height:56vw;
  max-width:520px; max-height:520px;
  border-radius:50%;
  filter:blur(var(--aurora-blur));
  pointer-events:none;
  background:radial-gradient(circle at 50% 50%, var(--success) 0%, transparent 70%);
  opacity:calc(var(--aurora-op-2) * .55);
  animation:aurora-c calc(var(--dur-aurora) * 1.7) var(--ease-in-out) infinite alternate;
}

@keyframes aurora-a{
  0%  { transform:translate3d(0,0,0) scale(1); }
  50% { transform:translate3d(6vw,5vh,0) scale(1.12); }
  100%{ transform:translate3d(-4vw,9vh,0) scale(1.04); }
}
@keyframes aurora-b{
  0%  { transform:translate3d(0,0,0) scale(1.06); }
  50% { transform:translate3d(-7vw,-6vh,0) scale(1); }
  100%{ transform:translate3d(4vw,-10vh,0) scale(1.14); }
}
@keyframes aurora-c{
  0%  { transform:translate3d(0,0,0) scale(1); }
  100%{ transform:translate3d(-8vw,-12vh,0) scale(1.2); }
}

/* ===========================================================================
   5. UTILIDADES DE LAYOUT
   ======================================================================== */
.container{
  width:100%;
  max-width:var(--maxw-content);
  margin-inline:auto;
  padding-inline:max(var(--sp-4), var(--safe-left)) ;
  padding-inline-end:max(var(--sp-4), var(--safe-right));
}

/* Pila vertical: el hijo controla el gap con --gap */
.stack{ display:flex; flex-direction:column; gap:var(--gap, var(--sp-4)); }
.stack--sm{ --gap:var(--sp-2); }
.stack--lg{ --gap:var(--sp-6); }

.row{ display:flex; flex-direction:row; align-items:center; gap:var(--gap, var(--sp-3)); }
.row--between{ justify-content:space-between; }
.row--wrap{ flex-wrap:wrap; }

.center{ display:flex; align-items:center; justify-content:center; }
.grow{ flex:1 1 auto; min-width:0; }   /* min-width:0 evita desbordes en flex */
.full{ width:100%; }
.hidden{ display:none!important; }

/* Alto de pantalla real en movil */
.screen{
  min-height:100vh;
  min-height:100dvh;
}

/* ===========================================================================
   6. TARJETA
   ======================================================================== */
.card{
  position:relative;
  background:var(--surface);
  border:1px solid var(--border);
  border-radius:var(--r-lg);
  box-shadow:var(--sh-3);
  padding:var(--sp-6);
  /* Contiene el brillo superior sin recortar sombras internas */
  overflow:hidden;
  transition:background-color var(--dur) var(--ease-in-out),
             border-color var(--dur) var(--ease-in-out),
             box-shadow var(--dur) var(--ease-out),
             height var(--dur-slow) var(--ease-spring);
}

/* Filo de luz superior: detalle sutil que da sensacion de cristal */
.card::before{
  content:"";
  position:absolute; inset:0 0 auto 0;
  height:1px;
  background:linear-gradient(90deg, transparent, var(--primary-soft), transparent);
  opacity:.9;
  pointer-events:none;
}

.card--flat{ box-shadow:var(--sh-1); }
.card--soft{ box-shadow:var(--sh-2); }
.card--pad-lg{ padding:var(--sp-8); }

/* Entrada de la tarjeta principal */
.card--enter{ animation:card-in var(--dur-slow) var(--ease-spring) both; }
@keyframes card-in{
  from{ opacity:0; transform:translateY(12px) scale(.98); }
  to  { opacity:1; transform:translateY(0) scale(1); }
}

/* La tarjeta anima su altura al cambiar de vista: JS fija height en px y la
   transicion de arriba hace el resto. Esta clase la deja medible. */
.card--animating{ overflow:hidden; }

@media (min-width:768px){
  .card{ padding:var(--sp-8); }
}

/* ===========================================================================
   7. BOTONES
      Marcado esperado:
      <button class="btn btn--primary btn--block" data-ripple>
        <span class="btn__label">Entrar</span>
        <span class="btn__spinner" aria-hidden="true"></span>
        <svg class="btn__check" viewBox="0 0 24 24" aria-hidden="true">
          <path d="M4 12.5 L9.5 18 L20 7"/>
        </svg>
      </button>
      setBtnState() alterna .is-loading / .is-success.
   ======================================================================== */
.btn{
  position:relative;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:var(--sp-2);
  min-height:var(--btn-h);
  min-width:var(--tap);
  padding:0 var(--sp-6);
  border-radius:var(--r-full);
  border:1px solid transparent;
  font-size:var(--fs-md);
  font-weight:var(--fw-semi);
  letter-spacing:-.01em;
  line-height:1;
  color:var(--text);
  background:var(--surface-2);
  cursor:pointer;
  overflow:hidden;                /* recorta el ripple al radio del boton */
  isolation:isolate;
  user-select:none;
  -webkit-tap-highlight-color:transparent;
  transition:transform var(--dur-fast) var(--ease-spring),
             box-shadow var(--dur) var(--ease-out),
             background-color var(--dur) var(--ease-out),
             border-color var(--dur) var(--ease-out),
             color var(--dur) var(--ease-out),
             opacity var(--dur) var(--ease-out);
}
.btn > *{ position:relative; z-index:1; }   /* por encima del ripple */

.btn:hover{ transform:translateY(-1px); }
.btn:active{ transform:scale(.97); }
.btn[disabled],
.btn.is-disabled{
  opacity:.5;
  cursor:not-allowed;
  transform:none;
  box-shadow:none;
}

/* --- Primario: gradiente de marca --- */
.btn--primary{
  color:var(--primary-ink);
  background-image:linear-gradient(135deg, var(--primary) 0%, var(--primary-2) 100%);
  box-shadow:var(--sh-primary);
}
.btn--primary:hover{ box-shadow:var(--sh-primary-lg); }
.btn--primary:active{ box-shadow:var(--sh-primary); }

/* --- Fantasma: sobre la tarjeta, sin peso visual --- */
.btn--ghost{
  color:var(--text);
  background:transparent;
  border-color:var(--border-strong);
}
.btn--ghost:hover{ background:var(--surface-2); border-color:var(--primary); color:var(--primary); }

/* --- Suave: relleno tenue de marca --- */
.btn--soft{
  /* Tinta: --primary sobre --primary-soft da 4.28:1 y este texto no siempre
     es grande; --primary-ink-soft cumple AA con holgura. */
  color:var(--primary-ink-soft);
  background:var(--primary-soft);
}
.btn--soft:hover{ box-shadow:var(--sh-1); }

/* --- Peligro --- */
.btn--danger{
  color:var(--primary-ink);
  background-image:linear-gradient(135deg, var(--danger) 0%, var(--warning) 160%);
  box-shadow:var(--sh-danger);
}

/* --- Solo texto --- */
.btn--plain{
  background:transparent;
  color:var(--primary);
  min-height:var(--tap);
  padding-inline:var(--sp-3);
  box-shadow:none;
}
.btn--plain:hover{ background:var(--primary-soft); }

/* --- Icono cuadrado --- */
/* .btn.btn--icon y no .btn--icon: ESPECIFICIDAD, no capricho.
   .btn--sm se declara MAS ABAJO en esta misma hoja y trae
   padding-inline:var(--sp-4). Con la misma especificidad ganaba el de abajo, asi
   que un boton con las dos clases quedaba de 44px CON 16px de padding por lado:
   12px utiles para un icono de 20px, que se aplastaba a 10 de ancho y se veia
   como un boton vacio. Le pasaba al de regresar de facturas.html. */
.btn.btn--icon{
  width:var(--tap); height:var(--tap);
  min-height:var(--tap);
  padding:0;
  border-radius:var(--r-full);
}
.btn--icon svg{ width:20px; height:20px; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }

/* --- Modificadores de tamano / ancho --- */
.btn--block{ display:flex; width:100%; }
/* min-height var(--tap) y no 40px: lo usa #btnLogout, que en celular es un
   objetivo tactil real y debe cumplir los 44px minimos. */
.btn--sm{ min-height:var(--tap); font-size:var(--fs-sm); padding-inline:var(--sp-4); }
.btn--lg{ min-height:58px; font-size:var(--fs-lg); }

/* --- Estados: cargando --- */
.btn__label{
  /* Fila icono + texto centrada: sin esto el .ico se apila SOBRE el texto en
     cualquier botón que no sea el de Salir (que traía su propia regla). */
  display:inline-flex;
  align-items:center;
  justify-content:center;
  gap:var(--sp-2);
  transition:opacity var(--dur-fast) var(--ease-out),
             transform var(--dur-fast) var(--ease-out);
}
.btn__label > svg{ flex:0 0 auto; }   /* el icono no se encoge junto al texto */
.btn__spinner,
.btn__check{
  position:absolute;
  top:50%; left:50%;
  opacity:0;
  pointer-events:none;
}
.btn__spinner{
  width:22px; height:22px;
  margin:-11px 0 0 -11px;
  border:2.5px solid currentColor;
  border-top-color:transparent;
  border-radius:50%;
  transition:opacity var(--dur-fast) var(--ease-out);
}
.btn.is-loading{ cursor:progress; pointer-events:none; }
.btn.is-loading .btn__label{ opacity:0; transform:translateY(-6px); }

/* El spinner se recorre a la izquierda para dejarle sitio al texto de progreso:
   un boton que solo gira, sin palabras, se lee como "colgado". */
.btn.is-loading .btn__spinner{
  opacity:.95;
  animation:spin 720ms linear infinite;
  width:18px; height:18px;
  margin:-9px 0 0 0;
  left:auto; right:auto;
  transform:translateX(-64px);
}

/* --- Texto de progreso + puntos suspensivos animados --- */
.btn__loading{
  position:absolute;
  top:50%; left:50%;
  transform:translate(-50%,-50%) translateX(12px);
  opacity:0;
  white-space:nowrap;
  font-weight:var(--fw-semi);
  letter-spacing:var(--ls-tight);
  pointer-events:none;
  transition:opacity var(--dur-fast) var(--ease-out);
}
.btn.is-loading .btn__loading{ opacity:1; }
/* Los tres puntos aparecen uno tras otro. Se anima el ANCHO en pasos (no la
   propiedad content: animar content no es fiable fuera de WebKit/Blink) y el
   hueco se reserva siempre, asi el texto no baila. */
.btn__loading::after{
  content:'...';
  display:inline-block;
  width:1.1em;
  overflow:hidden;
  vertical-align:bottom;
  white-space:nowrap;
  animation:puntos-carga 1.4s steps(4,end) infinite;
}
@keyframes puntos-carga{
  from{ width:0; }
  to  { width:1.1em; }
}

/* --- Barrido de brillo: recorre el boton mientras trabaja --- */
.btn.is-loading::after{
  content:'';
  position:absolute;
  inset:0;
  border-radius:inherit;
  pointer-events:none;
  background:linear-gradient(100deg,
              transparent 20%,
              rgba(255,255,255,.28) 48%,
              transparent 76%);
  transform:translateX(-100%);
  animation:barrido-carga 1.5s var(--ease-in-out) infinite;
}
@keyframes barrido-carga{
  0%  { transform:translateX(-100%); }
  100%{ transform:translateX(100%); }
}

/* --- Barra indeterminada al pie del boton --- */
.btn__bar{
  position:absolute;
  left:0; right:0; bottom:0;
  height:3px;
  border-radius:0 0 var(--r-full) var(--r-full);
  overflow:hidden;
  opacity:0;
  pointer-events:none;
  transition:opacity var(--dur-fast) var(--ease-out);
}
.btn__bar::before{
  content:'';
  position:absolute;
  top:0; bottom:0;
  width:38%;
  border-radius:var(--r-full);
  background:currentColor;
  opacity:.85;
  animation:barra-indet 1.25s var(--ease-in-out) infinite;
}
.btn.is-loading .btn__bar{ opacity:.9; }
@keyframes barra-indet{
  0%  { left:-40%; }
  100%{ left:100%; }
}

/* --- Estados: exito (el check se DIBUJA) --- */
.btn__check{
  width:28px; height:28px;
  margin:-14px 0 0 -14px;
  transform:scale(.7);
  transition:opacity var(--dur-fast) var(--ease-out),
             transform var(--dur) var(--ease-spring);
}
.btn__check path{
  fill:none;
  stroke:currentColor;
  stroke-width:2.6;
  stroke-linecap:round;
  stroke-linejoin:round;
  stroke-dasharray:26;
  stroke-dashoffset:26;
}
.btn.is-success{ pointer-events:none; }
.btn.is-success .btn__label{ opacity:0; transform:translateY(6px); }
.btn.is-success .btn__spinner{ opacity:0; animation:none; }
.btn.is-success .btn__check{ opacity:1; transform:scale(1); }
.btn.is-success .btn__check path{ animation:draw-check var(--dur-slow) var(--ease-out) forwards; }
.btn--primary.is-success{
  background-image:linear-gradient(135deg, var(--success) 0%, var(--success) 100%);
  box-shadow:var(--sh-success);
}

@keyframes spin{ to{ transform:rotate(360deg); } }
@keyframes draw-check{ to{ stroke-dashoffset:0; } }

/* ===========================================================================
   8. RIPPLE
      initRipple() inserta <span class="ripple"> en [data-ripple] con
      --rx/--ry (punto del clic) y --rs (diametro) en linea.
   ======================================================================== */
[data-ripple]{ position:relative; overflow:hidden; }
.ripple{
  position:absolute;
  z-index:0;
  top:var(--ry, 50%);
  left:var(--rx, 50%);
  width:var(--rs, 120px);
  height:var(--rs, 120px);
  margin-top:calc(var(--rs, 120px) / -2);
  margin-left:calc(var(--rs, 120px) / -2);
  border-radius:50%;
  background:var(--ripple-dark);
  transform:scale(0);
  opacity:.85;
  pointer-events:none;
  animation:ripple-out 620ms var(--ease-out) forwards;
}
/* Sobre fondos oscuros/gradiente la tinta debe ser clara */
.btn--primary .ripple,
.btn--danger .ripple{ background:var(--ripple-ink); }

@keyframes ripple-out{
  to{ transform:scale(1); opacity:0; }
}

/* ===========================================================================
   9. CAMPOS (label flotante + icono + estados)
      Marcado esperado (EL ORDEN IMPORTA):
      <div class="field field--icon">
        <input id="loginEmail" class="field__input" type="email" placeholder=" ">
        <label class="field__label" for="loginEmail">Correo electronico</label>
        <span class="field__icon" aria-hidden="true"><svg>…</svg></span>
        <button class="field__eye" type="button">…</button>   (solo contrasena)
        <span class="field__ok" aria-hidden="true"><svg>…</svg></span>
        <p class="field__msg" role="alert"></p>
      </div>
      POR QUE ese orden: el label flota con el selector adyacente
      (.field__input:focus + .field__label) y el icono se tine con el selector
      hermano (~), que solo mira hacia ADELANTE. El input debe ir primero.
      OJO: placeholder=" " es OBLIGATORIO — sin el, el label no flota.
      Modificadores del contenedor: .field--icon (reserva espacio a la
      izquierda) y .field--eye (reserva espacio a la derecha).
   ======================================================================== */
.field{
  position:relative;
  display:block;
  padding-bottom:0;
  transition:padding-bottom var(--dur-fast) var(--ease-out);
}

.field__input,
.field__control{
  width:100%;
  min-height:var(--field-h);
  padding:var(--sp-6) var(--sp-4) var(--sp-2);
  border:1.5px solid var(--border);
  border-radius:var(--r-sm);
  background:var(--surface-2);
  color:var(--text);
  /* 16px reales en movil: por debajo, iOS hace zoom al enfocar */
  font-size:16px;
  font-weight:var(--fw-medium);
  line-height:1.2;
  transition:border-color var(--dur-fast) var(--ease-out),
             box-shadow var(--dur-fast) var(--ease-out),
             background-color var(--dur-fast) var(--ease-out);
}
.field__input::placeholder{ color:transparent; }  /* el label hace de placeholder */

.field__label{
  position:absolute;
  top:calc(var(--field-h) / 2);
  left:var(--sp-4);
  transform:translateY(-50%);
  transform-origin:left center;
  font-size:16px;
  font-weight:var(--fw-medium);
  color:var(--text-soft);
  pointer-events:none;
  white-space:nowrap;
  max-width:calc(100% - var(--sp-8));
  overflow:hidden;
  text-overflow:ellipsis;
  transition:transform var(--dur) var(--ease-spring),
             color var(--dur-fast) var(--ease-out),
             opacity var(--dur-fast) var(--ease-out);
}

/* Label arriba: al enfocar o cuando ya hay valor */
.field__input:focus + .field__label,
.field__input:not(:placeholder-shown) + .field__label{
  transform:translateY(calc(-50% - 12px)) scale(.78);
  color:var(--text-muted);
}
.field__input:focus + .field__label{ color:var(--primary); }

/* Foco: borde de marca + glow */
.field__input:focus{
  outline:none;
  border-color:var(--primary);
  background:var(--surface);
  box-shadow:0 0 0 4px var(--ring);
}

.field__input[disabled],
.field__input[readonly]{ opacity:.6; cursor:not-allowed; }

/* --- Icono a la izquierda --- */
.field__icon{
  position:absolute;
  top:calc(var(--field-h) / 2);
  left:var(--sp-4);
  transform:translateY(-50%);
  width:20px; height:20px;
  color:var(--text-soft);
  pointer-events:none;
  transition:color var(--dur-fast) var(--ease-out);
}
.field__icon svg{ width:20px; height:20px; stroke:currentColor; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; }

.field--icon .field__input{ padding-left:calc(var(--sp-4) + 20px + var(--sp-3)); }
.field--icon .field__label{ left:calc(var(--sp-4) + 20px + var(--sp-3)); }
.field--icon .field__input:focus ~ .field__icon{ color:var(--primary); }

/* --- Boton de ojo (contrasena) --- */
.field__eye{
  position:absolute;
  top:calc(var(--field-h) / 2);
  right:var(--sp-2);
  transform:translateY(-50%);
  width:var(--tap); height:var(--tap);
  display:flex; align-items:center; justify-content:center;
  border-radius:var(--r-full);
  color:var(--text-soft);
  transition:color var(--dur-fast) var(--ease-out),
             background-color var(--dur-fast) var(--ease-out);
}
.field__eye:hover{ color:var(--primary); background:var(--primary-soft); }
.field__eye svg{ width:20px; height:20px; stroke:currentColor; stroke-width:1.9; stroke-linecap:round; stroke-linejoin:round; }
/* El JS alterna .is-on; cada icono se muestra segun el estado */
.field__eye .icon-eye-off{ display:none; }
.field__eye.is-on .icon-eye{ display:none; }
.field__eye.is-on .icon-eye-off{ display:block; }
.field--eye .field__input{ padding-right:calc(var(--tap) + var(--sp-2)); }

/* --- Palomita de exito --- */
.field__ok{
  position:absolute;
  top:calc(var(--field-h) / 2);
  right:var(--sp-4);
  transform:translateY(-50%) scale(.6);
  width:20px; height:20px;
  color:var(--success);
  opacity:0;
  pointer-events:none;
  transition:opacity var(--dur-fast) var(--ease-out),
             transform var(--dur) var(--ease-spring);
}
.field__ok svg{ width:20px; height:20px; stroke:currentColor; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }
.field.is-ok .field__ok{ opacity:1; transform:translateY(-50%) scale(1); }
.field.is-ok .field__input{ border-color:var(--success); }
/* Con ojo presente, la palomita se recorre para no encimarse */
.field--eye.is-ok .field__ok{ right:calc(var(--tap) + var(--sp-3)); }

/* --- Mensaje inline (error / ayuda) --- */
.field__msg{
  display:block;
  max-height:0;
  overflow:hidden;
  margin-top:0;
  padding-left:var(--sp-2);
  font-size:var(--fs-xs);
  font-weight:var(--fw-medium);
  line-height:1.4;
  /* Tinta, no relleno: --danger sobre la tarjeta blanca da 3.76:1 y este
     texto es de 12px, asi que no cumple AA. --danger-ink si. */
  color:var(--danger-ink);
  opacity:0;
  transform:translateY(-4px);
  transition:max-height var(--dur-fast) var(--ease-out),
             opacity var(--dur-fast) var(--ease-out),
             transform var(--dur-fast) var(--ease-out),
             margin-top var(--dur-fast) var(--ease-out);
}
.field.is-error .field__msg,
.field .field__msg.is-visible{
  /* 12em y no 60px: a 320px de ancho un mensaje largo de la API se cortaba
     sin aviso (overflow:hidden). 12em deja caber varias lineas. */
  max-height:12em;
  margin-top:var(--sp-2);
  opacity:1;
  transform:translateY(0);
}
.field__hint{
  display:block;
  margin-top:var(--sp-2);
  padding-left:var(--sp-2);
  font-size:var(--fs-xs);
  color:var(--text-soft);
}

/* --- Estado de error: borde rojo + micro-shake --- */
.field.is-error .field__input{
  border-color:var(--danger);
  background:var(--danger-soft);
}
/* --ring-danger y no --danger-soft: en claro ese relleno es casi blanco y
   sobre la tarjeta blanca un campo en error ENFOCADO se veia igual que uno
   sin foco. */
.field.is-error .field__input:focus{ box-shadow:0 0 0 4px var(--ring-danger); }
.field.is-error .field__icon,
.field.is-error .field__label{ color:var(--danger); }
.field.is-error .field__input,
.shake{ animation:shake 300ms var(--ease-in-out) both; }

@keyframes shake{
  0%,100%{ transform:translateX(0); }
  15%{ transform:translateX(-6px); }
  35%{ transform:translateX(6px); }
  55%{ transform:translateX(-4px); }
  75%{ transform:translateX(3px); }
  90%{ transform:translateX(-1px); }
}

/* ===========================================================================
   10. MEDIDOR DE FUERZA DE CONTRASENA
       <div class="strength" data-level="0">
         <div class="strength__bars"><i></i><i></i><i></i><i></i></div>
         <span class="strength__text">…</span>
       </div>
   ======================================================================== */
.strength{
  display:flex;
  align-items:center;
  gap:var(--sp-3);
  margin-top:var(--sp-3);
  padding-left:var(--sp-2);
}
.strength__bars{ display:flex; gap:var(--sp-1); flex:1 1 auto; }
.strength__bars i{
  flex:1 1 0;
  height:5px;
  border-radius:var(--r-full);
  background:var(--border);
  transform-origin:left center;
  transition:background-color var(--dur) var(--ease-out),
             transform var(--dur) var(--ease-spring);
}
.strength__text{
  font-size:var(--fs-xs);
  font-weight:var(--fw-semi);
  color:var(--text-soft);
  min-width:64px;
  text-align:right;
  transition:color var(--dur) var(--ease-out);
}
/* Nivel 1-2 debil, 3 media, 4 fuerte */
.strength[data-level="1"] .strength__bars i:nth-child(-n+1),
.strength[data-level="2"] .strength__bars i:nth-child(-n+2){ background:var(--danger); }
.strength[data-level="3"] .strength__bars i:nth-child(-n+3){ background:var(--warning); }
.strength[data-level="4"] .strength__bars i{ background:var(--success); }
/* Las BARRAS de arriba usan el color de relleno; el TEXTO usa la tinta:
   a 12px, --warning sobre la tarjeta clara da 2.19:1 y --danger 3.76:1. */
.strength[data-level="1"] .strength__text,
.strength[data-level="2"] .strength__text{ color:var(--danger-ink); }
.strength[data-level="3"] .strength__text{ color:var(--warning-ink); }
.strength[data-level="4"] .strength__text{ color:var(--success-ink); }

/* ===========================================================================
   11. CODIGO OTP
       <div class="otp" id="recOtp"></div>  -> mountOtp() crea los .otp__box
   ======================================================================== */
.otp{
  display:flex;
  /* POR QUE envuelve: 8 casillas en una fila daban 28px de ancho en un
     telefono de 360px y 23px en uno de 320px — imposibles de tocar. En
     celular se parten en dos filas de 4; desde tablet vuelven a una sola. */
  flex-wrap:wrap;
  gap:var(--sp-2);
  justify-content:space-between;
  align-items:center;
}
.otp__box{
  flex:1 1 calc(25% - var(--sp-2));
  min-width:52px;
  height:56px;
  padding:0;
  text-align:center;
  font-size:18px;              /* >=16px: sin zoom en iOS */
  font-weight:var(--fw-bold);
  text-transform:uppercase;
  color:var(--text);
  background:var(--surface-2);
  border:1.5px solid var(--border);
  border-radius:var(--r-xs);
  caret-color:var(--primary);
  transition:border-color var(--dur-fast) var(--ease-out),
             box-shadow var(--dur-fast) var(--ease-out),
             background-color var(--dur-fast) var(--ease-out),
             transform var(--dur) var(--ease-spring);
}
.otp__box:focus{
  outline:none;
  border-color:var(--primary);
  background:var(--surface);
  box-shadow:0 0 0 4px var(--ring);
  transform:translateY(-2px);
}
/* Cada casilla late al llenarse */
.otp__box.is-filled{
  border-color:var(--primary);
  background:var(--primary-soft);
  animation:otp-pop var(--dur) var(--ease-spring);
}
@keyframes otp-pop{
  0%{ transform:scale(1); }
  45%{ transform:scale(1.12); }
  100%{ transform:scale(1); }
}
/* Codigo completo: pulso verde en todo el bloque */
.otp.is-complete .otp__box{
  border-color:var(--success);
  background:var(--success-soft);
  animation:otp-done var(--dur-slow) var(--ease-out);
}
@keyframes otp-done{
  0%  { box-shadow:0 0 0 0 var(--success-soft); }
  40% { box-shadow:0 0 0 6px var(--success-soft); }
  100%{ box-shadow:0 0 0 0 transparent; }
}
.otp.is-error{ animation:shake 300ms var(--ease-in-out) both; }
.otp.is-error .otp__box{ border-color:var(--danger); background:var(--danger-soft); }

/* Desde tablet ya hay ancho: las 8 casillas vuelven a una sola fila */
@media (min-width:768px){
  .otp{ gap:var(--sp-3); flex-wrap:nowrap; }
  .otp__box{ flex:1 1 0; min-width:0; height:64px; font-size:22px; border-radius:var(--r-sm); }
}

/* ===========================================================================
   12. TOASTS
       <div id="toastHost" class="toast-host" role="status" aria-live="polite">
       toast() inyecta:
       <div class="toast toast--success">
         <span class="toast__icon"><svg>…</svg></span>
         <p class="toast__msg">…</p>
         <button class="toast__close" aria-label="Cerrar"><svg>…</svg></button>
         <span class="toast__bar"></span>
       </div>
   ======================================================================== */
.toast-host{
  position:fixed;
  z-index:var(--z-toast);
  top:calc(var(--safe-top) + var(--sp-3));
  left:50%;
  transform:translateX(-50%);
  width:min(440px, calc(100% - var(--sp-8)));
  display:flex;
  flex-direction:column;
  gap:var(--sp-2);
  pointer-events:none;          /* el host no bloquea; cada toast si recibe clic */
}

.toast{
  position:relative;
  display:flex;
  align-items:flex-start;
  gap:var(--sp-3);
  padding:var(--sp-4);
  /* Reserva el ancho del boton de cerrar (ahora de 44px) mas aire, para que
     el mensaje no quede debajo del boton */
  padding-right:calc(var(--tap) + var(--sp-3));
  border-radius:var(--r-md);
  border:1px solid var(--border);
  background:var(--surface);
  box-shadow:var(--sh-3);
  color:var(--text);
  overflow:hidden;
  cursor:pointer;
  pointer-events:auto;
  animation:toast-in var(--dur-slow) var(--ease-spring) both;
}
.toast.is-leaving{ animation:toast-out var(--dur) var(--ease-out) both; }

.toast__icon{
  flex:0 0 auto;
  width:24px; height:24px;
  display:flex; align-items:center; justify-content:center;
  border-radius:var(--r-full);
  color:var(--primary);
  background:var(--primary-soft);
}
.toast__icon svg{ width:15px; height:15px; stroke:currentColor; stroke-width:2.4; stroke-linecap:round; stroke-linejoin:round; }

.toast__msg{
  flex:1 1 auto;
  min-width:0;
  font-size:var(--fs-sm);
  font-weight:var(--fw-medium);
  line-height:var(--lh-snug);
  overflow-wrap:anywhere;       /* mensajes de la API pueden traer cadenas largas */
}
.toast__title{
  display:block;
  font-weight:var(--fw-bold);
  margin-bottom:2px;
}
/* 44px: es el UNICO cierre manual del toast en celular, asi que tiene que
   cumplir el area tactil minima (antes media 32x32) */
.toast__close{
  position:absolute;
  top:var(--sp-1); right:var(--sp-1);
  width:var(--tap); height:var(--tap);
  display:flex; align-items:center; justify-content:center;
  border-radius:var(--r-full);
  color:var(--text-soft);
  transition:color var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.toast__close:hover{ color:var(--text); background:var(--surface-2); }
.toast__close svg{ width:14px; height:14px; stroke:currentColor; stroke-width:2.4; stroke-linecap:round; }

/* Barra de progreso: se agota en --toast-dur (la fija el JS) */
.toast__bar{
  position:absolute;
  left:0; bottom:0;
  height:3px;
  width:100%;
  transform-origin:left center;
  background:var(--primary);
  opacity:.65;
  animation:toast-bar var(--toast-dur, 4200ms) linear forwards;
}

.toast--success .toast__icon{ color:var(--success); background:var(--success-soft); }
.toast--success .toast__bar{ background:var(--success); }
.toast--error   .toast__icon{ color:var(--danger);  background:var(--danger-soft); }
.toast--error   .toast__bar{ background:var(--danger); }
.toast--warning .toast__icon{ color:var(--warning); background:var(--warning-soft); }
.toast--warning .toast__bar{ background:var(--warning); }
.toast--info    .toast__icon{ color:var(--primary); background:var(--primary-soft); }
.toast--info    .toast__bar{ background:var(--primary); }

@keyframes toast-in{
  from{ opacity:0; transform:translateY(-18px) scale(.96); }
  to  { opacity:1; transform:translateY(0) scale(1); }
}
@keyframes toast-out{
  to{ opacity:0; transform:translateY(-12px) scale(.96); }
}
@keyframes toast-bar{
  from{ transform:scaleX(1); }
  to  { transform:scaleX(0); }
}

/* En escritorio los toasts caen en la esquina superior derecha */
@media (min-width:1200px){
  .toast-host{
    left:auto;
    right:var(--sp-6);
    transform:none;
  }
}

/* ===========================================================================
   13. LOADER DE PANTALLA COMPLETA
       <div id="loaderOverlay" class="loader" hidden aria-hidden="true">
         <div class="loader__box"><span class="loader__ring"></span>
         <p class="loader__txt">Un momento…</p></div>
       </div>
   ======================================================================== */
.loader{
  position:fixed;
  inset:0;
  z-index:var(--z-loader);
  display:flex;
  align-items:center;
  justify-content:center;
  background:var(--scrim);
  -webkit-backdrop-filter:blur(14px) saturate(140%);
  backdrop-filter:blur(14px) saturate(140%);
  opacity:0;
  transition:opacity var(--dur) var(--ease-out);
}
.loader[hidden]{ display:none; }
.loader.is-open{ opacity:1; }

.loader__box{
  display:flex;
  flex-direction:column;
  align-items:center;
  gap:var(--sp-4);
  padding:var(--sp-8);
  border-radius:var(--r-lg);
  background:var(--surface);
  border:1px solid var(--border);
  box-shadow:var(--sh-3);
  animation:card-in var(--dur) var(--ease-spring) both;
}
.loader__ring{
  width:44px; height:44px;
  border-radius:50%;
  border:3px solid var(--border);
  border-top-color:var(--primary);
  animation:spin 800ms linear infinite;
}
.loader__txt{
  font-size:var(--fs-sm);
  font-weight:var(--fw-semi);
  color:var(--text-muted);
  animation:pulse-soft 1.6s var(--ease-in-out) infinite;
}
@keyframes pulse-soft{
  0%,100%{ opacity:.55; }
  50%    { opacity:1; }
}

/* ===========================================================================
   14. SWITCH DE TEMA (morph sol <-> luna, puro CSS)
       <button id="themeToggle" class="theme-toggle" aria-label="Cambiar tema"
               aria-pressed="false">
         <span class="theme-toggle__icon" aria-hidden="true">
           <span class="theme-toggle__core"></span>
           <span class="theme-toggle__mask"></span>
           <span class="theme-toggle__rays"></span>
         </span>
       </button>
       La luna se "talla": el core encoge y una mascara del color del boton
       entra por arriba-derecha dejando la media luna.
   ======================================================================== */
.theme-toggle{
  position:relative;
  width:var(--tap); height:var(--tap);
  display:flex; align-items:center; justify-content:center;
  border-radius:var(--r-full);
  border:1px solid var(--border);
  background:var(--surface);
  box-shadow:var(--sh-1);
  color:var(--primary);
  -webkit-tap-highlight-color:transparent;
  transition:transform var(--dur) var(--ease-spring),
             box-shadow var(--dur) var(--ease-out),
             background-color var(--dur) var(--ease-in-out),
             border-color var(--dur) var(--ease-in-out);
}
.theme-toggle:hover{ transform:translateY(-1px) rotate(-8deg); box-shadow:var(--sh-2); }
.theme-toggle:active{ transform:scale(.94); }

.theme-toggle__icon{
  position:relative;
  width:22px; height:22px;
  display:block;
}
.theme-toggle__core{
  position:absolute;
  top:50%; left:50%;
  width:14px; height:14px;
  margin:-7px 0 0 -7px;
  border-radius:50%;
  background:currentColor;
  transition:transform var(--dur-slow) var(--ease-spring);
}
/* Mascara del mismo color del boton: al entrar recorta el circulo */
.theme-toggle__mask{
  position:absolute;
  top:50%; left:50%;
  width:14px; height:14px;
  margin:-7px 0 0 -7px;
  border-radius:50%;
  background:var(--surface);
  transform:translate(60%,-60%) scale(0);
  transition:transform var(--dur-slow) var(--ease-spring),
             background-color var(--dur) var(--ease-in-out);
}
/* 8 rayos hechos con box-shadow sobre un solo punto */
.theme-toggle__rays{
  position:absolute;
  top:50%; left:50%;
  width:2.5px; height:2.5px;
  margin:-1.25px 0 0 -1.25px;
  border-radius:50%;
  background:transparent;
  color:currentColor;
  box-shadow:
     0 -10px 0 currentColor,
     0  10px 0 currentColor,
   -10px   0  0 currentColor,
    10px   0  0 currentColor,
     -7px -7px 0 currentColor,
      7px -7px 0 currentColor,
     -7px  7px 0 currentColor,
      7px  7px 0 currentColor;
  transform:scale(1) rotate(0deg);
  opacity:1;
  transition:transform var(--dur-slow) var(--ease-spring),
             opacity var(--dur) var(--ease-out);
}

/* --- Estado luna: manual --- */
:root[data-theme="dark"] .theme-toggle__core{ transform:scale(1.18); }
:root[data-theme="dark"] .theme-toggle__mask{ transform:translate(38%,-38%) scale(1.05); }
:root[data-theme="dark"] .theme-toggle__rays{ transform:scale(.2) rotate(-70deg); opacity:0; }

/* --- Estado luna: automatico por sistema --- */
@media (prefers-color-scheme: dark){
  :root:not([data-theme="light"]) .theme-toggle__core{ transform:scale(1.18); }
  :root:not([data-theme="light"]) .theme-toggle__mask{ transform:translate(38%,-38%) scale(1.05); }
  :root:not([data-theme="light"]) .theme-toggle__rays{ transform:scale(.2) rotate(-70deg); opacity:0; }
}

/* ===========================================================================
   15. SEGMENTED CONTROL (estilo iOS)
       <div class="segmented" id="authTabs" role="tablist">
         <span class="segmented__pill" id="tabIndicator" aria-hidden="true"></span>
         <button class="segmented__btn is-active" id="tabLogin" role="tab">Entrar</button>
         <button class="segmented__btn" id="tabRegister" role="tab">Crear cuenta</button>
       </div>
       El JS posiciona la pildora con --seg-x y --seg-w; sin JS, el fallback
       de 2 opciones ya funciona.
   ======================================================================== */
.segmented{
  position:relative;
  display:flex;
  align-items:center;
  gap:var(--sp-1);
  padding:var(--sp-1);
  border-radius:var(--r-full);
  background:var(--surface-2);
  border:1px solid var(--border);
  isolation:isolate;
}
.segmented__pill{
  position:absolute;
  z-index:0;
  top:var(--sp-1);
  left:var(--sp-1);
  height:calc(100% - var(--sp-2));
  width:var(--seg-w, calc(50% - var(--sp-1) - 2px));
  border-radius:var(--r-full);
  background:var(--surface-raised);
  box-shadow:var(--sh-2);
  transform:translateX(var(--seg-x, 0px));
  transition:transform var(--dur-slow) var(--ease-spring),
             width var(--dur-slow) var(--ease-spring),
             background-color var(--dur) var(--ease-in-out);
}
.segmented__btn{
  position:relative;
  z-index:1;
  flex:1 1 0;
  min-height:var(--tap);
  padding:0 var(--sp-3);
  border-radius:var(--r-full);
  font-size:var(--fs-sm);
  font-weight:var(--fw-semi);
  color:var(--text-muted);
  -webkit-tap-highlight-color:transparent;
  transition:color var(--dur) var(--ease-out), transform var(--dur-fast) var(--ease-spring);
}
.segmented__btn:active{ transform:scale(.96); }
.segmented__btn.is-active{ color:var(--primary); }

/* ===========================================================================
   16. VISTAS ANIMADAS
       switchView() aplica las clases de salida/entrada segun la direccion.
   ======================================================================== */
.view{ display:block; }
.view[hidden],
.view.is-hidden{ display:none; }

.view--in-right{ animation:view-in-right var(--dur-slow) var(--ease-spring) both; }
.view--in-left { animation:view-in-left  var(--dur-slow) var(--ease-spring) both; }
.view--out-left{ animation:view-out-left var(--dur) var(--ease-out) both; }
.view--out-right{animation:view-out-right var(--dur) var(--ease-out) both; }

@keyframes view-in-right{
  from{ opacity:0; transform:translateX(34px); }
  to  { opacity:1; transform:translateX(0); }
}
@keyframes view-in-left{
  from{ opacity:0; transform:translateX(-34px); }
  to  { opacity:1; transform:translateX(0); }
}
@keyframes view-out-left{
  to{ opacity:0; transform:translateX(-28px); }
}
@keyframes view-out-right{
  to{ opacity:0; transform:translateX(28px); }
}

/* Entrada escalonada de los hijos de una vista: se ve "armada", no volcada */
.stagger > *{ animation:stagger-in var(--dur-slow) var(--ease-out) both; }
.stagger > *:nth-child(1){ animation-delay:40ms; }
.stagger > *:nth-child(2){ animation-delay:80ms; }
.stagger > *:nth-child(3){ animation-delay:120ms; }
.stagger > *:nth-child(4){ animation-delay:160ms; }
.stagger > *:nth-child(5){ animation-delay:200ms; }
.stagger > *:nth-child(6){ animation-delay:240ms; }
.stagger > *:nth-child(7){ animation-delay:280ms; }
.stagger > *:nth-child(8){ animation-delay:320ms; }
@keyframes stagger-in{
  from{ opacity:0; transform:translateY(10px); }
  to  { opacity:1; transform:translateY(0); }
}

/* ===========================================================================
   17. SEPARADOR Y ENLACES
   ======================================================================== */
.divider{
  display:flex;
  align-items:center;
  gap:var(--sp-3);
  color:var(--text-soft);
  font-size:var(--fs-xs);
  font-weight:var(--fw-medium);
}
.divider::before,
.divider::after{
  content:"";
  flex:1 1 auto;
  height:1px;
  background:var(--border);
}
.divider--plain::before,
.divider--plain::after{ content:none; }

.link{
  display:inline-flex;
  align-items:center;
  gap:var(--sp-1);
  color:var(--primary);
  font-weight:var(--fw-semi);
  font-size:var(--fs-sm);
  border-radius:var(--r-xs);
  cursor:pointer;
  transition:color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}
.link:hover{ opacity:.75; }
/* Subrayado que crece del centro: detalle de marca */
.link--underline{ position:relative; }
.link--underline::after{
  content:"";
  position:absolute;
  left:0; right:0; bottom:-2px;
  height:1.5px;
  background:currentColor;
  border-radius:var(--r-full);
  transform:scaleX(0);
  transition:transform var(--dur) var(--ease-out);
}
.link--underline:hover::after{ transform:scaleX(1); }

/* Enlaces que deben cumplir area tactil de 44px en movil */
.link--tap{ min-height:var(--tap); padding-inline:var(--sp-2); }

/* ===========================================================================
   18. PIEZAS AUXILIARES COMPARTIDAS
   ======================================================================== */

/* --- Marca / logo --- */
.brand{ display:inline-flex; align-items:center; gap:var(--sp-3); }
/* El cuadro azul con degradado existia porque la marca era un SVG BLANCO
   generico. Ahora es el isotipo REAL de CYC Facturacion, que ya trae su propio
   azul: dejarle el fondo encima lo volvia azul sobre azul, invisible. Se queda
   solo la caja, sin relleno ni sombra. */
.brand__mark{
  width:44px; height:44px;
  display:flex; align-items:center; justify-content:center;
  border-radius:var(--r-sm);
  color:var(--primary-ink);
}
/* El isotipo se lee igual en tema claro y oscuro (es azul sobre transparente),
   por eso NO hace falta una version para fondo oscuro. */
.brand__mark img{ width:100%; height:100%; object-fit:contain; display:block; }
.brand__mark svg{ width:24px; height:24px; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.brand__name{
  font-size:var(--fs-lg);
  font-weight:var(--fw-black);
  letter-spacing:var(--ls-tight);
}
.brand__name span{ color:var(--primary); }

/* --- Etiquetas de estado --- */
.badge{
  display:inline-flex;
  align-items:center;
  gap:var(--sp-1);
  padding:var(--sp-1) var(--sp-3);
  border-radius:var(--r-full);
  font-size:var(--fs-xs);
  font-weight:var(--fw-semi);
  background:var(--surface-2);
  color:var(--text-muted);
  border:1px solid var(--border);
}
/* Fondo = relleno semantico, texto = tinta: el badge es texto de 12px sobre
   su propio *-soft y con el color de relleno daba 2.19-2.38:1 (falla AA). */
.badge--success{ background:var(--success-soft); color:var(--success-ink); border-color:transparent; }
.badge--danger { background:var(--danger-soft);  color:var(--danger-ink);  border-color:transparent; }
.badge--warning{ background:var(--warning-soft); color:var(--warning-ink); border-color:transparent; }
.badge--primary{ background:var(--primary-soft); color:var(--primary-ink-soft); border-color:transparent; }

/* --- Aviso / banner (usado por #validateBanner) --- */
.notice{
  display:flex;
  flex-direction:column;
  gap:var(--sp-3);
  padding:var(--sp-4);
  border-radius:var(--r-md);
  border:1px solid var(--border);
  background:var(--surface-2);
  animation:card-in var(--dur-slow) var(--ease-spring) both;
}
.notice__head{ display:flex; align-items:flex-start; gap:var(--sp-3); }
.notice__icon{
  flex:0 0 auto;
  width:36px; height:36px;
  display:flex; align-items:center; justify-content:center;
  border-radius:var(--r-full);
  background:var(--warning-soft);
  color:var(--warning);
}
.notice__icon svg{ width:19px; height:19px; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.notice__title{ font-size:var(--fs-md); font-weight:var(--fw-bold); letter-spacing:var(--ls-tight); }
.notice__text{ font-size:var(--fs-sm); color:var(--text-muted); line-height:var(--lh-snug); }
.notice--warning{ background:var(--warning-soft); border-color:transparent; }
.notice--success{ background:var(--success-soft); border-color:transparent; }
.notice--success .notice__icon{ background:var(--surface); color:var(--success); }
.notice--danger{ background:var(--danger-soft); border-color:transparent; }
.notice--danger .notice__icon{ background:var(--surface); color:var(--danger); }

@media (min-width:768px){
  .notice{ flex-direction:row; align-items:center; justify-content:space-between; }
}

/* --- Celebracion: check grande dibujandose + destellos ---
       <div class="celebrate"><svg class="celebrate__check" viewBox="0 0 52 52">
         <circle cx="26" cy="26" r="24"/><path d="M15 27 L23 35 L38 18"/>
       </svg></div>                                                          */
.celebrate{
  position:relative;
  display:flex; align-items:center; justify-content:center;
  width:96px; height:96px;
  margin-inline:auto;
  color:var(--success);
}
.celebrate__check{ width:80px; height:80px; overflow:visible; }
.celebrate__check circle{
  fill:none; stroke:currentColor; stroke-width:2.5;
  stroke-dasharray:151; stroke-dashoffset:151;
  animation:draw-circle var(--dur-slow) var(--ease-out) forwards;
}
.celebrate__check path{
  fill:none; stroke:currentColor; stroke-width:3.6;
  stroke-linecap:round; stroke-linejoin:round;
  stroke-dasharray:40; stroke-dashoffset:40;
  animation:draw-check var(--dur-slow) var(--ease-out) 320ms forwards;
}
/* Destellos: un punto con 8 box-shadows que se expande y se apaga */
.celebrate::after{
  content:"";
  position:absolute;
  top:50%; left:50%;
  width:5px; height:5px;
  margin:-2.5px 0 0 -2.5px;
  border-radius:50%;
  box-shadow:
     0 -34px 0 var(--success),  0 34px 0 var(--primary),
   -34px  0 0 var(--primary-2), 34px  0 0 var(--success),
    -24px -24px 0 var(--primary), 24px -24px 0 var(--success),
    -24px  24px 0 var(--success), 24px  24px 0 var(--primary-2);
  opacity:0;
  animation:sparkle var(--dur-xslow) var(--ease-out) 420ms forwards;
}
@keyframes draw-circle{ to{ stroke-dashoffset:0; } }
@keyframes sparkle{
  0%  { transform:scale(.2); opacity:0; }
  35% { opacity:1; }
  100%{ transform:scale(1.5); opacity:0; }
}

/* --- Skeleton de carga --- */
.skeleton{
  position:relative;
  overflow:hidden;
  background:var(--surface-2);
  border-radius:var(--r-sm);
  min-height:14px;
}
.skeleton::after{
  content:"";
  position:absolute; inset:0;
  transform:translateX(-100%);
  background:linear-gradient(90deg, transparent, var(--border), transparent);
  animation:shimmer 1.4s var(--ease-in-out) infinite;
}
@keyframes shimmer{ to{ transform:translateX(100%); } }

/* ===========================================================================
   19. AJUSTES POR TAMANO (mobile-first: solo se SUBE)
   ======================================================================== */
@media (min-width:768px){
  .container{ padding-inline:var(--sp-6); }
  .btn{ padding-inline:var(--sp-8); }
}
@media (min-width:1200px){
  .container{ padding-inline:var(--sp-8); }
}

/* Punteros finos (mouse): los hovers de elevacion solo aplican ahi, para que
   en tactil no se queden "pegados" tras el tap */
@media (hover:none){
  .btn:hover{ transform:none; }
  .theme-toggle:hover{ transform:none; }
}

/* Impresion: fuera fondos vivos y cromos */
@media print{
  body::before, body::after, .aurora{ display:none; }
  .card{ box-shadow:none; border-color:var(--border-strong); }
  .toast-host, .loader, .theme-toggle{ display:none!important; }
}

/* ===========================================================================
   20. MOVIMIENTO REDUCIDO
       Obligatorio: quien lo pide en su sistema recibe la misma interfaz,
       sin animacion. Va AL FINAL para ganar en cascada.
   ======================================================================== */
@media (prefers-reduced-motion: reduce){
  *,*::before,*::after{
    animation-duration:.01ms!important;
    animation-iteration-count:1!important;
    transition-duration:.01ms!important;
    scroll-behavior:auto!important;
  }
}
