/* ═══════════════════════════════════════
   ▓ COMPONENTE · OTP — gemelo vanilla ▓
   v1.3 · 2026-10-03 (foco en error = gris, excepción OTP). Ficha: `design-system/COMPONENTE_OTP.md`
   Kit: `catalogo-cd/components/Otp.jsx`

   Las casillas donde se teclea un código: 2FA, verificación de correo o de celular,
   confirmación de cotización, y el uso histórico de producción (backoffice: código de la
   disposición, `.exv3-codebox`) — este último NO aplica al onboarding de expediente, donde
   el NIP es la autorización a Buró, no una firma (`PATRON_BLOQUE_AUTORIZACION.md`). **Una
   sola anatomía para todas las superficies de Exante** — sólo cambia el tamaño.

   ⛔ LAS CASILLAS SON SIEMPRE CUADRADAS. Ancho y alto salen de la MISMA variable
   (`--otp-size`), así que no existe la forma de dejar una rectangular por descuido. Es la
   regla transversal de la ficha y aquí está hecha imposible de romper, no recordada.

   ⛔ CONTORNO, NUNCA RELLENO. Fondo blanco y borde: una casilla vacía tiene que verse vacía.

   ⛔ MONO 600 TABULAR, y es la ÚNICA excepción del sistema a «las cifras van en Inter». El
   código llega por correo escrito en monoespaciada; si en la pantalla se ve con otro glifo,
   el usuario compara dos dibujos distintos del mismo número —y el 0/O y el 1/l son justo
   donde eso falla.

   REQUISITOS DEL ANFITRIÓN:
     1. Los tokens del canon (`colors_and_type.css`).
     2. `box-sizing: border-box`.
   ═══════════════════════════════════════ */

/* ── LA SACUDIDA ─────────────────────────────────────────────────────────────
   ⛔ HOMOLOGADA (Ricardo, 2026-09-13): «el shake ya está en catálogo» — es «la ÚNICA
   sacudida del sistema» (`--ease-shake`, canonizada 2026-08-11; comentario propio en
   `colors_and_type.css:1246-1260`), y los keyframes son los mismos que ya usa el Input
   (`componentes/input/input.css` `.is-shake` / `@keyframes exds-input-shake`) — no unos
   propios con otro desplazamiento. Antes esta pieza sacudía con `--ease-default` (la
   curva de entrada, no la de regaño) y con 20/40/60/80% · ±5/±5/±3/±3, una receta más de
   las que el canon ya identificó como duplicadas y a homologar. */
@keyframes exds-otp-shake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-5px); }  30% { transform: translateX(5px); }
  45% { transform: translateX(-4px); }  60% { transform: translateX(4px); }
  75% { transform: translateX(-2px); }  90% { transform: translateX(2px); }
}

/* ── LA FILA ─────────────────────────────────────────────────────────────────
   Un `role="group"` con su nombre accesible: quien navega con lector de pantalla oye
   «Código de verificación» una vez, y después «Dígito 3 de 6» en cada casilla. Sin el grupo
   oiría seis campos sueltos sin saber de qué son. */
.exds-otp-row { display: flex; gap: var(--space-2, 8px); }
.exds-otp-row[data-estado="error"] {
  animation: exds-otp-shake var(--motion-shake, 380ms) var(--ease-shake, cubic-bezier(0.36, 0.07, 0.19, 0.97)) both;
}

/* ── LA CASILLA ──────────────────────────────────────────────────────────────
   ⛔ LOS DOS NÚMEROS QUE CAMBIAN POR INSTANCIA VIAJAN EN VARIABLES, y no es un capricho de
   estilo: escritos como propiedades sueltas habría que repetirlos en cada casilla y bastaría
   una para que la fila saliera desalineada.

     · `--otp-size`   52 escritorio · **48 por defecto** (portal del cliente y 2FA) · 44 mínimo táctil.
     · `--otp-digit`  24 cuando la casilla mide 52 o más; **22 por defecto**.

   Se ponen UNA vez, en la fila, y las casillas los heredan:
   `<div class="exds-otp-row" style="--otp-size:52px; --otp-digit:24px">`.

   ⛔ 44 ES EL SUELO Y NO SE BAJA. Es el mínimo táctil: por debajo, en un teléfono, el dedo
   toca la casilla de al lado — y en un código de seis dígitos eso significa volver a empezar. */
