/*
 * Gestor Comercial — componentes de interfaz.
 * Lineamiento Gráfico §7.
 *
 * Los componentes transversales del sistema de diseño viven acá completos:
 * marca, íconos, botones, campos, tarjetas, mensajes, chips, tablas, filtros,
 * paginación, pestañas, modales, hojas inferiores, toasts, muestras de material
 * y barras de progreso.
 *
 * Los patrones de pantalla del Lineamiento §8 —desglose de precio,
 * configurador, aprobación parcial, Smart Panel— NO están acá: cada uno se
 * construye en el paso del plan que trae su funcionalidad, apoyado en estas
 * piezas. Ponerlos ahora sería dibujar una interfaz sin datos detrás.
 *
 * Ningún color se escribe en este archivo: todos vienen de `tokens.css`.
 */

/* ── Íconos (Lineamiento §9) ────────────────────────────────────────────── */
/*
 * Un único set lineal, servido como sprite desde `assets/brand/icons.svg`:
 *
 *   <svg class="icon" aria-hidden="true"><use href="…/icons.svg#i-home"/></svg>
 *
 * El trazo se define acá y no dentro del sprite justamente para poder fijarlo
 * en los 1,75 px que pide el Lineamiento: Lucide dibuja con 2. Como el ícono
 * hereda `currentColor`, toma el color del texto que acompaña y nunca hace
 * falta pintarlo aparte.
 */

.icon {
    width: 20px;
    height: 20px;
    flex: none;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.75;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.icon--sm { width: 16px; height: 16px; }   /* dentro de chips */
.icon--lg { width: 24px; height: 24px; }   /* navegación móvil */

/* ── Marca (Lineamiento §2) ─────────────────────────────────────────────── */
/*
 * El logotipo se sirve como SVG desde `assets/brand/`, generado a partir del
 * arte vectorial original del cliente. El alto lo fija el contenedor; el ancho
 * lo resuelve el propio SVG por su `viewBox`.
 *
 * Área de seguridad: el manual la fija en X = 25 pt proporcionales, que en
 * pantalla es un margen libre igual a la altura de la «M» del logotipo en los
 * cuatro lados. De ahí el padding, que además es lo que impide que el logo
 * quede pegado al buscador de la barra superior.
 */

.brand-logo {
    display: inline-flex;
    align-items: center;
    padding: var(--sp-3) var(--sp-4);
}

.brand-logo__image {
    display: block;
    height: 32px;
    width: auto;
}

/*
 * El Lineamiento §2.2 fija 200 px para el ingreso. A ese ancho la bajada
 * «— since 1960 —» del arte original queda por debajo del tamaño en que se lee
 * y aparece como una mancha gris; 240 px es lo mínimo que la deja legible. Es
 * una desviación deliberada del número del Lineamiento, tomada por el mismo
 * criterio que lo ordena: primero que se lea. Vale igual para el ingreso y para
 * las pantallas de error, que muestran el mismo bloque.
 */
.brand-logo--login .brand-logo__image {
    height: auto;
    width: 240px;
}

/*
 * La variante apilada mide el doble de alto que la horizontal, así que fijarle
 * 32 px de alto la dejaba en unos 60 px de ancho: por debajo del mínimo de
 * 132 px del Lineamiento §2.2, con el logotipo y la bajada superpuestos e
 * ilegibles. Se le fija el ancho —160 px, con holgura sobre el mínimo— y el
 * alto sale de la proporción del archivo.
 */
.brand-logo--stacked .brand-logo__image {
    height: auto;
    width: 160px;
}

/* El monograma solo aparece en la barra superior angosta; el logotipo
 * horizontal, solo cuando hay ancho para respetar su área de seguridad. */
.brand-logo--responsive .brand-logo__image--mono {
    display: none;
    width: 32px;
    height: 32px;
}

@media (max-width: 767px) {
    .brand-logo--responsive {
        padding: var(--sp-2) var(--sp-3);
    }

    .brand-logo--responsive .brand-logo__image--wide {
        display: none;
    }

    .brand-logo--responsive .brand-logo__image--mono {
        display: block;
    }
}

/*
 * Lockup tipográfico de reserva (Lineamiento §2.2).
 *
 * Ya tenemos los vectoriales, así que dejó de ser la marca por defecto. Se
 * conserva por dos motivos concretos: es lo que se muestra si el SVG no carga,
 * y es lo que se usará si el cliente pide una variante que el arte recibido no
 * trae. Respeta la estructura del logotipo real.
 */

.mys-lockup {
    display: inline-flex;
    flex-direction: column;
    line-height: 1.15;
    padding: var(--sp-3) var(--sp-4);
    min-width: 132px;
}

.mys-lockup__wordmark {
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--n-900);
    font-size: 20px;
}

.mys-lockup__tld {
    font-weight: 300;
    color: var(--brand-yellow);
}

.mys-lockup__since {
    font-weight: 300;
    font-size: 0.34em;
    letter-spacing: 0.18em;
    text-align: center;
    color: var(--brand-taupe);
}

.mys-lockup--negative .mys-lockup__wordmark { color: var(--n-000); }
.mys-lockup--negative .mys-lockup__since { color: var(--brand-sand); }

.mys-lockup--large .mys-lockup__wordmark { font-size: 32px; }

/* Por debajo de 132 px de ancho la bajada deja de leerse y se oculta, como
 * indica el Lineamiento §2.2. */
@media (max-width: 400px) {
    .mys-lockup__since { display: none; }
}

/* Monograma cuadrado para la barra superior colapsada. El favicon y el ícono de
 * aplicación usan el archivo SVG equivalente, no esta clase. */
.mys-monogram {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 32px;
    height: 32px;
    border-radius: var(--r-md);
    background-color: var(--n-900);
    color: var(--brand-yellow);
    font-weight: 700;
    font-size: 15px;
}

/* ── Botones (Lineamiento §7) ───────────────────────────────────────────── */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--sp-2);
    /* El alto lo manda la densidad: 44 px en cómoda, 34 px en compacta
     * (Lineamiento §5). No se escribe el número acá. */
    height: var(--control-h);
    padding: 0 20px;
    border: 1px solid transparent;
    border-radius: var(--r-md);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    cursor: pointer;
    text-decoration: none;
    transition: background-color .12s ease;
}

.button--primary {
    background-color: var(--brand-yellow);
    color: var(--n-900);
}

.button--primary:hover,
.button--primary:active {
    background-color: var(--brand-yellow-dark);
}

.button--secondary {
    background-color: var(--n-000);
    color: var(--n-900);
    border-color: var(--n-300);
}

.button--secondary:hover {
    background-color: var(--n-100);
}

.button--text {
    background: none;
    border: none;
    color: var(--n-700);
    padding: 0 var(--sp-2);
}

.button--text:hover {
    text-decoration: underline;
}

/*
 * Destructivo: fuera de un modal se muestra contenido —borde y texto rojos
 * sobre blanco—, para que el rojo lleno quede reservado a la confirmación. Que
 * el botón de borrar sea idéntico en la lista y en el modal invita a apretarlo
 * de memoria.
 */
.button--danger {
    background-color: var(--n-000);
    color: var(--err);
    border-color: var(--err);
}

.button--danger:hover {
    background-color: var(--err-bg);
}

.modal__actions .button--danger,
.button--danger-confirm {
    background-color: var(--err);
    border-color: var(--err);
    color: var(--n-000);
}

/* Botón de solo ícono: cuadrado y con el nombre de la acción en `aria-label`,
 * nunca sin nombre accesible. */
.button--icon {
    width: var(--control-h);
    padding: 0;
}

/*
 * Acción principal de contexto en móvil (Lineamiento §6.2). Se ubica sobre la
 * barra inferior y respeta el área segura del dispositivo, para no quedar
 * debajo del gesto de inicio.
 */
.fab {
    position: fixed;
    right: var(--sp-4);
    bottom: calc(var(--bottomnav-h) + var(--sp-4) + env(safe-area-inset-bottom, 0px));
    z-index: var(--z-bottomnav);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 56px;
    height: 56px;
    border: none;
    border-radius: var(--r-pill);
    background-color: var(--brand-yellow);
    color: var(--n-900);
    box-shadow: var(--sh-2);
    cursor: pointer;
}

.fab:hover {
    background-color: var(--brand-yellow-dark);
}

@media (min-width: 768px) {
    .fab { display: none; }
}

/* Un botón que el usuario no puede usar no se oculta: se deshabilita y se
 * explica por qué (Lineamiento §7). Esconderlo hace que nadie entienda que la
 * función existe ni a quién pedírsela. */
.button:disabled,
.button[aria-disabled="true"] {
    background-color: var(--n-200);
    border-color: var(--n-200);
    color: var(--n-400);
    cursor: not-allowed;
}

.button__reason {
    display: block;
    font-size: 12px;
    line-height: 16px;
    color: var(--n-500);
    margin-top: var(--sp-1);
}

/* ── Campos de formulario ───────────────────────────────────────────────── */

.field {
    display: block;
    margin-bottom: var(--sp-4);
}

.field__label {
    display: block;
    font-size: 13px;
    line-height: 18px;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--n-700);
    margin-bottom: var(--sp-1);
}

.field__required {
    color: var(--err);
}

