/* El código del correo o del segundo factor, en casillas.

   Una por carácter, en los mismos grupos que el correo separa con un guion o
   que la aplicación separa con un espacio (templates/partials/codigo.html).
   Va en las pantallas de acceso y en las de la cuenta, donde se activa la
   aplicación de códigos. Lo que se escribe va a un único campo
   que está encima, transparente: las casillas solo lo dibujan. Sin
   JavaScript (sin .is-casillas) o con los colores forzados del sistema, que
   volverían visible el texto del campo encima de ellas, queda el campo
   normal. */

.codigo { position: relative; }
.codigo__casillas { display: none; }

@media not (forced-colors: active) {
  .codigo.is-casillas .codigo__casillas:not([hidden]) {
    display: flex;
    align-items: center;
    gap: 10px;
  }

  /* Encima de todo y del mismo tamaño, para que cualquier toque caiga en él;
     sin letra, cursor ni selección a la vista. La letra, de 16 px: con menos,
     el iPhone acerca la página al enfocarlo. */
  .codigo.is-casillas .codigo__input {
    position: absolute;
    inset: 0;
    z-index: 1;
    width: 100%;
    height: 100%;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: transparent;
    box-shadow: none;
    color: transparent;
    caret-color: transparent;
    font-size: 16px;
    cursor: text;
  }
  .codigo.is-casillas .codigo__input:focus { box-shadow: none; }
  .codigo.is-casillas .codigo__input::selection { background: transparent; }
  /* El autorrelleno de Chrome pinta el campo de color y su letra encima de las
     casillas: ni lo uno (no hay manera de quitarle el fondo, se le retrasa
     para siempre) ni lo otro. */
  .codigo.is-casillas .codigo__input:-webkit-autofill {
    -webkit-box-shadow: none;
    -webkit-text-fill-color: transparent;
    transition: background-color 0s 86400s;
  }
}

.codigo__grupo { flex: 1; display: flex; gap: 6px; min-width: 0; }

.codigo__casilla {
  flex: 1 1 0;
  min-width: 0;
  height: 56px;
  display: grid;
  place-items: center;
  border: 1px solid var(--borde);
  border-radius: var(--radio-caja);
  background: var(--superficie-baja);
  font-family: var(--display);
  font-size: 24px;
  font-weight: 500;
  line-height: 1;
  color: var(--tinta-fuerte);
  transition: border-color 140ms ease, box-shadow 140ms ease, background 140ms ease;
}
.codigo__casilla.is-llena { border-color: var(--borde-fuerte); background: var(--superficie); }
.codigo__casilla.is-activa {
  border-color: var(--tinta);
  background: var(--superficie);
  box-shadow: 0 0 0 3px var(--anillo);
}

/* El cursor de la casilla vacía que toca escribir: el ámbar de la casa, en
   el único sitio de la pantalla en que se escribe. */
.codigo__casilla.is-cursor::after {
  content: "";
  width: 2px;
  height: 26px;
  border-radius: 1px;
  background: var(--acento);
}

/* El guion entre los grupos, el mismo que lleva el código en el correo. */
.codigo__guion {
  flex: 0 0 10px;
  height: 2px;
  border-radius: 1px;
  background: var(--borde-fuerte);
}

/* Entre los grupos del de la aplicación, solo el hueco: no lleva guion. */
.codigo__guion--hueco { background: none; }

.field--codigo.is-invalid .codigo__casilla { border-color: var(--error-campo); }
.field--codigo.is-invalid .codigo__casilla.is-activa {
  border-color: var(--error-campo-foco);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--error) 14%, transparent);
}

/* Debajo de las casillas, dos renglones de 12,5 px apartados para los mensajes:
   que salga uno no mueve nada, ni aquí ni en el panel, que va centrado. Si
   alguno diera para tres, se sale por abajo antes que empujar. */
.codigo__mensajes { display: block; height: 36px; }

/* El aviso de lo que no se ha dejado escribir o enviar. Vacío no ocupa sitio
   pero sigue ahí, para que el lector de pantalla lo lea en cuanto se llena. */
