/* ============================================================
   HUF — Composer de nueva venta (.hd-composer) — Discoteca/Caja
   ------------------------------------------------------------
   El modal donde el cajero arma un pedido: catálogo a la izquierda, pedido en
   curso a la derecha. Es una pantalla de trabajo, no un formulario.

   Las cuatro secciones de la vista en vivo (mesas · mostrador · domicilios ·
   para recoger) NO tienen estilos propios: son <hd-tabs> del catálogo, con el
   conteo en el título de cada pestaña.

   Sin TagHelper, igual que .hd-order-card (ver panel-card.css §OrderCard):
   JavaScript regenera parte de este markup en cada evento SignalR y un
   TagHelper no es invocable desde JS. El contrato público es la clase.
   ============================================================ */

/* ------------------------------------------------------------
   Composer de nueva venta (modal "Nueva Venta")
   ------------------------------------------------------------
   Dos columnas: catálogo a la izquierda, pedido en curso a la derecha.
   Antes todo iba apilado en una sola columna de 960px con el carrito
   incrustado en el footer del modal: con varios productos y fotos, el
   catálogo empujaba el carrito a una franja de ~120px y armar un pedido
   de mesa era un ejercicio de scroll.

   El carrito vive DENTRO del cuerpo, no en el footer, justamente para que
   pueda crecer y scrollear por su cuenta sin robarle alto al catálogo.
   ------------------------------------------------------------ */
/* La región de contenido del composer NO scrollea: scrollea cada columna por su
   lado. Si scrollea la región, bajar por el catálogo se lleva el carrito y su
   total fuera de la pantalla.

   La clase estaba en el markup de Venta.cshtml desde el primer día, con este
   mismo comentario, pero **nunca existió la regla** — se escribió la intención y
   no el CSS. Daba igual mientras la cáscara no acotaba nada (ver shell.css,
   `.hd-shell__content`): sin alto definido no había scroll que apagar. Al acotarla
   sí lo hay, y esta regla es la que decide quién se lo queda. */
.hd-page__content--composer {
    overflow: hidden;
}

.hd-composer {
    display: grid;
    /* minmax(0,·) en ambas: sin eso, una tarjeta de producto de nombre largo
       ensancha su columna de grid por encima del contenedor (min-width:auto
       es el default de un ítem de grid) y el modal desborda a lo ancho. */
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    gap: var(--hd-space-5);
    align-items: stretch;
    height: 100%;
    min-height: 0;
}

/* Estado en vivo (conexión al hub · sonido · avisos) alojado en la fila del
   TÍTULO en vez de en una región B propia — el porqué está en Venta.cshtml, junto
   al markup. Hereda la escala de meta y el color apagado que tenía en la banda,
   para que siga leyéndose como estado y no como una acción de cabecera. */
.hd-composer__estado {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--hd-space-2) var(--hd-space-4);
    font-size: var(--hd-text-meta);
    color: var(--hd-theme-text-muted);
}

/* Categorías: por defecto (página angosta, <1440px) un carril horizontal a
   sangre encima de catálogo y pedido — mismo lugar y forma que tenían
   siempre. `order` fuerza el bloque a ir primero aunque en el DOM esté antes
   de `.hd-composer__catalogo` por razones del riel vertical de abajo. Ver
   @media (min-width: 90rem) para la versión de columna propia. */
.hd-composer__cats {
    grid-column: 1 / -1;
    order: -1;
    min-width: 0;
}

/* Ficha de producto — badge de cantidad. Oculto por defecto (atributo
   `hidden`, HD.js lo destapa vía sincronizarCatalogoConCarrito()) y no
   `display:none` a mano: mismo contrato que el resto de la pantalla. */