.field__control {
    display: block;
    width: 100%;
    height: var(--control-h);
    padding: 0 var(--sp-3);
    border: 1px solid var(--n-300);
    border-radius: var(--r-md);
    background-color: var(--n-000);
    color: var(--n-900);
    font-family: inherit;
    font-size: 15px;
}

.field__control::placeholder {
    color: var(--n-400);
}

/* Reemplazo equivalente del contorno, no su eliminación: el borde cambia de
 * color y aparece el halo (Lineamiento §10). */
.field__control:focus {
    outline: none;
    border-color: var(--focus);
    box-shadow: var(--focus-ring);
}

.field__control--invalid {
    border-color: var(--err);
}

/* ── Campo de archivo ───────────────────────────────────────────────────── */
/*
 * El control nativo queda escondido pero presente: la etiqueta hace de botón y
 * el nombre del archivo se escribe al lado. El anillo de foco se traslada al
 * botón, porque el control real no se ve y sin esto el foco desaparecería
 * (Lineamiento §10).
 */
.file-field {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    flex-wrap: wrap;
}

.file-field__button {
    cursor: pointer;
}

.file-field__input:focus-visible + .file-field__button {
    outline: 3px solid var(--focus);
    outline-offset: 2px;
}

.file-field__name {
    color: var(--n-500);
    font-size: 15px;
    overflow-wrap: anywhere;
}

/* Un área de texto crece; no puede tener el alto de un control de una línea. */
textarea.field__control {
    height: auto;
    min-height: calc(var(--control-h) * 2);
    padding: var(--sp-3);
    line-height: 22px;
    resize: vertical;
}

.field__hint {
    display: block;
    margin-top: var(--sp-1);
    font-size: 12px;
    line-height: 16px;
    color: var(--n-500);
}

/* El error nunca se comunica solo con color: lleva icono y texto. */
.field__error {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-1);
    margin-top: var(--sp-1);
    font-size: 12px;
    line-height: 16px;
    color: var(--err);
}

.field--inline {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-bottom: var(--sp-4);
}

.field--inline .field__label {
    margin-bottom: 0;
}

/* ── Tarjetas ───────────────────────────────────────────────────────────── */

.card {
    background-color: var(--n-000);
    border: 1px solid var(--n-200);
    border-radius: var(--r-lg);
    padding: var(--sp-4);
}

@media (min-width: 768px) {
    .card {
        padding: var(--sp-5);
    }
}

.card__title {
    margin-bottom: var(--sp-3);
}

/*
 * Subtítulo dentro de una tarjeta que ya tiene título: separa un segundo bloque
 * —«Cambiar el estado» debajo de la ficha del presupuesto— sin abrir otra
 * tarjeta. Lleva aire arriba, que es lo único que lo distingue del título.
 */
.card__subtitle {
    margin-top: var(--sp-5);
    margin-bottom: var(--sp-3);
}

/*
 * Tarjeta destacada: meta cumplida, campaña vigente. Borde arena y una franja
 * superior de 3 px en amarillo (Lineamiento §7). La franja se dibuja con
 * `border-top` y no con un elemento aparte para que no haya nada que el lector
 * de pantalla tenga que ignorar.
 */
.card--featured {
    border-color: var(--brand-sand);
    border-top: 3px solid var(--brand-yellow);
}

/* ── Ofrecimiento de instalar la aplicación ─────────────────────────────────
 *
 * La tarjeta viaja siempre en el HTML con `hidden` y la revela `pwa.js`. Dentro
 * hay dos caminos excluyentes y es el atributo `data-install-mode` el que elige:
 * `prompt` para el navegador que sabe instalar solo, `ios` para Safari, que no
 * puede y necesita que le expliquen los tres pasos. Se resuelve con CSS y no
 * escribiendo HTML desde JavaScript: así los dos caminos se pueden leer en la
 * plantilla y traducir sin tocar el script.
 */
.install-card__steps {
    display: none;
    margin: var(--sp-3) 0 0;
    padding-left: var(--sp-5);
}

.install-card[data-install-mode="ios"] .install-card__prompt {
    display: none;
}

.install-card[data-install-mode="ios"] .install-card__steps {
    display: block;
}

.install-card__steps li + li {
    margin-top: var(--sp-1);
}

/* ── Mensajes de estado ─────────────────────────────────────────────────── */

/*
 * Bloque, no caja flexible. Un aviso casi siempre es un párrafo con algo
 * resaltado adentro, y en `display: flex` cada trozo de texto suelto y cada
 * <strong> se convierten en un ítem distinto: el mensaje sale partido en
 * columnas y deja de leerse como una oración.
 */
.notice {
    display: block;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-md);
    border-left: 3px solid transparent;
    margin-bottom: var(--sp-4);
    font-size: 14px;
    line-height: 20px;
}

.notice--success {
    background-color: var(--ok-bg);
    border-left-color: var(--ok);
    color: var(--ok);
}

.notice--error {
    background-color: var(--err-bg);
    border-left-color: var(--err);
    color: var(--err);
}

.notice--info {
    background-color: var(--info-bg);
    border-left-color: var(--info);
    color: var(--info);
}

/*
 * Advertencia: algo quedó a medio hacer y va a dar un resultado raro, pero
 * nada falló todavía. No es un error —no hay nada que corregir para que la
 * pantalla ande— y por eso no usa el rojo, que en este sistema significa «esto
 * no se pudo hacer».
 */
.notice--warn {
    background-color: var(--warn-bg);
    border-left-color: var(--warn);
    color: var(--warn);
}

/* ── Chip ───────────────────────────────────────────────────────────────── */

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    height: 32px;
    padding: 0 var(--sp-3);
    border: 1px solid var(--n-300);
    border-radius: var(--r-md);
    background-color: var(--n-000);
    color: var(--n-900);
    font-size: 13px;
    font-weight: 600;
}

/*
 * El Lineamiento (§7) fija 24 px de alto y una sola línea, y acá se cumplen las
 * dos cosas: `white-space: nowrap`. Un chip partido en dos o en cuatro líneas
 * deja de ser un chip —la cápsula se estira y se lee como un error de dibujo—,
 * así que la solución no es dejarlo crecer sino no meter en un chip una
 * etiqueta que no entra: quien tenga textos largos usa Overline sin cápsula
 * (ver `.audit-action`) o ensancha su columna.
 */
.chip--tag {
    display: inline-flex;
    align-items: center;
    height: 24px;
    padding: 0 10px;
    border-radius: var(--r-pill);
    white-space: nowrap;
    text-align: center;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .10em;
    text-transform: uppercase;
    background-color: var(--n-100);
    border-color: var(--n-200);
    color: var(--n-500);
}

/*
 * Acción de auditoría demasiado larga para una cápsula. Mismo tamaño y peso que
 * el chip, en texto terciario y sin fondo: la columna sigue siendo legible de un
 * vistazo y ninguna píldora queda deformada.
 */
.audit-action {
    display: inline-block;
    color: var(--n-500);
}

/* El identificador de pedido va recortado a sus últimos ocho caracteres y no se
 * parte: el valor completo está en el `title`. */
/*
 * Última columna de la auditoría: la dirección IP arriba y el identificador de
 * la petición abajo.
 *
 * No es una columna de importes, así que no va alineada a la derecha ni en el
 * cuerpo de 14 px de la tabla: la IP va en el tipo de datos a 12 px, que es lo
 * que la vuelve comparable de un renglón a otro, y el identificador debajo en
 * Caption, dicho con todas las letras. Antes se leía «…XVPENNAK» a secas y no
 * había forma de saber qué era ese texto.
 */
.audit-origin__ip {
    display: block;
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-size: 12px;
    line-height: 16px;
    color: var(--n-700);
    white-space: nowrap;
}

.audit-request {
    white-space: nowrap;
}

/* Regla de alcance de una campaña: el modo arriba, el destino abajo. */
.scope-rule {
    display: block;
}

.scope-rule + .scope-rule {
    margin-top: var(--sp-2);
}

.scope-rule .overline {
    display: block;
    color: var(--n-500);
}

.scope-rule .caption {
    display: block;
    color: var(--n-700);
}

/*
 * Columna «Cómo se ve» de la configuración de estados. Lleva ancho mínimo
 * propio porque el chip que muestra ya no se parte en dos líneas: sin reservarle
 * lugar, «APROBADO PARCIALMENTE» empujaría al resto de la tabla.
 */
.status-preview-column {
    min-width: 180px;
}

/* ── Chips de estado semánticos (Lineamiento §7) ────────────────────────── */
/*
 * El color refuerza, nunca reemplaza a la palabra: el punto y el fondo
 * acompañan a un texto que ya dice de qué se trata. Por eso el chip no tiene
 * variante "solo color".
 */

.chip--info { background-color: var(--info-bg); border-color: var(--info-bg); color: var(--info); }
.chip--ok { background-color: var(--ok-bg); border-color: var(--ok-bg); color: var(--ok); }
.chip--warn { background-color: var(--warn-bg); border-color: var(--warn-bg); color: var(--warn); }
.chip--err { background-color: var(--err-bg); border-color: var(--err-bg); color: var(--err); }

/* Punto de 6 px del color semántico, a la izquierda de la palabra. Hereda
 * `currentColor`, así que sirve para cualquiera de los tonos sin declarar una
 * regla por variante. */
