/* =====================================================================
   form.css — Capa de FORMULARIOS de la Doco style library.

   Antes de este archivo el DS no tenía ni un solo componente de formulario
   (las landings solo llevaban campos ocultos). Todo lo de aquí es nuevo y
   está pensado para TODO el sitio, no solo para el checkout.

   Carga DESPUÉS de button.css, así que los selectores de una sola clase de
   este archivo (por ejemplo .ds-btn-pay) le ganan a .btn-primary por puro
   orden de fuente, sin subir especificidad. Es la misma regla de cascada que
   documenta button.css: si dos reglas tienen la misma especificidad, gana la
   que aparece después.

   ---------------------------------------------------------------------
   CÓMO SE PROPAGAN LOS ESTADOS (léelo antes de tocar nada)
   ---------------------------------------------------------------------
   En BEM lo normal para pintar un estado sería escribir
   `.ds-field--error .ds-field__control`, o sea DOS clases. Aquí no hacemos
   eso: el modificador del bloque solo cambia una custom property
   (`--ds-field-borde`, `--ds-field-msg`, …) y el elemento la lee con
   var(). Como las custom properties se heredan de padre a hijo, el estado
   viaja solo y CADA selector se queda en UNA sola clase.

   Ventaja práctica: todas las reglas tienen la misma especificidad, así que
   el orden de fuente manda y nunca vas a pelear con un `!important`.
   Consecuencia a recordar: si un campo lleva --valid y --error a la vez,
   gana el que esté declarado más abajo en este archivo (--error).

   Única excepción: la casilla y el interruptor necesitan reaccionar al
   `:checked` / `:disabled` del input real, y eso no se puede leer con una
   custom property; ahí sí usamos el combinador de hermano adyacente (`+`).

   ---------------------------------------------------------------------
   CONSTANTES LOCALES (no son tokens de marca; por eso viven aquí y no en
   tokens.css, que no se toca)
   ---------------------------------------------------------------------
   - radio 4px          → el mismo de .btn (button.css:25); un formulario y su
                          botón tienen que tener la misma esquina.
   - alto mínimo 44px   → área táctil mínima para un dedo (WCAG 2.5.5 y la
                          guía de Apple). No es estética, es accesibilidad.
   - 1rem en los controles → 16px exactos. Safari en iPhone hace zoom
                          automático cuando enfocas un campo con menos de
                          16px. Nunca bajes el tamaño del control en móvil.
   - line-height y letter-spacing sin unidad → constantes tipográficas
                          intrínsecas, igual que en base.css.
   ===================================================================== */

/* =====================================================================
   0. Colores derivados para accesibilidad (AA)

   NO son tokens nuevos ni un fork de la paleta: se CALCULAN con color-mix()
   a partir de los tokens que ya existen. Si mañana cambia --p-verde, estos
   cambian solos.

   Por qué hacen falta:
     · Texto blanco sobre --p-verde (#18b3af) da 2.59:1. WCAG AA pide 4.5:1
       para texto normal. tokens.css lo documenta como decisión de marca
       aceptada para superficies decorativas, pero un botón de PAGO no es
       decorativo: si no se lee, no se paga.
     · --p-rosado (#ed5482) sobre blanco da 3.39:1. Alcanza para un BORDE
       (WCAG 1.4.11 pide 3:1 a elementos de interfaz) pero NO para el texto
       del mensaje de error.
     · --hairline (#c8ccd4) sobre blanco da 1.61:1. Como el campo es blanco
       igual que la página, el borde es lo ÚNICO que dice "aquí se escribe",
       así que ese borde cae bajo 1.4.11 y necesita 3:1.

   Cómo se calculó cada porcentaje (fórmula WCAG 2.x):
     1. se linealiza cada canal sRGB,
     2. L = 0.2126·R + 0.7152·G + 0.0722·B,
     3. contraste contra blanco = 1.05 / (L + 0.05).
   Se bajó el porcentaje de a 1% hasta cruzar el umbral, y se dejó el primer
   valor que pasa con un poco de aire (el redondeo a 8 bits del navegador
   mueve el resultado en la tercera cifra).
   ===================================================================== */
