/* =========================================================================
   ELEMENTOS COMPARTIDOS — utilidades, encabezado de sección, antetítulo y botones

   Primera hoja de la cadena de componentes. Lo que usan todas las vistas y no
   pertenece a ningún componente concreto.

   Parte de la cadena de hojas de componentes. El ORDEN DE CASCADA lo
   garantiza la cadena de dependencias declarada en inc/assets.php: ninguna
   vista encola estas hojas directamente, todas dependen del handle
   'nvl-components', que cierra la cadena. Mover una regla de archivo puede
   cambiar qué gana; mover un archivo de sitio en la cadena, también.

   Mobile-first. Todo valor visual es token var(--nvl-*).
   ========================================================================= */

/* --- Utilidades ---------------------------------------------------------- */

.nvl-screen-reader-text {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

.nvl-section {
    padding: var(--nvl-space-2xl) 0;
}

.nvl-section-title {
    display: flex;
    align-items: center;
    gap: var(--nvl-space-2xs);
    font-size: var(--nvl-text-xl);
    /* H2 de sección en BOLD (tres pesos, 2026-10-17; venía de 600). Pesa lo
       mismo que el H1 comercial, igual que ya pasaba con el 600: entre esos dos
       niveles la jerarquía la sostienen el tamaño y el color, nunca el peso. */
    font-weight: var(--nvl-weight-bold);
    color: var(--nvl-color-secondary);
    margin-bottom: var(--nvl-space-lg);
    line-height: var(--nvl-leading-snug);
}

@media (min-width: 768px) {
    .nvl-section {
        padding: var(--nvl-space-3xl) 0;
    }

    .nvl-section-title {
        font-size: var(--nvl-text-2xl);
        margin-bottom: var(--nvl-space-xl); /* ritmo vertical §8.3: título de sección → contenido */
    }
}

/* Ritmo vertical (auditoría ui-ux §8.3): escalón de escritorio grande, hoy
   inexistente — es lo que hace que la página respire en pantallas anchas. */
@media (min-width: 1280px) {
    .nvl-section {
        padding: var(--nvl-space-4xl) 0;
    }
}

/* --- Encabezado de sección compartido (.nvl-section-head, nuevo) --------- */
/* Usado por parrilla de categorías, destacados, por qué, pasos, testimonios
   y FAQ de la Home; y disponible para las vistas de categoría que activen
   los mismos bloques compartidos. */

.nvl-section-head {
    display: grid;
    gap: 0;
    margin-bottom: var(--nvl-space-xl);
}

@media (min-width: 768px) {
    .nvl-section-head {
        margin-bottom: var(--nvl-space-2xl);
    }
}

.nvl-section-head__eyebrow {
    margin-bottom: var(--nvl-space-2xs);
}

.nvl-section-head__title {
    font-size: var(--nvl-text-xl);
    /* H2 de sección en BOLD (tres pesos, 2026-10-17; venía de 600). Pesa lo
       mismo que el H1 comercial, igual que ya pasaba con el 600: entre esos dos
       niveles la jerarquía la sostienen el tamaño y el color, nunca el peso. */
    font-weight: var(--nvl-weight-bold);
    line-height: var(--nvl-leading-snug);
    color: var(--nvl-color-text-primary);
    text-wrap: balance;
}

@media (min-width: 768px) {
    .nvl-section-head__title {
        font-size: var(--nvl-text-2xl);
    }
}

@media (min-width: 1280px) {
    .nvl-section-head__title {
        font-size: var(--nvl-text-3xl);
    }
}

.nvl-section-head__lead {
    margin-top: var(--nvl-space-xs);
    max-width: var(--nvl-measure-narrow);
    color: var(--nvl-color-text-primary); /* sube de la tinta secundaria: sobre banda, el #64748b oficial da 4.36:1 — bajada de sección, aparece en todas las vistas */
    font-size: var(--nvl-text-base);
    font-weight: var(--nvl-weight-regular);
    line-height: var(--nvl-leading-relaxed);
    text-wrap: pretty;
}

@media (min-width: 768px) {
    .nvl-section-head__lead {
        font-size: var(--nvl-text-md);
    }
}

.nvl-section-head--center {
    text-align: center;
}

.nvl-section-head--center .nvl-section-head__lead {
    margin-inline: auto;
}

/* --- Etiqueta / antetítulo (.nvl-eyebrow, usado en toda la Home) ---------

   SIN caja alta (owner, 2026-10-17). Con el sistema de tres pesos desapareció
   el letter-spacing, y unas mayúsculas de 13px sin separación es lo único que
   el antetítulo no soporta: es la cadena en caja alta más larga del tema. En
   caja de frase el papel lo cargan el peso, el tamaño y el color de marca.
   Su réplica del pie (.nvl-site-footer__coltitle, nvl-base.css) va con él. */

.nvl-eyebrow {
    display: inline-block;
    font-size: var(--nvl-text-xs);
    font-weight: var(--nvl-weight-bold);
    line-height: var(--nvl-leading-heading);
    color: var(--nvl-color-primary-dark);
}

/* Microcopia secundaria densa. En REGULAR desde el sistema de tres pesos
   (owner, 2026-10-17): el medium que compensaba la densidad a 12-13px ya no
   existe. Lo que ese medium corregía sigue siendo cierto y conviene saberlo —
   Poppins es geométrica monolineal, con a y g de un solo piso y sin kerning
   para latino, así que a ese cuerpo diferencia peor entre letras; no es
   cuestión de altura de x, que medida (0.548 em) es un 0.4% MAYOR que la de
   Inter—. Si en algún sitio se queda corta, se compensa subiendo el TAMAÑO a
   --nvl-text-sm. Nunca con peso, que ya no hay escalón intermedio. */
.nvl-microcopy {
    font-size: var(--nvl-text-xs);
    font-weight: var(--nvl-weight-regular);
    line-height: var(--nvl-leading-normal);
    color: var(--nvl-color-text-secondary);
}

@media (min-width: 768px) {
    .nvl-microcopy {
        font-size: var(--nvl-text-sm);
    }
}


/* --- Botones --------------------------------------------------------------
   Set completo: reposo, hover (solo puntero fino), foco visible, activo y
   deshabilitado común. --nvl-focus-color se redefine por contexto (ver
   nvl-base.css para bandas oscuras; el CTA amarillo se redefine aquí). */

.nvl-button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--nvl-space-2xs);
    min-height: var(--nvl-tap-min);
    padding: var(--nvl-space-xs) var(--nvl-space-lg);
    border: var(--nvl-border-thick) solid transparent;
    border-radius: var(--nvl-radius-md);
    font-weight: var(--nvl-weight-bold); /* Botón: bold con los tres pesos (2026-10-17). Es la acción primaria y su blanco sobre primary ya va justo de contraste: no se aligera */
    font-size: var(--nvl-text-base);
    line-height: var(--nvl-leading-snug);
    text-decoration: none;
    cursor: pointer;
    transition:
        background-color var(--nvl-duration-base) var(--nvl-ease-standard),
        color var(--nvl-duration-base) var(--nvl-ease-standard),
        border-color var(--nvl-duration-base) var(--nvl-ease-standard),
        transform var(--nvl-duration-base) var(--nvl-ease-standard),
        box-shadow var(--nvl-duration-base) var(--nvl-ease-standard);
}