.chip__dot {
    width: 6px;
    height: 6px;
    flex: none;
    border-radius: var(--r-pill);
    background-color: currentColor;
}

/* Punto semirrelleno para «Aprobado parcialmente»: la forma distingue el estado
 * intermedio del aprobado pleno incluso en blanco y negro. */
.chip__dot--half {
    background: linear-gradient(90deg, currentColor 50%, transparent 50%);
    box-shadow: inset 0 0 0 1px currentColor;
}

/* Borde del propio color: lo piden «Aprobado parcialmente», «Vencido» y
 * «Cancelado», que necesitan distinguirse de un estado de la misma familia. */
.chip--outline { border-color: currentColor; }

/* Estado pleno: fondo sólido y texto blanco. Reservado a los estados
 * terminales favorables —«Aprobado», «Entregado»—, que son los únicos que
 * conviene que salten a la vista en una lista larga. */
.chip--ok-solid {
    background-color: var(--ok);
    border-color: var(--ok);
    color: var(--n-000);
}

/*
 * Paleta cerrada para los estados que crea el administrador (Lineamiento §7).
 * Elige uno de estos ocho tonos y no un color libre: así ningún estado
 * inventado rompe el contraste. Cada clase se genera desde el mismo par de
 * variables, de modo que agregar un noveno tono es agregarlo en `tokens.css`.
 */
.chip--state-1 { background-color: var(--state-1-bg); border-color: var(--state-1-bg); color: var(--state-1); }
.chip--state-2 { background-color: var(--state-2-bg); border-color: var(--state-2-bg); color: var(--state-2); }
.chip--state-3 { background-color: var(--state-3-bg); border-color: var(--state-3-bg); color: var(--state-3); }
.chip--state-4 { background-color: var(--state-4-bg); border-color: var(--state-4-bg); color: var(--state-4); }
.chip--state-5 { background-color: var(--state-5-bg); border-color: var(--state-5-bg); color: var(--state-5); }
.chip--state-6 { background-color: var(--state-6-bg); border-color: var(--state-6-bg); color: var(--state-6); }
.chip--state-7 { background-color: var(--state-7-bg); border-color: var(--state-7-bg); color: var(--state-7); }
.chip--state-8 { background-color: var(--state-8-bg); border-color: var(--state-8-bg); color: var(--state-8); }

/*
 * Variante plena de cada tono, para los estados terminales favorables
 * —«Aprobado», «Entregado»— que el Lineamiento §7 pide que salten a la vista en
 * una lista larga. Va después de `.chip--ok-solid` y con dos clases de
 * especificidad, así que gana sobre el fondo suave del mismo tono sin depender
 * del orden en que la plantilla las escriba.
 *
 * Los ocho pares texto/fondo dan 4.6:1 o más con blanco encima, así que
 * cualquier estado que el administrador cree en cualquier tono cumple el
 * contraste mínimo del Lineamiento §10 sin que él tenga que verificar nada.
 */
.chip--state-1.chip--solid { background-color: var(--state-1); border-color: var(--state-1); color: var(--n-000); }
.chip--state-2.chip--solid { background-color: var(--state-2); border-color: var(--state-2); color: var(--n-000); }
.chip--state-3.chip--solid { background-color: var(--state-3); border-color: var(--state-3); color: var(--n-000); }
.chip--state-4.chip--solid { background-color: var(--state-4); border-color: var(--state-4); color: var(--n-000); }
.chip--state-5.chip--solid { background-color: var(--state-5); border-color: var(--state-5); color: var(--n-000); }
.chip--state-6.chip--solid { background-color: var(--state-6); border-color: var(--state-6); color: var(--n-000); }
.chip--state-7.chip--solid { background-color: var(--state-7); border-color: var(--state-7); color: var(--n-000); }
.chip--state-8.chip--solid { background-color: var(--state-8); border-color: var(--state-8); color: var(--n-000); }

/*
 * Estado desactivado, solo en la pantalla de configuración: conserva su color
 * para que el administrador lo reconozca, atenuado para que se lea que ya no se
 * ofrece. Nunca va solo: la fila dice «Inactivo» con todas las letras, porque el
 * color por sí mismo no comunica un estado (Lineamiento §10). La atenuación es
 * suave a propósito —el texto sigue por encima de 4.5:1 sobre el fondo blanco de
 * la fila— así que no es un elemento deshabilitado, es uno en reposo.
 */
.chip--muted { opacity: 0.7; }

/*
 * Tono neutro con nombre propio, para los estados que no dicen ni bien ni mal.
 * Hoy lo usa el pedido a la fábrica que el vendedor dio de baja: no es un error
 * —nadie hizo nada mal— ni una advertencia —no hay nada que atender—, y pintarlo
 * de cualquiera de los dos colores semánticos haría que la bandeja pareciera
 * tener un problema que no tiene.
 *
 * Es el par `--n-500` sobre `--n-100` que el Lineamiento §7 asigna a
 * «Cancelado», escrito una sola vez.
 */
.chip--neutral {
    background-color: var(--n-100);
    border-color: var(--n-300);
    color: var(--n-500);
}

/* ── Tablas de datos (Lineamiento §7) ───────────────────────────────────── */
/*
 * Encabezado Overline sobre --n-100 y pegado al hacer scroll, filas separadas
 * por borde y sin franjas alternas. El realce al pasar el puntero ayuda a
 * seguir la fila de punta a punta en pantallas anchas.
 */

.data-table__wrap {
    background-color: var(--n-000);
    border: 1px solid var(--n-200);
    border-radius: var(--r-lg);
    overflow: hidden;
}

.data-table {
    width: 100%;
    border-collapse: collapse;
    font-family: var(--font-data);
    font-size: 14px;
    line-height: 20px;
}

.data-table th {
    position: sticky;
    top: 0;
    z-index: 1;
    background-color: var(--n-100);
    border-bottom: 1px solid var(--n-300);
    padding: var(--sp-3) var(--sp-4);
    text-align: left;
    color: var(--n-500);
    font-family: var(--font-ui);
    font-size: 11px;
    line-height: 14px;
    font-weight: 700;
    letter-spacing: .10em;
    text-transform: uppercase;
    white-space: nowrap;
}

.data-table td {
    border-bottom: 1px solid var(--n-200);
    padding: var(--sp-3) var(--sp-4);
    vertical-align: top;
    color: var(--n-700);
}

/*
 * Encabezado de fila (`<th scope="row">`): es la celda que nombra la fila —el
 * cliente, el producto— y para un lector de pantalla vale como encabezado. Lo
 * que no puede es verse como el encabezado de columna: en mayúsculas de 11 px y
 * sobre fondo gris, el dato principal de la fila quedaría ilegible. Se lo
 * devuelve al aspecto de una celda común, con un poco más de peso porque es el
 * dato por el que se busca la fila con la vista.
 */
.data-table tbody th {
    position: static;
    z-index: auto;
    background-color: transparent;
    border-bottom: 1px solid var(--n-200);
    padding: var(--sp-3) var(--sp-4);
    vertical-align: top;
    color: var(--n-900);
    font-family: var(--font-data);
    font-size: 14px;
    line-height: 20px;
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    white-space: normal;
}

.data-table tbody tr:last-child td,
.data-table tbody tr:last-child th {
    border-bottom: 0;
}

.data-table tbody tr:hover {
    background-color: var(--row-hover);
}

.data-table__empty {
    padding: var(--sp-6) var(--sp-4);
    text-align: center;
    color: var(--n-500);
}

/*
 * En móvil la tabla no se desplaza en horizontal: cada fila se apila como una
 * tarjeta con etiqueta y valor (Lineamiento §7). La etiqueta sale del atributo
 * `data-label` de cada celda, así que no hay que repetir el texto en el HTML.
 */
@media (max-width: 767px) {
    .data-table thead {
        display: none;
    }

    .data-table,
    .data-table tbody,
    .data-table tr,
    .data-table td,
    .data-table tbody th {
        display: block;
        width: 100%;
    }

    .data-table tr {
        border-bottom: 1px solid var(--n-200);
        padding: var(--sp-2) 0;
    }

    .data-table td,
    .data-table tbody th {
        display: grid;
        grid-template-columns: 40% 1fr;
        gap: var(--sp-3);
        border-bottom: 0;
        padding: var(--sp-2) var(--sp-4);
        text-align: left;
    }

    /*
     * El renglón de «no hay resultados» no es un dato con etiqueta: es un
     * mensaje. Apilado como el resto quedaba corrido al 60 % del ancho, con una
     * columna de etiqueta vacía a la izquierda. Ocupa la tarjeta entera.
     */
    .data-table td.data-table__empty {
        display: block;
        text-align: center;
    }

    .data-table td.data-table__empty::before {
        content: none;
    }

    .data-table td::before,
    .data-table tbody th::before {
        content: attr(data-label);
        color: var(--n-500);
        font-family: var(--font-ui);
        font-size: 11px;
        line-height: 20px;
        font-weight: 700;
        letter-spacing: .10em;
        text-transform: uppercase;
    }
}

/* ── Barra de filtros ───────────────────────────────────────────────────── */