:root {
  /* Teal para TEXTO BLANCO ENCIMA (botón de pago, chips rellenos).
     68% de --p-verde + 32% de --gris-oscuro = #19827f.
     Blanco sobre ese teal = 4.62:1 → PASA AA (4.5:1).
     Punto de quiebre medido: 70% da 4.45:1 (NO pasa) y 68% da 4.62:1 (pasa),
     así que 68% es el mínimo en pasos de 1% que cumple. Mezclamos contra
     --gris-oscuro (un token) y no contra negro puro para no meter un literal
     de color y para que el teal no se apague de más. */
  --ds-teal-aa: color-mix(in srgb, var(--p-verde) 68%, var(--gris-oscuro));

  /* Hover del mismo teal: 60% → 5.41:1. Se oscurece (no se aclara) para que
     el hover nunca baje del umbral que acabamos de ganar. */
  --ds-teal-aa-hover: color-mix(in srgb, var(--p-verde) 60%, var(--gris-oscuro));

  /* Rosado para TEXTO de error sobre blanco.
     80% de --p-rosado + 20% de --gris-oscuro = #c3486d → 4.70:1 → PASA AA.
     El punto de quiebre está en 82% (4.53:1) y 84% ya no pasa (4.39:1); se
     tomó 80% para dejar margen. El BORDE del campo en error sí usa
     --p-rosado puro: a un borde le basta 3:1 y así el error se ve del rosa
     de la marca. */
  --ds-rosado-aa: color-mix(in srgb, var(--p-rosado) 80%, var(--gris-oscuro));

  /* Hairline endurecido para el borde de los controles.
     50% de --hairline + 50% de --gris-secundario = #828892 → 3.57:1 sobre
     blanco → cumple 1.4.11. Sigue siendo un gris frío de la misma familia,
     no un borde negro. --hairline puro se queda para separadores y bordes
     decorativos, donde no aplica el requisito. */
  --ds-hairline-aa: color-mix(in srgb, var(--hairline) 50%, var(--gris-secundario));
}

/* =====================================================================
   0-bis. box-sizing propio de los controles.

   Bootstrap ya pone `box-sizing: border-box` en todo, así que dentro de
   base_ds.html esto es redundante. Se declara igual a propósito: sin él, el
   padding se SUMA al ancho y un campo con padding extra (el de la contraseña,
   el del select) se sale de su columna de la retícula. Es un bug que aparece
   solo si alguien reusa estos componentes fuera del stack del DS, y cuesta
   tres líneas evitarlo.
   ===================================================================== */
.ds-field__control,
.ds-field__toggle,
.ds-check__box,
.ds-switch__track,
.ds-code,
.ds-btn-pay {
  box-sizing: border-box;
}

/* =====================================================================
   1. Anillo de foco de doble trazo (.ds-focus-ring)

   OJO: base.css:57 YA define un :focus-visible universal (contorno teal de
   2px con 4px de separación). NO se duplica aquí y NO se toca ese archivo.

   El problema que sí resolvemos: ese anillo es teal, así que sobre un botón
   teal desaparece; y sobre blanco se queda en 2.59:1, por debajo del 3:1 que
   pide WCAG 1.4.11 para indicadores.

   La solución es un anillo de DOS trazos concéntricos: uno claro pegado al
   borde y uno oscuro por fuera. Sobre fondo claro se ve el oscuro; sobre
   fondo teal u oscuro se ve el claro. Siempre queda uno de los dos visible,
   sin importar el fondo.

   Se aplica solo con :focus-visible (no con :focus) para que el anillo salga
   al navegar con teclado y no al hacer clic con el mouse.

   El `outline` transparente no es decorativo: en modo de alto contraste de
   Windows el navegador descarta los box-shadow y fuerza el outline a un
   color del sistema, así que ese contorno "invisible" es el que salva el
   foco ahí. Es la razón de existir de esas dos líneas.
   ===================================================================== */