/* Reposo --nvl-color-primary y estados --nvl-color-primary-dark: exactamente el
   par que usa la landing (landing-peces.css:813-814). El blanco sobre el reposo
   da 4.10:1, por debajo de AA — es el ratio que la landing tiene hoy y el owner
   lo aceptó a cambio de fidelidad literal. El hover sí cumple, con 5.93:1. */
.nvl-button--primary {
    background: var(--nvl-color-primary);
    color: var(--nvl-color-surface);
    box-shadow: var(--nvl-shadow-primary);
}

@media (hover: hover) and (pointer: fine) {
    .nvl-button--primary:hover {
        background: var(--nvl-color-primary-dark);
        transform: translateY(-1px);
        box-shadow: var(--nvl-shadow-primary-h);
    }
}

.nvl-button--primary:active {
    background: var(--nvl-color-primary-dark);
    transform: translateY(0) scale(0.98);
    box-shadow: var(--nvl-shadow-sm);
}

/* CTA de compra: amarillo marketplace con texto oscuro (contraste WCAG).

   NO redefine el foco. Antes lo fijaba a --nvl-color-secondary para separarlo
   del relleno amarillo, pero los dos CTA reales del tema viven DENTRO de
   bandas de ese mismo color (components/home-hero.php y components/cta-contact.php,
   las dos con fondo --nvl-color-secondary): el anillo se pintaba del color de
   su propio fondo, 1.00:1, y era invisible en el 100 % de sus emplazamientos.

   Heredando el anillo global de nvl-base.css queda --nvl-color-primary sobre
   superficie clara (3.76:1 sobre la franja de acciones de la tarjeta, y es el
   mismo azul de foco que usa la landing) y --nvl-color-accent dentro de las
   bandas oscuras (8.33:1), que ya lo redefine nvl-base.css para ese contexto.
   El offset de 2px lo separa del relleno amarillo.

   EXCEPCIÓN: dentro del héroe de la Home este botón SÍ redefine el foco, porque
   allí su relleno pasó a ser el propio acento. Ver el bloque de más abajo. */