/*
 * Grilla y no flex: con flex, cada fila reparte el sobrante entre los campos que
 * le tocaron y las columnas terminan de anchos distintos fila por fila. Con
 * nueve filtros —los cinco de la Especificación §23 más familia, producto,
 * año y período— eso se ve como una escalera. `auto-fit` mantiene la columna
 * quieta y `align-items: end` deja todos los controles apoyados en la misma
 * línea aunque uno tenga texto de ayuda debajo de la etiqueta.
 */
/*
 * Cuatro columnas y no seis. La medida no es estética: con 180 px de mínimo, en
 * una pantalla de 1440 entraban seis desplegables por fila y el navegador
 * recortaba el texto de la opción elegida —«Todos los vendedo», «Todas las
 * campañ»—. Un filtro cuyo valor no se puede leer no es un filtro. Con 220 px
 * de mínimo y un espacio de 24 px, el valor por omisión de cada filtro entra
 * entero.
 */
.filter-bar {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    align-items: start;
    row-gap: var(--sp-5);
    column-gap: var(--sp-5);
    margin-bottom: var(--sp-4);
}

.filter-bar .field {
    min-width: 0;
    margin-bottom: 0;
}

/*
 * Todos los filtros ocupan el mismo alto, tengan o no texto de ayuda.
 *
 * Sin esto, el único filtro con ayuda —«Producto»— estiraba su fila entera y la
 * separación entre filas pasaba de 24 px a 94 px en un solo salto: la barra se
 * leía como tres bloques sueltos en lugar de una grilla. El espacio de la ayuda
 * se reserva en las nueve celdas, con el alto de dos renglones de 12 px, y las
 * filas vuelven a estar todas a la misma distancia.
 */
.filter-bar .field__hint {
    min-height: 32px;
}

.filter-bar .field:not(:has(.field__hint))::after {
    content: "";
    display: block;
    min-height: 32px;
    margin-top: var(--sp-1);
}

/* El control ocupa toda su columna: si se encoge al ancho de su contenido,
 * vuelve el recorte que esta regla existe para evitar. */
.filter-bar .field__control {
    width: 100%;
    height: var(--control-h);
}

/*
 * Los filtros se alinean por arriba y no por abajo: alguno tiene texto de ayuda
 * debajo del control y, alineados por abajo, ese campo levantaba su desplegable
 * por encima de los demás. El botón compensa a mano el alto de una etiqueta
 * —18 px de línea más 4 px de separación— para quedar apoyado en la misma
 * línea que los desplegables de su fila.
 */
.filter-bar__actions {
    display: flex;
    gap: var(--sp-2);
    margin-top: calc(18px + var(--sp-1));
}

/* ── Paginación ─────────────────────────────────────────────────────────── */
/*
 * Se arma a mano y no con el paginador del framework: el que trae Laravel emite
 * marcado con clases de Tailwind, que este proyecto no usa.
 */

.pagination {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    margin-top: var(--sp-4);
}

.pagination__pages {
    color: var(--n-500);
    font-size: 12px;
    line-height: 16px;
}

.pagination__links {
    display: flex;
    gap: var(--sp-2);
}

/* Los datos secundarios de una celda (roles del actor, contexto, número de
 * petición) van debajo del principal, no al lado. */
.data-table .caption {
    display: block;
}

/* Densidad compacta: la fila baja de 48 a 36 px acortando el relleno, no el
 * tamaño del texto, que sigue teniendo un mínimo de 12 px (Lineamiento §4). */
.density-compact .data-table th,
.density-compact .data-table td {
    padding: var(--sp-2) var(--sp-3);
}

/* ── Pestañas (Lineamiento §8, revisiones de presupuesto) ───────────────── */
/*
 * Se marcan con `role="tablist"` y `aria-selected`, y la activa lleva un
 * subrayado de 3 px en amarillo. El amarillo acá es indicador, no fondo: sobre
 * claro no se lee como texto (2,05:1) pero sí funciona como barra.
 */

.tabs {
    display: flex;
    gap: var(--sp-1);
    border-bottom: 1px solid var(--n-200);
    margin-bottom: var(--sp-4);
    overflow-x: auto;
}

.tabs__tab {
    flex: none;
    appearance: none;
    background: none;
    border: none;
    border-bottom: 3px solid transparent;
    padding: var(--sp-3) var(--sp-4);
    font-family: inherit;
    font-size: 15px;
    font-weight: 600;
    color: var(--n-500);
    cursor: pointer;
    white-space: nowrap;
}

.tabs__tab:hover {
    color: var(--n-900);
}

.tabs__tab[aria-selected="true"] {
    color: var(--n-900);
    border-bottom-color: var(--brand-yellow);
}

.tabs__panel[hidden] {
    display: none;
}

/*
 * Variante enlazada: las mismas pestañas, pero cuando cada una es una dirección
 * propia y no un panel que se muestra y se esconde. Es lo que usa el historial
 * del cliente (Especificación §12), donde cada solapa va a cargar su propia
 * lista paginada: traerlas todas juntas para esconder cuatro sería trabajo de
 * servidor tirado a la basura, y además la solapa abierta se puede compartir por
 * enlace y sobrevive sin JavaScript.
 *
 * La activa se marca con `aria-current="page"`, que es lo correcto para
 * navegación —`aria-selected` pertenece a un `tablist` real—, y se ve igual que
 * la del `tablist`: mismo subrayado amarillo, misma tinta.
 */
.tabs__tab--link {
    display: inline-block;
    text-decoration: none;
}

.tabs__tab[aria-current="page"] {
    color: var(--n-900);
    border-bottom-color: var(--brand-yellow);
}

/*
 * Solapa cuyo módulo todavía no existe: se muestra apagada y no navega, como
 * un botón sin permiso (Lineamiento §7). Se deja a la vista a propósito, para
 * que el mapa de la ficha no cambie de forma cuando el módulo llegue.
 */
.tabs__tab[aria-disabled="true"] {
    color: var(--n-400);
    cursor: not-allowed;
}

/* ── Lista de datos (ficha del cliente) ─────────────────────────────────── */
/*
 * Etiqueta y valor en dos columnas, que es como se lee una ficha: nadie busca
 * "Villa Crespo", busca la fila "Localidad". En pantalla angosta las dos
 * columnas se apilan, igual que las tablas (Lineamiento §7).
 */

.detail-list {
    display: grid;
    grid-template-columns: minmax(160px, 240px) 1fr;
    gap: var(--sp-2) var(--sp-4);
    margin: 0;
}

.detail-list__term {
    margin: 0;
    color: var(--n-500);
    font-size: 13px;
    font-weight: 600;
    line-height: 20px;
}

.detail-list__value {
    margin: 0;
    color: var(--n-900);
    font-size: 15px;
    line-height: 20px;
    overflow-wrap: anywhere;
}

@media (max-width: 768px) {
    .detail-list {
        grid-template-columns: 1fr;
        gap: var(--sp-1) 0;
    }

    .detail-list__value {
        margin-bottom: var(--sp-3);
    }
}

/* ── Modales (Lineamiento §7) ───────────────────────────────────────────── */
/*
 * Se usa `<dialog>` nativo: trae la trampa de foco, el cierre con Escape y el
 * velo del navegador sin JavaScript propio. `ui.js` solo lo abre y lo cierra.
 */

.modal {
    width: min(560px, calc(100vw - var(--sp-6)));
    max-height: calc(100vh - var(--sp-7));
    padding: 0;
    border: none;
    border-radius: var(--r-lg);
    box-shadow: var(--sh-3);
    background-color: var(--n-000);
    color: var(--n-700);
}

.modal::backdrop {
    background-color: var(--scrim);
}

.modal__header,
.modal__body,
.modal__actions {
    padding: var(--sp-4) var(--sp-5);
}

.modal__header {
    border-bottom: 1px solid var(--n-200);
}

.modal__body {
    overflow-y: auto;
}

/* El primario va último: en castellano se lee de izquierda a derecha y la
 * confirmación es el final del recorrido. */
.modal__actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--sp-2);
    border-top: 1px solid var(--n-200);
}

/*
 * En móvil el modal se convierte en hoja inferior a ancho completo, con la
 * barra de arrastre que indica que se puede cerrar hacia abajo.
 */
@media (max-width: 767px) {
    .modal {
        width: 100vw;
        max-width: none;
        margin: 0 0 0 0;
        position: fixed;
        inset: auto 0 0 0;
        border-radius: 16px 16px 0 0;
        max-height: 85vh;
        padding-bottom: env(safe-area-inset-bottom, 0px);
    }

    .modal::before {
        content: "";
        display: block;
        width: 36px;
        height: 4px;
        margin: var(--sp-2) auto 0;
        border-radius: var(--r-pill);
        background-color: var(--n-300);
    }

    .modal__actions {
        flex-direction: column-reverse;
    }

    .modal__actions .button {
        width: 100%;
    }
}

/* ── Notificaciones efímeras (Lineamiento §7) ───────────────────────────── */
/*
 * Abajo a la derecha en escritorio, arriba en móvil. Nunca se usan para errores
 * de validación de formulario: esos van junto al campo, porque un aviso que
 * desaparece a los cinco segundos no sirve para corregir un dato.
 *
 * La región es `aria-live="polite"`, así que el lector de pantalla anuncia el
 * mensaje sin interrumpir lo que la persona esté haciendo.
 */

