/* =============================================================================
   Pantalla de acceso.
   Solo se carga en /login.php. Los tokens vienen de app.css; aqui solo vive lo
   propio de esta pantalla: el fondo tipo "pantalla de inicio" y el panel.
   ========================================================================== */

body.auth-body {
  margin: 0;
  overflow-x: hidden;
  background: #fff;
}

/* Distribucion clasica: panel de imagen a la izquierda, formulario blanco a la
   derecha. En movil la imagen se retira y el formulario ocupa la pantalla. */
.auth {
  min-height: 100svh;
  display: grid;
  grid-template-columns: 1fr;
  --auth-cycle: 9000ms;
  --auth-fade: 2000ms;
}

@media (min-width: 992px) {
  .auth { grid-template-columns: 7fr 5fr; }
}

/* A partir de cierto ancho la proporcion deja de servir: con 7fr/5fr, a 1920 la
   columna del formulario se va a 800px y el panel de 400 se queda flotando en
   medio de la nada. Se le pone techo y el excedente se lo lleva la fotografia,
   que es lo unico que gana con mas espacio. */
@media (min-width: 1500px) {
  .auth { grid-template-columns: 1fr minmax(0, 40rem); }
}

/* ── Panel de imagen ─────────────────────────────────────────────────────── */

.auth-media {
  position: relative;
  overflow: hidden;
  display: none;
  /* Color base bajo las fotos: evita el destello antes de que carguen. */
  background: #0a1020;
}

@media (min-width: 992px) {
  .auth-media { display: block; }
}

.auth-media-content {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 2;
  padding: clamp(2rem, 4vw, 3.25rem);
  color: #fff;
}

/* ── Fondo: pila de fotos que se cruzan una a una ───────────────────────── */

.auth-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.auth-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--auth-fade) cubic-bezier(.4, 0, .2, 1);
}

.auth-slide.is-active {
  opacity: 1;
}

/* El zoom vive en un hijo: asi el fundido (opacidad del padre) y el
   desplazamiento (transform del hijo) no compiten por la misma propiedad. */
.auth-slide-img {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  transform: scale(1.05);
  will-change: transform;
}

/* Sin zoom continuo. Antes cada foto se acercaba y la siguiente se alejaba, en
   bucle: sentado delante, eso se lee como un vaiven que nunca para y compite
   con el formulario, que es lo unico que hay que mirar. El fundido entre fotos
   ya aporta el movimiento, y ese si tiene motivo (muestra otro edificio). */

/* Velo. Calibrado para la foto MAS clara del set (cielo azul + blancos), no
   para la mas oscura: si funciona con esa, funciona con todas. El peso esta
   abajo, que es donde se apoya el texto. */
.auth-scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    linear-gradient(to bottom,
      rgba(6, 12, 26, .32) 0%,
      rgba(6, 12, 26, .38) 42%,
      rgba(6, 12, 26, .82) 78%,
      rgba(6, 12, 26, .94) 100%);
}

/* ── Texto sobre la imagen ──────────────────────────────────────────────── */

.auth-title {
  font-size: clamp(1.9rem, 3.1vw, 2.9rem);
  font-weight: 850;
  line-height: 1.05;
  letter-spacing: -.03em;
  text-wrap: balance;
  max-width: 18ch;
  margin: 0 0 .85rem;
}

.auth-lead {
  font-size: clamp(.98rem, 1.15vw, 1.1rem);
  line-height: 1.55;
  /* Blanco al 80%: sobre el velo pasa 4.5:1 de sobra y no compite con el titulo. */
  color: rgba(255, 255, 255, .8);
  max-width: 48ch;
  margin: 0;
}

/* ── Columna derecha: formulario sobre blanco ───────────────────────────── */

/* ── Escenario de marca (solo movil) ────────────────────────────────────────
   Mitad superior de la pantalla en movil. Sustituye a la banda plana de color:
   un campo azul profundo con la arquitectura dibujada en CSS (louvers
   verticales y cantos de losa en perspectiva, cielo entre los edificios). Cero
   peticiones de red, asi que esta pintado en el primer frame y sobrevive a que
   la PWA abra sin conexion. */

