/* ═══════════════════════════════════════════════════════════════════════════
   EL OJO DE LAS CLAVES — la hoja, una sola copia
   ═══════════════════════════════════════════════════════════════════════════
   ⭐⭐⭐⭐ Este archivo existe porque el ojo vive en DOS mundos que no comparten
   hoja: el app (`styles.css`) y la cara pública (`landing.css`, que NO carga
   styles.css a propósito, son ~580 KB). La alternativa era escribir el bloque
   en las dos y poner una compuerta que exija que coincidan — el patrón que este
   repo ya usa para `styles.css` contra `bundle.py`—, pero una compuerta caza la
   divergencia DESPUÉS de que ocurre y un archivo compartido no la deja ocurrir.
   Lo cargan `base.html` y `landing_base.html`, igual que `tema.js`.

   ⚠️⚠️ Por eso mismo **no usa ninguna ficha de color**: las dos hojas tienen
   paletas distintas y ninguna ficha vive en las dos. El ojo toma el color del
   TEXTO que hereda (`currentColor`) y se atenúa, así que se tiñe solo en los dos
   temas y en las tres familias de campo sin saber nada de ninguna.

   ⚠️ Y **no va acotado a `[data-sistema]`**: `login`, `signup` y `reset_password`
   se rinden sin el armazón del app y ahí ese atributo no existe. Es la misma
   excepción de `:root` y de los velos que htmx pega hermanos de `.app`.
   ═══════════════════════════════════════════════════════════════════════════ */

/* La caja envuelve al campo. El guion la inserta; ninguna plantilla la escribe.
   ⚠️ `display: block` + `width: 100%` porque el envoltorio pasa a ser el ítem
   de rejilla o de flex que antes era el campo: `.cu-campo` es `flex-direction:
   column` y `.alta-campo` es `grid`, y un `<span>` en línea se encogería al
   ancho de su contenido dejando el campo angosto. */
.ojo-campo { position: relative; display: block; width: 100%; }

/* El hueco del botón. ⚠️ Va sobre el campo ENVUELTO y no sobre
   `input[type=password]`: al mostrar la clave el tipo pasa a `text`, así que un
   selector por tipo perdería el relleno justo mientras el ojo sigue encima y el
   texto pasaría por debajo del icono. */
.ojo-campo > input { padding-right: var(--tt-tacto, 44px); }

.ojo-btn {
  /* ⚠️⚠️ **44 px, no 40.** Lo cazó `test_ningun_control_de_la_guia_es_menor_al
     _blanco_minimo`, que ya existía: por debajo de 44×44 un control no se puede
     pulsar con el dedo de forma fiable, y el ojo aparece en la guía de conectar,
     que se recorre desde el teléfono. El respaldo es el mismo número porque la
     cara pública no tiene esa ficha. */
  position: absolute; top: 0; right: 0; height: 100%; width: var(--tt-tacto, 44px);
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; margin: 0; border: 0; background: none; cursor: pointer;
  /* ⚠️ Un `<button>` NO hereda el color: hay que pedírselo. */
  color: inherit; opacity: .6;
  border-radius: var(--tt-r-s, 8px);
}
.ojo-btn svg { width: 18px; height: 18px; display: block; }

/* ⚠️ El `:hover` va tras la consulta de puntero fino: en un teléfono el estado
   se queda PEGADO después del toque y el ojo se ve encendido sin serlo. */
@media (hover: hover) and (pointer: fine) {
  .ojo-btn:hover { opacity: 1; }
}
.ojo-btn:focus-visible { opacity: 1; }

/* Qué dibujo se ve. ⭐ Lo decide `aria-pressed`, que es el MISMO atributo que
   anuncia el estado al lector de pantalla: con una clase aparte serían dos
   verdades que se pueden desfasar, y la que se olvida es siempre la del lector. */
.ojo-btn .ojo-cerrado { display: none; }
.ojo-btn[aria-pressed="true"] .ojo-abierto { display: none; }
.ojo-btn[aria-pressed="true"] .ojo-cerrado { display: block; }


/* ═══════════════════════════════════════════════════════════════════════════
   EL MEDIDOR DE FUERZA
   ═══════════════════════════════════════════════════════════════════════════
   Vivía en `landing.css` y solo lo veían el registro y el restablecer. El owner
   lo pidió también en «Cambiar tu clave», que es del app.

   ⭐⭐⭐⭐ **El puente entre las dos paletas se declara ARRIBA y una sola vez.**
   `styles.css` no tiene `--err`/`--ok`/`--ok-tarjeta` y `landing.css` no tiene
   `--tt-mal`/`--tt-bien`: copiar el bloque a la otra hoja habría dejado la barra
   **invisible**, porque una `var()` sin declarar no da error — deja la propiedad
   inválida al calcular y cae a su valor inicial. Acá cada nivel se pide UNA vez
   con su respaldo, así que el resto del bloque no sabe en qué mundo está.

   ⚠️ Los nombres siguen siendo `.alta-*` y eso ya no describe dónde viven: el
   prefijo es del embudo de ALTA y hoy el medidor sale también en Mi cuenta.
   **Deuda anotada a propósito**: renombrarlo toca las dos pantallas del embudo,
   que es el camino del dinero, y no compra ninguna propiedad — solo claridad.
   ═══════════════════════════════════════════════════════════════════════════ */