.nvl-button--cta {
    background: var(--nvl-color-cta-yellow);
    color: var(--nvl-color-secondary);
    box-shadow: var(--nvl-shadow-cta);
}

@media (hover: hover) and (pointer: fine) {
    .nvl-button--cta:hover {
        background: var(--nvl-color-cta-yellow-hover);
        transform: translateY(-1px);
        box-shadow: var(--nvl-shadow-cta-h);
    }
}

.nvl-button--cta:active {
    background: var(--nvl-color-cta-yellow-active);
    transform: scale(0.98);
}

/* EL CTA DEL HÉROE VA EN EL AZUL DEL ANTETÍTULO (owner, 2026-10-20): «el mismo
   azul que usamos en Antetítulo, sustituyendo el amarillo actual». Acotado al
   héroe por decisión suya: «Agregar al carrito» y el botón de la banda de
   cierre siguen amarillos.

   Por qué SOLO aquí, medido: sobre la banda del héroe el acento da 8.33:1 de
   forma y 8.33:1 de tinta, de sobra para un control. Sobre la tarjeta BLANCA de
   producto daría 2.14:1 de forma, por debajo del 3:1 que pide 1.4.11 — de ahí
   que «Agregar al carrito» no lo herede. (El amarillo allí da 1.42:1: ese
   emplazamiento ya incumplía y sigue pendiente; no lo arregla esta tanda.)

   La tinta encima NO se redeclara: hereda --nvl-color-secondary del bloque de
   arriba, y sobre el acento da 8.33:1.

   EL ANILLO DE FOCO ES LO QUE HAY QUE MIRAR AQUÍ. nvl-base.css redefine
   --nvl-focus-color a --nvl-color-accent dentro de .nvl-home-hero; con el
   relleno pasado a ese mismo acento, el anillo se pintaría del color del botón
   al que rodea: 1.00:1, invisible. Es la misma mina que el comentario de arriba
   documenta —ya se pisó una vez con --nvl-color-secondary— y se desactiva
   subiendo el anillo a blanco SOLO en este botón: 17.85:1 sobre la banda, y con
   el offset de 2px queda separado del relleno. El bloque de foco de
   nvl-base.css no se toca: el resto de interactivos del héroe conservan el
   anillo de acento. */