.auth-stage {
  position: relative;
  z-index: 1;
  display: grid;
  /* La marca se apoya en la base, no en el centro. El centro de esta foto es el
     hueco de cielo —lo mejor que tiene— y taparlo con un velo para poder poner
     texto encima seria gastar la imagen en nada. Abajo la foto ya es oscura y
     ademas es donde la hoja recoge la mirada. */
  place-items: end center;
  overflow: hidden;
  isolation: isolate;
  /* Base solida bajo los degradados: si algo falla, nunca hay franja blanca
     bajo la barra de estado translucida de iOS. */
  background: #0b1a3a;
  /* Alto propio, no el de su contenido. Sin esto el escenario se encoge a la
     altura del logo y la perspectiva no tiene recorrido donde leerse. */
  min-height: 38svh;
  padding: calc(env(safe-area-inset-top) + 2.6rem) 1.5rem 2.9rem;
  padding-left: max(1.5rem, env(safe-area-inset-left));
  padding-right: max(1.5rem, env(safe-area-inset-right));
  /* Sin transicion. Esto animaba `min-height` y `padding`, que son propiedades
     de LAYOUT: durante 300ms el navegador rehacia el calculo de la pagina en
     cada frame. Y justo encima hay una capa desenfocada, asi que cada uno de
     esos frames arrastraba tambien un repintado del velo. Se disparaba al
     enfocar un campo -entonces se encogia el escenario- y se sentia como que el
     teclado tironeaba la pantalla. El teclado del telefono aparece de golpe;
     que la cabecera ceda el sitio de golpe va con eso, no contra eso. */
}

@media (min-width: 992px) {
  .auth-stage { display: none; }
}

.auth-stage-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
}

.auth-stage-photo {
  position: absolute;
  inset: 0;
  /* Anclada arriba: la parte alta de la foto es el cielo y la fuga entre las
     torres. La baja la tapa la hoja del formulario. */
  /* El encuadre ya viene resuelto en el recorte; aqui solo se elige que lado
     se sacrifica cuando la pantalla no da la misma proporcion. */
  background-position: 50% 38%;
  background-size: cover;
  background-repeat: no-repeat;
  /* La deriva lenta se retiro. Era una animacion `infinite` sobre una foto a
     sangre, y por debajo tenia una capa con backdrop-filter: mientras corriera
     -o sea, siempre- el navegador tenia que recalcular ese desenfoque en cada
     frame, porque lo que hay detras cambiaba en cada frame. Costaba bateria y
     fluidez todo el rato a cambio de un movimiento que, por diseno, estaba
     pensado para "notarse al mirar y no al usar".
     El encuadre ampliado si se queda: es lo que da el recorte, y estatico no
     cuesta nada. */
  transform: scale(1.04);
}

/* Velo intermedio. Suma densidad en la mitad baja, que es donde se apoyan el
   nombre y el descriptivo; arriba deja la foto limpia, que es donde esta el
   hueco de cielo.
   Antes esto era un `backdrop-filter: blur(16px)` enmascarado -profundidad de
   campo de verdad-. Se retiro: un desenfoque de fondo con mascara es de lo mas
   caro que se le puede pedir a un telefono, no se puede cachear y se recalcula
   entero con CUALQUIER repintado de la zona, incluido el de abrir el teclado.
   Encima cubria media pantalla y vivia dentro del elemento que se reajusta al
   enfocar un campo, asi que se pagaba justo en el peor momento. El contraste no
   depende de el: lo garantiza .auth-stage-scrim por si solo. */
.auth-stage-haze {
  position: absolute;
  inset: 38% 0 0 0;
  background: linear-gradient(180deg,
    rgba(4, 10, 24, 0) 0%,
    rgba(4, 10, 24, .18) 52%,
    rgba(4, 10, 24, .30) 100%);
}

.auth-stage-scrim {
  position: absolute;
  inset: 0;
  /* La rampa arranca tarde y sube rapido: los dos tercios de arriba dejan la
     foto casi limpia —ahi esta el hueco de cielo— y el peso se concentra en la
     banda donde se apoyan el nombre y el descriptivo. */
  background: linear-gradient(180deg,
    rgba(4, 10, 24, .16) 0%,
    rgba(4, 10, 24, .10) 32%,
    rgba(4, 10, 24, .40) 52%,
    rgba(4, 10, 24, .78) 68%,
    rgba(4, 10, 24, .92) 88%,
    rgba(4, 10, 24, .95) 100%);
}

.auth-stage-content {
  position: relative;
  text-align: center;
  color: #fff;
}

/* 52 y no 64: el bloque de marca se apoya sobre el hueco de cielo de la foto, y
   cada pixel que ocupa de mas es imagen que tapa. */