.alta-fuerza {
  --f-mal:   var(--tt-mal,  var(--err));
  --f-medio: var(--tt-ojo,  #E9A23B);
  --f-bien:  var(--tt-bien, var(--ok));
  --f-dicho: var(--tt-bien, var(--ok-tarjeta));
  --f-apagado: var(--tt-tinta-3, var(--text-dim));
  --f-riel:  var(--tt-sup-2, var(--surface-2));
  display: grid; gap: 6px; margin-top: 2px;
}
.alta-fuerza-barra {
  height: 4px; border-radius: 100px;
  background: var(--f-riel); overflow: hidden;
}
/* ⚠️⚠️ El ancho lo ESCRIBE el guion en `--x`. Al portar el prototipo esa línea
   se quedó afuera una vez: la barra existía, cambiaba de color con el nivel y
   **nunca se llenaba** — se veía como una línea gris inerte, y el owner
   preguntó qué hacía. Por eso el default es `0%` y no una fracción. */
.alta-fuerza-barra i {
  display: block; height: 100%; width: var(--x, 0%);
  border-radius: 100px; background: var(--f-mal);
  transition: width .3s var(--ease-soft, ease), background-color .3s var(--ease-soft, ease);
}
.alta-fuerza[data-nivel="2"] i { background: var(--f-medio); }
.alta-fuerza[data-nivel="3"] i { background: var(--f-bien); }

/* ⭐ El estado EN PALABRAS, al lado del color. Una barra que solo cambia de tono
   no dice QUÉ falta, y para quien no distingue esos tonos no dice nada. */
.alta-fuerza-dice {
  font-size: 12px; line-height: 1.4; color: var(--f-apagado); margin: 0;
}
.alta-fuerza[data-nivel="1"] .alta-fuerza-dice { color: var(--f-mal); }
.alta-fuerza[data-nivel="2"] .alta-fuerza-dice { color: var(--f-medio); }
.alta-fuerza[data-nivel="3"] .alta-fuerza-dice { color: var(--f-dicho); }

.alta-reglas {
  list-style: none; display: flex; flex-wrap: wrap;
  gap: 4px 14px; font-size: 12px; color: var(--f-apagado);
  margin: 0; padding: 0;
}
/* ⚠️⚠️ Los glifos van LITERALES y el espacio es `\00a0`. Escritos como
   `content: "\25CB "` el espacio **termina la secuencia de escape** en vez de
   formar parte del texto, así que la viñeta sale pegada a la palabra
   («●Una mayúscula»). Se ve como un descuido de maquetación, no como un error
   de CSS, y no lo nota ninguna prueba de texto: el `::before` no está en el
   `textContent`. Lo encontró mirar la captura. */
.alta-reglas li::before { content: "○\00a0"; }
.alta-reglas li[data-ok] { color: var(--f-bien); }
.alta-reglas li[data-ok]::before { content: "●\00a0"; }

/* ⚠️ Con movimiento reducido la barra SALTA a su ancho en vez de apagarse: lo
   que importa es dónde quedó, no el viaje. Apagarla dejaría al medidor mudo
   justo para quien pidió menos animación. */
@media (prefers-reduced-motion: reduce) {
  .alta-fuerza-barra i { transition: none; }
}


/* ═══════════════════════════════════════════════════════════════════════════
   LOS CAMPOS OBLIGATORIOS
   ═══════════════════════════════════════════════════════════════════════════
   Dos pedidos del owner en la misma frase: que los obligatorios lleven un `*`,
   y que se pongan en ROJO —todos— al intentar avanzar con alguno vacío.

   ⭐⭐⭐ **El `*` sale de `required`, no de una clase que haya que acordarse de
   poner.** Un campo obligatorio nuevo lo estrena solo; uno que deja de serlo lo
   pierde solo. Escribirlo en la plantilla sería la enumeración de siempre: el
   asterisco se desfasa del atributo y entonces miente en la dirección peor —
   dice «puedes dejarlo» sobre algo que el POST va a rechazar.

   ⚠️⚠️ Se busca por ESTRUCTURA (`label` seguido de su control) y no por las
   clases de las tres familias de campo —`.cu-campo`, `.alta-campo`,
   `.auth-field`—, que es una lista que se desfasa. La segunda mitad del
   selector existe porque el ojo de las claves ENVUELVE su campo: después de que
   el guion corre, el vecino del `label` ya no es el `input` sino `.ojo-campo`.

   ⚠️ El rojo cuelga de `[data-intentado]`, que pone el guion la primera vez que
   el navegador rechaza algo. Sin esa marca, `:invalid` pintaría de rojo cada
   campo obligatorio **desde que se abre la pantalla**, que es acusar a alguien
   de no haber escrito todavía.
   ═══════════════════════════════════════════════════════════════════════════ */
label:has(+ input:required)::after,
label:has(+ select:required)::after,
label:has(+ textarea:required)::after,
label:has(+ .ojo-campo > :required)::after {
  content: "\00a0*";
  /* ⚠️ El tono del error, NO el acento: un `*` del color de marca se lee como
     un enlace. Con respaldo, porque esta hoja sirve a las dos paletas. */
  color: var(--tt-mal, var(--err, #e5484d));
  font-weight: 700;
}

/* El rojo, solo DESPUÉS de intentar. ⚠️ `:required` además de `:invalid`: un
   `type="email"` a medio escribir también es inválido, y ahí el rojo es
   correcto, pero el borde se pinta igual — lo que no queremos es teñir un campo
   OPCIONAL vacío, que no es inválido pero tampoco tiene por qué mirarse. */
form[data-intentado] input:required:invalid,
form[data-intentado] select:required:invalid,
form[data-intentado] textarea:required:invalid {
  border-color: var(--tt-mal, var(--err, #e5484d));
}