.ds-focus-ring:focus-visible,
.ds-field__control:focus-visible,
.ds-field__toggle:focus-visible,
.ds-code:focus-visible,
.ds-btn-pay:focus-visible,
.ds-check__input:focus-visible + .ds-check__box,
.ds-switch__input:focus-visible + .ds-switch__track {
  outline: 2px solid transparent;
  outline-offset: 2px;
  box-shadow:
    0 0 0 2px var(--paper),        /* trazo claro: se ve sobre teal y sobre oscuro */
    0 0 0 5px var(--gris-oscuro);  /* trazo oscuro: se ve sobre blanco y sobre claro */
}

/* =====================================================================
   2. .ds-field — el bloque de campo

   Estructura:
     .ds-field                      (bloque; aquí van los modificadores)
       .ds-field__label             (siempre visible; nada de placeholder-como-etiqueta)
       .ds-field__group             (opcional; envuelve control + adorno)
         .ds-field__control         (input / select / textarea)
         .ds-field__toggle          (solo en --password)
       .ds-field__hint              (ayuda; se lee siempre)
       .ds-field__error             (solo aparece con --error)
   ===================================================================== */
.ds-field {
  /* Valores por defecto que los modificadores reescriben. Todo el sistema de
     estados cuelga de estas cuatro líneas. */
  --ds-field-borde: var(--ds-hairline-aa);
  --ds-field-borde-foco: var(--ds-teal-aa);
  --ds-field-fondo: var(--paper);
  --ds-field-texto: var(--gris-oscuro);
  --ds-field-label-color: var(--gris-oscuro);
  --ds-field-pad-derecha: var(--space-3);
  /* 44px = área táctil mínima cómoda para un dedo (WCAG 2.5.5). */
  --ds-field-alto-min: 44px;
  --ds-field-resize: none;
  --ds-field-error-display: none; /* el nodo del error existe siempre, se muestra con --error */
  --ds-field-adorno-display: none; /* la flecha del select se prende con --select */

  display: block;
  margin-bottom: var(--space-4);
  font-family: var(--font-sans);
  text-align: start; /* un campo nunca hereda el centrado de la sección que lo contiene */
}

/* Etiqueta. Va arriba y siempre visible: la etiqueta flotante se ve bonita
   pero desaparece justo cuando el usuario está escribiendo, que es cuando
   más se necesita. */
.ds-field__label {
  display: block;
  font-family: var(--font-sans);
  font-size: var(--fs-small);
  font-weight: 600;
  line-height: 1.4;
  color: var(--ds-field-label-color);
  margin-bottom: var(--space-1);
}

/* Marca de "obligatorio". Es un <span> aparte para poder darle
   aria-hidden="true" y que el lector de pantalla no diga "asterisco": la
   obligatoriedad se comunica con el atributo required del control. */
.ds-field__required {
  color: var(--ds-rosado-aa);
  margin-inline-start: var(--space-1);
}

/* Envoltura del control. Solo hace falta cuando el campo lleva algo encima
   (el ojo de la contraseña, la flecha del select). Si el campo es un input
   pelado, puedes omitirla. */
.ds-field__group {
  position: relative;
  display: block;
}

/* Flecha del select, dibujada con CSS (dos bordes de un cuadrado girado).
   Nada de iconos de fuente ni de imágenes: se hereda el color y escala con
   el texto. Se prende con el modificador --select. */
.ds-field__group::after {
  content: "";
  display: var(--ds-field-adorno-display);
  position: absolute;
  inset-inline-end: var(--space-3);
  top: 50%;
  width: 0.5em;
  height: 0.5em;
  border-right: 2px solid var(--gris-secundario);
  border-bottom: 2px solid var(--gris-secundario);
  /* -70% en vez de -50%: al girar 45° el cuadrado, su centro visual queda
     más abajo que su caja, y esto lo vuelve a centrar a ojo. */
  transform: translateY(-70%) rotate(45deg);
  pointer-events: none; /* que el clic pase al select que está debajo */
}