/* EL AMARILLO ES DE «AGREGAR AL CARRITO» Y DE NADA MÁS (owner, 2026-10-22):
   «Ese amarillo está reservado exclusivamente para los botones de Agregar al
   carrito dentro de las tarjetas de productos o peces… Todos los demás botones
   de llamada a la acción deben utilizar el color azul que tenemos establecido
   en el Hero.»

   Solo tres sitios imprimen .nvl-button--cta: components/add-control.php, que
   ES «Agregar al carrito» y se queda amarillo, y estos dos, que pasan al
   acento. El resto de los botones de acción del tema ya son --primary.

   LAS DOS SUPERFICIES SON DE TINTA, y eso no es casualidad: es la condición.
   El acento relleno da 8.33:1 con la tinta encima sobre la banda del héroe y
   sobre la caja de cierre, pero sobre una tarjeta BLANCA daría 2.14:1 de
   forma, por debajo del 3:1 de WCAG 1.4.11. Si algún día este par de reglas
   tiene que alcanzar un botón sobre fondo claro, el azul correcto es
   --nvl-color-primary-dark (el de --primary), no este.

   Y REDEFINEN EL ANILLO DE FOCO A BLANCO, que no es cosmético: las dos
   superficies suben --nvl-focus-color al acento para todos sus interactivos
   (nvl-base.css), así que un relleno de acento se habría comido su propio
   foco (1.00:1). Blanco sobre la tinta: 17.85:1. */
.nvl-home-hero .nvl-button--cta,
.nvl-cta-band .nvl-button--cta {
    background: var(--nvl-color-accent);
    box-shadow: var(--nvl-shadow-accent);
    --nvl-focus-color: var(--nvl-color-surface);
}

/* Los estados oscurecen el acento con la tinta oficial: el mismo color-mix de
   dos colores de la paleta que usan el hover y el :active de la cápsula, ningún
   hex nuevo. Tinta encima: 6.70:1 en hover, 5.77:1 en activo. */
@media (hover: hover) and (pointer: fine) {
    .nvl-home-hero .nvl-button--cta:hover,
    .nvl-cta-band .nvl-button--cta:hover {
        background: color-mix(in srgb, var(--nvl-color-accent) 88%, var(--nvl-color-secondary));
        box-shadow: var(--nvl-shadow-accent-h);
    }
}

.nvl-home-hero .nvl-button--cta:active,
.nvl-cta-band .nvl-button--cta:active {
    background: color-mix(in srgb, var(--nvl-color-accent) 80%, var(--nvl-color-secondary));
}

/* Variante de contorno: CTA secundario del hero de marca y de la Home.
   Sobre superficie clara usa la tinta de marca; sobre banda oscura (hero,
   CTA de cierre) pasa a blanco — ver el bloque de más abajo. */
.nvl-button--secondary {
    background: transparent;
    border-color: currentColor;
    color: var(--nvl-color-primary-dark);
}

@media (hover: hover) and (pointer: fine) {
    .nvl-button--secondary:hover {
        background: var(--nvl-color-section-alt);
    }
}

/* El acuse del pulsado NO puede ser solo el scale: nvl-components.css lo anula
   bajo prefers-reduced-motion y el botón se quedaba sin ninguna señal. Es el
   único de los tres que no tenía respaldo de color. */
.nvl-button--secondary:active {
    background: var(--nvl-color-border);
    transform: scale(0.98);
}

/* Los tres bloques que adaptaban --secondary a las bandas oscuras se retiraron
   el 2026-10-20: el héroe perdió su botón secundario (owner) y la banda de
   cierre nunca tuvo uno —imprime un --cta—, así que no les quedaba ni un
   emplazamiento. La variante sigue viva sobre superficie clara: páginas
   legales, confirmación del pedido y página de transferencia. */