.toast-region {
    position: fixed;
    right: var(--sp-4);
    bottom: var(--sp-4);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    width: min(360px, calc(100vw - var(--sp-6)));
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--r-md);
    background-color: var(--n-900);
    color: var(--n-000);
    box-shadow: var(--sh-3);
    font-size: 14px;
    line-height: 20px;
}

.toast__icon--ok { color: var(--ok); }
.toast__icon--err { color: var(--err); }
.toast__icon--info { color: var(--brand-yellow); }

.toast__message {
    flex: 1;
}

/* «Deshacer» y el cierre van sobre fondo oscuro: acá el amarillo sí se puede
 * usar como texto, porque sobre --n-900 da 10,2:1. */
.toast__action {
    appearance: none;
    background: none;
    border: none;
    padding: 0;
    color: var(--brand-yellow);
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    text-decoration: underline;
    cursor: pointer;
}

/* La acción propia del aviso —«Actualizar», «Deshacer»— se distingue de
 * «Cerrar», que siempre está. Sobre el fondo oscuro del toast el amarillo de
 * marca da 10,2:1, que es de las pocas combinaciones donde puede ser texto
 * (Lineamiento §3.1). */
.toast__action--primary {
    padding: var(--sp-1) var(--sp-3);
    border: 1px solid currentcolor;
    border-radius: var(--r-sm);
    text-decoration: none;
    min-height: 32px;
}

@media (max-width: 767px) {
    .toast-region {
        top: calc(var(--topbar-h) + var(--sp-2));
        bottom: auto;
        left: var(--sp-4);
        right: var(--sp-4);
        width: auto;
    }
}

/* ── Muestra de material (Lineamiento §8 y §9) ──────────────────────────── */
/*
 * El cuadrado de 40 px con la textura de la madera o del tapizado. Es el único
 * lugar de la interfaz, junto con el login y el encabezado del PDF, donde las
 * texturas del manual tienen lugar: de fondo de pantalla recargan y arruinan la
 * legibilidad de las tablas.
 */

.swatch {
    width: 40px;
    height: 40px;
    flex: none;
    border-radius: var(--r-sm);
    border: 1px solid var(--n-300);
    background-position: center;
    background-size: cover;
}

/*
 * La versión chica, para acompañar un valor de texto dentro de una lista de
 * definiciones: el color de acento con el que la empresa imprime sus documentos.
 * El color se pinta con un estilo en línea porque es un dato de la empresa
 * (Especificación §30) y no un token del sistema de diseño.
 */
.swatch--inline {
    display: inline-block;
    width: 20px;
    height: 20px;
    vertical-align: text-bottom;
    margin-right: var(--sp-2);
}

/* ── Vista previa del logo del documento ────────────────────────────────── */
/*
 * Sobre fondo claro y con borde, porque un logo suele venir con fondo
 * transparente y sin encuadre no se sabe dónde termina la imagen.
 */

.document-logo-preview {
    display: block;
    width: auto;
    max-width: 240px;
    height: auto;
    max-height: 80px;
    padding: var(--sp-3);
    border: 1px solid var(--n-200);
    border-radius: var(--r-md);
    background-color: var(--n-000);
}

/* ── Barra de progreso ──────────────────────────────────────────────────── */
/*
 * Sirve para el avance de meta del vendedor y para la vigencia del presupuesto.
 * El valor va siempre además en texto al lado: una barra sola no dice cuánto
 * falta, y el color por sí solo no comunica un estado (Lineamiento §10).
 */

.progress {
    height: 8px;
    border-radius: var(--r-pill);
    background-color: var(--n-200);
    overflow: hidden;
}

.progress__value {
    display: block;
    height: 100%;
    background-color: var(--brand-yellow);
}

.progress--thin { height: 4px; }

/* Separación mínima cuando la barra va justo debajo de un texto: pegada, se lee
 * como un subrayado del texto y no como una medida de avance. */
.progress--spaced { margin-top: var(--sp-2); }
.progress--ok .progress__value { background-color: var(--ok); }
.progress--warn .progress__value { background-color: var(--warn); }
.progress--err .progress__value { background-color: var(--err); }

/* ── Vigencia del presupuesto ───────────────────────────────────────────── */
/*
 * La barra de los tres tramos del Lineamiento §8: verde mientras faltan más de
 * cinco días, ámbar en los últimos cinco, roja una vez vencida. Es la misma
 * barra de progreso, con el color decidido por el dominio
 * (`QuoteValidityUrgency::barClass()`) y no por la plantilla: el umbral de cinco
 * días es una regla de negocio y no puede quedar escrita en una hoja de estilos.
 *
 * El color nunca va solo: al lado siempre está el texto «Vence el 14/09 ·
 * faltan 6 días», porque bajo la luz de un salón la diferencia entre verde y
 * ámbar puede no verse (Lineamiento §10).
 */

.validity-bar { max-width: 240px; }
.validity-bar--vigente .progress__value { background-color: var(--ok); }
.validity-bar--por-vencer .progress__value { background-color: var(--warn); }
.validity-bar--vencido .progress__value { background-color: var(--err); }

/* ── Acciones dentro de una fila ────────────────────────────────────────── */
/*
 * En las pantallas de estructura del catálogo conviven varias acciones en la
 * misma celda —subir, bajar, activar— y cada una es su propio formulario,
 * porque cada una es un envío distinto. El contenedor solo las alinea y las
 * deja bajar de renglón en pantallas angostas: no aporta color, borde ni fondo.
 */

.row-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--sp-2);
}

/*
 * Variante para cuando la fila no son botones sino una lectura corrida —un chip
 * de estado, el nombre del cliente, la fecha—: ahí el arranque en el borde
 * superior desalinea el chip respecto del texto, porque el chip es más alto que
 * un renglón. Centrar verticalmente los deja sobre la misma línea óptica.
 */
.row-actions--aligned {
    align-items: center;
}

/* ── Lista para elegir uno ──────────────────────────────────────────────── */
/*
 * Media docena de renglones donde cada uno se lee de un vistazo —un rótulo a la
 * izquierda, una acción a la derecha— y de los que se elige exactamente uno: el
 * presupuesto de destino en el configurador es el caso que la trajo.
 *
 * No es una tabla y no debe parecerlo: no hay columnas que comparar. Tampoco es
 * `.row-actions`, que alinea botones sueltos sin rótulo. Se separa con líneas de
 * 1 px como el resto de las superficies (Lineamiento §5: bordes antes que
 * sombras) y cada renglón respeta el alto táctil mínimo.
 */

.pick-list {
    list-style: none;
    margin: var(--sp-3) 0;
    padding: 0;
}

.pick-list__item {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    min-height: 48px;
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--n-200);
}

.pick-list__item:last-child {
    border-bottom: none;
}

/* ── Formulario en línea ────────────────────────────────────────────────── */
/*
 * Un alta corta —un nombre, a lo sumo un desplegable y un botón— no merece una
 * pantalla aparte ni un campo por renglón. Los campos crecen, el botón no, y
 * por debajo del punto de quiebre todo se apila solo sin una regla adicional.
 */

/*
 * Alineación por arriba y no por abajo. Alineados por abajo, un campo con texto
 * de ayuda debajo del control empujaba su etiqueta hacia arriba —«Tipo» quedaba
 * más alto que «Nombre del estado» y que «Color»— y el botón de crear terminaba
 * apoyado en la última línea de esa ayuda, o sea a media altura de la fila.
 *
 * Con `start`, todas las etiquetas arrancan en la misma línea y todos los
 * controles quedan a la misma altura. El botón compensa a mano el alto de una
 * etiqueta —18 px de línea más 4 px de separación— para apoyarse en la base de
 * los controles y no en la de los textos de ayuda. Es el mismo criterio que usa
 * la barra de filtros.
 */
.form-inline {
    display: flex;
    flex-wrap: wrap;
    align-items: start;
    gap: var(--sp-3);
}

.form-inline .field {
    flex: 1 1 220px;
    margin-bottom: 0;
}

.form-inline__actions {
    display: flex;
    gap: var(--sp-2);
    flex: none;
    margin-top: calc(18px + var(--sp-1));
}

/* ── Miniatura de producto ──────────────────────────────────────────────── */
/*
 * Relación 4:3 y `object-fit: cover`, como pide el Lineamiento §9 para las
 * grillas de catálogo. El fondo `--n-100` es el placeholder mientras la imagen
 * carga: sin él la celda parpadea en blanco y la tabla "salta".
 *
 * El alto y el ancho reales van además en los atributos `width` y `height` del
 * `<img>`; el CSS los sobreescribe, pero el navegador ya reservó el espacio.
 */

.product-thumb {
    display: block;
    width: 64px;
    height: 48px;
    border-radius: var(--r-md);
    border: 1px solid var(--n-200);
    background-color: var(--n-100);
    object-fit: cover;
}

.product-thumb--empty {
    background-color: var(--n-100);
}

/* ── Lista de chips dentro de una celda ─────────────────────────────────── */

.chip-list {
    display: flex;
    flex-wrap: wrap;
    gap: var(--sp-1);
    margin-top: var(--sp-1);
}

/* ── Columnas de importe ────────────────────────────────────────────────── */
/*
 * Los números se alinean a la derecha para que las unidades queden una debajo
 * de la otra (Lineamiento §4). En móvil la tabla se apila como tarjeta y la
 * alineación a la derecha deja de tener sentido: ahí vuelve a la izquierda.
 */

