/* ══ LA PANTALLA DE ENTRAR — EL MISMO PRODUCTO ═══════════════════════
   (v6 — Edgar 2026-08-18: «el css del login no se parece en nada al css
   del dashboard, analiza, fuerte. y mejora».)

   ── POR QUÉ NO SE PARECÍA (medido, no opinado) ──────────────────────
   La v5 ya copiaba los COLORES del mundo galáctico (#0E0819, #A855F7,
   #C4A6FF) y ninguna de sus piezas de sistema. Las diez medidas:

     · 0 tokens compartidos. Cargaba assets/css/base.css —la hoja del
       LANDING, con su navy #0F172A, su azul #008FC8 y sus radios
       8/12/16— y encima declaraba un vocabulario privado (--cielo,
       --carta, --radio-campo). De 47 `var()`, ninguna del producto.
     · La tipografía, al revés. base.css pone Nunito en el body y esta
       hoja lo pisaba con la pila de Segoe: SOLO el <h1> salía en Nunito
       (por la regla de elemento de base.css), todo lo demás en Segoe.
       En el dashboard es exactamente al contrario.
     · Sin cielo. `background: #0E0819` plano. Medido: L=10.6 en los
       siete puntos de la pantalla, delta 0.0; en el producto la luz va
       de 12.9 a 27.2. Lo primero que se ve del mundo galáctico son sus
       cuatro nebulosas, y aquí no había ninguna.
     · La tarjeta era una caja, no cristal: velo violeta al 82% que
       RESTA luz, donde toda superficie del producto pone velo blanco
       al 4-6% que la SUMA. Con ese relleno el blur no desenfocaba nada.
     · Sin naranja. El acento del producto es #FF8A00 con tinta #2A1200
       —y la puerta gemela, el pop-up de alta, lo usa—. Aquí el objeto
       más ruidoso de la pantalla era lila.
     · Cuatro colores inventados: #EDEAF4 (el único del proyecto; en
       todas partes es #F4F4F6), #A99BC4, #7ED9A7, #FF6B62.
     · Movimiento ajeno: cubic-bezier(.22,.9,.28,1), cero apariciones en
       las 26.349 líneas de CSS del dashboard, y el resto `ease` pelado.
     · Sin foco visible en el botón de entrar ni en la salida.

   ── LO QUE SE HIZO ──────────────────────────────────────────────────
   login.php carga ahora dashboard/css/global.css: los radios, el
   movimiento, las sombras y la tipografía son LOS MISMOS OBJETOS, no
   copias. Encima, esta hoja hace el mismo REMAPEO que courses.css hace
   en .crs-page: reescribe los tokens del producto con los valores del
   cosmos, conservando SUS NOMBRES. Por eso todo lo de abajo se escribe
   en var(--accent), var(--text-main), var(--r-md), var(--tr-e) — el
   vocabulario del resto del sitio. El día que el sistema cambie, esta
   pantalla cambia con él en vez de quedarse atrás.

   ── DOS REGLAS QUE NO SE TOCAN ──────────────────────────────────────
   1. NADA se superpone al iframe de Google. GIS mide su posición para
      detectar clickjacking y ya bloqueó el login una vez. El «cuadro
      blanco» se domestica con overflow, jamás con una capa encima.
   2. Es una pantalla DE PASO. Nada de animaciones que retrasen el
      primer pintado: la tarjeta entra en 0.42s con la curva del
      sistema y ya está.

   El contrato con login.js sigue intacto (#auth-error, #btn-login,
   #google-signin-host, #form-login, #form-totp, .auth-unlock*,
   .is-loading, .auth-card--unlocking). Lo que SÍ murió es el modo alta:
   ya no hay .fg--signup-only, ni .auth-terms, ni body[data-auth-mode] —
   el alta es UNA, el pop-up compartido, y el pie lleva allí. */