.auth-stage-logo {
  width: 52px;
  height: 52px;
  border-radius: 15px;
  background: rgba(255, 255, 255, .95);
  padding: 6px;
  /* Halo propio: despega el logo del campo oscuro sin recurrir a un borde. */
  box-shadow:
    0 0 0 1px rgba(255, 255, 255, .16),
    0 14px 34px rgba(2, 6, 23, .55);
  /* Sin transicion de width/height. Animarlas es animar LAYOUT: durante 300ms
     el navegador rehacia, en cada frame, el logo -> la fila de marca -> el
     escenario -> la rejilla exterior. Y se disparaba justo cuando entra
     al enfocar un campo, o sea mientras el bloque de marca se reestructuraba
     de columna a fila y el sistema animaba la subida del teclado: tres cosas
     compitiendo por los mismos frames.
     Ahora el logo cambia de tamaño de una vez, igual que el resto de la
     cabecera. Una cabecera nativa tampoco interpola cuando aparece el teclado:
     salta, porque el teclado salta. */
}

/* El nombre lo pone la configuracion y varia por cliente: puede ser "Condominio"
   o "Gestion de Condominios". Se dimensiona con el ancho disponible y se
   equilibra al partir, para que ninguno de los dos casos quede mal. */
.auth-stage-name {
  margin: .7rem 0 0;
  font-size: clamp(1.3rem, 6.4vw, 1.6rem);
  font-weight: 850;
  letter-spacing: -.025em;
  line-height: 1.15;
  text-wrap: balance;
  /* Sombra propia: sostiene el texto si la foto trae una zona clara detras. */
  text-shadow: 0 1px 12px rgba(4, 10, 24, .55);
}

.auth-stage-tag {
  margin: .35rem 0 0;
  font-size: .9rem;
  line-height: 1.4;
  /* Sobre el velo, blanco al 82% se mantiene por encima de 4.5:1 incluso en la
     zona mas clara de la foto. */
  color: rgba(255, 255, 255, .82);
  text-shadow: 0 1px 10px rgba(4, 10, 24, .6);
}

/* Asa de la hoja. No arrastra —la hoja no se puede cerrar, detras no hay nada—
   pero es la señal que dice "esto es una superficie que se levanta". */
.auth-sheet-grip {
  display: block;
  width: 40px;
  height: 4px;
  border-radius: 99px;
  background: #cbd5e1;
  margin: 0 auto 1rem;
}

@media (min-width: 992px) {
  .auth-sheet-grip { display: none; }
}

/* La marca del panel ya la da el escenario: repetirla seria ruido. */
@media (max-width: 991.98px) {
  .auth-panel .auth-brand { display: none; }
}

.auth-panel-side {
  background: #fff;
  display: grid;
  align-content: center;
  justify-items: center;
  padding: clamp(1.5rem, 5vw, 3rem);
  /* Muescas y barra de gestos cuando corre instalada como PWA. */
  padding-left: max(clamp(1.5rem, 5vw, 3rem), env(safe-area-inset-left));
  padding-right: max(clamp(1.5rem, 5vw, 3rem), env(safe-area-inset-right));
  padding-bottom: max(clamp(1.5rem, 5vw, 3rem), env(safe-area-inset-bottom));
}

/* ── La hoja (solo movil) ───────────────────────────────────────────────────
   El formulario deja de ser "lo que queda debajo de la cabecera" y pasa a ser
   una superficie que se levanta sobre el escenario. Se apoya en tres cosas:
   solapa el campo oscuro, tiene esquinas grandes arriba y sombra hacia arriba, y
   es ella —no la pagina— la que desplaza. Esa ultima es la que mas se nota:
   con el documento fijo desaparecen el rebote elastico y el vaiven de la barra
   de direcciones, que son las dos cosas que gritan "navegador". */