.data-table__cell--numeric {
    text-align: right;
}

.field__control--numeric {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    text-align: right;
}

@media (max-width: 768px) {
    .data-table__cell--numeric {
        text-align: left;
    }
}

/* ── Agrupación de campos ───────────────────────────────────────────────── */
/*
 * `<fieldset>` de verdad, con su `<legend>`: es lo que le anuncia al lector de
 * pantalla que las casillas que siguen son un grupo y no campos sueltos.
 */

.fieldset {
    border: 1px solid var(--n-200);
    border-radius: var(--r-md);
    padding: var(--sp-4);
    margin: 0 0 var(--sp-4);
}

.fieldset__legend {
    font-size: 16px;
    line-height: 24px;
    font-weight: 600;
    padding: 0 var(--sp-2);
}

/* ── Casilla con etiqueta ───────────────────────────────────────────────── */
/*
 * El área táctil mínima es 44 px de alto (Lineamiento §10): la casilla sola
 * mide 16 px, así que la que se toca es la fila entera.
 */

.choice {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: 44px;
    padding-right: var(--sp-3);
    cursor: pointer;
}

.choice input[type="checkbox"] {
    width: 20px;
    height: 20px;
    accent-color: var(--n-900);
    cursor: pointer;
}

/* ── Galería de imágenes del producto ───────────────────────────────────── */
/*
 * El borde punteado ámbar y el chip "Pendiente de aprobación" son obligatorios
 * y no decorativos: la Especificación §25 prohíbe mostrarle al cliente o
 * imprimir una imagen que todavía no aprobó una persona. La etiqueta
 * "Generado por IA" responde al Lineamiento §8, que la exige en toda salida de
 * IA del sistema.
 */

.image-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--sp-4);
    list-style: none;
    margin: var(--sp-4) 0 0;
    padding: 0;
}

.image-card {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--sp-2);
    border: 1px solid var(--n-200);
    border-radius: var(--r-lg);
    padding: var(--sp-3);
    background-color: var(--n-000);
}

.image-card--pending {
    border: 2px dashed var(--warn);
}

.image-card__figure {
    width: 100%;
    height: auto;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    border-radius: var(--r-md);
    background-color: var(--n-100);
}

.image-card .overline {
    color: var(--info);
}

/* ── Bloque plegable ────────────────────────────────────────────────────── */
/*
 * `<details>` nativo. Se usa cuando una pantalla tiene muchos formularios que
 * hacen lo mismo sobre cosas distintas —editar cada opción de un atributo, por
 * ejemplo—: mostrarlos todos abiertos convierte la pantalla en un muro de
 * campos donde no se encuentra nada.
 *
 * Es un elemento nativo y no un desplegable hecho a mano porque abre y cierra
 * con teclado, se anuncia solo al lector de pantalla y no necesita JavaScript;
 * además el navegador lo abre al buscar texto adentro.
 *
 * El formulario que tiene errores se dibuja abierto desde el servidor: un
 * mensaje de error escondido dentro de un bloque cerrado es un mensaje que no
 * existe.
 */

.disclosure {
    border: 1px solid var(--n-200);
    border-radius: var(--r-md);
    padding: var(--sp-2) var(--sp-3);
    margin-top: var(--sp-3);
    background-color: var(--n-000);
}

.disclosure__summary {
    display: flex;
    align-items: center;
    min-height: 44px;
    font-size: 13px;
    line-height: 18px;
    font-weight: 600;
    letter-spacing: 0.01em;
    color: var(--n-700);
    cursor: pointer;
}

.disclosure[open] > .disclosure__summary {
    border-bottom: 1px solid var(--n-200);
    margin-bottom: var(--sp-3);
}

/* ── Línea de tiempo de actividad ───────────────────────────────────────── */
/*
 * El historial del cliente (Especificación §13). Es una lista ordenada real
 * —`<ol>`—: lo que se está mostrando es una secuencia de hechos, y esa es la
 * información que el lector de pantalla tiene que recibir.
 *
 * El hilo vertical se dibuja con un borde izquierdo sobre el ítem y no con un
 * pseudo-elemento absoluto por ítem: así el hilo se corta solo en el último,
 * sin depender de contar filas, y sigue al contenido cuando el navegador
 * reflowea por zoom.
 *
 * Los hechos automáticos se distinguen del contacto cargado a mano por el
 * fondo del ícono, no por el color del texto: el color no puede ser la única
 * señal (Lineamiento §10) y la etiqueta de tipo, que va escrita, ya lo dice.
 */

.timeline {
    list-style: none;
    margin: var(--sp-4) 0 0;
    padding: 0;
}

.timeline__item {
    display: flex;
    gap: var(--sp-3);
    padding: 0 0 var(--sp-4) var(--sp-4);
    margin-left: 16px;
    border-left: 1px solid var(--n-200);
}

.timeline__item:last-child {
    border-left-color: transparent;
    padding-bottom: 0;
}

.timeline__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: none;
    width: 32px;
    height: 32px;
    margin-left: calc(-1 * var(--sp-4) - 17px);
    border: 1px solid var(--n-300);
    border-radius: var(--r-pill);
    background-color: var(--n-000);
    color: var(--n-700);
}

.timeline__item--automatic .timeline__icon {
    background-color: var(--n-100);
    border-color: var(--n-200);
    color: var(--n-500);
}

.timeline__body {
    min-width: 0;
    padding-top: var(--sp-1);
}

.timeline__title {
    margin: 0;
    font-size: 15px;
    line-height: 22px;
    color: var(--n-900);
}

.timeline__title .overline {
    display: block;
    color: var(--n-500);
}

.timeline__detail {
    margin: var(--sp-1) 0 0;
    font-size: 14px;
    line-height: 20px;
    color: var(--n-700);
    white-space: pre-line;
}

.timeline__meta {
    margin: var(--sp-1) 0 0;
}

/* ── Desglose de precio (Lineamiento §8) ────────────────────────────────── */
/*
 * Lista vertical, no tabla: son conceptos encadenados en un orden que importa,
 * no filas comparables entre sí. El orden es el del motor de precios y no se
 * altera por presentación.
 *
 * El frontend no calcula nada de esto: lo muestra. La autoridad del número es
 * del backend (Especificación §10 y §35).
 */

.price-breakdown {
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

.price-breakdown__line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-4);
}

.price-breakdown__concept {
    font-family: var(--font-data);
    font-size: 14px;
    line-height: 20px;
    color: var(--n-700);
    margin: 0;
}

.price-breakdown__amount {
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-size: 14px;
    line-height: 20px;
    text-align: right;
    margin: 0;
    /*
     * Un importe no se parte en dos renglones. Con conceptos largos —el de una
     * combinación lo es— el signo quedaba arriba y el número abajo, que es
     * exactamente el tipo de lectura ambigua que el Lineamiento §4 prohíbe en
     * una pantalla operativa. El que se acomoda es el concepto, no la cifra.
     */
    white-space: nowrap;
}

.price-breakdown__line--strong {
    border-top: 1px solid var(--n-300);
    padding-top: var(--sp-2);
    margin-top: var(--sp-1);
}

.price-breakdown__line--strong .price-breakdown__concept,
.price-breakdown__line--strong .price-breakdown__amount {
    font-weight: 600;
}

.price-breakdown__line--total .price-breakdown__amount {
    font-family: var(--font-ui);
    font-size: 20px;
    line-height: 28px;
    font-weight: 700;
    color: var(--n-900);
}

/*
 * El resumen del presupuesto es el mismo desglose, un escalón más arriba: no
 * tiene los pasos del motor, tiene los dos totales. Se separa del último ítem
 * con un borde para que no se lea como un renglón más de ese ítem.
 */
.price-breakdown--summary {
    margin-top: var(--sp-5);
    padding-top: var(--sp-4);
    border-top: 1px solid var(--n-300);
}

/* ── Ítem cotizado (Lineamiento §8, Especificación §16) ─────────────────── */
/*
 * Cada ítem es una tarjeta dentro de la tarjeta: se separa con un borde y no
 * con sombra, como todo lo demás. El total de la línea va arriba a la derecha y
 * en el mismo tamaño del precio final del desglose, porque es lo que el
 * vendedor busca con la vista cuando el cliente pregunta «¿y ese cuánto sale?».
 *
 * La configuración se muestra como pares atributo/opción y no como una línea
 * corrida: el cliente los lee de a uno y pregunta por uno.
 */

.quote-item {
    border: 1px solid var(--n-200);
    border-radius: var(--r-md);
    padding: var(--sp-4);
    margin-top: var(--sp-4);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.quote-item__header {
    display: flex;
    /*
     * En un teléfono los tres bloques —casilla de aprobación, nombre del
     * producto e importe— no entran en un renglón: sin `wrap`, el importe se
     * salía del borde derecho y quedaba cortado justo en el dato que el
     * vendedor le está mostrando al cliente.
     */
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3) var(--sp-4);
}

/*
 * La foto del producto cotizado (Lineamiento §9: relación 4:3, `object-fit:
 * cover`, fondo de carga neutro y sin sombra propia). Va primero en el
 * encabezado y con `flex: none`: es una referencia para reconocer el mueble de
 * un vistazo, no la protagonista de la fila, así que no crece ni se encoge
 * mientras el nombre y el importe se acomodan alrededor.
 *
 * `align-self: flex-start` la despega de la línea de base del encabezado: sin
 * eso, la imagen apoya su borde inferior sobre el mismo renglón que el nombre y
 * empuja el bloque entero hacia abajo.
 */