/* El control. Sirve igual para input, select y textarea. */
.ds-field__control {
  display: block;
  width: 100%;
  min-height: var(--ds-field-alto-min);
  resize: var(--ds-field-resize); /* solo hace efecto en textarea */
  padding: var(--space-2) var(--ds-field-pad-derecha) var(--space-2) var(--space-3);
  font-family: var(--font-sans);
  /* 1rem = 16px exactos: por debajo de eso Safari en iPhone hace zoom al enfocar. */
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.5;
  color: var(--ds-field-texto);
  background-color: var(--ds-field-fondo);
  border: 1px solid var(--ds-field-borde);
  border-radius: 4px; /* el mismo de .btn */
  /* Quita el estilo nativo (la flecha del select, el relieve de iOS) para que
     el campo se vea igual en todos lados. */
  appearance: none;
  transition: border-color 150ms ease, background-color 150ms ease;
}

.ds-field__control:hover {
  border-color: var(--gris-secundario);
}

/* Al enfocar, el borde se engrosa con un box-shadow interno en vez de subir
   el border-width: si cambiara el grosor del borde, el campo daría un
   brinco de 1px. El anillo de foco lo pone la sección 1. */
.ds-field__control:focus {
  border-color: var(--ds-field-borde-foco);
  outline: none;
}

.ds-field__control::placeholder {
  color: var(--gris);
  opacity: 1; /* Firefox le baja la opacidad por su cuenta y lo deja ilegible */
}

/* Deshabilitado en el propio control, por si llega sin el modificador del
   bloque (por ejemplo cuando el atributo lo pone JavaScript). */
.ds-field__control:disabled {
  background-color: var(--gris-claro);
  color: var(--gris);
  border-color: var(--hairline);
  cursor: not-allowed;
}

/* Texto de ayuda. --gris sobre blanco = 5.74:1, pasa AA de sobra.
   Engánchalo con aria-describedby para que el lector de pantalla lo lea. */
.ds-field__hint {
  display: block;
  font-size: var(--fs-small);
  line-height: 1.4;
  color: var(--gris);
  margin-top: var(--space-1);
}

/* Mensaje de error. Oculto hasta que el bloque lleve --error.
   Lleva un punto rosa dibujado en CSS para no depender SOLO del color
   (WCAG 1.4.1: el color no puede ser el único canal de información; el
   texto del mensaje ya cumple, el punto es refuerzo visual). */
.ds-field__error {
  display: var(--ds-field-error-display);
  align-items: baseline;
  gap: var(--space-1);
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1.4;
  color: var(--ds-rosado-aa);
  margin-top: var(--space-1);
}
.ds-field__error::before {
  content: "";
  flex: none;
  width: 0.4em;
  height: 0.4em;
  border-radius: 50%;
  background-color: currentColor;
  /* lo baja a la línea base del texto en vez de dejarlo flotando arriba */
  transform: translateY(-0.1em);
}

/* ---- Modificadores de estado ----
   Cada uno solo reescribe variables. --error va al final a propósito: si un
   campo trae --valid y --error al mismo tiempo, gana el error. */
.ds-field--valid {
  --ds-field-borde: var(--ds-teal-aa);
}
.ds-field--error {
  --ds-field-borde: var(--p-rosado);       /* borde: 3.39:1, cumple el 3:1 de elementos de interfaz */
  --ds-field-borde-foco: var(--p-rosado);
  --ds-field-error-display: flex;
}
.ds-field--disabled {
  --ds-field-borde: var(--hairline);
  --ds-field-fondo: var(--gris-claro);
  --ds-field-texto: var(--gris);
  --ds-field-label-color: var(--gris);
}

/* ---- Modificador: select ----
   Prende la flecha y le abre espacio a la derecha para que el texto largo de
   una opción no se meta debajo del adorno. */
.ds-field--select {
  --ds-field-adorno-display: block;
  --ds-field-pad-derecha: var(--space-5);
}

/* ---- Modificador: área de texto ----
   ~4 renglones de alto y solo se estira en vertical (en horizontal rompería
   la retícula del formulario). */