@media (max-width: 991.98px) {
  html { height: 100%; }

  body.auth-body {
    height: 100%;
    overflow: hidden;
    overscroll-behavior: none;
  }

  .auth {
    min-height: 0;
    height: 100dvh;
    /* Escenario al tamaño de su contenido; la hoja se queda con el resto. */
    grid-template-rows: auto 1fr;
  }

  .auth-panel-side {
    position: relative;
    z-index: 2;
    /* Solapa el escenario: sin esto son dos bloques apilados, no una hoja. */
    margin-top: -26px;
    border-radius: 26px 26px 0 0;
    align-content: start;
    min-height: 0;
    /* La hoja desplaza en vertical y solo en vertical. Declarar el eje
       horizontal es obligatorio: en cuanto un eje deja de ser `visible`, el
       otro se vuelve `auto` por si solo, y esa columna de scroll implicita
       mide la pista por su max-content — que es lo que hacia que el
       `min(100%, 25rem)` del panel se resolviera a 25rem y desbordara en
       pantallas de 390px. */
    grid-template-columns: minmax(0, 1fr);
    overflow-x: hidden;
    overflow-y: auto;
    /* El scroll de la hoja no se contagia al documento cuando llega al tope. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    box-shadow: 0 -16px 38px rgba(2, 6, 23, .3);
    padding: 1.15rem 1.4rem calc(1.75rem + env(safe-area-inset-bottom));
    padding-left: max(1.4rem, env(safe-area-inset-left));
    padding-right: max(1.4rem, env(safe-area-inset-right));
    animation: auth-sheet-in .52s cubic-bezier(.22, 1, .36, 1) backwards;
  }

  /* Presentacion de hoja, una sola vez al abrir: sube desde abajo mientras el
     escenario ya esta puesto. Es el gesto con el que un sistema operativo
     presenta una pantalla modal. */
  @keyframes auth-sheet-in {
    from { transform: translateY(4.5%); opacity: .6; }
    to   { transform: none; opacity: 1; }
  }

  /* El panel se centra dentro de la pista, no la ensancha. */
  .auth-panel {
    width: 100%;
    max-width: 25rem;
    margin-inline: auto;
  }

  /* Con el escenario cubriendo la marca, "Bienvenido de nuevo" es el titulo
     real de la hoja y necesita el peso que le corresponde. */
  .auth-panel-title { font-size: 1.7rem; }
  /* ── Teclado abierto ─────────────────────────────────────────────────────
     Aqui ya no hay nada, y esa es la solucion.
     Este bloque encogia el escenario al enfocar un campo (`.is-compact`, que
     ponia auth-login.js): cambiaba `min-height` y `padding`, y ocultaba el
     nombre y el descriptivo. Aunque no quedaba ni una transicion, el gesto
     seguia costando un reflow de pantalla completa —y con el, reescalar la foto
     de portada, que es `background-size: cover` sobre una caja que cambia de
     alto—, sumado al reajuste que el propio navegador hacia de la pagina al
     abrir el teclado. Por eso se seguia sintiendo trabado: el problema no era
     que la reduccion se animara, era que se hiciera.
     Ahora el escenario no se mueve. El teclado no reajusta la pagina
     (`interactive-widget=resizes-visual` en el <meta viewport> del login), el
     navegador solo desplaza la vista hasta el campo, y la hoja —que ya tiene su
     propio scroll— sigue alcanzando cualquier campo por si sola. */
}

/* Pantallas cortas (telefonos pequeños, o en horizontal): el escenario no puede
   quedarse con la mitad de una pantalla que ya es baja. */
@media (max-width: 991.98px) and (max-height: 700px) {
  .auth-stage {
    min-height: 0;
    padding-top: calc(env(safe-area-inset-top) + 1.5rem);
    padding-bottom: 2.4rem;
  }

  .auth-stage-logo { width: 52px; height: 52px; }
  .auth-stage-name { font-size: 1.35rem; }
}

/* ── Que el formulario entre de una vez (movil) ─────────────────────────────
   En un telefono nadie deberia tener que desplazar para encontrar el boton con
   el que entra, ni perseguir los enlaces legales medio ocultos contra el borde
   inferior. La altura sale de la portada, que es lo que sobra: la foto sigue
   ahi, solo que ocupa lo que le toca. */
@media (max-width: 991.98px) {
  .auth-stage {
    min-height: 26svh;
    padding-top: calc(env(safe-area-inset-top) + 1.6rem);
    padding-bottom: 2.1rem;
  }

  .auth-stage-logo {
    width: 46px;
    height: 46px;
    border-radius: 13px;
    padding: 5px;
  }

  .auth-stage-name {
    margin-top: .55rem;
    font-size: clamp(1.2rem, 5.6vw, 1.45rem);
  }

  .auth-stage-tag {
    margin-top: .25rem;
    font-size: .84rem;
  }

  .auth-panel-title { font-size: 1.5rem; }
  .auth-panel-sub { margin-bottom: 1.15rem; }

  /* Las utilidades de Bootstrap traen !important, asi que sin el no se ganan.
     El ritmo vertical de esta pantalla lo manda la pantalla, no la utilidad. */
  .auth-panel form .mb-3 { margin-bottom: .8rem !important; }
  .auth-panel .auth-submit { margin-bottom: .8rem !important; }
  .auth-panel .auth-links { margin-bottom: .65rem !important; }

  .auth-alert {
    margin-bottom: .9rem;
    padding: .6rem .8rem;
  }

  .auth-legal {
    font-size: .74rem;
    line-height: 1.45;
  }

  /* Los enlaces legales son el ultimo renglon de la hoja. Si queda pegado al
     borde —o debajo de la barra de gestos del sistema— se lee pero no se puede
     tocar, que es justo lo que pasaba. */
  .auth-panel-side {
    padding-bottom: calc(1.5rem + max(env(safe-area-inset-bottom), .75rem));
  }
}

.auth-brand {
  display: flex;
  align-items: center;
  gap: .7rem;
  margin-bottom: 1.75rem;
}

.auth-brand img {
  width: 48px;
  height: 48px;
  border-radius: 12px;
  box-shadow: 0 4px 14px rgba(15, 23, 42, .12);
}

.auth-brand span {
  font-weight: 850;
  font-size: 1.15rem;
  letter-spacing: -.015em;
  color: var(--app-text);
}

/* ── Indicador de fotos ──────────────────────────────────────────────────── */

.auth-dots {
  display: flex;
  gap: .5rem;
  margin-top: 1.75rem;
}

.auth-dot {
  width: 46px;
  height: 24px;
  display: grid;
  align-items: center;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: 6px;
}

.auth-dot > span {
  height: 3px;
  border-radius: 99px;
  background: rgba(255, 255, 255, .3);
  overflow: hidden;
}

.auth-dot > span > i {
  display: block;
  height: 100%;
  width: 0;
  background: #fff;
  border-radius: inherit;
}

.auth-dot.is-active > span > i {
  animation: auth-fill var(--auth-cycle) linear forwards;
}

.auth-dot.is-seen > span > i { width: 100%; }

.auth-dot:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 3px;
}