.quote-item__photo {
    flex: none;
    display: block;
    align-self: flex-start;
    width: 96px;
    height: 72px;
    border-radius: var(--r-md);
    border: 1px solid var(--n-200);
    background-color: var(--n-100);
    object-fit: cover;
}

@media (max-width: 767px) {
    /* En el teléfono la foto cede tamaño antes que el precio: 64 px alcanzan
     * para reconocer el producto y dejan el nombre completo en el renglón. */
    .quote-item__photo {
        width: 64px;
        height: 48px;
    }
}

/* El nombre es lo que cede: se encoge y parte en varios renglones antes que
 * empujar al importe fuera de la tarjeta. */
.quote-item__identity {
    flex: 1 1 200px;
    min-width: 0;
}

@media (max-width: 767px) {
    /* Debajo de 768 px la casilla se queda con el renglón entero, para que el
     * nombre y el importe compartan el siguiente y no quede cada cosa en una
     * línea distinta. */
    .quote-item__header .approval-check {
        flex: 1 0 100%;
    }
}

.quote-item__name {
    margin: 0;
    font-size: 16px;
    line-height: 24px;
    font-weight: 600;
    color: var(--n-900);
}

.quote-item__total {
    flex: none;
    margin: 0;
    font-variant-numeric: tabular-nums;
    font-size: 20px;
    line-height: 28px;
    font-weight: 700;
    color: var(--n-900);
    white-space: nowrap;
}

.quote-item__configuration {
    margin: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: var(--sp-2);
}

.quote-item__attribute {
    display: flex;
    gap: var(--sp-2);
    font-size: 14px;
    line-height: 20px;
}

.quote-item__attribute dt {
    color: var(--n-500);
}

.quote-item__attribute dd {
    margin: 0;
    color: var(--n-900);
    font-weight: 600;
}

.quote-item__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--sp-4);
    padding-top: var(--sp-3);
    border-top: 1px solid var(--n-200);
}

.quote-item__actions form {
    display: flex;
    align-items: flex-end;
    gap: var(--sp-2);
}

.quote-item__actions .field {
    margin: 0;
}

.quote-item__actions .field__control {
    max-width: 96px;
}

/* ── Descuento del vendedor (Especificación §20, Lineamiento §8) ────────── */
/*
 * El campo vive pegado al desglose porque el descuento se decide mirando el
 * total, y debajo va siempre la ayuda con el máximo autorizado: el vendedor
 * tiene que ver cuánto más puede descontar sin probar hasta que el sistema le
 * diga que no.
 *
 * La ayuda no se achica ni se recorta cuando el bloque se apila en el celular:
 * un techo a medio leer es peor que no mostrarlo.
 */

.quote-item__discount {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--sp-3) var(--sp-4);
    margin-top: var(--sp-3);
    padding-top: var(--sp-3);
    border-top: 1px solid var(--n-200);
}

.quote-item__discount .field {
    margin: 0;
    max-width: 320px;
}

.quote-item__discount .field__control {
    max-width: 96px;
}

.quote-item__discount .button {
    margin-top: 22px;
}

.quote-item__override {
    margin: 22px 0 0;
}

.quote-item__discount-block {
    margin-top: var(--sp-3);
}

/* ── Producto especial cotizado por la fábrica (decisión D-22) ──────────── */
/*
 * Es la única línea del presupuesto que puede no tener precio, y la pantalla
 * tiene que decirlo sin disimularlo. Por eso la ficha se parece a un ítem
 * —mismo borde, mismo radio, misma caja— pero no es igual: el borde izquierdo
 * de 3 px lleva el color del estado, y donde iría el importe hay una frase.
 *
 * Un ítem a medias disfrazado de ítem normal es exactamente cómo un vendedor
 * termina leyéndole al cliente un total que todavía no incluye el ropero.
 */

.special-item {
    border: 1px solid var(--n-200);
    border-left: 3px solid var(--n-300);
    border-radius: var(--r-md);
    padding: var(--sp-4);
    margin-top: var(--sp-4);
    display: flex;
    flex-direction: column;
    gap: var(--sp-3);
}

.special-item--pendiente { border-left-color: var(--warn); }
.special-item--cotizado { border-left-color: var(--ok); }
.special-item--rechazado { border-left-color: var(--err); }
.special-item--cancelado { border-left-color: var(--n-300); }

.special-item__header {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--sp-2) var(--sp-3);
}

.special-item__name {
    margin: 0;
    font-size: 16px;
    line-height: 24px;
    font-weight: 600;
}

/*
 * El importe cotizado, cuando lo hay, en la familia de datos densos y con
 * cifras tabulares, igual que cualquier otro dinero del sistema
 * (Lineamiento §4). Que este número lo haya escrito una persona y no el motor no
 * cambia cómo se lee.
 */
.special-item__amount {
    margin-left: auto;
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    color: var(--n-900);
}

.special-item__detail {
    margin: 0;
    display: grid;
    gap: var(--sp-2);
}

.special-item__detail div {
    display: grid;
    grid-template-columns: 148px 1fr;
    gap: var(--sp-3);
}

.special-item__detail dt {
    font-size: 13px;
    line-height: 18px;
    font-weight: 600;
    color: var(--n-500);
}

.special-item__detail dd {
    margin: 0;
    /* Las características vienen escritas a mano en el mostrador, con saltos de
     * línea y todo: se respetan, porque una lista de medidas convertida en un
     * párrafo corrido es la forma de que la fábrica lea mal una medida. */
    white-space: pre-line;
}

@media (max-width: 640px) {
    .special-item__detail div {
        grid-template-columns: 1fr;
        gap: var(--sp-1);
    }
}

.special-item__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--sp-3);
}

.special-item__actions .field {
    margin: 0;
    min-width: 180px;
}

/*
 * La bandeja de la fábrica es una lista de fichas, no una tabla: cada pedido
 * trae dos textos largos y un formulario, y eso en una celda no entra. El
 * ancho se limita para que las características no se lean a 1.400 px de largo.
 */
.factory-inbox {
    max-width: 880px;
}

/* ── Aprobación por ítem (Especificación §17, Lineamiento §8) ───────────── */
/*
 * El cliente aprueba parte del presupuesto y el resto lo sigue pensando. La
 * pantalla tiene que mostrar las dos cosas al mismo tiempo, y por eso el ítem
 * aprobado se marca —borde izquierdo verde y fondo apenas teñido— pero el no
 * aprobado NO se atenúa: sigue siendo un ítem vivo, editable, sobre el que el
 * cliente todavía puede decir que sí.
 *
 * La casilla es un cuadrado real de 24 px dentro de un área táctil de 44, como
 * pide el Lineamiento §10: se toca con el dedo, de pie, con el cliente al lado.
 */

.quote-item--aprobado {
    border-left: 3px solid var(--ok);
    background: var(--ok-tint);
}

.approval-check {
    flex: none;
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    min-height: var(--control-h);
    margin: 0;
}

.approval-check__box {
    flex: none;
    width: 24px;
    height: 24px;
    margin: 0;
    accent-color: var(--ok);
    cursor: pointer;
}

.approval-check__label {
    /* El área táctil se agranda desde la etiqueta, no desde la casilla: así el
     * dedo tiene 44 px de alto sin que el cuadrado se deforme. */
    display: flex;
    align-items: center;
    min-height: var(--control-h);
    font-size: 13px;
    line-height: 18px;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--n-700);
    cursor: pointer;
}

/*
 * Con JavaScript, `ui.js` esconde este botón y manda el formulario al tildar.
 * Sin JavaScript queda visible y es el único modo de guardar: una casilla que no
 * guarda nada sería una promesa falsa.
 */
.approval-check__submit {
    padding: 0 var(--sp-3);
}

.approval-check--registrada {
    align-items: center;
    gap: var(--sp-1);
    color: var(--ok);
    font-size: 13px;
    line-height: 18px;
    font-weight: 600;
}

/*
 * La barra de aprobación (Lineamiento §8).
 *
 * Pegajosa al pie del contenido: mientras el vendedor recorre veinte ítems con
 * el cliente, los dos importes que importan —cotizado y aprobado— no se le van
 * de la pantalla. En móvil se levanta por encima de la barra de navegación
 * inferior, que es fija, para no quedar tapada.
 */
.approval-bar {
    position: sticky;
    bottom: 0;
    z-index: 1;
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: var(--sp-4);
    margin-top: var(--sp-5);
    padding: var(--sp-4);
    background: var(--n-000);
    border: 1px solid var(--n-200);
    border-top: 3px solid var(--brand-yellow);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-2);
}

.approval-bar__totals {
    margin: 0;
    font-family: var(--font-data);
    font-variant-numeric: tabular-nums;
    font-size: 16px;
    line-height: 24px;
    font-weight: 600;
    color: var(--n-900);
}

.approval-bar__figures {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

.approval-bar__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--sp-4);
}

.approval-bar__actions form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--sp-2);
}

.approval-bar__actions .field {
    margin: 0;
    min-width: 220px;
}

.approval-bar__actions .caption {
    flex-basis: 100%;
    margin: 0;
}