.ds-field--textarea {
  --ds-field-alto-min: 7rem;
  --ds-field-resize: vertical;
}

/* =====================================================================
   3. .ds-field--password + .ds-field__toggle — el ojo de mostrar/ocultar

   El BOTÓN lo damos nosotros; el ICONO lo pone quien lo use. Dejamos el hueco
   listo: lo que metas adentro se dibuja a 1em y con currentColor, así que
   hereda tamaño y color sin que tengas que estilizarlo.

   El que lo integre debe recordar: <button type="button"> (si no, envía el
   formulario), aria-pressed para el estado y aria-label con texto ("Mostrar
   contraseña" / "Ocultar contraseña"), porque un icono solo no se lee.
   ===================================================================== */
.ds-field--password {
  /* espacio para que el texto de la contraseña no pase por debajo del ojo */
  --ds-field-pad-derecha: var(--space-6);
}

.ds-field__toggle {
  position: absolute;
  inset-inline-end: 0;
  top: 0;
  /* 44x44 de área táctil aunque el icono mida 1em: el área que se toca es más
     grande que lo que se ve, que es justo lo que se busca en móvil. */
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: none;
  border: none;
  border-radius: 4px;
  color: var(--gris-secundario);
  cursor: pointer;
  transition: color 150ms ease;
}
.ds-field__toggle:hover {
  color: var(--ds-teal-aa);
}
/* El hueco del icono: cualquier <svg> o <span> que metas adentro sale a 1em
   y del color del botón. */
.ds-field__toggle > * {
  width: 1em;
  height: 1em;
  fill: currentColor;
  stroke: currentColor;
  pointer-events: none; /* el clic siempre le llega al botón, no al icono */
}

/* =====================================================================
   4. .ds-check — la casilla

   El input real sigue ahí (accesible y enfocable), solo que transparente y
   encima de la caja dibujada. Así el lector de pantalla y el teclado ven un
   checkbox de verdad, y nosotros pintamos lo que queremos.

   Se usa envolviendo todo en un <label>, para que tocar el texto también
   marque la casilla (más área para el dedo, gratis).
   ===================================================================== */
.ds-check,
.ds-switch {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  /* toda la fila es área táctil, no solo el cuadrito */
  min-height: 44px;
  padding-block: var(--space-2);
  font-family: var(--font-sans);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--gris-oscuro);
  cursor: pointer;
}

/* El input real: invisible pero presente. Nada de display:none ni de
   visibility:hidden, que lo sacarían del recorrido con Tab. */
.ds-check__input,
.ds-switch__input {
  position: absolute;
  inset-inline-start: 0;
  top: var(--space-2);
  width: 1.5rem;
  height: 1.5rem;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

/* La caja dibujada.
   El borde lee --ds-field-borde con valor de respaldo: si la casilla vive
   dentro de un .ds-field--error (el caso típico de "acepta los términos" sin
   marcar), toma el rosa del error sola; si vive suelta, usa el gris. */
.ds-check__box {
  position: relative;
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  background-color: var(--paper);
  border: 1px solid var(--ds-field-borde, var(--ds-hairline-aa));
  border-radius: 4px;
  transition: background-color 150ms ease, border-color 150ms ease;
}

/* La palomita, dibujada con dos bordes de un cuadrado girado. Vive siempre
   en el DOM y solo cambia de opacidad: así no hay salto de layout al marcar. */
.ds-check__box::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 46%; /* óptico: la palomita girada se ve centrada un pelo más arriba */
  width: 0.35rem;
  height: 0.7rem;
  border-right: 2px solid var(--paper);
  border-bottom: 2px solid var(--paper);
  transform: translate(-50%, -50%) rotate(45deg);
  opacity: 0;
  transition: opacity 120ms ease;
}

.ds-check__input:checked + .ds-check__box {
  background-color: var(--ds-teal-aa);
  border-color: var(--ds-teal-aa);
}
.ds-check__input:checked + .ds-check__box::after {
  opacity: 1;
}

