/* ==========================================================================
   Pypper · Pantalla de acceso
   Se carga DESPUÉS de theme-soft.css y solo de él: de ahí salen los tokens de
   color, la Jakarta local y —lo importante— el degradado del <body>, que es
   EXACTAMENTE el mismo del panel.
   No se carga styles.css ni app.js: mientras no haya sesión, el navegador no
   debe recibir nada de la aplicación.
   ========================================================================== */

/* ---------- Reset ----------
   El mismo de styles.css:84, repetido porque esta página no lo carga. Sin
   box-sizing: border-box el relleno SE SUMA al width y los campos se salen. */
*{ box-sizing: border-box; margin: 0; padding: 0; }
html, body{ height: 100%; }

/* ==========================================================================
   COMPOSICIÓN
   Dos columnas, y en cada una UN SOLO bloque ópticamente centrado.

   Es la corrección importante respecto al borrador: allí cada mitad repartía
   sus piezas con space-between, y en una pantalla alta eso las dispara a las
   esquinas —logotipo pegado al techo, formulario flotando en medio de la nada,
   onda como una tira suelta abajo—. Con los huecos decidiéndolos el alto de la
   ventana, la página nunca se ve compuesta: se ve descuartizada.

   Ahora cada columna es una rejilla de dos renglones: el contenido se agrupa y
   se centra en el primero, y solo el pie queda anclado abajo. Los dos bloques
   caen a la misma altura, así que se alinean de un lado al otro de la costura.

   El fondo NO son dos fondos: el degradado del panel cubre la página entera
   —lo pone theme-soft.css en el <body>— y la columna del formulario es un
   cristal esmerilado encima. La luz es continua, la esquina saturada cae donde
   siempre (detrás de la marca) y al entrar al panel el fondo no cambia.
   ========================================================================== */
.login{
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  grid-template-columns: minmax(0, 46%) minmax(0, 1fr);
}

/* Ancho de la columna de lectura. Un solo valor para el formulario y para el
   aviso legal, para que compartan el mismo eje vertical. */
:root{
  --col: 358px;
  /* Banda del pie en la columna del formulario: la franja donde vive el aviso
     legal. La columna de marca ya no reserva nada porque la onda dejó de estar
     anclada abajo y ahora forma grupo con el titular. */
  --reserva: clamp(120px, 21vh, 200px);
  /* El relleno lateral de la columna de marca, como variable: la onda lo usa en
     negativo para salirse a sangre por los dos lados. */
  --pad-x: clamp(26px, 3.6vw, 52px);
}

/* ---------- Columna del formulario: el cristal ---------- */
.login-form{
  position: relative;
  background: rgba(255,255,255,.86);
  backdrop-filter: blur(26px) saturate(120%);
  -webkit-backdrop-filter: blur(26px) saturate(120%);
  /* Filo claro y no una línea gris: sobre un fondo cálido, un borde neutro se
     lee como suciedad; este se lee como el canto del propio cristal. */
  border-right: 1px solid rgba(255,255,255,.72);
  box-shadow: 1px 0 40px rgba(60,20,20,.06);
  padding: clamp(28px, 3.2vw, 44px) clamp(24px, 3.4vw, 48px);

  display: grid;
  grid-template-rows: 1fr auto;   /* bloque centrado · pie anclado */
  gap: 28px;
}

/* El bloque: logotipo, título, formulario y nota, todo junto y centrado.
   El logotipo va AQUÍ dentro y no pegado arriba del todo: pertenece al
   formulario —dice de qué es esta puerta— y separarlo lo dejaba huérfano. */
.bloque{
  align-self: center;
  justify-self: center;
  width: 100%;
  max-width: var(--col);
  display: flex;
  flex-direction: column;
}

/* Los archivos reales del logotipo, los mismos de la barra del panel, para que
   la marca no cambie de forma al entrar. Los -2x están rasterizados a 122x48 y
   226x60: a 25 y 36px de alto el navegador solo reduce, nunca amplía, y el
   trazo fino aguanta en retina. Se dimensionan por height con width auto para
   que la proporción salga del archivo. */