@keyframes auth-fill { to { width: 100%; } }

/* ── Panel del formulario ───────────────────────────────────────────────── */

/* Sin tarjeta ni sombra: el formulario vive directamente sobre el blanco de la
   columna, como en la distribucion original. */
.auth-panel {
  width: min(100%, 25rem);
}

/* ── Escala de escritorio ───────────────────────────────────────────────────
   En movil el formulario ocupa la pantalla y manda por tamaño. En escritorio
   vive en una columna ancha y con el mismo cuerpo que en el telefono se veia
   diminuto: no era un problema de centrado sino de que nunca crecio con la
   pantalla. Aqui sube la escala del bloque entero —marca, titulo, campos y
   boton— para que sostenga su columna. */
@media (min-width: 992px) {
  .auth-panel { width: min(100%, 27rem); }

  .auth-brand { margin-bottom: 2.15rem; }
  .auth-brand img { width: 52px; height: 52px; }
  .auth-brand span { font-size: 1.24rem; }

  .auth-panel-title {
    font-size: 1.95rem;
    letter-spacing: -.028em;
    margin-bottom: .35rem;
  }

  .auth-panel-sub {
    font-size: 1rem;
    margin-bottom: 1.9rem;
  }

  .auth-label { font-size: .76rem; }

  .auth-panel .form-control {
    padding: .92rem 1rem;
    font-size: 1rem;
    border-radius: 12px;
  }

  .auth-field .form-control { padding-right: 3.25rem; }

  .auth-submit {
    padding: .95rem 1rem;
    font-size: 1.05rem;
    border-radius: 12px;
  }

  .auth-links { font-size: .9rem; }
}

/* La marca viaja con el formulario. Anclarla arriba de la columna dejaba un
   vacio enorme entre el logo y "Bienvenido de nuevo": dos bloques sueltos en
   vez de una pantalla. Juntos forman un solo bloque centrado, que es lo que
   la vista busca al entrar. */
@media (min-width: 992px) {
  .auth-panel-side { position: relative; }
}

.auth-panel-title {
  font-size: 1.5rem;
  font-weight: 850;
  letter-spacing: -.02em;
  color: var(--app-text);
  margin: 0 0 .25rem;
}

.auth-panel-sub {
  color: var(--app-muted);
  font-size: .92rem;
  margin: 0 0 1.5rem;
}

.auth-label {
  display: block;
  font-size: .74rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--app-muted);
  margin-bottom: .4rem;
}

.auth-panel .form-control {
  border: 1px solid var(--app-border);
  background: var(--app-surface-soft);
  border-radius: 11px;
  padding: .8rem .9rem;
  font-size: .97rem;
  transition: border-color .16s ease, box-shadow .16s ease, background-color .16s ease;
}

.auth-panel .form-control:focus {
  background: var(--app-surface);
  border-color: var(--app-primary);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--app-primary) 18%, transparent);
}

.auth-panel .form-control::placeholder { color: #94a3b8; }

.auth-panel .form-control.is-invalid {
  border-color: var(--app-danger);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--app-danger) 15%, transparent);
}

/* Campo de contrasena con boton de ver/ocultar */
.auth-field { position: relative; }

.auth-field .form-control { padding-right: 3rem; }

.auth-peek {
  position: absolute;
  top: 50%;
  right: .25rem;
  transform: translateY(-50%);
  /* 44px: minimo tactil de la guia de accesibilidad del proyecto. */
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 9px;
  background: none;
  color: var(--app-muted);
  cursor: pointer;
}

.auth-peek:hover { color: var(--app-text); background: var(--app-surface-soft); }