/* --- Stepper de cantidad --------------------------------------------------
   Un control, dos superficies: el bloque de agregado (tarjeta del catálogo,
   parrilla de la Home y ficha) y la línea del carrito (panel lateral y modal).
   Misma anatomía y mismos estados en UNA sola definición: estaban duplicados
   en nvl-product-card.css y nvl-order.css, y la duplicación ya había divergido
   —la línea del carrito usaba `2px solid var(--nvl-color-border-hover)`, un
   literal sin token y 1.48:1 sobre blanco, por debajo del 3:1 que WCAG 1.4.11
   exige al límite de un control—.

   RECTÁNGULO AGRUPADO (owner, 2026-09-22, adaptación de la referencia): el
   borde, el radio y el fondo se MUDARON del botón al grupo. Los tres círculos
   sueltos pasan a ser un solo control segmentado —«−», cifra y «+»— con las
   esquinas redondeadas. Los botones pierden su cáscara, NO su caja: siguen
   midiendo --nvl-tap-min en los dos ejes.

   EL HOVER SIGUE SIN SER UN BLOQUE DE COLOR. Antes pintaba el fondo de Sky 700
   e invertía el glifo a blanco, y ese bloque de color no pertenece al lenguaje
   visual del sitio; esa prohibición sigue en pie y no vuelve ningún azul. Lo
   que entra ahora es --nvl-color-background, el gris NEUTRO de página que el
   propio :active ya usaba como señal portante: dentro de un segmento sin borde
   propio no hay nada que entintar, y una sombra la recortaría el overflow del
   grupo.

   El grupo mide 47px de alto (44 de los botones + 2×1.5 de borde), que es
   exactamente lo que mide el botón de compra a su lado: quedan alineados en el
   renglón de la ficha sin una sola declaración extra.

   El foco NO se redefine aquí: hereda el anillo global de nvl-base.css. Es un
   outline con separación, no un relleno, y solo aparece con teclado.

   Va en esta hoja porque está aguas arriba de nvl-product-card y nvl-order en
   la cadena de inc/assets.php: si alguna de las dos volviera a declarar estos
   selectores, ganaría por orden y el azul volvería. */

/* El GRUPO es el control. overflow: hidden para que el relleno de los segmentos
   de los extremos respete el radio.

   BORDE HAIRLINE (owner, 2026-09-22): se midió el control de la referencia y su
   límite es `1px solid #e6e7eb` — una hairline clara, no un trazo de control de
   formulario. Nosotros veníamos de 1.5px sobre Slate 500 (#64748b), que es casi
   cuatro veces más contrastado y es lo que hacía que el control pesara más que
   todo lo que tiene alrededor. (Ese tono tuvo token propio —--nvl-color-border-
   strong— hasta que los campos del checkout hicieron el mismo camino el
   2026-10-14 y se quedó sin un solo uso.)
   --nvl-color-border (#e2e8f0) es el token existente más cercano al de la
   referencia; no se crea ninguno nuevo.

   El límite sigue cumpliendo WCAG 1.4.11 sin depender de este borde: el control
   real son los dos botones, cuyo glifo va en tinta primaria (14.63:1), y el
   estado de foco lo da el anillo global, no el contorno. */
/* EL BOTÓN DE AGREGAR, EN CÁPSULA (owner, 2026-10-16). Pidió border-radius de
   50px en los botones de agregar al carrito; el token --nvl-radius-pill ya
   existe y su propio comentario dice que «sustituye 99px y 50px», así que la
   cifra entra por ahí y no como literal.

   SOLO EL DE AGREGAR, no .nvl-button--cta: esa clase la llevan también el botón
   del héroe de la Home y el de la banda de contacto, y el encargo era para los
   de agregar productos. .nvl-add-control__button la pone components/add-control.php
   a todos ellos por construcción —tarjeta y ficha—, así que una regla basta.

   El radio base de .nvl-button (--nvl-radius-md) se conserva para el resto; esta
   solo lo pisa aquí, y por especificidad de clase, sin !important.

   .nvl-product-card__add ENTRA EN LA MISMA REGLA (owner, 2026-11-05). Desde que
   un producto con tallas enseña un ENLACE «Elegir talla» en vez del control de
   agregado, la llamada a la acción de la tarjeta no siempre lleva
   .nvl-add-control__button — y sin ella le quedaba el radio base, que es el
   defecto que el owner vio: dos botones vecinos con esquinas distintas. El
   texto puede cambiar según el producto; la forma, no.

   Va aquí, en la misma declaración, y no en una regla suelta de la hoja de la
   tarjeta: la cápsula es UNA decisión y se toma en un solo sitio. */