.exds-otp-box {
  width: var(--otp-size, 48px);
  height: var(--otp-size, 48px);
  flex: none;
  border-radius: var(--radius-sm, 6px);
  border: 1px solid var(--color-border-default, #D1D5DB);
  background: var(--color-surface, #FFFFFF);
  text-align: center;
  padding: 0;
  font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
  font-weight: 600;
  font-size: var(--otp-digit, 22px);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--fg1, #0F1419);
  appearance: none;
  -webkit-appearance: none;
  transition: border-color var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1));
}

/* ── LA ESCALERA DEL BORDE · DOS PELDAÑOS, LOS MISMOS QUE UNA CAJA DE TEXTO ──
   reposo → error → foco. El borde dice UNA sola cosa: dónde está el teclado.

   ⛔ EL ESCALÓN «LLENA» ESTÁ RETIRADO (Ricardo, 2026-09-15 — opción C de
   `plans/decisiones/_decision_foco_otp_2026-09-15.html`, «Decisión 2, vamos con C»). La regla
   retirada era exds-otp-box[data-llena=true] → --fg3 (se cita sin punto ni selector vivo para
   que ningún lint la lea como existente). Motivo, medido en la hoja: una casilla llena y
   enfocada se distinguía de su vecina llena en 1.14:1 — el caso de corregir un dígito, que es
   el normal. Hoy llena→enfocada mide 5.13:1 y vacía→enfocada también 5.13:1: el foco se ve
   igual tenga dígito o no. El «ya tecleé aquí» lo dice el dígito, que ya está en pantalla;
   gastar el borde en repetirlo aplastaba la señal que sí hacía falta. Es el mismo razonamiento
   con el que se retiró el borde verde de éxito el 2026-09-13.

   ⛔ EL ATRIBUTO `data-llena` SIGUE EXISTIENDO EN EL DOM Y YA NO PINTA NADA. Lo pone el JS
   porque es el DATO de la casilla («ésta tiene contenido»), lo leen las hojas fieles ya
   capturadas y las pruebas; ninguna regla de esta hoja lo mira.

   Las dos que quedan pesan lo mismo en especificidad, así que **gana la que se escribe
   después**: el foco va al final y además lleva !important. Mover una línea de sitio cambia
   la conducta sin que ningún medidor de sintaxis se queje — no las reordenes.

   ⛔ EL GROSOR NUNCA CAMBIA Y NO HAY ANILLO (Ricardo, 2026-09-13): «el delineado debe ser del
   mismo grosor que la línea original… sólo cambia de color en su estado focus» y «tampoco hay
   un anillo exterior». 1px en todos los estados, cero box-shadow, cero outline.

   ⛔ NO HAY ESCALÓN VERDE DE ÉXITO — RETIRADO (Ricardo, 2026-09-13): «no quiero ningún
   componente fuera del canon». `COMPONENTE_OTP.md` nunca declaró un borde verde de éxito
   (sólo Error, Verificando, Tapado y Deshabilitado); vivía aquí desde el v1.0 sin ficha que
   lo respaldara. El código correcto **avanza el paso** — eso ya es la señal de éxito; no
   necesita además pintarse, y pintarlo era cromo sin ficha.

   ⛔ Y EL FOCO LLEVA `!important` A PROPÓSITO: tiene que ganarle a las demás. Un usuario que
   está tecleando necesita saber en qué casilla está aunque la fila esté en rojo; si el error
   se comiera el foco, corregir un código a ciegas sería el caso normal. */