.codigo__aviso:empty {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

@media (prefers-reduced-motion: no-preference) {
  .codigo__casilla.is-nueva { animation: codigo-entra 160ms var(--transicion-curva); }
  .codigo__casilla.is-cursor::after { animation: codigo-parpadeo 1.1s steps(1) infinite; }
}

@keyframes codigo-entra {
  from { transform: scale(.9); }
}
@keyframes codigo-parpadeo {
  50% { opacity: 0; }
}

@media (max-width: 560px) {
  .codigo.is-casillas .codigo__casillas { gap: 8px; }
  .codigo__grupo { gap: 5px; }
  .codigo__casilla { height: 50px; font-size: 21px; }
  .codigo__guion { flex-basis: 8px; }
}

/* Pasar del código de la aplicación a uno de recuperación y vuelta: un
   enlace bajo las casillas, que sin JavaScript no hace falta (el campo admite
   los dos). */
.codigo__cambiar {
  display: inline-block;
  align-self: flex-start;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  color: var(--texto-suave);
  cursor: pointer;
}
.codigo__cambiar:hover { color: var(--tinta-fuerte); text-decoration: underline; }
.codigo__cambiar[hidden] { display: none; }

/* --- Comprobar el código (static/js/verificacion.js) ------------------------

   Lo que se mueve son las casillas, con la Web Animations API, porque el giro
   dura lo que tarde el servidor. Aquí van el aspecto de cada fase y el dibujo
   del centro: --tono es de qué color va la cosa en cada momento. */

.codigo.is-verificando .codigo__input { pointer-events: none; }
.codigo.is-verificando .codigo__casilla.is-cursor::after { display: none; }

/* El aviso de voz, del mismo modo que el aviso vacío. */
.codigo__estado {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* Lo que no se puede tocar mientras se comprueba. */
.form [inert],
.tarjeta-cuerpo [inert] { opacity: .45; transition: opacity 180ms ease; }

/* El corro ocupa el sitio del rótulo y del enlace de debajo: se apartan sin
   dejar de medir lo que miden. */
.field--codigo > .field__label,
.field--codigo > .codigo__cambiar { transition: opacity 180ms ease; }
.field--codigo.is-corro > .field__label,
.field--codigo.is-corro > .codigo__cambiar { opacity: 0; }

/* En la cuenta el botón mide lo que su texto: con este ancho le caben «Verificando»
   y «Sin más intentos» sin crecer al cambiar. */
.form:not([data-acceso]):has(.field--codigo) .button-solid[type="submit"] {
  min-width: 170px;
  justify-content: center;
  text-align: center;
}

/* Las casillas, de vidrio: un filo ámbar que da la vuelta, con la luz detrás. */
.codigo.is-escena .codigo__casilla,
.codigo.is-escena .codigo__casilla.is-activa {
  border-color: transparent;
  background:
    linear-gradient(var(--superficie), var(--superficie)) padding-box,
    conic-gradient(
      from 215deg,
      var(--acento),
      color-mix(in srgb, var(--acento) 18%, var(--borde)) 32%,
      var(--borde-fuerte) 55%,
      var(--acento) 100%
    ) border-box;
  box-shadow: 0 8px 22px color-mix(in srgb, var(--acento) 24%, transparent);
  will-change: transform;
}

/* El guion del medio no tiene casilla que lo lleve: se queda en su sitio, que
   es justo el centro del corro. */
.codigo.is-escena .codigo__guion { opacity: 0; transition: opacity 120ms ease; }

/* Sin movimiento, los colores cuentan lo mismo. */
.codigo.is-verificando .codigo__casilla { transition: border-color 180ms ease, color 180ms ease; }
.codigo.is-verificando:not(.is-escena) .codigo__casilla { border-color: var(--acento); }
.codigo.is-verificando.is-bien .codigo__casilla { border-color: var(--bien); color: var(--bien); }
.codigo.is-verificando.is-mal .codigo__casilla { border-color: var(--error); color: var(--error); }
.codigo.is-verificando.is-aviso .codigo__casilla { border-color: var(--aviso); }

.codigo-escena {
  --tono: var(--acento);
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  place-content: center;
  pointer-events: none;
}
.codigo-escena[data-fase="bien"] { --tono: var(--bien); }
.codigo-escena[data-fase="mal"] { --tono: var(--error); }
.codigo-escena[data-fase="aviso"] { --tono: var(--aviso); }

/* Un punto con una sombra enorme: una caja de ese tamaño daría barras de scroll
   al panel de acceso, y una sombra no. */
.codigo-escena__halo,
.codigo-escena__dibujo { grid-area: 1 / 1; }
.codigo-escena__halo {
  width: 2px;
  height: 2px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--tono) 14%, transparent);
  box-shadow: 0 0 170px 120px color-mix(in srgb, var(--tono) 14%, transparent);
  opacity: .45;
  animation: escena-entra 360ms ease both;
  transition: opacity 320ms ease, box-shadow 320ms ease;
}
.codigo-escena[data-fase="bien"] .codigo-escena__halo,
.codigo-escena[data-fase="mal"] .codigo-escena__halo { opacity: 1; }
.codigo-escena[data-fase="sale"] .codigo-escena__halo { opacity: 0; }

.codigo-escena__dibujo {
  width: var(--lado);
  height: var(--lado);
  overflow: visible;
  transition: opacity 160ms ease;
}
.codigo-escena[data-fase="sale"] .codigo-escena__dibujo { opacity: 0; }