.nvl-add-control__button,
.nvl-product-card__add {
    border-radius: var(--nvl-radius-pill);
}

.nvl-add-control__stepper,
.nvl-cart-line__controls {
    display: inline-flex;
    align-items: center;
    gap: 0;
    border: var(--nvl-border-hairline) solid var(--nvl-color-border);
    border-radius: var(--nvl-radius-md);
    background: var(--nvl-color-surface);
    overflow: hidden;
}

/* Es hija de la rejilla de la línea del carrito: sin esto el rectángulo se
   estiraría a toda la columna en vez de medir lo que mide el control. */
.nvl-cart-line__controls {
    justify-self: start;
}

/* La cifra, entre los dos objetivos táctiles.

   SIN DIVISIONES (owner, 2026-09-22). Aquí había dos hairlines verticales
   argumentando que separaban los objetivos táctiles. El argumento era falso en
   la práctica: lo que separaba los segmentos era el `gap` de 8px que
   nvl-product-card.css pisaba por encima (ya retirado), y con las dos cosas
   juntas el control se leía despiezado en tres cajas. La referencia no divide.
   Los objetivos siguen midiendo 44×44 y el «−»/«+» los anuncia por aria-label:
   la separación la da la posición, no una línea.

   Se retira también el padding-inline: sin líneas que respetar solo engordaba
   el rectángulo (135px contra los 96 de la referencia). */
.nvl-add-control__stepper .nvl-add-control__value,
.nvl-cart-line__controls .nvl-cart-line__qty {
    display: flex;
    align-items: center;
    justify-content: center;
    align-self: stretch;
}

.nvl-add-control__step,
.nvl-cart-line__step {
    width: var(--nvl-tap-min);
    height: var(--nvl-tap-min);
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 0;
    background: transparent;
    color: var(--nvl-color-text-primary);
    box-shadow: none;
    cursor: pointer;
    /* 120ms y no 200: en un objetivo de 44px el puntero entra y sale rápido, y
       a 200ms el entintado se arrastra por detrás del cursor. */
    transition:
        background-color var(--nvl-duration-fast) var(--nvl-ease-standard),
        color            var(--nvl-duration-fast) var(--nvl-ease-standard),
        border-color     var(--nvl-duration-fast) var(--nvl-ease-standard),
        box-shadow       var(--nvl-duration-fast) var(--nvl-ease-standard);
}

/* SIN HOVER (owner, 2026-09-22). Aquí el − y el + se entintaban de gris al
   pasar el puntero. El control de la referencia no tiene NINGUNA regla de
   hover: solo `cursor: pointer`. Se retira el relleno y no se sustituye por
   otro acuse, que es justo lo que se pidió — «no quiero que agreguemos un
   efecto que cambie demasiado su apariencia».

   No deja el control mudo: el `cursor: pointer` sigue anunciando que es
   pulsable, el foco de teclado conserva su anillo y el PULSADO sí acusa (el
   bloque de :active, abajo). El hover era el único de los tres que la
   referencia no tiene.

   La guarda `:not(:disabled):not([aria-disabled])` que vivía aquí sigue siendo
   necesaria en :active por el mismo motivo por el que se escribió: :disabled es
   el AGOTADO, que decide el servidor, y [aria-disabled] el TOPE DE EXISTENCIAS
   (owner, 2026-09-20), que deja el botón vivo a propósito para explicar cuántas
   unidades quedan. Ninguno de los dos debe responder al puntero. */