.exds-otp-row[data-estado="error"] .exds-otp-box { border-color: var(--color-error, #DA342B); }
/* LINT-ALLOW: rol-color border-color var(--fg2) · foco de casilla OTP en --fg2 por decisión de Ricardo, 2026-10-03, opción B: «¿Crees que B es una buena opción? Si es así y estás de acuerdo, continuamos con esa opción.» (COMPONENTE_OTP.md v1.4) */
.exds-otp-row .exds-otp-box:focus { border-color: var(--fg2, #4B5563) !important; outline: none !important; }
/* Foco en ERROR — EXCEPCION OTP a la regla general de inputs (Ricardo, 2026-10-03, opcion B de
   `plans/decisiones/_decision_foco_casillas_2026-10-03.html`): la casilla con foco pinta su MISMA
   linea de 1px en el gris del foco normal (--fg2), NO en el -deep del error. Aqui el foco gana sobre
   el color porque hay varias casillas iguales y hace falta ver cual esta activa; las demas conservan
   el rojo. Sigue siendo SOLO color de linea: sin anillo, sombra, outline ni mas grosor. Todos los
   demas campos del catalogo van al -deep del estado (opcion A). Contraste medido de la casilla
   activa contra sus vecinas rojas: ver COMPONENTE_OTP.md.
   LINT-ALLOW: rol-color border-color var(--fg2) · misma decisión de Ricardo, 2026-10-03, opción B: «¿Crees que B es una buena opción? Si es así y estás de acuerdo, continuamos con esa opción.» (COMPONENTE_OTP.md v1.4) */
.exds-otp-row[data-estado="error"] .exds-otp-box:focus { border-color: var(--fg2, #4B5563) !important; }

/* ── DESHABILITADO (lote 2, 2026-09-13) ───────────────────────────────────────
   ⛔ PISA A TODOS los escalones de arriba, con !important: un NIP bloqueado (p. ej.
   "verifica tu correo antes de fijarlo") no es un error — es un negocio distinto — así que
   aquí gana siempre, sin importar el orden de escritura de las reglas de arriba. Nunca
   opacidad sola (regla dura de Botón y StepperTimeline): fondo, borde y tinta bajan juntos,
   y el texto de al lado (`.exds-otp-msg[data-tono="info"]`) dice por qué. */
.exds-otp-row[data-deshabilitado="true"] .exds-otp-box {
  background: var(--color-bg-subtle, #F2F4F6) !important;
  border-color: var(--color-border-default, #D1D5DB) !important;
  color: var(--fg4, #B8BDC7) !important;
  cursor: not-allowed;
}

/* ── LA FILA + EL OJO (lote 2, 2026-09-13) ────────────────────────────────────
   El ojo revela/oculta las N casillas A LA VEZ (decisión de Ricardo, 2026-09-13) — nunca
   una por una. Va al lado de la fila, no dentro de ella: es un IconButton independiente,
   no una casilla más. */
.exds-otp-wrap { display: flex; align-items: center; gap: var(--space-3, 12px); }
/* ⛔ LA MEDIDA DEL OJO VIVE AQUÍ, NO EN EL ATRIBUTO `width`/`height` DEL SVG (forma #1 de
   GOBIERNO.md §0-ter). `IconButton` no fija tamaño de ícono — cada uso decide, igual que
   `.exds-copiar-ic` en BotonCopiar. Copiar el SVG a mano sin esta clase lo deja al tamaño
   intrínseco del `viewBox` (24), no a 18. */
.exds-otp-ojo-ic { width: 18px; height: 18px; }

/* ── EL MENSAJE DE ERROR ─────────────────────────────────────────────────────
   ⛔ SIN ÍCONO — RETIRADO (Ricardo, 2026-09-13): «no quiero ningún componente fuera del
   canon». Llevaba un triángulo de alerta que ningún aviso del catálogo dibuja
   (`COMPONENTE_INPUT.md` L440/L550: el error del Input tampoco lleva ícono desde 2026-08-04,
   «el color ya comunica»; `COMPONENTE_BANNER.md`: «ningún banner dibuja un ícono»). Va debajo
   de la fila, no dentro, porque una fila de casillas no tiene «dentro» donde meterlo y el
   mensaje suele ser una frase entera («El código expiró, pide otro») que no cabe al lado de
   un dígito — esa parte sí sigue siendo distinta del Input. */
.exds-otp-msg {
  display: block;
  margin-top: var(--space-2, 8px);
  font-family: var(--font-sans, "Inter", -apple-system, system-ui, sans-serif);
  font-weight: 500;
  font-size: var(--fs-caption, 12px);
  line-height: 1.4;
  color: var(--color-error, #DA342B);
}
/* El texto del deshabilitado no es un error: tono neutro (lo pone quien monta). */
.exds-otp-msg[data-tono="info"] { color: var(--fg3, #5C6B7A); }

/* ⛔ Apagar el movimiento NO es dejar sin señal, y aquí la señal sobrevive entera: los seis
   bordes siguen rojos y el mensaje sigue escrito. Lo que se pierde es la sacudida, que era
   el énfasis, nunca el dato. */
@media (prefers-reduced-motion: reduce) {
  .exds-otp-row, .exds-otp-msg { animation: none !important; }
}