/* Estado indeterminado (el típico "seleccionar todo" a medias): una barra en
   lugar de la palomita. */
.ds-check__input:indeterminate + .ds-check__box {
  background-color: var(--ds-teal-aa);
  border-color: var(--ds-teal-aa);
}
.ds-check__input:indeterminate + .ds-check__box::after {
  opacity: 1;
  width: 0.6rem;
  height: 0;
  border-right: none;
  border-bottom: 2px solid var(--paper);
  transform: translate(-50%, -50%) rotate(0deg);
}

.ds-check__input:disabled + .ds-check__box {
  background-color: var(--gris-claro);
  border-color: var(--hairline);
}
.ds-check__input:disabled ~ .ds-check__text,
.ds-switch__input:disabled ~ .ds-switch__text {
  color: var(--gris);
}

/* El texto al lado de la casilla o del interruptor. */
.ds-check__text,
.ds-switch__text {
  display: block;
  /* empuja el texto a la altura óptica del cuadro cuando es de una sola línea */
  padding-top: 0.125rem;
}

/* =====================================================================
   5. .ds-switch — el interruptor

   Mismo esqueleto que la casilla, pero la caja es una pista con una perilla.
   Úsalo SOLO para cosas que se aplican al instante (activar notificaciones);
   si el usuario tiene que darle Guardar, va una casilla, no un interruptor.

   El HTML debe llevar role="switch" en el input para que el lector de
   pantalla diga "activado/desactivado" en vez de "casilla marcada".
   ===================================================================== */
.ds-switch__track {
  position: relative;
  flex: none;
  width: 2.75rem;
  height: 1.5rem;
  background-color: var(--gris);
  border: 1px solid var(--gris);
  border-radius: 999px; /* píldora: un número grande siempre da el semicírculo */
  transition: background-color 150ms ease, border-color 150ms ease;
}

/* La perilla. */
.ds-switch__track::after {
  content: "";
  position: absolute;
  top: 50%;
  inset-inline-start: 0.125rem;
  width: 1.125rem;
  height: 1.125rem;
  background-color: var(--paper);
  border-radius: 50%;
  transform: translateY(-50%);
  transition: transform 150ms ease;
}

.ds-switch__input:checked + .ds-switch__track {
  background-color: var(--ds-teal-aa);
  border-color: var(--ds-teal-aa);
}
.ds-switch__input:checked + .ds-switch__track::after {
  /* recorre el ancho de la pista menos la perilla y los dos márgenes */
  transform: translate(1.25rem, -50%);
}

.ds-switch__input:disabled + .ds-switch__track {
  background-color: var(--hairline);
  border-color: var(--hairline);
}

/* =====================================================================
   6. .ds-code — el campo de código de verificación (6 dígitos)

   Un SOLO input, no seis. Seis inputs se ven muy bien en el mockup y son un
   infierno real: rompen el autocompletado del SMS, el pegar desde el
   portapapeles y el borrar con retroceso. Con uno solo, `autocomplete
   ="one-time-code"` hace que iOS y Android ofrezcan el código del mensaje.

   El HTML debe llevar: inputmode="numeric" (teclado numérico en móvil),
   autocomplete="one-time-code", maxlength="6" y pattern="[0-9]*".

   Se ve claramente distinto de un input normal: más grande, centrado,
   monoespaciado y con las letras muy separadas.

   Puede ir dentro de un .ds-field: como lee --ds-field-borde, hereda el
   estado --error del bloque sin ninguna regla extra.
   ===================================================================== */