/* ══ 1 · EL REMAPEO ══════════════════════════════════════════════════
   Los valores salen de movil/src/tema/tokens.js y están copiados al
   byte en dashboard/css/courses.css. Se declara también sobre
   html[data-theme="dark"] (0,1,1) para ganarle al bloque oscuro de
   global.css sin depender del orden de las hojas. */
:root,
html[data-theme="dark"] {
  /* — el mundo — */
  --cielo:       #0E0819;
  --vio:         #A855F7;
  --vio-luz:     #C4A6FF;
  --vio-tinta:   #1A1226;
  --cristal:     rgba(22, 16, 38, .72);

  /* — acción: el naranja de la marca, como en la puerta gemela — */
  --accent:      #FF8A00;
  --accent-mid:  #E67A00;
  --accent-soft: rgba(255, 138, 0, .16);
  --accent-glow: rgba(255, 138, 0, .26);
  --shadow-accent: 0 4px 18px rgba(255, 138, 0, .28);

  /* — superficies — */
  --bg-main:     var(--cielo);
  --bg-panel:    rgba(255, 255, 255, .05);
  --bg-soft:     rgba(255, 255, 255, .035);
  --bg-elevated: #1B1426;

  /* — texto: los tres escalones del producto, sin inventar — */
  --text-main:   #F4F4F6;
  --text-soft:   #A0A0AC;
  --text-muted:  #7E7594;
  --text-invert: #2A1200;

  /* — bordes: sobre negro lo que separa es un filo de luz — */
  --border-soft: rgba(255, 255, 255, .09);
  --border-mid:  rgba(255, 255, 255, .16);
  --border-vio:  rgba(196, 166, 255, .22);

  --green:       #5BE39C;
  --red:         #F0616D;
}

/* ══ 2 · EL LIENZO ═══════════════════════════════════════════════════
   Las cuatro manchas de riel.css y courses.css, idénticas al byte, con
   `fixed` para que el cielo no se mueva. Esto es lo que se ve PRIMERO
   de Piofly y era justo lo que faltaba. */
html, body { height: 100%; }

body {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
  color: var(--text-main);
  background:
    radial-gradient(42% 34% at 14% 6%,  rgba(168, 85, 247, .30), transparent 68%),
    radial-gradient(36% 28% at 88% 4%,  rgba(236, 72, 153, .17), transparent 70%),
    radial-gradient(58% 42% at 52% 46%, rgba(49, 46, 129, .26),  transparent 74%),
    radial-gradient(30% 26% at 8% 88%,  rgba(59, 130, 246, .10), transparent 70%),
    var(--cielo);
  background-attachment: fixed;
  /* global.css pone `transition: background .3s` para el cambio de tema.
     Aquí no hay cambio de tema y esa transición solo puede pintar el
     cielo tarde en la primera carga. */
  transition: none;
}

/* ── Las estrellas ─────────────────────────────────────────────────
   La app las tiene (movil/src/ui/Cielo.js), así que se quedan — pero
   más tenues que en la v5: ahora hay nebulosa debajo y era ella la que
   faltaba. Cero imágenes, cero peso: todo radial-gradients. */