.auth-peek:active {
  color: var(--app-primary);
  background: color-mix(in srgb, var(--app-primary) 12%, white);
}

.auth-peek:focus-visible {
  outline: 2px solid var(--app-primary);
  outline-offset: 1px;
}

.auth-hint {
  display: flex;
  align-items: center;
  gap: .35rem;
  font-size: .78rem;
  font-weight: 700;
  color: var(--app-warning);
  margin-top: .45rem;
}

/* Boton principal */
.auth-submit {
  width: 100%;
  padding: .85rem 1rem;
  border: 0;
  border-radius: 11px;
  background: var(--app-primary);
  color: #fff;
  font-weight: 700;
  font-size: 1rem;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .55rem;
  transition: background-color .16s ease, transform .16s ease, box-shadow .16s ease;
  box-shadow: 0 10px 24px color-mix(in srgb, var(--app-primary) 32%, transparent);
}

.auth-submit:hover:not(:disabled) {
  background: color-mix(in srgb, var(--app-primary) 88%, black);
  transform: translateY(-1px);
}

/* En tactil no hay hover: el :active es el unico acuse de recibo antes de que
   aparezca el spinner. Sin el, pulsar "Ingresar" no produce nada visible. */
.auth-submit:active:not(:disabled) {
  transform: scale(.985);
  background: color-mix(in srgb, var(--app-primary) 78%, black);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--app-primary) 28%, transparent);
}

.auth-submit:focus-visible {
  outline: 3px solid color-mix(in srgb, var(--app-primary) 40%, transparent);
  outline-offset: 2px;
}

.auth-submit:disabled { opacity: .62; cursor: not-allowed; box-shadow: none; }

.auth-spinner {
  width: 1.05em;
  height: 1.05em;
  border: 2px solid rgba(255, 255, 255, .45);
  border-top-color: #fff;
  border-radius: 50%;
  animation: auth-spin .6s linear infinite;
}

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

/* Avisos */
.auth-alert {
  display: flex;
  align-items: flex-start;
  gap: .55rem;
  padding: .8rem .9rem;
  border-radius: 11px;
  font-size: .88rem;
  line-height: 1.45;
  margin-bottom: 1.15rem;
  animation: auth-pop .32s cubic-bezier(.16, 1, .3, 1);
}

.auth-alert-error {
  background: color-mix(in srgb, var(--app-danger) 9%, white);
  color: #9f1239;
  border: 1px solid color-mix(in srgb, var(--app-danger) 22%, white);
}

.auth-alert-ok {
  background: color-mix(in srgb, var(--app-success) 10%, white);
  color: #065f46;
  border: 1px solid color-mix(in srgb, var(--app-success) 24%, white);
}

@keyframes auth-pop {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* ── Instalar la app ────────────────────────────────────────────────────── */

.auth-install {
  display: flex;
  align-items: center;
  gap: .85rem;
  margin-top: 1.75rem;
  padding: .95rem 1rem;
  border: 1px solid var(--app-border);
  border-radius: 14px;
  background: var(--app-surface-soft);
}

.auth-install-icon {
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: 11px;
  font-size: 1.15rem;
  background: color-mix(in srgb, var(--app-primary) 11%, white);
  color: var(--app-primary);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--app-primary) 18%, transparent);
}

.auth-install-text { min-width: 0; flex: 1 1 auto; }

.auth-install-title {
  margin: 0;
  font-weight: 800;
  font-size: .92rem;
  color: var(--app-text);
}

.auth-install-sub {
  margin: .1rem 0 0;
  font-size: .8rem;
  line-height: 1.4;
  color: var(--app-muted);
}

.auth-install .btn { flex: 0 0 auto; }

/* El aviso de iOS no lleva boton: el texto ocupa el ancho. */
.auth-install-hint .auth-install-sub { font-size: .82rem; }

@media (max-width: 400px) {
  .auth-install { flex-wrap: wrap; }
  .auth-install .btn { width: 100%; }
}

.auth-links {
  font-size: .85rem;
  color: var(--app-muted);
  text-align: center;
}

.auth-links a { color: var(--app-primary); text-decoration: none; font-weight: 600; }
.auth-links a:hover { text-decoration: underline; }
.auth-links a:active { color: color-mix(in srgb, var(--app-primary) 72%, black); }

/* Sin resaltado gris del navegador al tocar: ya damos feedback propio. */
.auth-submit,
.auth-peek,
.auth-dot,
.auth-links a,
.auth-legal a,
.auth-install .btn {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}

/* El cromo no es texto seleccionable. Mantener pulsado sobre el logo o el
   titulo y ver aparecer la lupa de seleccion delata el navegador al instante;
   los campos y los textos legales si se pueden seleccionar. */