.approval-bar__next {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
    max-width: 280px;
}

@media (max-width: 767px) {
    .approval-bar {
        bottom: calc(var(--bottomnav-h) + env(safe-area-inset-bottom, 0px));
    }
}

/* ── Revisiones del presupuesto (Especificación §15) ────────────────────── */
/*
 * Una revisión histórica es el documento tal como se emitió. Se lee igual que el
 * presupuesto vigente —mismo parcial de ítem, mismo desglose— y lo único que
 * cambia es el marco: fondo más apagado y una banda arriba que lo dice con
 * palabras.
 *
 * La banda no se resuelve solo con color: dice «solo lectura» y lleva
 * `role="status"`, porque quien navega con lector de pantalla también tiene que
 * enterarse de que está mirando algo viejo (Lineamiento §10).
 */

.historic-band {
    display: flex;
    align-items: center;
    min-height: 32px;
    margin: 0 0 var(--sp-4);
    padding: 0 var(--sp-4);
    background: var(--n-200);
    border-radius: var(--r-md);
    color: var(--n-700);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.card--historic {
    background: var(--n-100);
}

/* ── Comparación entre revisiones (Lineamiento §8) ──────────────────────── */
/*
 * «Las diferencias resaltadas: aumento con fondo #FBEDE3, baja con fondo
 * #E6F2EC» — que son `--warn-bg` y `--ok-bg`, ya declarados en tokens.css.
 *
 * El fondo acompaña; no informa solo. Cada fila escribe además qué se movió, y
 * el borde izquierdo da una segunda pista de forma para quien no distingue esos
 * dos tonos. Un renglón sin cambios queda sin marcar a propósito: si todo se
 * pinta, nada resalta.
 */

.compare-row--agregado,
.compare-row--quitado,
.compare-row--modificado {
    border-left: 3px solid transparent;
}

.compare-row--agregado {
    background: var(--ok-bg);
    border-left-color: var(--ok);
}

.compare-row--quitado {
    background: var(--err-bg);
    border-left-color: var(--err);
}

.compare-row--modificado {
    background: var(--warn-bg);
    border-left-color: var(--warn);
}

/*
 * El importe de la diferencia. Va en el color semántico y con el signo siempre
 * explícito (Lineamiento §4): un «+ $ 42.000» se entiende sin mirar el color.
 */

.compare-amount--aumento {
    color: var(--warn);
    font-weight: 600;
}

.compare-amount--baja {
    color: var(--ok);
    font-weight: 600;
}

.compare-amount--igual {
    color: var(--n-500);
}

/* ── Paneles: cifra destacada ───────────────────────────────────────────── */
/*
 * La grilla de cifras del Smart Panel. Son tarjetas dentro de una tarjeta, así
 * que no llevan borde propio: se separan por el aire, que es lo que el
 * Lineamiento §5 pide antes que una sombra.
 *
 * El número va en Montserrat 700 a 32 px —el tamaño Display— porque es lo único
 * que alguien mira de lejos; la etiqueta va arriba en Overline y la aclaración
 * abajo en Caption, de modo que la cifra nunca aparezca sin decir de qué es.
 */

.metric-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: var(--sp-4);
    margin-top: var(--sp-4);
    /* Las cifras de una misma fila se leen juntas: si una arranca cuatro
     * píxeles más abajo que sus vecinas, la fila deja de leerse como fila. */
    align-items: baseline;
}

.metric {
    display: flex;
    flex-direction: column;
    gap: var(--sp-1);
}

.metric__label {
    color: var(--n-500);
}

.metric__value {
    margin: 0;
    color: var(--n-900);
    font-size: 32px;
    font-weight: 700;
    line-height: 40px;
}

/*
 * El dinero va un punto más chico que un porcentaje y no se parte nunca.
 *
 * El Lineamiento §4 prohíbe abreviar importes —nada de «$ 1,2 M»—, así que un
 * total de siete cifras ocupa lo que ocupa. A 32 px la columna no le alcanza y
 * el navegador corta por el espacio que separa el símbolo del número: se lee
 * «$» arriba y «2.266.000,00» abajo, que es exactamente el tipo de cifra
 * ambigua que esta pantalla no puede permitirse. Un porcentaje, en cambio,
 * entra siempre y se queda en Display.
 */
.metric__value.numeric {
    font-size: 28px;
    line-height: 36px;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
    /*
     * `.numeric` alinea a la derecha porque nació para columnas de tabla, donde
     * eso es lo que hace cuadrar las cifras. Acá no hay columna que cuadrar: hay
     * una etiqueta arriba a la izquierda y su número debajo, y separarlos a los
     * dos extremos de la tarjeta obliga a leer en diagonal.
     */
    text-align: left;
}

/* En pantalla angosta el Display se achica: 32 px por seis cifras no entra. */
@media (max-width: 480px) {
    .metric__value {
        font-size: 24px;
        line-height: 32px;
    }

    .metric__value.numeric {
        font-size: 21px;
        line-height: 28px;
    }
}

/* ── Prioridades de hoy ─────────────────────────────────────────────────── */
/*
 * Las filas de 56 px del Lineamiento §8: ícono, cliente, motivo y una acción
 * explícita a la derecha. La fila entera es clickeable —el enlace del título
 * ocupa su área con un pseudoelemento— pero el botón de la derecha sigue siendo
 * un objetivo propio, para que en una tablet nadie tenga que apuntar fino.
 */

.priority-list {
    list-style: none;
    margin: var(--sp-3) 0 0;
    padding: 0;
}

.priority-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    min-height: 56px;
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--n-200);
}

.priority-row:last-child {
    border-bottom: 0;
}

.priority-row__icon {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 40px;
    width: 40px;
    height: 40px;
    border-radius: var(--r-md);
    background-color: var(--n-100);
    color: var(--n-500);
}

.priority-row__body {
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 1 1 auto;
    min-width: 0;
}

/*
 * El enlace del título cubre la fila. El botón de la derecha se levanta con
 * `position: relative` para quedar por encima y seguir siendo clickeable por su
 * cuenta.
 */
.priority-row__link::after {
    content: '';
    position: absolute;
    inset: 0;
}

.priority-row__meta {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: var(--sp-1);
    flex: 0 0 auto;
}

.priority-row__action {
    position: relative;
    flex: 0 0 auto;
}

/* En celular la fila se apila y el botón pasa a ancho completo. */
@media (max-width: 768px) {
    .priority-row {
        flex-wrap: wrap;
    }

    .priority-row__meta {
        align-items: flex-start;
        flex-direction: row;
        flex-basis: 100%;
    }

    .priority-row__action {
        flex-basis: 100%;
    }

    .priority-row__action .button {
        width: 100%;
    }
}

/* ── Pipeline por estado ────────────────────────────────────────────────── */
/*
 * Una fila por estado: el chip con el nombre configurado, la barra con la
 * proporción de operaciones, el conteo y el importe. La barra es decorativa
 * —`aria-hidden`— porque el número está escrito al lado: un lector de pantalla
 * no gana nada leyendo un ancho.
 */

.pipeline {
    list-style: none;
    margin: var(--sp-3) 0 0;
    padding: 0;
}

.pipeline__row {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--n-200);
}

.pipeline__row:last-child {
    border-bottom: 0;
}

.pipeline__row .chip {
    flex: 0 0 auto;
}

.pipeline__row .numeric {
    margin-left: auto;
}

.pipeline__bar {
    flex: 1 1 auto;
    min-width: 40px;
    height: 4px;
    border-radius: var(--r-pill);
    background-color: var(--n-200);
    overflow: hidden;
}

.pipeline__value {
    display: block;
    height: 100%;
    background-color: var(--chart-3);
}

/* ── Barra de ranking ───────────────────────────────────────────────────── */
/*
 * Ocupa un renglón propio debajo de la fila y se compara contra la más alta, no
 * contra la suma. Antes iba pegada bajo el nombre y a 3 px de alto: así no se
 * leía como una medida sino como un subrayado del texto. Ahora va separada 8 px,
 * a lo ancho de la tabla y en arena de marca; el amarillo sigue reservado para
 * la serie actual (Lineamiento §8).
 */

.ranking-bar-row td {
    border-bottom: 0;
    padding: 0 var(--sp-3) var(--sp-3);
}

.data-table tbody tr.ranking-bar-row:hover {
    background-color: transparent;
}

.ranking-bar {
    display: block;
    height: 4px;
    border-radius: var(--r-pill);
    background-color: var(--n-200);
    overflow: hidden;
}

.ranking-bar__value {
    display: block;
    height: 100%;
    border-radius: var(--r-pill);
    background-color: var(--brand-sand);
}

/* En celular la fila se apila como tarjeta: la barra es la última línea de esa
 * tarjeta, sin etiqueta propia y sin abrir una tarjeta nueva. */
@media (max-width: 767px) {
    .ranking-bar-row td.ranking-bar-row__cell {
        display: block;
        padding: 0 var(--sp-4) var(--sp-3);
    }

    .ranking-bar-row td.ranking-bar-row__cell::before {
        content: none;
    }

    .ranking-bar-row {
        border-bottom: 1px solid var(--n-200);
        padding-top: 0;
    }

    .ranking-row {
        border-bottom: 0;
        padding-bottom: 0;
    }
}