.ds-code {
  display: block;
  width: 100%;
  max-width: 20ch; /* el ancho de 6 dígitos separados; más ancho se ve vacío */
  min-height: 56px; /* más alto que un control normal: es el protagonista de la pantalla */
  margin-inline: auto;
  padding: var(--space-2) var(--space-3);
  /* Monoespaciado del sistema: todos los dígitos ocupan lo mismo, así el
     código no "baila" mientras se escribe. Es una constante del componente,
     no un token de marca; --font-sans (Poppins) no tiene versión mono. */
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: var(--fs-h3);
  font-weight: 600;
  line-height: 1.2;
  /* Separación amplia entre dígitos: es lo que hace que se lea como un
     código y no como una palabra. Constante tipográfica intrínseca. */
  letter-spacing: 0.5em;
  /* Compensa el espacio que letter-spacing agrega DESPUÉS del último dígito;
     sin esto el texto se ve corrido a la izquierda aunque diga center. */
  text-indent: 0.5em;
  text-align: center;
  color: var(--gris-oscuro);
  background-color: var(--paper);
  /* 2px en vez de 1px: refuerza que este campo no es uno más del formulario. */
  border: 2px solid var(--ds-field-borde, var(--ds-hairline-aa));
  border-radius: 4px;
  appearance: none;
  transition: border-color 150ms ease;
}
.ds-code:focus {
  border-color: var(--ds-field-borde-foco, var(--ds-teal-aa));
  outline: none;
}
/* El placeholder usa --gris (5.74:1) y no --hairline (1.61:1): un placeholder
   casi invisible se ve elegante en el mockup y es ilegible en un celular al
   sol. Aun así, el placeholder NUNCA reemplaza a la etiqueta. */
.ds-code::placeholder {
  color: var(--gris);
  opacity: 1;
}
/* Quita las flechitas de incremento por si alguien usa type="number".
   (Lo correcto es type="text" + inputmode="numeric", pero esto lo cubre.) */
.ds-code::-webkit-outer-spin-button,
.ds-code::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}

/* =====================================================================
   7. .ds-btn-pay — el CTA de pago que sí cumple AA

   Se usa JUNTO a .btn: class="btn ds-btn-pay". Hereda de .btn la tipografía,
   el radio y las transiciones, y solo cambia los colores y el tamaño.

   Por qué no usa .btn-primary tal cual: blanco sobre --p-verde da 2.59:1 y
   WCAG AA pide 4.5:1. En una landing eso es una decisión de marca discutible;
   en el botón que cobra, no: si el usuario no lee "Pagar $19.000 COP", no
   paga. Usamos --ds-teal-aa (ver sección 0) = 4.62:1, calculado con
   color-mix() a partir de --p-verde, sin forkear ningún token.

   Como form.css carga después de button.css, esta clase (una sola clase) le
   gana a .btn-primary por orden de fuente. No le subas la especificidad.

   ---------------------------------------------------------------------
   LOS ESTADOS LOS PINTA BOOTSTRAP, VÍA VARIABLES (y es a propósito)
   ---------------------------------------------------------------------
   El estado NORMAL sale de las declaraciones de abajo. Hover, presionado y
   foco de teclado NO: los pinta .btn leyendo sus variables --bs-btn-*, y por
   eso esta clase las declara con los mismos tokens del componente.

   Por qué no se arregla con reglas propias: el botón lleva las DOS clases
   (`class="btn ds-btn-pay"`) y en los estados Bootstrap gana.
     · presionado → `.btn:first-child:active` (0,3,0) le gana a
       `.ds-btn-pay:active` (0,2,0). No es orden de carga: form.css carga
       después y aun así pierde, porque la especificidad se mira antes que el
       orden. Y ese selector depende de la POSICIÓN en el DOM, así que el
       problema aparecía en unos botones y no en otros.
     · foco de teclado → `.btn:focus-visible` (0,2,0) trae fondo y color;
       `.ds-btn-pay:focus-visible` (sección 1) solo pone el anillo, no el
       fondo, así que no había nada que le compitiera.

   Y como este botón NO lleva clase de variante (nada de .btn-primary), esas
   variables no las declaraba nadie. Un `var()` sin valor deja la declaración
   "inválida al calcular el valor", que equivale a escribir `unset`: el fondo
   no se hereda, así que cae a su valor inicial (transparente), y el color sí
   se hereda, así que agarra el texto oscuro de la página. De ahí el síntoma
   medido: botón TRANSPARENTE CON TEXTO OSCURO al mantenerlo presionado y al
   llegar a él con Tab.

   Se quitaron las reglas `.ds-btn-pay:hover` / `.ds-btn-pay:active` que
   vivían aquí: con las variables puestas pintaban el mismo color dos veces, y
   dos fuentes para un mismo valor acaban divergiendo (cambias una, el foco de
   teclado se queda con la otra). `:disabled` sí sigue siendo regla, porque
   Bootstrap no tiene selector para `[aria-disabled="true"]` y ahí la regla del
   DS es la única que pinta.
   ===================================================================== */