.hd-composer__producto-badge {
    position: absolute;
    top: 6px;
    right: 6px;
    z-index: 1;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: var(--hd-radius-pill);
    background: var(--hd-theme-accent);
    color: var(--hd-color-accent-contrast);
    font-size: var(--hd-text-micro);
    font-weight: 800;
    line-height: 20px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* El "−" que resta una unidad SIN pasar por el carrito — solo para productos
   sin grupos de opciones ni adicionales (ADR-013/027): con variantes, quitar
   "una unidad" es ambiguo (¿cuál papa francesa o criolla?), así que esos
   productos no lo reciben — Venta.cshtml no imprime el botón en ese caso. */
.hd-composer__producto-restar {
    position: absolute;
    top: 6px;
    left: 6px;
    z-index: 1;
}

.hd-composer__producto-restar button {
    width: 22px;
    height: 22px;
    border: 1px solid var(--hd-color-border-strong);
    border-radius: var(--hd-radius-sm);
    background: var(--hd-theme-surface);
    color: var(--hd-theme-text);
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
}

.hd-composer__producto-restar button:hover {
    border-color: var(--hd-theme-accent);
    color: var(--hd-theme-accent);
}

/* La ficha necesita position:relative para anclar badge/restar a sus
   esquinas — el resto de sus reglas siguen más abajo, sin tocarlas. */
.hd-composer__producto {
    position: relative;
}

/* Ya está en el pedido: el acento es lo único que HVL reserva para "activo",
   y una ficha en el carrito es justamente eso — no decoración. */
.nv-producto--en-carrito .hd-composer__producto {
    border-color: var(--hd-theme-accent);
}

/* ── Stock: lo que no alcanza (ADR-057) ──────────────────────────────────
   La ficha agotada se atenúa pero SIGUE EN EL GRID y sigue legible: el
   cajero necesita ver que el producto existe y está en cero, que es el dato
   con el que va a reponerlo. Ocultarla lo dejaría creyendo que se borró del
   catálogo. El cursor y el aria-disabled del markup son los que dicen que no
   se puede pulsar; esto es solo el acompañamiento visual. */
.nv-producto.is-agotado .hd-composer__producto {
    opacity: .55;
    border-style: dashed;
}

.nv-producto.is-agotado .hd-composer__producto[aria-disabled="true"] {
    cursor: not-allowed;
}

/* El número de unidades que quedan, bajo el precio. Contador, no insignia de
   estado: va en la escala de meta y sin fondo para no competir con el precio,
   que es lo que el cajero lee primero. */
.nv-producto__stock {
    margin-top: var(--hd-space-1);
    font-size: var(--hd-text-meta);
    font-weight: 600;
    color: var(--hd-color-warning);
}

.nv-producto__stock--agotado {
    color: var(--hd-color-error);
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* ── "Ya en la mesa": a dónde va lo que se agrega (ADR-031) ──────────────
   La pregunta y las tarjetas de destino, cuando la mesa ya tiene pedidos
   abiertos. No es un <select>: son tres o cuatro opciones como mucho, cada
   una con su total y su número de ronda — datos que un desplegable esconde
   justo cuando son lo que hay que comparar para decidir. */
.hd-composer__ask {
    margin: var(--hd-space-3) 0 0;
    color: var(--hd-theme-text-muted);
    font-size: var(--hd-text-meta);
    line-height: 1.45;
}

.hd-composer__tiles {
    display: flex;
    flex-direction: column;
    gap: var(--hd-space-2);
    margin-top: var(--hd-space-2);
}

.hd-composer__tile {
    display: flex;
    align-items: center;
    gap: var(--hd-space-2);
    width: 100%;
    padding: var(--hd-space-2) var(--hd-space-3);
    border: 1px solid var(--hd-color-border-strong);
    border-radius: var(--hd-radius-sm);
    background: var(--hd-theme-surface);
    color: var(--hd-theme-text);
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.hd-composer__tile:hover {
    border-color: var(--hd-theme-accent);
}

/* Elegido = "activo", el único significado que HVL §5.3 le da al acento. */
.hd-composer__tile[aria-pressed="true"] {
    border-color: var(--hd-theme-accent);
    background: rgba(var(--hd-theme-accent-rgb), 0.10);
}

.hd-composer__tile-check {
    display: grid;
    flex: 0 0 auto;
    place-items: center;
    width: 20px;
    height: 20px;
    border: 1.5px solid var(--hd-color-border-strong);
    border-radius: 50%;
    color: transparent;
}

.hd-composer__tile[aria-pressed="true"] .hd-composer__tile-check {
    border-color: var(--hd-theme-accent);
    background: var(--hd-theme-accent);
    color: var(--hd-color-accent-contrast);
}

.hd-composer__tile-cuerpo {
    display: flex;
    flex: 1;
    flex-direction: column;
    min-width: 0;
}

.hd-composer__tile-titulo {
    font-size: var(--hd-text-caption);
    font-weight: 700;
}

.hd-composer__tile-meta {
    margin-top: 2px;
    color: var(--hd-theme-text-muted);
    font-size: var(--hd-text-micro);
    font-variant-numeric: tabular-nums;
}

.hd-composer__tile-monto {
    flex: 0 0 auto;
    color: var(--hd-theme-text-muted);
    font-size: var(--hd-text-caption);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

/* Las rondas que el pedido elegido ya tiene — solo lectura: es lo que cocina
   ya recibió, y por eso no se edita desde acá (para eso está el lápiz del
   tablero, que sí puede corregir). */
.hd-composer__rondas {
    display: flex;
    flex-direction: column;
    gap: var(--hd-space-2);
    margin-top: var(--hd-space-3);
}

.hd-composer__ronda {
    display: flex;
    align-items: center;
    gap: var(--hd-space-2);
    padding: var(--hd-space-2);
    border-radius: var(--hd-radius-sm);
    background: rgba(255, 255, 255, 0.03);
    color: var(--hd-theme-text-muted);
    font-size: var(--hd-text-meta);
}

.hd-composer__ronda-n {
    display: grid;
    flex: 0 0 auto;
    place-items: center;
    width: 26px;
    height: 20px;
    border-radius: 5px;
    background: rgba(255, 255, 255, 0.06);
    font-size: var(--hd-text-micro);
    font-weight: 800;
}

.hd-composer__ronda-items {
    flex: 1;
    min-width: 0;
}

.hd-composer__ronda-monto {
    flex: 0 0 auto;
    color: var(--hd-theme-text);
    font-variant-numeric: tabular-nums;
}

/* Resumen de una línea cuando el bloque está plegado ("Mesa 4 · Consumir en
   el local"): lo pone actualizarResumenDestino(), visible solo mientras
   aria-expanded="false" — la cabecera abierta ya muestra los campos, repetir
   el resumen ahí sería ruido. */
.hd-composer__bloque-resumen {
    margin-left: auto;
    /* Un ítem flex no encoge por debajo de su contenido sin esto, así que el
       `text-overflow` de abajo nunca llegaba a activarse: el resumen empujaba la
       cabecera a lo ancho y la columna lo recortaba en seco (tiene
       `overflow-x: hidden`). Se nota desde que el bloque del cliente también
       lleva resumen — "Ivonne Marcela · 3001234567 · cr9#42-58" no cabe. */
    min-width: 0;
    overflow: hidden;
    color: var(--hd-theme-text-muted);
    font-size: var(--hd-text-meta);
    font-weight: 500;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Quien cede el ancho es el resumen, nunca el título: sin esto el título es el
   que encoge hasta su palabra más larga y "Datos del cliente (opcional)" se
   parte en dos renglones, con la cabecera cambiando de alto al plegar. */
.hd-composer__bloque-head > span:not(.hd-composer__bloque-resumen) {
    white-space: nowrap;
}

.hd-composer__bloque-head[aria-expanded="true"] .hd-composer__bloque-resumen {
    display: none;
}

/* Quien scrollea es CADA COLUMNA, no el cuerpo del modal. Con el scroll en el
   cuerpo, bajar por el catálogo se llevaba el carrito y su total fuera de la
   pantalla — verificado en navegador: con 6 líneas el total ya no se veía sin
   scrollear. Así el catálogo se recorre sin mover el pedido. */
.hd-modal--xl .hd-modal__body {
    overflow: hidden;
}

.hd-composer__catalogo,
.hd-composer__pedido {
    min-width: 0;
    min-height: 0;
    max-height: 100%;
    overflow-y: auto;
    /* Los `.row` de Bootstrap llevan márgenes negativos de gutter; al darle
       scroll propio a la columna eso se convierte en una barra horizontal real
       (vista en navegador bajo el catálogo). Vertical sí, horizontal nunca. */
    overflow-x: hidden;
}

/* Buscador + chips de categoría: se quedan arriba mientras el grid de
   productos scrollea debajo. */
.hd-composer__filtros {
    position: sticky;
    top: 0;
    z-index: 2;
    padding-bottom: var(--hd-space-2);
    background: var(--hd-theme-surface);
}

/* Carril horizontal, NO una grilla que envuelve. Con una carta real de 17
   categorías los chips ocupaban 168px en seis filas — el 42% de la columna del
   catálogo a 1366×768, medido en navegador — y el operario tenía que bajar antes
   de ver el primer producto. En una sola fila son 36px. */
.hd-composer__chips {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    gap: var(--hd-space-2);
    margin-top: var(--hd-space-2);
    padding-bottom: var(--hd-space-1);
    scrollbar-width: thin;
    scroll-snap-type: x proximity;
    /* El carril se desplaza con la rueda del mouse sin robarle el scroll vertical
       al catálogo: el navegador lo resuelve solo mientras el carril no lo propague. */
    overscroll-behavior-x: contain;
    /* Desvanecido en el borde derecho: sin él la última categoría queda cortada a
       mitad de palabra y parece un defecto en vez de "hay más, desliza". La máscara
       se aplica a la caja del carril, no al contenido, así que no viaja al scrollear.
       Donde no haya soporte simplemente no se pinta — el carril sigue funcionando. */
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
    mask-image: linear-gradient(to right, #000 calc(100% - 2.5rem), transparent);
}

.hd-composer__chips > .hd-btn {
    flex: 0 0 auto;
    white-space: nowrap;
    scroll-snap-align: start;
}

/* Cuántos productos hay detrás del chip: la carta de un restaurante tiene
   categorías de dos productos y de veinte, y el operario no tiene por qué
   descubrirlo tocando una por una. */
.hd-composer__chip-conteo {
    margin-left: var(--hd-space-1);
    opacity: 0.65;
    font-variant-numeric: tabular-nums;
}

/* Rejilla del catálogo: se mide contra el ancho REAL de la columna. Los
   `col-6 col-md-4 col-xl-3` de Bootstrap miran el viewport, así que un monitor
   grande forzaba 4 tarjetas por fila dentro de 742px (188px cada una) y partía
   los nombres largos. */
.hd-composer__grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: var(--hd-space-2);
    margin-top: var(--hd-space-2);
    align-items: stretch;
}

/* El filtrado usa el atributo `hidden`; un ítem de grid ignora el display:none
   del user-agent si algo le fija display, así que se declara explícito. */
.nv-producto[hidden] {
    display: none;
}

/* Rejilla de los campos del pedido (destino y datos del cliente): mismo motivo
   que la del catálogo. `col-xl-7`/`col-xl-5` respondían al monitor y partían en
   dos una columna de 464px, dejando selects de ~180px que mostraban "Cor" en vez
   de "Consumir en el local". Aquí manda el ancho de la columna. */
.hd-composer__campos {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
    gap: var(--hd-space-2);
}

.hd-composer__campo--full {
    grid-column: 1 / -1;
}

/* Estado vacío del catálogo: antes la rejilla quedaba en blanco y no se sabía si
   el producto no existe o si un filtro lo está escondiendo. */
.hd-composer__vacio {
    margin: var(--hd-space-5) 0 0;
    padding: 0 var(--hd-space-3);
    text-align: center;
    color: var(--hd-theme-text-muted);
}

/* Columna derecha en flex vertical: los bloques de arriba (destino, cliente)
   ocupan lo suyo y el del carrito se queda con el resto, para que el total y
   el último producto agregado se vean siempre sin scrollear la columna. */
.hd-composer__pedido {
    display: flex;
    flex-direction: column;
}

.hd-composer__bloque--carrito {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    /* Piso: con "Datos del cliente" abierto (un domicilio) el carrito recibe muy
       poco sobrante y se comprimía hasta mostrar una sola línea. 16rem deja
       siempre unas tres a la vista, que es lo mínimo para revisar un pedido
       antes de confirmarlo. */
    min-height: 16rem;
}

.hd-composer__bloque {
    padding: var(--hd-space-4);
    border-radius: var(--hd-radius-md);
    background: var(--hd-color-inset);
    /* Sin esto un ítem flex no encoge por debajo de su contenido, y con los tres
       bloques abiertos la columna no cuadra: medido a 1366×768, "Destino" (376px)
       + "Datos del cliente" (67) + el piso del carrito (256) piden 723px dentro de
       467 disponibles. El desbordado terminaba siendo el carrito, que quedaba
       ENTERO fuera de vista — el cajero veía dos formularios y ningún pedido. */
    min-height: 0;
}

/* Quién cede el alto cuando no alcanza: los bloques de arriba, nunca el carrito.
   El carrito es el objeto dominante de esta columna (HVL §8.3, "el contenido
   operativo tiene prioridad sobre el chrome"): destino y datos del cliente se
   llenan una vez al empezar, el carrito se mira en cada producto que se agrega y
   es donde vive el total que el cajero le canta al cliente.

   Scrollean por dentro en vez de recortarse, así que a 768px no se pierde ningún
   campo: se baja dentro del bloque. De 900px para arriba los tres caben y esto
   no se activa. */
.hd-composer__bloque:not(.hd-composer__bloque--carrito) {
    overflow-y: auto;
}

.hd-composer__bloque + .hd-composer__bloque {
    margin-top: var(--hd-space-3);
}

/* ── Un bloque PLEGADO es un renglón, no una tarjeta ─────────────────────
   El estado normal de una venta de mostrador es con destino y datos del cliente
   PLEGADOS: el cajero no los toca. Aun así cada uno cobraba la caja entera —
   16px de padding arriba y abajo, más los 12px con que su cabecera se separa de
   un cuerpo que está `hidden`— para enseñar un renglón de texto.

   Medido a 1280×608 (que es 1920×912 al 150% de zoom, el caso del operador que
   agranda la letra porque no ve bien): los dos bloques plegados se llevaban 158
   de los 364px de la columna del pedido —el 43%— y al carrito le quedaban 74px
   de lista. Con líneas de 61px eso es UNA línea: el pedido de cuatro productos
   de la captura no se podía revisar sin scrollear dentro de una ranura.

   `flex-shrink: 0` porque un bloque plegado YA ESTÁ en su mínimo: dejarlo
   encoger le sacaba su propia barra de scroll a un solo renglón (medido a
   1024×560, donde los dos quedaban en 59px con 67 de contenido). Quien cede
   alto es el carrito, que para eso tiene scroll propio y un piso. */
.hd-composer__bloque:has(> .hd-composer__bloque-head[aria-expanded="false"]) {
    padding-block: var(--hd-space-2);
    flex-shrink: 0;
}

.hd-composer__bloque:has(> .hd-composer__bloque-head[aria-expanded="false"]) > .hd-composer__bloque-head {
    margin-bottom: 0;
}

.hd-composer__bloque-head {
    display: flex;
    align-items: center;
    gap: var(--hd-space-2);
    font-weight: 600;
    margin-bottom: var(--hd-space-3);
}

.hd-composer__bloque-head .hd-badge {
    margin-left: auto;
}

/* Cabecera de un bloque plegable: es un <button> real (foco, teclado,
   aria-expanded) disfrazado de encabezado. */
button.hd-composer__bloque-head {
    width: 100%;
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.hd-composer__bloque-head .hd-composer__chevron {
    transition: transform var(--hd-motion-standard) var(--hd-ease);
}

.hd-composer__bloque-head[aria-expanded="true"] .hd-composer__chevron {
    transform: rotate(180deg);
}

/* Lista del carrito: se queda con el alto sobrante del bloque y scrollea sola.
   Sin esto, un pedido de veinte líneas empuja el total fuera de la pantalla —
   que es justo lo que hacía el carrito viejo dentro del footer del modal.
   El contenedor intermedio (#nvCarritoContainer) tiene que repartir el flex
   igual, o la lista no encoge y vuelve a desbordar. */
.hd-composer__bloque--carrito > div:not(.hd-composer__bloque-head) {
    display: flex;
    flex-direction: column;
    flex: 1 1 auto;
    min-height: 0;
}

.hd-composer__carrito {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    margin: 0 calc(var(--hd-space-2) * -1);
    padding: 0 var(--hd-space-2);
}

.hd-composer__linea {
    display: flex;
    align-items: flex-start;
    gap: var(--hd-space-2);
    padding: var(--hd-space-2) 0;
    border-bottom: 1px solid var(--hd-color-border);
}

.hd-composer__linea:last-child {
    border-bottom: 0;
}

.hd-composer__linea-nombre {
    flex: 1;
    min-width: 0;
}

.hd-composer__linea-precio {
    font-variant-numeric: tabular-nums;
    color: var(--hd-color-success);
    white-space: nowrap;
}

/* Control de cantidad − N + : antes solo existía el "−" y sumar una unidad
   obligaba a volver a buscar el producto en el catálogo. */
.hd-composer__cantidad {
    display: inline-flex;
    align-items: center;
    gap: var(--hd-space-1);
}

.hd-composer__cantidad-valor {
    min-width: 1.75rem;
    text-align: center;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

/* Pegado al fondo de la columna: con el bloque de datos del cliente abierto (un
   domicilio) la columna sí necesita scroll, y sin esto el total quedaba fuera
   de la pantalla — el cajero apretaba "Confirmar orden" sin haber visto nunca
   cuánto suma el pedido. Verificado en navegador antes de este cambio. */
.hd-composer__total {
    position: sticky;
    bottom: 0;
    display: flex;
    flex: 0 0 auto;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--hd-space-2);
    margin-top: var(--hd-space-3);
    padding-top: var(--hd-space-3);
    border-top: 1px solid var(--hd-color-border);
    background: var(--hd-color-inset);
    font-size: var(--hd-text-title);
    font-weight: 700;
}

/* Desglose sobre el total: subtotal, impuestos y descuentos. Se pega al fondo junto
   con .hd-composer__total (que trae el borde y el fondo), así que acá NO se repite
   el border-top: serían dos líneas seguidas separando lo mismo. Peso y tamaño de
   texto secundarios a propósito — lo que el cajero canta es el Total, esto es el
   respaldo de por qué esa cifra no es la suma simple de los precios de arriba. */
.hd-composer__subtotales {
    position: sticky;
    bottom: calc(var(--hd-text-title) + var(--hd-space-5));
    flex: 0 0 auto;
    margin-top: var(--hd-space-3);
    padding-top: var(--hd-space-2);
    border-top: 1px solid var(--hd-color-border);
    background: var(--hd-color-inset);
}

.hd-composer__subtotales + .hd-composer__total {
    margin-top: 0;
    border-top: 0;
}

.hd-composer__subtotal {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--hd-space-2);
    color: var(--hd-theme-text-muted);
    font-size: var(--hd-text-meta);
}

/* Ficha de producto del catálogo. Clase propia y no .product-card-selectable
   (admin-portal.css): esa la comparte Ventas/Index y no debe cambiar de
   tamaño por esta pantalla. */
.hd-composer__producto {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
    border: 1px solid var(--hd-color-border);
    border-radius: var(--hd-radius-md);
    background: var(--hd-theme-surface);
    cursor: pointer;
    text-align: center;
    transition: border-color var(--hd-motion-standard) var(--hd-ease),
                transform var(--hd-motion-standard) var(--hd-ease);
}

.hd-composer__producto:hover,
.hd-composer__producto:focus-visible {
    border-color: var(--hd-theme-accent);
    transform: translateY(-2px);
}

/* Imagen con proporción fija: el catálogo real trae fotos de tamaños
   dispares y sin esto cada fila del grid quedaba de una altura distinta. */
.hd-composer__producto-img {
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: cover;
    background: var(--hd-color-inset);
}

.hd-composer__producto-cuerpo {
    display: flex;
    flex: 1;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
    padding: var(--hd-space-2);
}

/* Dos líneas y elipsis: los nombres de una carta real ("Hamburguesa doble carne",
   "Pechuga a la plancha") no caben en una y sin tope una tarjeta de cinco líneas
   desalineaba toda la fila. */
.hd-composer__producto-nombre {
    font-size: var(--hd-text-caption);
    font-weight: 600;
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.hd-composer__producto-precio {
    font-size: var(--hd-text-caption);
    font-weight: 600;
    color: var(--hd-color-success);
    font-variant-numeric: tabular-nums;
}

/* "+8% imp." junto al precio, solo en los productos que manejan impuesto
   (ADR-037). Caja y el portal público muestran el mismo precio sin impuesto,
   y esta insignia es lo que evita que el cajero tenga que explicar de dónde
   sale la diferencia entre el precio del plato y el total del pie. Va en
   texto apagado y peso normal: acompaña al precio, no compite con él. */
.hd-composer__producto-tax {
    font-size: var(--hd-text-meta);
    font-weight: 400;
    color: var(--hd-theme-text-muted);
    margin-left: var(--hd-space-1);
    white-space: nowrap;
}

/* Tres columnas desde 1440px: categorías en riel vertical propio, en vez del
   carril horizontal que sigue sirviendo por debajo de este ancho. Por qué
   1440 y no antes: a 1366×768 —el laptop más angosto que se prueba— la
   columna del catálogo ya se aprieta con dos columnas; sumar una tercera ahí
   habría repetido el mismo error que corrigió el carril en 2026-09-05 (ver
   arriba), solo que en vertical. El riel solo gana su lugar cuando sobra
   ancho de verdad. */
@media (min-width: 90rem) {
    .hd-composer {
        grid-template-columns: 12.5rem minmax(0, 1.6fr) minmax(0, 1fr);
    }

    /* Vuelve a su lugar natural en el grid — ya no necesita el `order`/
       `grid-column` a sangre que lo pone arriba en pantallas angostas. */
    .hd-composer__cats {
        grid-column: auto;
        order: 0;
        max-height: 100%;
        overflow-y: auto;
    }

    /* El MISMO markup y las MISMAS clases del carril (`.hd-composer__chips`,
       `.hd-btn` hijos) — filtrarCategoria()/marcarChipCategoriaNV() no se
       tocan, solo cambia cómo se ve. Columna en vez de fila, botones a lo
       ancho y alineados a la izquierda; sin scroll horizontal ni máscara de
       desvanecido, que son cosas de un carril, no de una lista. */
    .hd-composer__chips {
        flex-direction: column;
        flex-wrap: nowrap;
        overflow-x: visible;
        overflow-y: auto;
        margin-top: 0;
        padding-bottom: 0;
        scroll-snap-type: none;
        -webkit-mask-image: none;
        mask-image: none;
    }

    .hd-composer__chips > .hd-btn {
        justify-content: flex-start;
        width: 100%;
    }

    .hd-composer__chips > .hd-btn .hd-composer__chip-conteo {
        margin-left: auto;
    }
}

/* ── El composer cede por ALTURA ─────────────────────────────────────────
   Misma idea y mismos cortes que los escalones de cromo de ADR-079
   (shell.css + page-header.css): la ventana corta no es un dispositivo, es un
   recurso escaso, y `max-height` se evalúa en píxeles CSS, así que el zoom del
   navegador lo resuelve solo. El operador que pone el navegador al 150% porque
   no ve bien pasa de 912 a 608px CSS y entra acá sin que nadie detecte nada.

   Lo que cede es la FOTO del producto, no la tarjeta ni el texto. En un
   catálogo con imágenes la foto es lo más alto de la ficha con diferencia
   (4/3 sobre ~190px de ancho son 142px de los ~190 que mide la tarjeta), y es
   lo que menos hace falta para reconocer un producto que el cajero ya conoce de
   memoria: el nombre y el precio se quedan en su tamaño. Con `object-fit:cover`
   la foto se recorta, no se aplasta.

   `max-height` además del `aspect-ratio`: sin el tope, una columna de catálogo
   ancha (1920) devuelve la foto a su altura original y el escalón no se nota
   justo donde hay más productos por fila. */
@media (min-width: 992px) and (max-height: 850px) {
    .hd-composer__producto-img {
        aspect-ratio: 3 / 2;
        max-height: 6.5rem;
    }
}

@media (min-width: 992px) and (max-height: 700px) {
    .hd-composer__producto-img {
        aspect-ratio: 16 / 9;
        max-height: 4.75rem;
    }

    /* El piso del carrito baja con la ventana. 16rem se pensó para que con los
       datos del cliente ABIERTOS quedaran tres líneas a la vista; por debajo de
       700px de ventana ese mismo número deja de ser un piso y pasa a ser una
       exigencia que la columna no puede pagar — el sobrante lo aplastaban los
       vecinos (medido a 1024×560: bloques plegados con barra de scroll propia).
       11rem sigue dejando dos líneas y el total, y deja de romper la columna. */
    .hd-composer__bloque--carrito {
        min-height: 11rem;
    }

    /* El aire interno de los bloques, incluido el del carrito: 16px por lado en
       una columna de 316px es cromo caro. No se toca el ancho —los selects de
       destino necesitan su sangría— solo el alto. */
    .hd-composer__bloque {
        padding-block: var(--hd-space-3);
    }

    /* El resto de los costos fijos de la columna del pedido. Ninguno de estos
       solo vale la pena; juntos son ~40px, que a 62px por línea es una línea
       más de carrito — y la línea que se gana acá es la diferencia entre
       revisar un pedido y adivinarlo.

       LO QUE NO SE APRIETA, A PROPÓSITO: el alto de la LÍNEA del carrito. Sus
       botones − / + son blancos táctiles de un POS que se opera con el dedo;
       recortarlos cambia un problema visible por errores de venta (misma
       frontera que `--hd-size-control` en page-header.css). El tipo de letra
       tampoco: el operador que agranda el navegador lo hace porque no ve. */
    .hd-composer__bloque-head {
        margin-bottom: var(--hd-space-1);
    }

    .hd-composer__bloque + .hd-composer__bloque {
        margin-top: var(--hd-space-2);
    }

    /* El desglose se pega al total; con el total justo debajo, el aire de
       arriba ya lo pone el borde. */
    .hd-composer__subtotales {
        margin-top: var(--hd-space-2);
        padding-top: var(--hd-space-1);
        /* El `bottom` del desglose tiene que valer lo que MIDE el total, que
           acaba de adelgazar 4px con su padding: si no se recalcula, queda una
           rendija por la que se ven pasar las líneas al scrollear. */
        bottom: calc(var(--hd-text-title) + var(--hd-space-4));
    }

    .hd-composer__total {
        margin-top: var(--hd-space-2);
        padding-top: var(--hd-space-2);
    }

    /* El carril de categorías va a sangre sobre LAS DOS columnas, así que lo
       que se le quite lo cobran las dos. Su margen superior era separación de
       un encabezado que en esta ventana ya casi no existe. */
    .hd-composer__chips {
        margin-top: 0;
    }
}

/* Una sola columna por debajo de 992px: en tablet el catálogo va arriba y el
   pedido debajo, ambos sin sticky (dos zonas pegajosas en una pantalla
   angosta dejan sin espacio útil al contenido). */
@media (max-width: 991.98px) {
    .hd-composer {
        grid-template-columns: minmax(0, 1fr);
        height: auto;
    }

    /* En una columna vuelve a scrollear el cuerpo del modal: dos zonas de
       scroll apiladas en una pantalla angosta dejan sin espacio útil a ambas. */
    .hd-modal--xl .hd-modal__body {
        overflow-y: auto;
    }

    .hd-composer__catalogo,
    .hd-composer__pedido,
    .hd-composer__carrito {
        max-height: none;
        overflow: visible;
    }

    .hd-composer__filtros {
        position: static;
    }

    /* El carril de categorías se queda: en una pantalla angosta es donde más
       daño hacía la grilla de chips envolviéndose en ocho filas. */
    .hd-composer__chips {
        flex-wrap: nowrap;
        overflow-x: auto;
    }

    /* ── Objetivo táctil ───────────────────────────────────────────
       Un dedo no es un cursor. Los mínimos son 44px (iOS) y 48px (Android), y
       medidos a 390px estos controles quedaban por debajo:

         · chips de categoría ............ 36px de alto
         · − / + / nota del carrito ...... 46×36
         · cabecera plegable de bloque ... 23px de alto

       Los del carrito son los peores: es lo que el cajero más toca —ajustar
       cantidades— y fallar el toque ahí cuesta una línea mal cobrada.

       Va en este corte y no en una consulta `pointer: coarse` porque acá el
       layout ya está apilado y sobra alto. En una caja táctil de escritorio el
       argumento sería el mismo, pero subir 8px por línea aprieta la columna del
       pedido a 1366×768 y eso no se puede afirmar sin probarlo en ese hardware. */
    .hd-composer__chips .hd-btn,
    .hd-composer__cantidad .hd-btn,
    .hd-composer__linea .hd-btn {
        min-height: 44px;
        min-width: 44px;
    }

    .hd-composer__bloque-head {
        min-height: 44px;
    }

    /* ── Píldora "Ver carrito" ─────────────────────────────────────
       Apilado, el carrito queda debajo de TODO el catálogo: medido a 360px,
       hay que bajar 835px para llegar al total, y con la carta de un cliente
       real (99 productos) son varias pantallas más. El cajero veía
       "Confirmar orden" siempre —el footer es pegajoso— y no sabía cuánto
       estaba por cobrar.

       Va SOBRE el footer y no dentro: el footer es de acciones, esto es
       información y un atajo. Se esconde sola cuando el carrito está vacío
       (no hay total que enseñar) y cuando ya está a la vista (no tiene nada
       que ofrecer) — si no, taparía justo lo que el cajero fue a mirar. */
    .hd-composer__ir-carrito:not([hidden]) {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: var(--hd-space-3);
    }
}

/* Fuera del apilado no existe: en dos columnas el carrito está siempre a la
   vista, al lado del catálogo. Se declara acá, fuera de la consulta de medios,
   para que el estado por defecto sea "oculta" venga de donde venga. */
.hd-composer__ir-carrito {
    display: none;
}

@media (max-width: 991.98px) {
    .hd-composer__ir-carrito {
        position: fixed;
        left: var(--hd-space-4);
        right: var(--hd-space-4);
        /* El footer mide distinto según quepa en una o en dos filas, así que
           el alto lo publica él mismo en --hd-pie-alto (ver actualizarPieVenta). */
        bottom: calc(var(--hd-pie-alto, 4rem) + var(--hd-space-3));
        z-index: var(--hd-z-dropdown);
        min-height: 48px;
        padding: 0 var(--hd-space-4);
        border: 0;
        border-radius: var(--hd-radius-pill);
        background: var(--hd-theme-accent);
        color: var(--hd-color-accent-contrast);
        font: inherit;
        font-weight: 700;
        font-variant-numeric: tabular-nums;
        box-shadow: var(--hd-shadow-floating);
        cursor: pointer;
    }

    .hd-composer__ir-carrito-izq {
        display: inline-flex;
        align-items: center;
        gap: var(--hd-space-2);
    }

    /* El conteo, en una insignia sobre el propio acento: el número es el dato
       que el cajero busca de reojo para saber si ya metió todo. */
    .hd-composer__ir-carrito-cant {
        min-width: 22px;
        height: 22px;
        padding: 0 6px;
        border-radius: var(--hd-radius-pill);
        background: var(--hd-color-accent-contrast);
        color: var(--hd-theme-accent);
        font-size: var(--hd-text-micro);
        line-height: 22px;
        text-align: center;
    }
}


/* ============================================================
   Asistente de dos pasos (solo apilado) — ADR-042
   ------------------------------------------------------------
   En una columna, el destino y el carrito caen DEBAJO de todo el catálogo.
   Medido con una carta real de 99 productos a 390px: "Destino del pedido" a
   3.541px, casi cuatro pantallas. El cajero no encontraba dónde elegir la mesa.

   Paso 1 = a dónde va el pedido (y para quién). Paso 2 = la carta.

   El estado vive en `.hd-page[data-nv-paso]` y no en `.hd-composer` porque el
   footer de acciones es HERMANO del composer, no hijo: desde la página el CSS
   alcanza a los dos. En escritorio estas reglas no existen — todo se ve a la
   vez, que es la gracia de tener dos columnas.
   ============================================================ */

.hd-composer__paso-continuar,
.hd-composer__paso-volver {
    display: none;
}

@media (max-width: 991.98px) {
    /* `display: contents` disuelve la columna del pedido y sus tres bloques pasan a ser hijos
       del composer. Es lo que permite mostrar destino y cliente en un paso y el carrito en el
       otro sin tocar el DOM ni duplicar markup. En apilado la columna ya no aportaba nada:
       su flex y su overflow los apaga el bloque de arriba. */
    .hd-composer__pedido {
        display: contents;
    }

    /* ── Paso 1: el destino ──────────────────────────────────── */
    .hd-page[data-nv-paso="1"] .hd-composer__cats,
    .hd-page[data-nv-paso="1"] .hd-composer__catalogo,
    .hd-page[data-nv-paso="1"] .hd-composer__bloque--carrito,
    .hd-page[data-nv-paso="1"] .hd-composer__paso-volver,
    .hd-page[data-nv-paso="1"] .hd-composer__ir-carrito {
        display: none;
    }

    /* El footer confirma la VENTA. En el paso 1 todavía no hay nada que confirmar, y dejarlo
       visible ofrecía "Confirmar orden" sobre un carrito vacío. */
    .hd-page[data-nv-paso="1"] .hd-page__footer.is-active {
        display: none;
    }

    .hd-page[data-nv-paso="1"] .hd-composer__paso-continuar {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: var(--hd-space-2);
        width: 100%;
        min-height: 48px;
        margin-top: var(--hd-space-3);
        border: 0;
        border-radius: var(--hd-radius-md);
        background: var(--hd-theme-accent);
        color: var(--hd-color-accent-contrast);
        font: inherit;
        font-weight: 700;
        cursor: pointer;
    }

    /* Deshabilitado mientras falte elegir a dónde va la ronda (ADR-031). Se apaga en vez de
       esconderse: el cajero tiene que ver que el camino existe y que le falta un dato. */
    .hd-page[data-nv-paso="1"] .hd-composer__paso-continuar:disabled {
        background: var(--hd-color-inset);
        color: var(--hd-theme-text-muted);
        cursor: not-allowed;
    }

    /* ── Paso 2: la carta ────────────────────────────────────── */
    .hd-page[data-nv-paso="2"] .hd-composer__bloque--destino,
    .hd-page[data-nv-paso="2"] .hd-composer__bloque--cliente,
    .hd-page[data-nv-paso="2"] .hd-composer__paso-continuar {
        display: none;
    }

    /* La cinta de vuelta: dice a dónde va el pedido y devuelve al paso 1 a cambiarlo. Sin ella
       el cajero que avanzó no puede corregir la mesa sin cancelar la venta entera. */
    .hd-page[data-nv-paso="2"] .hd-composer__paso-volver {
        /* Por delante del carril de categorías, que lleva `order: -1` para subir al principio.
           Sin esto la cinta caía DEBAJO de las categorías y se leía "elige categoría… ah, y
           esto iba a la mesa 4": primero dónde estás, después qué pides. */
        order: -2;
        display: flex;
        align-items: center;
        gap: var(--hd-space-2);
        width: 100%;
        min-height: 44px;
        margin-bottom: var(--hd-space-2);
        padding: 0 var(--hd-space-3);
        border: 1px solid var(--hd-color-border);
        border-radius: var(--hd-radius-md);
        background: var(--hd-color-inset);
        color: var(--hd-theme-text);
        font: inherit;
        font-weight: 600;
        text-align: left;
        cursor: pointer;
    }
}

/* ── Adicionales cobrables en el modal de producto (ADR-027) ──────────
   Se ven distinto de las opciones a propósito: la opción es una casilla
   que se marca y no cobra; el adicional lleva su precio y su contador,
   porque suma plata al pedido. */
.nv-adicional-fila {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--hd-space-2);
    padding: var(--hd-space-1) 0;
    border-bottom: 1px solid var(--hd-color-border);
}

.nv-adicional-fila:last-child { border-bottom: none; }

.nv-adicional-info {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.nv-adicional-precio {
    font-size: var(--hd-text-micro);
    color: var(--hd-theme-accent);
    font-weight: 700;
}

.nv-adicional-ctrl {
    display: flex;
    align-items: center;
    gap: var(--hd-space-1);
    flex-shrink: 0;
}

.nv-adicional-cant {
    font-weight: 700;
    min-width: 16px;
    text-align: center;
    font-variant-numeric: tabular-nums;
}