/* El pulsado hunde la pieza al gris de página. Ese cambio de fondo es PORTANTE,
   no decorativo: bajo prefers-reduced-motion el scale desaparece y es el único
   acuse que queda — y desde que el hover se retiró (owner, 2026-09-22) es
   también el único acuse de puntero que tiene el control. Por eso :active no se
   toca.

   El acuse se muda AL GLIFO. Escalar el segmento entero lo despegaría del filo
   del grupo y se vería como una pieza suelta; escalar el icono conserva la
   misma percepción, sobrevive al overflow del grupo y no toca layout. */
.nvl-add-control__step:active:not(:disabled):not([aria-disabled="true"]),
.nvl-cart-line__step:active:not(:disabled):not([aria-disabled="true"]) {
    background: var(--nvl-color-background);
    color: var(--nvl-color-secondary);
}

.nvl-add-control__step:active:not(:disabled):not([aria-disabled="true"]) .nvl-icon,
.nvl-cart-line__step:active:not(:disabled):not([aria-disabled="true"]) .nvl-icon {
    transform: scale(0.94);
}

/* Anillo por DENTRO: fuera caería sobre el segmento vecino y el overflow del
   grupo lo recortaría. */
.nvl-add-control__step:focus-visible,
.nvl-cart-line__step:focus-visible {
    outline-offset: calc(var(--nvl-focus-width) * -1);
}

/* Mismo aspecto para los dos: la diferencia entre agotado y tope de
   existencias es funcional (uno no responde, el otro explica), no cromática.
   Sin opacity, igual que el resto del sistema. Ya no hay border-color que
   cambiar —el borde vive en el grupo—: el segmento apagado se distingue por su
   relleno gris contra el blanco del rectángulo, que se lee mejor que antes. */
.nvl-add-control__step:disabled,
.nvl-cart-line__step:disabled,
.nvl-add-control__step[aria-disabled="true"],
.nvl-cart-line__step[aria-disabled="true"] {
    background: var(--nvl-color-background);
    color: var(--nvl-color-text-secondary);
    box-shadow: none;
    cursor: not-allowed;
}

/* Colores forzados: el sistema reescribe fondo, borde y texto y DESCARTA
   box-shadow, así que el borde del grupo es lo único que sobrevive para
   sostener la forma del control. Los system colors sí se respetan. */
@media (forced-colors: active) {
    /* Solo el grupo: las divisiones internas se retiraron (owner, 2026-09-22) y
       con ellas la regla que las forzaba a CanvasText. */
    .nvl-add-control__stepper,
    .nvl-cart-line__controls {
        border-color: CanvasText;
    }

    .nvl-add-control__step:disabled,
    .nvl-cart-line__step:disabled,
    .nvl-add-control__step[aria-disabled="true"],
    .nvl-cart-line__step[aria-disabled="true"] {
        color: GrayText;
    }
}

/* Deshabilitado real (atributo) y deshabilitado lógico (clase de estado).
   Sin opacity: el texto debe seguir siendo legible (6.95:1), no atenuado. */
.nvl-button:disabled,
.nvl-button.nvl-is-disabled {
    background: var(--nvl-color-background);
    color: var(--nvl-color-text-secondary);
    border-color: var(--nvl-color-border);
    box-shadow: none;
    cursor: not-allowed;
    transform: none;
    pointer-events: none;
}

/* El botón de revisión del carrito SÍ recibe clics aun "deshabilitado":
   el JS de la Fase 8 los usa para explicar qué falta (patrón validado en
   producción; con `disabled` real el clic nunca llegaría). */
/* Lo mismo en el botón de la página de checkout (owner, 2026-10-09): con
   `pointer-events: none` el clic nunca llegaba al JS y el cliente que pulsaba
   «Realizar pedido» con campos vacíos no veía ningún mensaje (el teclado sí
   funcionaba, por eso no saltó en las pruebas con jsdom). */
.nvl-cart-summary__review.nvl-is-disabled,
.nvl-checkout-bar__confirm.nvl-is-disabled {
    pointer-events: auto;
}