.auth-stage,
.auth-sheet-grip,
.auth-submit,
.auth-peek {
  -webkit-user-select: none;
  user-select: none;
}

.auth-stage-logo {
  -webkit-touch-callout: none;
  pointer-events: none;
}

.auth-legal {
  font-size: .78rem;
  line-height: 1.5;
  color: var(--app-muted);
  text-align: center;
}

.auth-legal a { color: var(--app-muted); text-decoration: underline; }
.auth-legal a:hover { color: var(--app-text); }

/* ── Modales de la pantalla de acceso ──────────────────────────────────────
   Comparten vocabulario con la hoja: mismas curvaturas, mismos campos, mismo
   boton. El color de estado vive en el chip del icono, no en el titulo: un
   titulo azul se lee como enlace. */

/* El fondo se desenfoca en lugar de solo oscurecerse. Es lo que hace que el
   modal se lea como una capa por encima y no como un recuadro pegado. */
body.auth-body .modal-backdrop.show {
  opacity: 1;
  background: rgba(4, 10, 24, .46);
  -webkit-backdrop-filter: blur(12px) saturate(1.08);
  backdrop-filter: blur(12px) saturate(1.08);
}

.auth-modal .modal-content {
  border: 0;
  border-radius: 20px;
  box-shadow: 0 26px 64px rgba(2, 6, 23, .3);
}

.auth-modal .modal-header {
  align-items: center;
  gap: .75rem;
  padding: 1.15rem 1.25rem 1rem;
  border-bottom: 1px solid var(--app-border-soft);
}

.auth-modal .modal-title {
  font-size: 1.08rem;
  font-weight: 800;
  letter-spacing: -.015em;
  color: var(--app-text);
  margin: 0;
}

.auth-modal-icon {
  width: 38px;
  height: 38px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  border-radius: 11px;
  font-size: 1.05rem;
  background: color-mix(in srgb, var(--app-primary) 11%, white);
  color: var(--app-primary);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--app-primary) 18%, transparent);
}

.auth-modal .btn-close { margin: 0 0 0 auto; }

.auth-modal .modal-body { padding: 1.25rem; }

.auth-modal-lead {
  color: var(--app-muted);
  font-size: .9rem;
  line-height: 1.55;
  margin: 0 0 1.25rem;
}

.auth-modal .modal-footer {
  border-top: 1px solid var(--app-border-soft);
  padding: 1rem 1.25rem;
}

/* Los campos del modal son los mismos campos del login. */
.auth-modal .form-control {
  border: 1px solid var(--app-border);
  background: var(--app-surface-soft);
  border-radius: 11px;
  padding: .8rem .9rem;
  font-size: .97rem;
  transition: border-color .16s ease, box-shadow .16s ease, background-color .16s ease;
}

.auth-modal .form-control:focus {
  background: var(--app-surface);
  border-color: var(--app-primary);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--app-primary) 18%, transparent);
}

/* Texto largo de politicas. Va en tinta, no en gris apagado: son varias
   pantallas de lectura seguida y buena parte del publico son residentes mayores
   y no tecnicos. El gris de 'texto secundario' esta pensado para etiquetas
   sueltas, no para un documento entero. */
.auth-modal-legal {
  color: #334155;
  font-size: .92rem;
  line-height: 1.7;
  /* En el modal ancho de escritorio la linea llegaba a ~110 caracteres y el ojo
     pierde el renglon al volver. Se corta la medida, no el ancho del modal: la
     cabecera y el pie siguen ocupando todo. */
  max-width: 68ch;
}

.auth-modal-legal p,
.auth-modal-legal ul { margin-bottom: .9rem; }

.auth-modal-legal ul { padding-left: 1.15rem; }
.auth-modal-legal li { margin-bottom: .35rem; }

.auth-modal-legal strong { color: var(--app-text); }

.auth-modal-legal h6 {
  font-size: .96rem;
  font-weight: 800;
  color: var(--app-text);
  letter-spacing: -.01em;
  margin: 1.5rem 0 .5rem;
}

.auth-modal-legal h6:first-of-type { margin-top: 1.15rem; }

/* Fecha de version: es metadato, y ahi el gris si es lo correcto. */
.auth-modal-stamp {
  font-size: .78rem;
  color: var(--app-muted);
  margin-bottom: 1.15rem;
}

.auth-modal-closing {
  font-weight: 700;
  color: var(--app-text);
  margin-bottom: 0;
}

/* En movil los modales bajan como hoja, igual que el formulario. Antes usaban
   'modal-fullscreen-sm-down': un formulario de un campo estirado a toda la
   pantalla dejaba dos tercios de blanco vacio debajo del boton. */