.ds-btn-pay {
  /* Hover, presionado y foco de teclado, los tres con --ds-teal-aa-hover: el
     mismo teal del hover, que OSCURECE (5.41:1) en vez de aclarar, para que
     ningún estado baje del contraste que ya ganamos con --ds-teal-aa. */
  --bs-btn-hover-color: var(--paper-pure);
  --bs-btn-hover-bg: var(--ds-teal-aa-hover);
  --bs-btn-hover-border-color: var(--ds-teal-aa-hover);
  --bs-btn-active-color: var(--paper-pure);
  --bs-btn-active-bg: var(--ds-teal-aa-hover);
  --bs-btn-active-border-color: var(--ds-teal-aa-hover);

  display: block;
  width: 100%; /* móvil primero: el botón que cobra es de ancho completo */
  min-height: 52px; /* más alto que los 44px mínimos: es la acción principal */
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-sans);
  font-size: var(--fs-body); /* un escalón arriba de .btn: tiene que pesar más */
  font-weight: 600;
  line-height: 1.5;
  text-align: center;
  color: var(--paper-pure); /* 4.62:1 contra --ds-teal-aa */
  background-color: var(--ds-teal-aa);
  border: 1px solid var(--ds-teal-aa);
  border-radius: 4px;
  cursor: pointer;
  transition: background-color 150ms ease, border-color 150ms ease;
}
.ds-btn-pay:disabled,
.ds-btn-pay[aria-disabled="true"] {
  background-color: var(--hairline);
  border-color: var(--hairline);
  color: var(--gris-secundario); /* 6.1:1 sobre el gris del deshabilitado */
  cursor: not-allowed;
}

/* Estado "procesando": deja el botón bloqueado pero legible. El texto lo
   cambia el JS ("Procesando…"); esta clase solo apaga la interacción, para
   que nadie mande el pago dos veces. */
.ds-btn-pay--loading {
  pointer-events: none;
  opacity: 0.8;
}

/* En pantallas grandes el botón de pago no necesita cruzar todo el ancho:
   se ajusta a su contenido con un mínimo cómodo. */
@media (min-width: 768px) {
  .ds-btn-pay--auto {
    display: inline-block;
    width: auto;
    min-width: 16rem;
  }
}

/* =====================================================================
   8. .ds-form-grid — retícula de formulario (móvil primero)

   Un campo por fila en móvil, dos columnas a partir de tablet. Es una
   alternativa a las utilidades de Bootstrap cuando quieres que el
   formulario se arme solo, sin repartir clases de columna en cada campo.
   ===================================================================== */
.ds-form-grid {
  display: grid;
  grid-template-columns: 1fr; /* móvil: SIEMPRE un campo por fila */
  column-gap: var(--space-3);
}
.ds-form-grid__wide {
  grid-column: 1 / -1; /* ocupa la fila completa aunque haya dos columnas */
}
@media (min-width: 768px) {
  .ds-form-grid--two {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* =====================================================================
   9. Preferencia de movimiento reducido.
   Quien lo pida en su sistema no ve transiciones. Los estados siguen
   cambiando (el color salta en vez de deslizarse), solo se quita la animación.
   ===================================================================== */
@media (prefers-reduced-motion: reduce) {
  .ds-field__control,
  .ds-field__toggle,
  .ds-check__box,
  .ds-check__box::after,
  .ds-switch__track,
  .ds-switch__track::after,
  .ds-code,
  .ds-btn-pay {
    transition: none;
  }
}