.logos{ display: flex; align-items: center; gap: 12px; margin-bottom: 34px; }
.logos img{ width: auto; flex: none; display: block; }
.logo-aloia{ height: 25px; }
.logo-lockup{ height: 36px; }

.bloque h1{
  font-size: 25px;
  font-weight: 600;
  letter-spacing: -.032em;
  line-height: 1.15;
  color: var(--ink);
}
.sub{
  margin-top: 7px;
  font-size: 13.5px;
  line-height: 1.5;
  font-weight: 500;
  color: var(--muted);
}

form{ margin-top: 26px; display: flex; flex-direction: column; gap: 16px; }

label{
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-2);
  margin-bottom: 7px;
}

/* Alto fijo en campos y botón: alinean sin depender del relleno. */
.campo{ position: relative; display: flex; align-items: center; }
.campo input{
  width: 100%;
  height: 46px;
  padding: 0 14px;
  font-family: var(--font);
  font-size: 14.5px;
  font-weight: 500;
  color: var(--ink);
  background: rgba(255,255,255,.72);
  border: 1px solid var(--line-2);
  border-radius: var(--r-sm);
  outline: none;
  transition: border-color .15s, box-shadow .15s, background-color .15s;
}
.campo input::placeholder{ color: var(--faint); font-weight: 500; }
.campo input:hover:not(:focus){ border-color: rgba(20,16,15,.22); }
/* Anillo coral con box-shadow y no con outline, para que respete el radio. */
.campo input:focus{ background: #fff; border-color: rgba(20,16,15,.28); }
/* El anillo coral, solo cuando el foco lo pidió una persona.
   :focus-visible no bastaba: el autofocus del arranque también lo dispara, y el
   campo de Usuario aparecía vacío, sin tocar, con borde coral y halo rosa —o
   sea, con el aspecto exacto de un campo con error, que es lo peor que puede
   enseñar una pantalla de acceso al abrirse.
   Con .arranque en el <body> el anillo queda suprimido; el script quita esa
   clase en cuanto hay una tecla o un clic. Así se conserva el autofocus (llegas
   y escribes) sin la falsa alarma. */
.campo input:focus-visible{
  background: #fff;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-rim);
}
body.arranque .campo input:focus-visible{
  border-color: rgba(20,16,15,.28);
  box-shadow: none;
}
.campo.con-ojo input{ padding-right: 46px; }

.ojo{
  position: absolute;
  right: 6px;
  width: 34px; height: 34px;
  display: grid; place-items: center;
  border: 0; border-radius: 9px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: color .15s, background-color .15s;
}
.ojo:hover{ color: var(--ink-2); background: rgba(20,16,15,.05); }
.ojo:focus-visible{ outline: 2px solid var(--brand); outline-offset: 1px; }
.ojo svg{
  width: 18px; height: 18px; fill: none; stroke: currentColor;
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

/* Bloq Mayús: la causa más común de un fallo de captura real. Es un aviso, no
   un error, así que va en gris y sin caja. */
.aviso-caps{
  display: none;
  align-items: center; gap: 6px;
  margin-top: 7px;
  font-size: 11.5px; font-weight: 500; color: var(--muted);
}
.aviso-caps.se-ve{ display: flex; }
.aviso-caps svg{
  width: 13px; height: 13px; flex: none; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}

/* ---------- Error ----------
   La marca YA es roja (--brand #FD3244). Un error en rojo, en una pantalla con
   acento rojo, no se lee como error: se lee como adorno. Por eso se distingue
   por FORMA —una caja con ícono que antes no estaba— y el color solo acompaña.
   De paso funciona para quien no distingue el rojo. */
.error{
  display: none;
  gap: 9px; align-items: flex-start;
  padding: 11px 13px;
  background: var(--brand-bg);
  border: 1px solid var(--brand-rim);
  border-radius: var(--r-sm);
  font-size: 12.5px; line-height: 1.5; font-weight: 500;
  color: var(--brand-ink);
}
.error.se-ve{ display: flex; }
.error svg{
  width: 15px; height: 15px; flex: none; margin-top: 1px; fill: none;
  stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}

/* ---------- Botón ----------
   Tinta oscura de base y coral en hover, igual que .btn-primary del tema. La
   sombra del hover se tiñe de coral en vez de quedarse negra: una sombra gris
   bajo un botón coral se ve sucia. */
.entrar{
  height: 46px;
  margin-top: 2px;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  font-family: var(--font); font-size: 14.5px; font-weight: 600;
  color: #fff;
  background: var(--ink);
  border: 0; border-radius: var(--r-sm);
  box-shadow: 0 6px 18px rgba(20,10,10,.18);
  cursor: pointer;
  transition: background-color .18s, transform .12s, box-shadow .18s;
}
.entrar:hover:not(:disabled){ background: var(--brand); box-shadow: 0 6px 18px rgba(253,50,68,.28); }
.entrar:active:not(:disabled){ transform: translateY(1px); }
.entrar:focus-visible{ outline: 2px solid var(--brand); outline-offset: 3px; }
.entrar:disabled{ cursor: default; opacity: .62; box-shadow: none; }

.giro{
  display: none;
  width: 15px; height: 15px; flex: none;
  border: 2px solid rgba(255,255,255,.32);
  border-top-color: #fff;
  border-radius: 50%;
  animation: giro .65s linear infinite;
}
.entrar.cargando .giro{ display: block; }
@keyframes giro{ to{ transform: rotate(360deg); } }

.nota{ margin-top: 14px; text-align: center; font-size: 12px; font-weight: 500; color: var(--muted); }

/* Pie anclado abajo, alineado con el mismo eje que el formulario. */
.legal{
  align-self: end;
  justify-self: center;
  width: 100%;
  max-width: var(--col);
  /* Centrado: al quedarse en una sola línea corta, alineado a la izquierda
     colgaba del borde y dejaba el resto del ancho vacío. align-items pasa a
     center porque ya no hay varias líneas que alinear contra el ícono. */
  display: flex; gap: 9px; align-items: center; justify-content: center;
  padding-top: 17px;
  border-top: 1px solid var(--line);
  font-size: 11.5px; line-height: 1.6; font-weight: 500; color: var(--muted);
}
.legal svg{
  width: 15px; height: 15px; flex: none; margin-top: 1px; color: var(--ink-2);
  fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}
.legal b{ font-weight: 700; color: var(--ink-2); }

/* ==========================================================================
   Columna de marca
   Sin fondo propio: deja ver el degradado del <body>.
   ========================================================================== */
.login-marca{
  position: relative;
  overflow: hidden;
  padding: clamp(34px, 3.6vw, 52px) var(--pad-x);
  display: grid;
  align-content: center;      /* un bloque centrado, no piezas repartidas */
  justify-items: start;
  gap: 30px;
}

/* Etiqueta de categoría. El filete corto delante no es adorno: ancla la línea a
   un margen y le da un punto de arranque al bloque entero, que es de donde sale
   la sensación de que la composición está construida y no puesta. */
.etiqueta{
  display: flex; align-items: center; gap: 11px;
  font-size: 11.5px; font-weight: 700;
  letter-spacing: .15em; text-transform: uppercase;
  color: var(--ink-2);
}
.etiqueta span{
  width: 26px; height: 1.5px; flex: none;
  background: var(--brand);
  border-radius: 2px;
}

.producto{
  /* Baja de 88px a 62px y gana presencia, no la pierde: una sola palabra
     enorme deja aire muerto alrededor; una frase de tres renglones con
     interlínea cerrada tiene masa. */
  margin-top: 18px;
  font-size: clamp(34px, 4.5vw, 62px);
  font-weight: 600;
  letter-spacing: -.042em;
  line-height: 1.02;
  color: var(--ink);
  max-width: 13ch;
  text-wrap: balance;
}

/* El sostén: el titular afirma, la onda es la voz y esto es la prueba. Va
   después de la onda a propósito — se lee cuando ya te convenció, no antes. */
.sosten{
  font-size: 14.5px;
  line-height: 1.65;
  font-weight: 500;
  color: var(--ink-2);
  max-width: 46ch;
  text-wrap: pretty;
}


/* ---------- La onda ----------
   Antes era una tira de 520px suelta en medio del panel, con vacío a los dos
   lados: por eso se veía como un objeto perdido. Ahora va a sangre, pegada al
   fondo de la pantalla y de borde a borde, y deja de ser un adorno flotando
   para ser el SUELO de la composición: el texto respira encima de ella.
   Las barras las dibuja el script a partir del ancho real, con grosor y
   separación fijos en píxeles, así que salen nítidas a cualquier tamaño. Un
   SVG estirado con preserveAspectRatio="none" habría convertido las esquinas
   redondeadas en elipses. */
.onda{
  /* Dentro del flujo y debajo del texto: la onda forma UN grupo con el titular,
     y ese grupo se centra en la columna. Antes iba anclada al fondo, y eso
     partía la marca en dos objetos lejanos.
     El width es EXPLÍCITO y no "auto": un <svg> es un elemento reemplazado, con
     tamaño intrínseco propio (300x150), así que con width:auto se queda en 300px
     en vez de estirarse como haría un <div>. Aquí además se pasa del ancho de la
     columna y se compensa con margen negativo, para salir a sangre por los dos
     lados; el overflow:hidden de la columna recorta lo que sobra. */
  width: calc(100% + var(--pad-x) * 2);
  margin-left: calc(var(--pad-x) * -1);
  height: clamp(120px, 21vh, 192px);
  display: block;
  pointer-events: none;
}
/* Las barras van con un degradado horizontal, no con un color plano, y con los
   colores de la MARCA en vez de tinta neutra. Antes eran tinta al 26% y sobre
   un fondo cálido eso no se lee como suave: se lee como gris muerto, el mismo
   choque que ya documenta el tema entre el gris frío y el coral. Subir la
   opacidad solo lo habría enlodado; lo que hacía falta era cambiar el TONO.
   El degradado es el del logotipo de aloia, coral virando a morado: así la onda
   no lleva un color inventado, lleva la marca. Los valores viven en el script,
   que es quien genera el <defs>.
   El fill va en el <svg> y las barras lo heredan: en CSS, fill gana a un
   atributo de presentación, así que ponerlo en cada rect no serviría. */
.onda{ fill: url(#gradOnda); }

/* La onda habla. No oscila entre dos valores como un ecualizador: cada ciclo
   tiene ráfagas y MICRO-PAUSAS —tramos donde el valor se queda quieto—, que es
   lo que distingue una voz de una máquina.
   Hay tres recorridos distintos, con las pausas en momentos diferentes, y el
   script reparte uno a cada barra. Con un solo recorrido las pausas caerían
   todas a la vez y se vería una ola barriendo la pantalla; con tres, y con la
   duración y el desfase propios de cada barra, no hay patrón que agarrar.
   Se anima transform y no el atributo height: transform no obliga al navegador
   a recalcular la geometría del SVG en cada cuadro.
   transform-box: fill-box pone el origen en el centro de CADA barra; sin eso
   todas escalarían desde la esquina del lienzo. */
.onda rect{
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation-name: habla-a;
  animation-timing-function: cubic-bezier(.35, 0, .5, 1);
  animation-iteration-count: infinite;
}

@keyframes habla-a{
  0%{ transform: scaleY(.46) }   9%{ transform: scaleY(1) }
  19%{ transform: scaleY(.58) }  27%{ transform: scaleY(.95) }
  36%{ transform: scaleY(.4) }   45%{ transform: scaleY(.4) }    /* pausa */
  54%{ transform: scaleY(.88) }  63%{ transform: scaleY(.52) }
  71%{ transform: scaleY(1) }    80%{ transform: scaleY(.44) }
  90%{ transform: scaleY(.72) }  100%{ transform: scaleY(.46) }
}
@keyframes habla-b{
  0%{ transform: scaleY(.9) }    8%{ transform: scaleY(.5) }
  16%{ transform: scaleY(.5) }                                    /* pausa */
  25%{ transform: scaleY(1) }    34%{ transform: scaleY(.62) }
  43%{ transform: scaleY(.86) }  52%{ transform: scaleY(.44) }
  61%{ transform: scaleY(.98) }  70%{ transform: scaleY(.56) }
  78%{ transform: scaleY(.56) }                                   /* pausa */
  88%{ transform: scaleY(.8) }   100%{ transform: scaleY(.9) }
}
@keyframes habla-c{
  0%{ transform: scaleY(.54) }   10%{ transform: scaleY(.92) }
  20%{ transform: scaleY(.42) }  30%{ transform: scaleY(1) }
  40%{ transform: scaleY(.6) }   49%{ transform: scaleY(.84) }
  58%{ transform: scaleY(.47) }  68%{ transform: scaleY(.47) }    /* pausa */
  77%{ transform: scaleY(1) }    86%{ transform: scaleY(.55) }
  100%{ transform: scaleY(.54) }
}

/* Un movimiento en bucle es justo lo que estorba a quien pidió menos
   movimiento: aquí se detiene del todo y la onda se queda como una figura. */
@media (prefers-reduced-motion: reduce){
  .onda rect{ animation: none; }
}

/* ---------- Entrada ----------
   Una sola secuencia al cargar, escalonada, y nada que se mueva en bucle
   después: el bucle es lo que termina cansando. Va dentro de no-preference
   para que, si alguien pidió menos movimiento, los elementos nazcan visibles
   en vez de nacer en opacity:0 y depender de que la animación corra. */
@media (prefers-reduced-motion: no-preference){
  @keyframes surge{ from{ opacity: 0; transform: translateY(12px); } to{ opacity: 1; transform: none; } }
  .marca-txt, .onda, .sosten, .bloque, .legal{
    animation: surge .6s cubic-bezier(.2,.7,.3,1) backwards;
  }
  .marca-txt{ animation-delay: .04s; }
  .bloque   { animation-delay: .10s; }
  .onda     { animation-delay: .18s; animation-duration: .9s; }
  .sosten   { animation-delay: .28s; }
  .legal    { animation-delay: .36s; }
}

/* ==========================================================================
   Responsivo
   ========================================================================== */
@media (max-width: 1180px){
  .login{ grid-template-columns: minmax(0, 50%) minmax(0, 1fr); }
  .producto{ max-width: 12ch; }
}

/* El corte no lo manda el ancho de la pantalla sino el de la columna de
   lectura: por debajo de esto el 46% ya no alcanza los 358px y el formulario
   empieza a apretarse antes que ninguna otra cosa. */
@media (max-width: 900px){
  .login{ grid-template-columns: 1fr; grid-template-rows: auto 1fr; }
  /* La marca sube: en una columna, lo primero tiene que decir dónde estás. */
  .login-marca{
    order: -1;
    padding: 30px 26px;
    gap: 20px;
  }
  .producto{ font-size: clamp(30px, 7.4vw, 40px); max-width: none; margin-top: 14px; }
  /* El párrafo de apoyo se cae en una columna. En un teléfono la prioridad es
     entrar: cada renglón de marca empuja los campos hacia abajo de la pantalla.
     El titular y la onda se quedan, que son los que dicen dónde estás. */
  .sosten{ display: none; }
  .onda{ height: clamp(88px, 14vh, 128px); }

  .login-form{
    border-right: 0;
    border-top: 1px solid rgba(255,255,255,.72);
    box-shadow: 0 -1px 40px rgba(60,20,20,.06);
    padding: 32px 26px 30px;
    gap: 26px;
  }
  .logos{ margin-bottom: 28px; }
}

@media (max-width: 430px){
  .logo-aloia{ height: 22px; }
  .logo-lockup{ height: 31px; }
  .bloque h1{ font-size: 23px; }
}

/* Ventanas bajas y anchas (una laptop con el navegador a media pantalla): el
   bloque deja de centrarse y se pega arriba para no cortarse. */
@media (max-height: 660px) and (min-width: 901px){
  .login-form{ padding-top: 30px; padding-bottom: 26px; }
  .bloque{ align-self: start; }
  .login-marca{ align-content: start; padding-top: 34px; }
}