@media (max-width: 575.98px) {
  .auth-modal,
  .auth-modal.modal-dialog-centered {
    align-items: flex-end;
    margin: 0;
    min-height: 100%;
    max-width: none;
  }

  .auth-modal .modal-content {
    border-radius: 22px 22px 0 0;
    max-height: 92svh;
  }

  /* Asa de hoja sin markup extra, la misma del formulario. */
  .auth-modal .modal-content::before {
    content: "";
    display: block;
    flex: 0 0 auto;
    width: 40px;
    height: 4px;
    border-radius: 99px;
    background: #cbd5e1;
    margin: .7rem auto -.35rem;
  }

  .auth-modal .modal-body {
    padding-bottom: calc(1.25rem + env(safe-area-inset-bottom));
  }

  .auth-modal .modal-footer {
    padding-bottom: calc(1rem + env(safe-area-inset-bottom));
  }

  /* 16px exactos, o Safari hace zoom al enfocar y descuadra la hoja. */
  .auth-modal .form-control {
    font-size: 16px;
    padding: .95rem .9rem;
    border-radius: 13px;
  }

  .auth-modal .modal-footer .btn {
    width: 100%;
    min-height: 48px;
    border-radius: 13px;
    font-weight: 700;
  }

  /* La hoja entra desde abajo, no cayendo desde arriba. */
  .modal.fade .auth-modal { transform: translateY(3rem); }
  .modal.show .auth-modal { transform: none; }
}

/* Bloque de captcha progresivo */
.auth-captcha { animation: auth-pop .32s cubic-bezier(.16, 1, .3, 1); }

.auth-captcha-q {
  font-weight: 800;
  color: var(--app-text);
}

/* ── Entrada escalonada ──────────────────────────────────────────────────
   Animacion CSS pura con fill backwards: siempre se dispara al cargar. No
   depende de que JS anada una clase, para que nunca quede contenido oculto. */
.auth-reveal {
  animation: auth-rise .62s cubic-bezier(.16, 1, .3, 1) backwards;
}

.auth-reveal-1 { animation-delay: .06s; }
.auth-reveal-2 { animation-delay: .14s; }
.auth-reveal-3 { animation-delay: .22s; }

@keyframes auth-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* ── Accesibilidad: sin movimiento ──────────────────────────────────────── */

/* ── Ajustes de movil ───────────────────────────────────────────────────── */

@media (max-width: 991.98px) {
  /* 16px exactos: por debajo de eso, Safari en iOS hace zoom al enfocar el
     campo y deja la pagina desencuadrada. */
  .auth-panel .form-control {
    font-size: 16px;
    padding: .95rem .9rem;
  }

  .auth-field .form-control { padding-right: 3.25rem; }

  /* Accion principal con cuerpo de boton de app: alto fijo generoso y la misma
     curvatura que la hoja, para que pertenezcan a la misma superficie. */
  .auth-submit {
    padding: 1rem;
    min-height: 54px;
    border-radius: 14px;
    font-size: 1.02rem;
  }

  .auth-panel .form-control { border-radius: 13px; }

  /* "¿Olvidaste tu contraseña?" es una accion por si sola: 44px completos.
     Los enlaces de politicas van DENTRO de una frase, donde la guia admite la
     excepcion de enlace en linea; agrandarlos rompeaia el parrafo. */
  .auth-links a {
    display: inline-block;
    padding: .7rem .5rem;
    min-height: 44px;
  }

  .auth-links { font-size: .95rem; }

  .auth-install .btn {
    min-height: 44px;
    padding-inline: 1.1rem;
  }

  /* Un poco mas de cuerpo y de interlineado: sube el area del enlace en linea
     y de paso se lee mejor, que es publico mayoritariamente no tecnico. */
  .auth-legal {
    font-size: .82rem;
    line-height: 1.75;
  }

  .auth-legal a { padding: .25rem 0; }
}

@media (prefers-reduced-motion: reduce) {
  .auth-slide,
  .auth-slide-img,
  .auth-dot > span > i,
  .auth-reveal,
  .auth-alert,
  .auth-captcha,
  .auth-stage-photo,
  .auth-panel-side {
    animation: none !important;
    transition: none !important;
  }

  /* El escenario se queda quieto pero no vacio: la foto sigue ahi, solo deja de
     derivar. */
  .auth-stage-photo { transform: none !important; }

  .auth-slide-img { transform: none; }
  .auth-dot.is-active > span > i { width: 100%; }
  .auth-reveal { opacity: 1; transform: none; }
  .auth-submit:hover:not(:disabled) { transform: none; }
  .auth-spinner { animation-duration: 1.4s; }
}