.codigo-escena__dibujo * {
  fill: none;
  stroke: var(--tono);
  transform-box: view-box;
  transform-origin: center;
  transition: stroke 200ms ease, fill 200ms ease, opacity 200ms ease;
}

.codigo-escena__orbita {
  stroke-width: 1;
  stroke-dasharray: 1 7;
  stroke-linecap: round;
  animation: escena-entra 360ms ease both, escena-orbita 9s linear infinite;
}
.codigo-escena:not([data-fase="gira"]) .codigo-escena__orbita { opacity: 0; }

/* La pieza en que se juntan las casillas: está, sin verse, desde el principio. */
.codigo-escena__pieza {
  stroke-width: 1.6;
  fill: var(--superficie);
  opacity: 0;
  transform: scale(.72);
  transition: opacity 90ms linear 150ms, transform 200ms var(--transicion-curva) 110ms,
    stroke 200ms ease, fill 200ms ease;
}
.codigo-escena:is([data-fase="junta"], [data-fase="bien"], [data-fase="mal"], [data-fase="aviso"]) .codigo-escena__pieza {
  opacity: 1;
  transform: scale(1);
}
.codigo-escena[data-fase="bien"] .codigo-escena__pieza {
  fill: var(--bien-suave);
  animation: escena-acierta 420ms var(--transicion-curva);
}
.codigo-escena[data-fase="mal"] .codigo-escena__pieza {
  fill: var(--error-suave);
  animation: escena-falla 340ms ease-in-out;
}
.codigo-escena[data-fase="aviso"] .codigo-escena__pieza { fill: var(--aviso-suave); }

.codigo-escena__marca {
  stroke-width: 3.4;
  stroke-linecap: round;
  stroke-linejoin: round;
  /* Sin dibujar, WebKit aún les pinta el remate redondo: un punto por marca.
     De ahí que estén ocultas, y que el hueco sea más largo que el trazo: con
     1 1, el trazo siguiente empieza justo donde acaba la línea. */
  visibility: hidden;
  stroke-dasharray: 1 2;
  stroke-dashoffset: 1;
}
.codigo-escena[data-fase="bien"] .codigo-escena__check,
.codigo-escena[data-fase="mal"] .codigo-escena__aspa,
.codigo-escena[data-fase="aviso"] .codigo-escena__exclamacion {
  visibility: visible;
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 240ms cubic-bezier(.3, .7, .2, 1) 60ms;
}
.codigo-escena[data-fase="mal"] .codigo-escena__aspa + .codigo-escena__aspa { transition-delay: 130ms; }

/* El acierto se abre en una onda desde la pieza; un error, nunca. */
.codigo-escena__onda {
  stroke-width: 1.6;
  opacity: 0;
}
.codigo-escena[data-fase="bien"] .codigo-escena__onda { animation: escena-onda 620ms cubic-bezier(.2, .7, .3, 1) both; }

/* El botón, en cada fase. */
.is-verificando .boton-entrar__glifo {
  width: 16px;
  height: 16px;
  border: 2px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  opacity: 1;
}
.boton-entrar.is-verificado,
.boton-entrar.is-verificado:hover,
.button-solid.is-verificado,
.button-solid.is-verificado:hover {
  border-color: var(--bien);
  background: var(--bien);
  cursor: default;
}
.boton-entrar.is-verificado .boton-entrar__progreso { background: var(--bien); }
.boton-entrar.is-agotado,
.boton-entrar.is-agotado:hover,
.button-solid.is-agotado,
.button-solid.is-agotado:hover {
  border-color: var(--error);
  background: var(--error);
  cursor: default;
}
.boton-entrar.is-agotado .boton-entrar__progreso { background: var(--error); }
.boton-entrar:is(.is-verificado, .is-agotado) .boton-entrar__glifo { opacity: 1; }
.button-solid.is-verificando { cursor: progress; opacity: .85; }

@media (prefers-reduced-motion: no-preference) {
  .is-verificando .boton-entrar__glifo { animation: escena-orbita 800ms linear infinite; }
}

@keyframes escena-entra { from { opacity: 0; } }
@keyframes escena-orbita { to { transform: rotate(360deg); } }
@keyframes escena-acierta {
  35% { transform: scale(1.14); }
}
@keyframes escena-falla {
  20% { transform: translateX(-5px) rotate(-4deg); }
  45% { transform: translateX(4px) rotate(3deg); }
  70% { transform: translateX(-3px) rotate(-2deg); }
}
/* Acaba dentro del dibujo (1.16 × el radio 50, con su trazo, no llega a 60): si
   pasara de ahí, tocaría el texto y se metería por debajo del botón apartados
   a la medida del corro, y parecería cortada. */
@keyframes escena-onda {
  from { opacity: .9; transform: scale(.42); }
  70% { opacity: .55; }
  to { opacity: 0; transform: scale(1.16); }
}