.auth-cielo {
  position: fixed; inset: 0; z-index: 0;
  overflow: hidden; pointer-events: none;
}
.auth-cielo i { position: absolute; inset: -50%; background-repeat: repeat; will-change: transform; }
.cielo-e1 {
  background-image:
    radial-gradient(1.6px 1.6px at 18% 28%, #fff, transparent),
    radial-gradient(1.3px 1.3px at 72% 14%, #E9D5FF, transparent),
    radial-gradient(1.8px 1.8px at 44% 68%, #fff, transparent),
    radial-gradient(1.2px 1.2px at 86% 54%, #C4B5FD, transparent),
    radial-gradient(1.5px 1.5px at 10% 80%, #fff, transparent),
    radial-gradient(1.3px 1.3px at 62% 92%, #DDD6FE, transparent);
  background-size: 330px 330px;
  opacity: .38;
  animation: cielo-vaga 190s linear infinite, cielo-latir 5s ease-in-out infinite;
}
.cielo-e2 {
  background-image:
    radial-gradient(1.2px 1.2px at 30% 44%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 80% 78%, #E9D5FF, transparent),
    radial-gradient(1.1px 1.1px at 56% 20%, #C4B5FD, transparent),
    radial-gradient(1.4px 1.4px at 8% 60%, #fff, transparent);
  background-size: 210px 210px;
  opacity: .28;
  animation: cielo-vaga 120s linear infinite reverse, cielo-latir 7s ease-in-out infinite;
}
.cielo-polvo {
  background-image:
    radial-gradient(2.6px 2.6px at 24% 36%, rgba(196, 166, 255, .85), transparent),
    radial-gradient(3px 3px at 70% 64%, rgba(168, 85, 247, .7), transparent),
    radial-gradient(2.4px 2.4px at 48% 86%, rgba(233, 213, 255, .75), transparent);
  background-size: 260px 260px;
  filter: blur(.4px);
  opacity: .38;
  animation: polvo-sube 26s linear infinite, cielo-latir 6s ease-in-out infinite;
}
@keyframes cielo-vaga { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(-330px, -330px, 0); } }
@keyframes polvo-sube { from { transform: translate3d(0, 0, 0); } to { transform: translate3d(0, -260px, 0); } }
@keyframes cielo-latir { 0%, 100% { opacity: .5; } 50% { opacity: .26; } }

/* ══ 3 · LA TARJETA: cristal de verdad ═══════════════════════════════
   El cristal violeta del producto (--crs-cristal de courses.css) MÁS un
   velo blanco encima. Ese velo es la diferencia que se medía: toda
   superficie del cosmos SUMA luz sobre el fondo; la v5 la restaba, y por
   eso se leía como una caja pegada. Con el relleno translúcido el
   backdrop-filter por fin tiene algo que desenfocar: la nebulosa pasa
   por detrás.

   Las alfas de la sombra son las del pop-up de alta (.62/.42), no las de
   la v5 (.55/.35): misma geometría exacta que ya se había desviado.

   El backdrop-filter se queda —lleva meses conviviendo con el iframe de
   Google en esta misma tarjeta— pero es lo primero que hay que quitar si
   GIS alguna vez se queja. */
.auth-card {
  position: relative; z-index: 2;
  width: min(420px, 100%);
  padding: 32px 28px 24px;
  border-radius: 20px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, .07), rgba(255, 255, 255, .035)),
    var(--cristal);
  border: 1px solid var(--border-vio);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  backdrop-filter: blur(20px) saturate(140%);
  box-shadow:
    0 32px 80px rgba(0, 0, 0, .62),
    0 12px 32px rgba(0, 0, 0, .42),
    inset 0 1px 0 rgba(255, 255, 255, .06);
  /* La curva de ENTRADA del sistema (137 usos en el dashboard), no una
     inventada. Y 0.42s como el pop-up gemelo: esto es una puerta, no un
     sitio donde quedarse. */
  animation: carta-entra .42s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@keyframes carta-entra {
  from { opacity: 0; transform: translateY(12px) scale(.985); }
  to   { opacity: 1; transform: none; }
}

/* ── El logo y el wordmark: «pio» claro, «fly» SIEMPRE naranja ────── */
.auth-logo {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  margin-bottom: 18px; text-decoration: none;
}
.auth-logo img { width: 34px; height: 34px; border-radius: 9px; }
.logo-wordmark { font-size: 21px; font-weight: 800; letter-spacing: -0.5px; }
.logo-wordmark span:first-child { color: var(--text-main); }
/* --brand-orange y no --accent: es cromo de marca, y los temas por
   sector del dashboard repintan --accent (realestate lo vuelve AZUL). */
.logo-wordmark span:last-child  { color: var(--brand-orange, #FF8A00); }

.auth-title {
  margin: 0 0 6px;
  font-size: 24px; font-weight: 900; letter-spacing: -0.4px;
  text-align: center; color: var(--text-main);
  line-height: 1.2;
}
.auth-sub {
  margin: 0 0 20px;
  font-size: 14px; line-height: 1.5; text-align: center;
  color: var(--text-soft);
}

/* ── El error: oculto de fábrica; el JS lo enciende ───────────────── */
.auth-error {
  display: none;
  margin: 0 0 14px; padding: 10px 13px;
  border-radius: var(--r-sm);
  background: rgba(240, 97, 109, .10);
  border: 1px solid rgba(240, 97, 109, .32);
  color: var(--red); font-size: 13px; font-weight: 700; line-height: 1.45;
}

/* ── El botón de Google: sin overlays JAMÁS (GIS bloquea). El «cuadro
     blanco» se domestica con overflow + radio; el host debe verse bien
     también como display:block (el flujo TOTP lo deja así). ───────── */
.lm-google-host {
  display: block; width: 100%;
  min-height: 44px;
  border-radius: 12px; overflow: hidden;
}
.lm-google-host > div { margin: 0 auto; }

.lm-divider {
  display: flex; align-items: center; gap: 12px;
  margin: 16px 0;
  color: var(--text-muted); font-size: 12px; font-weight: 600;
}
.lm-divider::before, .lm-divider::after {
  content: ''; flex: 1; height: 1px;
  background: var(--border-soft);
}
.lm-divider--small { margin: 14px 0; }

/* ══ 4 · LOS CAMPOS ══════════════════════════════════════════════════
   46 de alto, radio 14, blanco al 6% con borde al 10%: el campo de la
   app (ctaCampo de Perfil.js) y de profile.css, tal cual. La v5 iba a
   48/12 con borde de 1.5 — cerca, pero de nadie.

   El rótulo es el de la puerta gemela: 10.5 / 800 / 1.2px. */
.fg { margin-bottom: 14px; }
.fg label {
  display: block; margin-bottom: 7px;
  font-size: 10.5px; font-weight: 800; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--text-muted);
}
.fg input {
  width: 100%; height: 46px; padding: 0 15px;
  background: rgba(255, 255, 255, .06);
  border: 1px solid rgba(255, 255, 255, .10);
  border-radius: var(--r-md);
  font-size: 15px; font-weight: 600; font-family: inherit;
  color: var(--text-main);
  outline: none;
  transition: border-color var(--tr-e), background var(--tr-e), box-shadow var(--tr-e);
}
.fg input::placeholder { color: var(--text-muted); opacity: .8; }
.fg input:hover:not(:focus) {
  background: rgba(255, 255, 255, .08);
  border-color: rgba(255, 255, 255, .20);
}
/* El anillo del sistema: borde del acento + 0 0 0 3px del mismo color al
   16%. Va en :focus y no en :focus-visible a propósito — en un campo de
   texto, saber dónde estás escribiendo importa igual con ratón. */
.fg input:focus {
  background: rgba(255, 255, 255, .08);
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
/* El autofill amarillo de Chrome, neutralizado al color de la carta. */
.fg input:-webkit-autofill,
.fg input:-webkit-autofill:hover,
.fg input:-webkit-autofill:focus {
  -webkit-box-shadow: 0 0 0 1000px #1D1530 inset;
  -webkit-text-fill-color: var(--text-main);
  caret-color: var(--text-main);
  transition: background-color 99999s ease-in-out 0s;
}

/* ══ 5 · EL BOTÓN ════════════════════════════════════════════════════
   NARANJA con tinta oscura. Es el cambio que más se nota y el que más
   importa: en el mundo galáctico, lo que Pío quiere que hagas es
   #FF8A00 sobre #2A1200 — y la puerta gemela (el pop-up de alta) ya lo
   usa. Dos puertas al mismo sitio no pueden tener el botón de dos
   colores distintos.

   Blanco sobre naranja da 2.36:1 y está PROHIBIDO desde siempre; la
   tinta oscura da 7.84:1. */
.btn-auth {
  display: block;
  width: 100%; height: 48px; margin-top: 4px; padding: 0;
  background: var(--accent);
  color: var(--text-invert);
  border: 0; border-radius: var(--r-md);
  font-size: 15px; font-weight: 800; font-family: inherit;
  letter-spacing: -0.1px;
  cursor: pointer;
  box-shadow: var(--shadow-accent);
  transition: filter var(--tr-e), transform var(--tr-e), box-shadow var(--tr-e);
}
/* Hover del sistema: subir 1px y aclarar. No oscurecer — un primario que
   se apaga al pasar por encima se lee como deshabilitado. */
.btn-auth:hover:not(:disabled) {
  filter: brightness(1.06);
  transform: translateY(-1px);
  box-shadow: 0 10px 26px rgba(255, 138, 0, .34);
}
.btn-auth:active:not(:disabled) { transform: scale(.98); }
.btn-auth:disabled { opacity: .5; cursor: default; box-shadow: none; }

/* Secundario: el patrón real del producto es relleno blanco al 6% con
   borde al 16%, no transparente. */
.btn-auth--ghost {
  margin-top: 9px;
  background: rgba(255, 255, 255, .06);
  color: var(--text-soft);
  border: 1px solid var(--border-mid);
  box-shadow: none;
}
.btn-auth--ghost:hover:not(:disabled) {
  background: rgba(255, 255, 255, .09);
  color: var(--text-main);
  filter: none; box-shadow: none; transform: none;
}

/* El spinner que login.js mete en el botón: tinta oscura sobre naranja. */
.btn-inline-spinner {
  display: inline-block; vertical-align: -2px;
  width: 13px; height: 13px; margin-left: 8px;
  border: 2px solid rgba(42, 18, 0, .28);
  border-top-color: var(--text-invert);
  border-radius: 50%;
  animation: girar .7s linear infinite;
}
.btn-auth--ghost .btn-inline-spinner {
  border-color: rgba(255, 255, 255, .2);
  border-top-color: var(--text-main);
}
@keyframes girar { to { transform: rotate(360deg); } }
.btn-auth.is-loading { pointer-events: none; }

/* ══ 6 · EL 2FA, CIUDADANO DE PRIMERA ════════════════════════════════ */
#form-totp .auth-sub { margin-bottom: 16px; }
#login-totp-code {
  text-align: center;
  font-size: 22px; font-weight: 800; letter-spacing: 8px;
  /* El letter-spacing añade su hueco DESPUÉS del último dígito, así que
     un texto centrado sale 8px a la izquierda. Se compensa. */
  text-indent: 8px;
}
.auth-trust-check {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 2px 0 14px; cursor: pointer; user-select: none;
}
.auth-trust-check input {
  position: absolute; opacity: 0; width: 1px; height: 1px;
}
.auth-trust-check-box {
  flex: 0 0 auto; width: 19px; height: 19px; margin-top: 1px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 6px;
  border: 1.5px solid var(--border-mid);
  background: rgba(255, 255, 255, .06);
  color: transparent;
  transition: background var(--tr-e), border-color var(--tr-e), color var(--tr-e);
}
.auth-trust-check input:checked + .auth-trust-check-box {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-invert);
}
.auth-trust-check-text {
  font-size: 12.5px; line-height: 1.45; color: var(--text-soft);
}

/* ── LA SALIDA DEL 2FA ────────────────────────────────────────────────
   Discreta a propósito: no compite con «Verificar y Entrar», que es lo
   que casi todo el mundo va a pulsar. Pero se LEE — quien llega aquí sin
   su autenticador necesita encontrarla, no adivinarla.

   Iba con var(--tx3, #94A3B8): --tx3 no existía en ninguna hoja de esta
   página, así que lo último que se añadió al login era lo ÚNICO pintado
   con el gris azulado de la paleta retirada. */
.auth-rescate {
  display: block; width: 100%; margin-top: 14px;
  background: none; border: 0; padding: 6px;
  font-family: inherit; font-size: 13px; font-weight: 700;
  color: var(--text-muted); text-decoration: underline;
  text-underline-offset: 3px; cursor: pointer;
  transition: color var(--tr-e);
}
.auth-rescate:hover { color: var(--accent); }

/* La caja del rescate no tenía ni una regla: era un formulario suelto
   flotando bajo el del código. Es un paso aparte y se ve como tal. */
#totp-rescate-caja {
  padding: 14px;
  background: var(--bg-soft);
  border: 1px solid var(--border-soft);
  border-radius: var(--r-md);
}
#totp-rescate-caja .fg:last-of-type { margin-bottom: 0; }
/* El mensaje decía «Escribe un correo válido» y «el enlace va en camino»
   con el mismo gris. Un aviso y una confirmación no se ven igual. */
#totp-rescate-msg.is-mal { color: var(--red); font-weight: 700; }

/* ══ 7 · EL PIE: LA ÚNICA PUERTA DE ALTA ═════════════════════════════
   Aquí había un <button> que cambiaba la pantalla a «modo alta» sin
   recargar. Ese modo mandaba nombre, correo y contraseña, y el servidor
   exige además fecha de nacimiento, @usuario y aceptar los términos:
   contestaba 400 «Escribe tu fecha de nacimiento» en una pantalla que no
   tenía dónde escribirla. Era la TERCERA puerta de alta rota del
   producto; las otras dos se cerraron el mismo día.

   Ahora es un enlace de verdad a auth/registro.php, que lleva al pop-up
   compartido —el que sí pide los seis datos y crea cuentas—. Enlace y no
   botón: se puede abrir en otra pestaña, se ve a dónde va, y funciona
   aunque el JS no cargue. */
.auth-footer {
  margin-top: 18px; padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, .08);
  display: flex; align-items: center; justify-content: center;
  gap: 7px; flex-wrap: wrap;
  font-size: 13.5px; color: var(--text-soft);
}
.auth-alta {
  color: var(--brand-orange, #FF8A00);
  font-weight: 800; text-decoration: none;
}
.auth-alta:hover { text-decoration: underline; }

/* ── La salida, abajo a la izquierda, sin estorbar ────────────────── */
.auth-exit {
  position: fixed; left: 18px; bottom: 16px; z-index: 2;
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--text-muted); text-decoration: none;
  font-size: 13px; font-weight: 600;
  transition: color var(--tr-e);
}
.auth-exit:hover { color: var(--text-main); }

/* ── El toast de «cerraste sesión» ────────────────────────────────── */
.auth-toast {
  position: fixed; top: 18px; left: 50%; transform: translateX(-50%);
  z-index: 5;
  display: flex; align-items: center; gap: 10px;
  padding: 11px 16px; border-radius: var(--r-md);
  background: var(--cristal);
  border: 1px solid var(--border-vio);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  box-shadow: 0 16px 40px rgba(0, 0, 0, .5);
  font-size: 13px; font-weight: 600; color: var(--text-main);
  /* `backwards` y NO `both`. Con `both` el fotograma final de la entrada
     se queda pegado para siempre, y una animación gana a una declaración
     normal: `.auth-toast--out { opacity: 0 }` no se aplicaba nunca. El
     toast no se desvanecía a los 4.5s — se quedaba entero hasta que el JS
     lo arrancaba del DOM a los 5.2s, y tampoco se iba con la cinemática
     de desbloqueo. Medido: opacity seguía en 1 tras marcar la salida.
     Con `backwards` el reposo lo vuelve a mandar la regla de arriba
     (transform: translateX(-50%), opacity 1) y la salida por fin corre. */
  animation: toast-entra .4s cubic-bezier(0.22, 1, 0.36, 1) backwards;
  transition: opacity .6s ease, transform .6s ease;
}
.auth-toast-icon { display: inline-flex; color: var(--green); }
.auth-toast--out { opacity: 0; transform: translateX(-50%) translateY(-8px); }
@keyframes toast-entra {
  from { opacity: 0; transform: translateX(-50%) translateY(-10px); }
  to   { opacity: 1; transform: translateX(-50%); }
}
/* El gemelo sin el centrado, para cuando el toast va de borde a borde.
   Hace falta una animación aparte porque `toast-entra` mueve el transform
   a `translateX(-50%)` en cada fotograma: sobre un toast que ya va de
   borde a borde eso lo saca media pantalla a la izquierda MIENTRAS entra.
   El reposo sí lo arregla la media query (ver `transform: none` abajo). */
@keyframes toast-entra-ancho {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: none; }
}

/* ══ 8 · EL FOCO SE VE ═══════════════════════════════════════════════
   Había DOS reglas en 445 líneas, y ninguna sobre el botón de entrar, el
   pie ni la salida. Y global.css apaga el outline de todos los <button>,
   así que sin esto quien navega con teclado no sabría dónde está.

   Forma canónica del producto: 2px sólidos, offset 3 sobre el cosmos. Un
   solo color —el acento— en toda la pantalla: tres colores de anillo
   distintos es justo la incoherencia que veníamos a arreglar.

   Los campos NO entran aquí: ya tienen su anillo de 3px en :focus, que
   es más visible que un outline. */
.auth-card :is(a, button, [tabindex]):focus-visible,
.auth-exit:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
/* El checkbox real es invisible (1×1px): el foco lo enseña su caja. */
.auth-trust-check input:focus-visible + .auth-trust-check-box {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ══ 9 · LA CINEMÁTICA DE DESBLOQUEO ═════════════════════════════════
   login.js crea el overlay y espera 1300ms; TODO lo visual pasa aquí.
   Sin estas reglas quedan 1.3s de pantalla congelada.

   Se queda violeta a propósito: no es un control, es el cosmos
   abriéndose. Lo que cambia son las curvas (las del sistema) y el
   presupuesto: las ondas duraban 1.1s con 0.28s de retraso —acababan a
   1.38s— y el redirect dispara a 1.30s, así que la tercera se cortaba
   80ms antes de terminar. Ahora caben. */
.auth-unlock {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center;
  flex-direction: column; gap: 18px;
  pointer-events: none;
}
.auth-unlock-bg {
  position: absolute; inset: 0;
  background: radial-gradient(ellipse at center,
              rgba(168, 85, 247, 0.22), rgba(14, 8, 25, 0.94) 70%);
  animation: unlock-velo .5s ease both;
}
@keyframes unlock-velo { from { opacity: 0; } to { opacity: 1; } }
.auth-unlock-core {
  position: relative; width: 14px; height: 14px; border-radius: 50%;
  background: var(--vio-luz);
  box-shadow: 0 0 30px 8px rgba(196, 166, 255, 0.8);
  animation: unlock-nace .5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}
@keyframes unlock-nace {
  from { transform: scale(0); } to { transform: scale(1); }
}
.auth-unlock-ring {
  position: absolute; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid rgba(196, 166, 255, 0.55);
  animation: unlock-onda .95s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.auth-unlock-ring--2 { animation-delay: .12s; }
.auth-unlock-ring--3 { animation-delay: .24s; }
@keyframes unlock-onda {
  from { transform: scale(1); opacity: .9; }
  to   { transform: scale(22); opacity: 0; }
}
.auth-unlock-text {
  position: relative;
  font-size: 15px; font-weight: 800; letter-spacing: .4px;
  color: var(--text-main);
  animation: unlock-texto .45s cubic-bezier(0.22, 1, 0.36, 1) .25s both;
}
@keyframes unlock-texto {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}
/* OJO: .auth-card recibe `carta-entra` arriba y `carta-sale` aquí, con la
   misma especificidad. Gana ésta SOLO por estar declarada después.
   Reordenar el archivo rompería la salida de la tarjeta sin dar un
   aviso — este bloque tiene que seguir siendo el último de los dos. */
.auth-card--unlocking {
  animation: carta-sale .5s cubic-bezier(0.55, 0, 0.85, 0.4) both;
}
@keyframes carta-sale {
  to { opacity: 0; transform: translateY(-10px) scale(.985); }
}
body.auth-unlocking .auth-exit,
body.auth-unlocking .auth-toast { opacity: 0; transition: opacity .3s ease; }

/* ══ 10 · MOVIMIENTO REDUCIDO ════════════════════════════════════════
   El cielo se queda quieto y la cinemática es un fundido simple.
   La v5 ponía `animation:none; opacity:.35; transition:opacity .3s` a la
   tarjeta que sale: medido, esa transición NUNCA corría —la propiedad y
   el valor final llegan en el mismo cambio de estilo, así que el
   navegador salta directo—. Se quita la mentira y se deja el salto. */
@media (prefers-reduced-motion: reduce) {
  .auth-cielo i, .auth-card, .auth-toast { animation: none; }
  .auth-unlock-core, .auth-unlock-ring, .auth-unlock-text { display: none; }
  .auth-unlock-bg { animation: unlock-velo .3s ease both; }
  .auth-card--unlocking { animation: none; opacity: .35; }
  .btn-auth, .btn-auth:hover:not(:disabled) { transition: none; transform: none; }
  .btn-inline-spinner { animation-duration: 1.6s; }
}

/* ══ 11 · TELÉFONO ═══════════════════════════════════════════════════
   La vinculación de la app abre ESTA página en el móvil, así que el
   ancho angosto no es un caso raro: es uno de los normales. */
@media (max-width: 460px) {
  body { padding: 16px 12px; align-items: flex-start; padding-top: 6vh; }
  .auth-card { padding: 26px 20px 22px; }
  .auth-title { font-size: 21px; }
  .fg input { font-size: 16px; } /* 16 o iOS hace zoom al enfocar */

  /* ── EL TOAST NO SE SIENTA ENCIMA DE LA TARJETA ──────────────────
     `auth-just-out` lleva tiempo saliendo en el <body> sin que ninguna
     hoja lo mirara. No sobraba: faltaba su regla. En ancho la tarjeta va
     centrada y el toast le queda muy por encima, pero en el teléfono la
     tarjeta sube al principio de la pantalla y se tocaban. Medido con
     ?out=1: a 360 el toast ocupa 18→76 y la tarjeta empezaba en 26; a
     390 el texto va a dos líneas (18→114) y tapaba el logo entero.
     El `env()` es porque esta pantalla la abre la app en un webview:
     en un teléfono con muesca el toast ya baja solo. */
  body.auth-just-out {
    padding-top: max(128px, calc(env(safe-area-inset-top) + 116px));
  }
}

/* A 360px la salida fija se comía el borde de la tarjeta y el toast, que
   se centra con translateX(-50%) y ancho automático, se salía por la
   derecha. Se le reserva su banda abajo y el toast pasa a ir de borde a
   borde. */
@media (max-width: 380px) {
  body { padding: 14px 10px 54px; padding-top: 4vh; }
  .auth-card { padding: 22px 16px 20px; }
  .auth-exit { left: 10px; bottom: 12px; font-size: 12.5px; }
  .auth-toast {
    left: 10px; right: 10px;
    /* El centrado de arriba sobra cuando el toast ya va de borde a borde:
       sin esto se iría media pantalla a la izquierda al quedarse quieto. */
    transform: none;
    animation-name: toast-entra-ancho;
    font-size: 12.5px;
  }
  .auth-toast--out { transform: translateY(-8px); }
  .auth-footer { font-size: 13px; }
  /* Aquí el toast va de borde a borde y con letra más chica: cabe en una
     línea, así que necesita menos hueco que en la franja de arriba. */
  body.auth-just-out {
    padding-top: max(96px, calc(env(safe-area-inset-top) + 84px));
  }
}
