/* Los componentes del rediseño 2026 (PANTALLAS.md §7 paso 1).

   Una sola hoja, escrita contra los tokens `--nm-*` de web/tokens.cb6d70c7.css y
   nada más: ni un hex suelto, ni tokens de Curaca. Cada bloque es una pieza
   con nombre propio y todos sus estados; la galería (index.html) los
   muestra uno por uno, en claro y en oscuro, para revisarlos antes de que
   una pantalla los use.

   Movimiento (PANTALLAS.md §9): tres curvas y tres tiempos como tokens.
   `--nm-curva-salida` y los tiempos vienen de DESIGN.md; las dos curvas
   nuevas son la propuesta a Quipu (la del cajón de iOS para hoja y push, y
   una de ida y vuelta para lo que ya está en pantalla). Solo se animan
   `transform`, `opacity` y color de superficie; nada anima `height`,
   `padding` ni variables en listas. */

:root {
  --nm-curva-hoja:  cubic-bezier(0.32, 0.72, 0, 1);
  --nm-curva-mover: cubic-bezier(0.77, 0, 0.175, 1);
  --nm-t-toque: var(--nm-mov-micro);   /* 120 ms: presionar, hover, color */
  --nm-t-capa:  var(--nm-mov-base);    /* 200 ms: popover, toast, fila, chip */
  --nm-t-hoja:  280ms;                  /* hoja y push al entrar */
  --nm-t-hoja-salida: 200ms;            /* la salida siempre más rápida */
}

/* ---------- base ---------- */

.nm-num { font-variant-numeric: tabular-nums; }
/* Un monto destacado dentro de una frase («desde <strong>S/ 3.19</strong> el
   kilo») no se parte: en 375 px quedaba «S/» al final de una línea y el
   número en la siguiente. Solo el <strong>: un párrafo entero con .nm-num
   sí tiene que poder envolver. */
strong.nm-num { white-space: nowrap; }
.nm-rotulo {
  font-size: var(--nm-l1); font-weight: 600; letter-spacing: var(--nm-l1-tracking);
  text-transform: uppercase; color: var(--nm-texto-3);
}
.nm-meta { font-size: var(--nm-b3); line-height: var(--nm-lh-ui); color: var(--nm-texto-3); }

.nm-ico {
  width: 20px; height: 20px; flex: 0 0 auto;
  stroke: currentColor; fill: none; stroke-width: 1.8;
  stroke-linecap: round; stroke-linejoin: round;
}

/* Todo lo que se presiona responde al presionar, no al soltar (§9.1 regla 2). */
.nm-presionable {
  transition: transform var(--nm-t-toque) var(--nm-curva-salida),
              background-color var(--nm-t-toque) ease,
              border-color var(--nm-t-toque) ease,
              color var(--nm-t-toque) ease;
  -webkit-tap-highlight-color: transparent;
  user-select: none;
}
.nm-presionable:active { transform: scale(0.97); }
.nm-presionable:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: 2px; }

/* ---------- botones ---------- */

.nm-boton {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 44px; padding: 0 20px; border-radius: var(--nm-radio-full);
  font: 700 var(--nm-b2) / 1 var(--nm-cuerpo); border: 1px solid transparent;
  cursor: pointer; white-space: nowrap; background: transparent; color: inherit;
  transition: background-color var(--nm-mov-base) var(--nm-curva-salida);
}
.nm-boton--primario { background: var(--nm-primario); color: var(--nm-primario-tinta); }
@media (hover: hover) and (pointer: fine) {
  .nm-boton--primario:hover { background: var(--nm-primario-hover); }
  .nm-boton--secundario:hover { background: var(--nm-superficie-2); }
  .nm-boton--terciario:hover { color: var(--nm-texto); }
  .nm-boton--circular:hover { background: var(--nm-superficie-2); }
}
.nm-boton--secundario { border-color: var(--nm-borde); color: var(--nm-texto-2); }
.nm-boton--borde-tinta { border-color: var(--nm-texto); color: var(--nm-texto); border-width: 1.5px; }
/* Secundario en añil (helada en oscuro): una acción de la pantalla que
   tiene que leerse como botón sin competirle al primario —borde y texto del
   color de acción, sin relleno—. Hoy: «Volver al más barato» del plan de
   una sola tienda (Gonzalo, 2026-10-10). Nunca dorado. */
.nm-boton--anil { border-color: var(--nm-primario); color: var(--nm-primario); border-width: 1.5px; }
@media (hover: hover) and (pointer: fine) {
  .nm-boton--anil:hover { background: var(--nm-superficie-2); }
}
.nm-boton--terciario { padding: 0 8px; color: var(--nm-texto-2); font-weight: 600; }
.nm-boton--grande { height: 52px; padding: 0 28px; font-size: var(--nm-b1); }
.nm-boton--chico { height: 32px; padding: 0 12px; font-size: var(--nm-b3); font-weight: 600; }
.nm-boton--ancho { width: 100%; }
.nm-boton--circular {
  width: 56px; height: 56px; padding: 0; border-radius: var(--nm-radio-full);
  background: var(--nm-superficie-1); border-color: var(--nm-borde); color: var(--nm-texto);
  box-shadow: var(--nm-sombra-2);
}
.nm-boton--circular.nm-boton--chico { width: 36px; height: 36px; box-shadow: none; }
/* «Ya está en tu lista»: el mismo botón, relleno de tinta y con el número de
   veces que se agregó en vez del «+». Sigue agregando otro —no es un estado
   muerto—, y de un vistazo la fila dice si ya cayó (PANTALLAS §3 P2). */
.nm-boton--puesto {
  background: var(--nm-texto); border-color: var(--nm-texto); color: var(--nm-fondo);
  font: 700 13px / 1 var(--nm-cuerpo); font-variant-numeric: tabular-nums;
}
@media (hover: hover) and (pointer: fine) {
  .nm-boton--puesto:hover { background: var(--nm-texto); }
}
.nm-boton[disabled], .nm-boton[aria-disabled="true"] { opacity: .45; pointer-events: none; }

/* El botón que está esperando una respuesta LO DICE (QA G11): un botón
   deshabilitado con el mismo aspecto de siempre se lee como un botón que no
   hizo nada, y la persona vuelve a tocar. Late despacio en vez de girar: es un
   botón de 32 px y un aro ahí es ruido. */
.nm-boton--esperando { opacity: .55; animation: nm-latido 1.2s linear infinite; }
@keyframes nm-latido { 50% { opacity: .9; } }
@media (prefers-reduced-motion: reduce) {
  .nm-boton--esperando { animation: none; }
}

/* El botón con estados: el contenido cambia con blur + opacidad, nunca de golpe. */
.nm-boton__contenido { display: inline-flex; align-items: center; gap: 8px; transition: filter var(--nm-t-capa) ease, opacity var(--nm-t-capa) ease; }
.nm-boton[data-estado="cargando"] .nm-boton__contenido { filter: blur(2px); opacity: .7; }

/* ---------- chips ---------- */

.nm-chip {
  display: inline-flex; align-items: center; gap: 6px; height: 36px; padding: 0 14px;
  border-radius: var(--nm-radio-full); border: 1px solid var(--nm-borde);
  background: transparent; color: var(--nm-texto); font: 500 14px / 1 var(--nm-cuerpo);
  cursor: pointer; white-space: nowrap;
}
.nm-chip[aria-pressed="true"], .nm-chip--activo {
  border-color: var(--nm-texto); background: var(--nm-superficie-2); font-weight: 600;
}
.nm-chip--con-logo img { width: 22px; height: 22px; border-radius: var(--nm-radio-full); }
.nm-chip .nm-ico { width: 14px; height: 14px; }
.nm-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.nm-chips--carril { flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.nm-chips--carril::-webkit-scrollbar { display: none; }

/* ---------- campos ---------- */

.nm-campo {
  display: flex; align-items: center; gap: 10px; height: 48px; padding: 0 6px 0 16px;
  border-radius: var(--nm-radio-full); background: var(--nm-campo); border: 1px solid var(--nm-borde);
  color: var(--nm-texto-3);
  transition: border-color var(--nm-t-toque) ease;
}
.nm-campo:focus-within { border-color: var(--nm-texto); color: var(--nm-texto); }
.nm-campo input {
  flex: 1; min-width: 0; border: 0; background: transparent; color: var(--nm-texto);
  font: 400 16px / 1 var(--nm-cuerpo); outline: none;  /* 16 px: ni un pixel menos, o iOS hace zoom */
}
.nm-campo input::placeholder { color: var(--nm-texto-3); }
.nm-campo--chico { height: 40px; }
.nm-campo__accion {
  width: 36px; height: 36px; border-radius: var(--nm-radio-full); display: flex;
  align-items: center; justify-content: center; color: var(--nm-texto-2); border: 0; background: transparent; cursor: pointer;
}

/* El campo de varias líneas: la misma superficie hundida y el mismo foco que
   `.nm-campo`, con el radio de tarjeta en vez de la pill (una pill de cinco
   líneas no existe). Nació con la hoja «Cuéntanos» (`js/comentarios.js`). */
.nm-area {
  display: block; width: 100%; min-height: 7.5rem; resize: vertical;
  padding: 12px 16px; border-radius: var(--nm-radio-md);
  background: var(--nm-campo); border: 1px solid var(--nm-borde); color: var(--nm-texto);
  font: 400 16px / 1.5 var(--nm-cuerpo);  /* 16 px, o iOS hace zoom */
  transition: border-color var(--nm-t-toque) ease;
}
.nm-area:focus { outline: none; border-color: var(--nm-texto); }
.nm-area:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: 2px; }
.nm-area::placeholder { color: var(--nm-texto-3); }

/* Un formulario corto dentro de una hoja: rótulo arriba, campo abajo, y una
   sola columna con el primario al final. */
.nm-formulario { display: grid; gap: var(--nm-esp-4); padding-bottom: var(--nm-esp-4); }
.nm-formulario > p { margin: 0; }
/* El agradecimiento: la Bolsa arriba y el texto centrado debajo. */
.nm-formulario--gracias { justify-items: center; text-align: center; }
.nm-formulario__campo { display: grid; gap: var(--nm-esp-2); }
.nm-formulario__rotulo { font: 600 var(--nm-b2) / 1.3 var(--nm-cuerpo); color: var(--nm-texto); }
.nm-formulario__opcional { font-weight: 400; color: var(--nm-texto-2); }
.nm-formulario__estado { margin: 0; min-height: 1.4em; color: var(--nm-texto-2); }
.nm-formulario__estado:empty { min-height: 0; }

.nm-stepper {
  display: inline-flex; align-items: center; height: 32px; border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-full); color: var(--nm-texto); background: transparent;
}
.nm-stepper button {
  width: 32px; height: 30px; display: flex; align-items: center; justify-content: center;
  border: 0; background: transparent; color: var(--nm-texto-2); cursor: pointer;
}
.nm-stepper output { font: 600 14px / 1 var(--nm-cuerpo); min-width: 44px; text-align: center; padding: 0 4px; }
.nm-stepper--grande { height: 52px; background: var(--nm-superficie-2); }
.nm-stepper--grande button { width: 52px; height: 50px; color: var(--nm-texto); }
.nm-stepper--grande output { font-size: 17px; min-width: 60px; }
/* El stepper NO anima el número: es el gesto más frecuente (§9.3). Solo la presión. */

/* ---------- filas ---------- */

.nm-fila {
  display: flex; align-items: center; gap: 12px; padding: 14px 0;
  border-bottom: 1px solid var(--nm-borde); min-height: 72px;
  transition: background-color var(--nm-t-capa) ease;
}
.nm-fila:last-child { border-bottom: 0; }
.nm-fila__foto {
  width: 44px; height: 44px; border-radius: var(--nm-radio-md); object-fit: contain; flex: 0 0 auto;
  mix-blend-mode: multiply;   /* claro: la foto se funde con el fondo (ESPEC_UI §7) */
}
[data-theme="oscuro"] .nm-fila__foto { mix-blend-mode: normal; background: var(--nm-p-caliza); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="claro"]) .nm-fila__foto { mix-blend-mode: normal; background: var(--nm-p-caliza); }
}
.nm-fila__cuerpo { flex: 1; min-width: 0; }
/* El nombre entra en DOS renglones antes de cortarse, y la meta también
   (QA G8). Con un solo renglón y elipsis, en un teléfono se leía «Agua Micelar
   Beauty Care…» y —peor— «Metro · antes S/ 32.…»: el dato que justifica la
   pantalla de bajadas quedaba truncado en las diez filas. La fila crece lo que
   tenga que crecer; lo que no puede es mentir a medias. */
.nm-fila__nombre {
  font: 600 var(--nm-b1) / 1.25 var(--nm-cuerpo); color: var(--nm-texto);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; overflow-wrap: anywhere;
}
.nm-fila__nombre a { color: inherit; text-decoration: none; }
.nm-fila__meta {
  font-size: var(--nm-b3); color: var(--nm-texto-3);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; overflow-wrap: anywhere;
}
.nm-fila__derecha { text-align: right; flex: 0 0 auto; }
.nm-fila__precio { font: 700 16px / 1.2 var(--nm-cuerpo); font-variant-numeric: tabular-nums; }
.nm-fila__precio--gana { color: var(--nm-acento); }
/* El porcentaje de la bajada va ENTRE PARÉNTESIS y al lado del precio, en la
   misma línea (pedido de Gonzalo del 2026-09-16): «S/ 12.90 (−18 %)». El
   precio conserva su peso y su tinta; el porcentaje es metadato del precio y
   por eso va en terciario, sin robarle el oro a «−S/ 2.50». */
.nm-fila__pct { color: var(--nm-texto-3); font-weight: 600; font-size: var(--nm-b3); }
.nm-fila__bajada { font: 700 var(--nm-b3) / 1.3 var(--nm-cuerpo); color: var(--nm-acento); font-variant-numeric: tabular-nums; }
.nm-fila__bajada small { color: var(--nm-texto-3); font-weight: 600; font-size: inherit; }
.nm-fila__icono { color: var(--nm-texto-2); display: flex; border: 0; background: transparent; cursor: pointer; padding: 4px; }
.nm-fila--resaltada { background: var(--nm-superficie-2); margin: 0 -20px; padding-left: 20px; padding-right: 20px; }
.nm-fila--tachada .nm-fila__nombre { color: var(--nm-texto-3); text-decoration: line-through; }
.nm-fila--tachada .nm-fila__foto { opacity: .5; }

/* Entrada de una fila nueva: opacidad + 8 px, 200 ms, con @starting-style; sin stagger de a una. */
.nm-fila--nueva { opacity: 1; transform: translateY(0); transition: opacity var(--nm-t-capa) var(--nm-curva-salida), transform var(--nm-t-capa) var(--nm-curva-salida); }
@starting-style { .nm-fila--nueva { opacity: 0; transform: translateY(8px); } }
/* Salida (Quitar): opacidad 120 ms y colapso 200 ms con grid-template-rows, nunca height. */
.nm-fila--saliendo { display: grid; grid-template-rows: 0fr; opacity: 0; padding-top: 0; padding-bottom: 0; border-bottom-width: 0; transition: opacity var(--nm-t-toque) ease, grid-template-rows var(--nm-t-capa) var(--nm-curva-salida), padding var(--nm-t-capa) var(--nm-curva-salida); }
.nm-fila--saliendo > * { min-height: 0; overflow: hidden; }

/* Cabecera de categoría: fila tocable a todo el ancho, conteo y chevrón que gira. */
.nm-categoria {
  display: flex; align-items: center; justify-content: space-between;
  /* El botón mide lo mismo que su columna, pero el margen de página a su
     derecha sigue pintado del mismo `--nm-banda` (R56: la franja es a
     sangre completa), así que a los ojos es la MISMA fila. Un toque cerca
     de la punta —sobre o junto al chevrón— caía en ese margen y no hacía
     nada (reporte de Gonzalo, captura del chevrón). `width` + `margin-right`
     negativo extienden la CAJA real del botón hasta el borde de página, y
     el `padding-right` que crece lo mismo que el margen se lo come, así que
     el contenido (el chevrón incluido) no se mueve ni un píxel. `--nm-esp-4`
     es el margen real de página en app/móvil, que es donde se reportó; en
     escritorio el margen es mayor (`--nm-esp-5`) pero la extensión sigue
     cerrando la mayor parte del hueco sin acercarse a la columna del
     reparto, que empieza a `--nm-esp-6` (32 px) de distancia. Un valor mayor
     desbordaría el ancho de la ventana en móvil, y ahí `DOM.getBoxModel`
     —lo que usa Playwright `bounding_box()`, que miden los tests de
     `test_categorias_ui.py`— mide mal la caja de un `position: sticky` que
     se sale del viewport. */
  width: calc(100% + var(--nm-esp-4)); margin-right: calc(var(--nm-esp-4) * -1);
  padding: 18px var(--nm-esp-4) 4px 0; border: 0; background: transparent;
  cursor: pointer; text-align: left;
}
.nm-categoria .categoria__flecha { color: var(--nm-texto-3); width: 18px; height: 18px; transform: rotate(90deg); transition: transform var(--nm-t-capa) var(--nm-curva-mover); }
.nm-categoria[aria-expanded="false"] .categoria__flecha { transform: rotate(0deg); }
/* El glifo de categoría (R56): 20 px, en tinta, delante del nombre —mismo
   trazo que la tab bar, sin color propio. */
.nm-categoria .categoria__icono { color: var(--nm-texto-2); width: 20px; height: 20px; flex: 0 0 auto; }
.nm-categoria[aria-expanded="false"] { border-bottom: 1px solid var(--nm-borde); padding-bottom: 12px; }
.nm-categoria[aria-expanded="false"] + .nm-categoria__filas { display: none; }
/* Toda la cabecera es el control: el título, el conteo y el chevrón son
   pintura del botón y no blancos de clic propios, así que no hay ningún
   punto de la fila que responda distinto al de al lado (R5). */
.nm-categoria > * { pointer-events: none; }
.nm-categoria { min-height: 44px; }

/* ---------- carrusel de marcas ----------

   La card genérica de Buscar («Queso fresco · 17 marcas · … · desde S/ 8.00»)
   y, debajo, sus productos de marca en un carril que se desliza con el dedo y
   engancha (scroll-snap). Es la forma que pidió Gonzalo el 2026-09-16: el
   genérico manda y las marcas están a la vista sin desplegar nada. Cada
   tarjeta es un producto real, y tocarla abre su ficha con la marca fija. */
.nm-carrusel-caja { position: relative; }
.nm-carrusel {
  display: flex; gap: var(--nm-esp-2); overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x proximity; scroll-padding-left: var(--nm-esp-3);
  padding: var(--nm-esp-1) var(--nm-esp-3) var(--nm-esp-3);
  margin: 0; list-style: none; scrollbar-width: none;
  overscroll-behavior-x: contain; scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) {
  .nm-carrusel { scroll-behavior: auto; }
}
.nm-carrusel::-webkit-scrollbar { display: none; }
.nm-carrusel > li { display: flex; flex: 0 0 auto; scroll-snap-align: start; }
/* Las flechas del carril son de RATÓN (R23). Con el dedo el carril se desliza
   y dos botones flotando encima solo taparían tarjetas; con mouse no hay rueda
   horizontal y el carril se veía cortado en el borde derecho sin salida. Por
   eso viven dentro de `(hover: hover) and (pointer: fine)` y además se apagan
   en la forma app, que es un teléfono aunque el navegador diga otra cosa. */
.nm-carrusel__flecha { display: none; }
@media (hover: hover) and (pointer: fine) {
  .nm-carrusel__flecha {
    display: grid; place-items: center; position: absolute; top: 44%;
    transform: translateY(-50%); z-index: 2;
    width: 32px; height: 32px; padding: 0; border-radius: var(--nm-radio-full);
    border: 1px solid var(--nm-borde); background: var(--nm-superficie-2);
    color: var(--nm-texto); cursor: pointer; box-shadow: var(--nm-sombra-2);
  }
  .nm-carrusel__flecha:hover { background: var(--nm-superficie-1); border-color: var(--nm-borde-fuerte); }
  .nm-carrusel__flecha:active { transform: translateY(-50%) scale(0.94); }
  .nm-carrusel__flecha--izq { left: 2px; }
  .nm-carrusel__flecha--der { right: 2px; }
  .nm-carrusel__flecha .nm-ico { width: 18px; height: 18px; }
  .nm-carrusel__flecha[hidden] { display: none; }
  [data-forma="app"] .nm-carrusel__flecha { display: none; }
}
.nm-marca {
  position: relative;
  flex: 0 0 auto; width: 128px; scroll-snap-align: start;
  display: flex; flex-direction: column; gap: var(--nm-esp-1);
  padding: var(--nm-esp-2); border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-lg); background: var(--nm-superficie-1);
  text-align: left; cursor: pointer; color: var(--nm-texto);
}
/* La tarjeta de marca se abre por todo su cuerpo y se agrega por su «+»
   (R22): la zona se estira por debajo y el «+» va encima, igual que en la
   fila de resultados. Un botón dentro de otro botón no es marcado válido. */
.nm-marca__zona {
  position: absolute; inset: 0; z-index: 0; border: 0; padding: 0;
  background: transparent; border-radius: inherit; cursor: pointer;
}
.nm-marca > :not(.nm-marca__zona) { position: relative; z-index: 1; }
/* R35: el «+» vive DENTRO del recuadro, con el mismo aire que el padding de
   la tarjeta (`--nm-esp-2`, el mismo valor que separa el texto del borde) y
   no un valor suelto (6px) menor que ese padding — eso era lo que lo hacía
   asomar por la derecha en foco (el anillo de `.nm-presionable:focus-visible`
   suma 4px más de `outline` + `outline-offset` hacia afuera del botón, y con
   6px de por sí ya sobraban 168 px de 175 posibles, un `getBoundingClientRect`
   por encima del borde de la tarjeta). Con el mismo padding, el anillo queda
   4px por dentro del borde en vez de fuera. */
/* El «+» se convierte en el stepper de su fila en cuanto la marca entra a la
   lista (encargo 2026-09-27): lo que se posiciona es el WRAPPER
   `.nm-marca__agregar`, no el botón —así el mismo hueco sirve para el «+»
   circular Y para el stepper, sin que el CSS tenga que saber cuál de los dos
   hay adentro.

   Como en Rappi: el stepper ocupa el LUGAR del «+» —arriba, sobre la foto,
   a lo ancho de la tarjeta— y no empuja nada hacia abajo. Hasta el
   2026-09-27 pasaba a `position: static` y apilaba un renglón propio
   pegado a la derecha, y eso subía la tarjeta ~70 px sobre sus vecinas del
   carril (hallazgo de Gonzalo): acá se queda `absolute`, solo que ahora
   estirado de borde a borde en vez de flotando en la esquina, con su
   propia superficie para leerse encima de la foto. La cifra sin la unidad
   —«la unidad ya la dice la presentación de la card»— es lo único que
   entra en ese ancho. */
.nm-marca > .nm-marca__agregar {
  position: absolute; top: var(--nm-esp-2); right: var(--nm-esp-2); z-index: 2;
}
.nm-marca > .nm-marca__agregar:has(.resultado-stepper) {
  left: var(--nm-esp-2);
  display: flex; justify-content: center;
  background: var(--nm-superficie-1); border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-full);
}
.nm-marca .resultado-stepper { flex: 1; justify-content: space-between; }
/* Sin la unidad acá: la tarjeta ya la dice en su propia meta («800 ml»,
   «300 g»), y a lo ancho de la foto no entra además de la cifra. */
.nm-marca .resultado-stepper__unidad { display: none; }

/* El hueco de la card genérica mientras la fusión por familia viaja (R24):
   las filas ya están en pantalla y arriba queda la forma de lo que falta, en
   gris, para que nada salte cuando llegue. */
.nm-carrusel-espera { display: flex; gap: var(--nm-esp-2); overflow: hidden; padding: var(--nm-esp-1) var(--nm-esp-3) var(--nm-esp-3); }
.nm-carrusel-espera > .nm-esqueleto { flex: 0 0 auto; width: 128px; height: 132px; }
.nm-carrusel-espera__fila { height: 56px; margin: var(--nm-esp-2) var(--nm-esp-3) 0; }
/* La foto se corrió a la izquierda cuando la tarjeta ganó su «+» (R22): con
   128 px de ancho, centrada quedaba debajo del botón.

   R35: la regla lleva DOS clases (`.nm-marca .nm-marca__foto`), no una, a
   propósito — `estilo.css` carga después de esta hoja y su `.miniatura`
   genérica (44×44) es también de una clase; con la misma especificidad gana
   la última en cascada, que es la de `estilo.css`, y la miniatura de la
   tarjeta de marca quedaba en 44 px con 12 px de aire de más a la derecha en
   vez de 56 px, apretando el nombre contra el «+». Dos clases (0,2,0) le
   ganan a `.miniatura` (0,1,0) sin depender del orden de las hojas. */
.nm-marca .nm-marca__foto { width: 56px; height: 56px; align-self: start; }
.nm-marca__foto img { width: 100%; height: 100%; object-fit: contain; }
.nm-marca__nombre {
  font: 600 var(--nm-b3) / 1.25 var(--nm-cuerpo); color: var(--nm-texto);
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; overflow-wrap: anywhere;
}
.nm-marca__meta {
  font-size: var(--nm-b3); color: var(--nm-texto-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nm-marca__precio { font: 700 var(--nm-b3) / 1.3 var(--nm-cuerpo); font-variant-numeric: tabular-nums; }
/* El sello de tarjeta entra en la tarjeta sin desbordarla: con 128 px de
   ancho, «S/ 32.40 con Tarjeta Cencosud» se sale por la derecha. Se parte en
   dos renglones y se corta ahí: la cifra, que es el dato, viaja entera. */
.nm-marca .sello-beneficio {
  max-width: 100%; white-space: normal; overflow: hidden;
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
}
@media (hover: hover) and (pointer: fine) {
  .nm-marca:hover { border-color: var(--nm-borde-fuerte); background: var(--nm-superficie-2); }
}

/* ---------- tarjetas ---------- */

.nm-tarjeta {
  background: var(--nm-superficie-1); border: 1px solid var(--nm-borde-tarjeta);
  border-radius: var(--nm-radio-lg); padding: 14px;
  transition: background-color var(--nm-mov-base) var(--nm-curva-salida),
              border-color var(--nm-mov-base) var(--nm-curva-salida);
}
.nm-tarjeta--ganadora { border-color: var(--nm-texto); }
.nm-tarjeta--dentro { background: var(--nm-superficie-2); }

.nm-tienda { display: flex; align-items: center; gap: 10px; padding: 12px; margin-bottom: 8px; cursor: pointer; }
.nm-tienda__logo { width: 36px; height: 36px; border-radius: var(--nm-radio-full); flex: 0 0 auto; }
.nm-tienda__nombre { font: 700 var(--nm-b1) / 1.2 var(--nm-cuerpo); }
/* La cobertura parte en vez de cortarse: como TEXTO y no como caja flex, el
   ✓ es una palabra más de la frase y «· asistido» se lee entero (QA G13). */
.nm-tienda__cobertura { display: block; white-space: normal; overflow: visible; }
.nm-tienda__cobertura .nm-ico { width: 13px; height: 13px; display: inline-block; vertical-align: -2px; margin-right: 4px; flex: none; }
.nm-tienda > .nm-fila__cuerpo { flex: 1 1 auto; min-width: 0; }
.nm-tienda__total { font: 700 18px / 1.2 var(--nm-cuerpo); font-variant-numeric: tabular-nums; letter-spacing: -0.01em; flex: 0 0 auto; }

/* ---------- la comparación por tienda (ficha, PANTALLAS §3 P3) ----------

   Una fila por tienda: el logo de la cadena en su color real y chico, el
   nombre, el precio en tabular y el precio por unidad DEBAJO, y el precio
   con tarjeta debajo del precio de esa tienda (regla de Gonzalo: nunca
   debajo del unitario, donde se leería como otro «por litro»).

   La ganadora lleva el nudo de oro y su monto en el oro de texto; las demás
   filas, en tinta. Nudo y monto son UN SOLO lenguaje —«el oro marca dónde
   gana tu plata»— y cuentan como una sola aparición. Sin chip «Más barato»
   al lado: el nudo es todo el veredicto.

   Sin borde por fila (PANTALLAS §1 regla 5): un divisor de 1px basta. */
.nm-comparar { list-style: none; margin: 0; padding: 0; }
.nm-comparar__fila {
  display: grid;
  grid-template-columns: 28px minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 10px; row-gap: 2px;
  padding: 12px 0; min-height: 60px;
  border-top: 1px solid var(--nm-borde-sutil);
}
.nm-comparar__fila:first-child { border-top: 0; }
.nm-comparar__logo {
  grid-column: 1; grid-row: 1 / span 2; align-self: start;
  width: 28px; height: 28px; border-radius: var(--nm-radio-full);
}
.nm-comparar__tienda { grid-column: 2; grid-row: 1; font: 600 var(--nm-b2) / 1.3 var(--nm-cuerpo); }
.nm-comparar__meta {
  grid-column: 2; grid-row: 2;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 10px;
  font-size: var(--nm-b3); color: var(--nm-texto-3);
}
/* Dentro de una fila el enlace a la tienda no puede ser una pastilla —son
   ocho tiendas y serían ocho botones—: vuelve a ser texto con su cifra. */
.nm-comparar__meta .enlace-tienda--ir {
  padding: 0; border: 0; border-bottom: 1px solid transparent; border-radius: 0;
  color: var(--nm-texto-3); font-size: var(--nm-b3);
}
.nm-comparar__par { grid-column: 3; grid-row: 1; text-align: right; }
.nm-comparar__monto {
  font: 700 17px / 1.2 var(--nm-cuerpo); font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}
.nm-comparar__unitario {
  display: block; font-size: var(--nm-b3); color: var(--nm-texto-3);
  font-variant-numeric: tabular-nums;
}
/* El sello cuelga del MONTO, en la misma columna y debajo. */
.nm-comparar__sello { grid-column: 3; grid-row: 2; align-self: start; text-align: right; }
.nm-comparar__sello:empty { display: none; }
/* El nudo de la ganadora: relleno `--nm-nudo` (ámbar en claro, maíz en
   oscuro), que es el oro de RELLENO y no el de texto. */
.nm-comparar__fila--gana .nm-comparar__tienda::before {
  content: ""; display: inline-block; vertical-align: middle;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--nm-nudo); margin-right: 7px;
}
.nm-comparar__fila--gana .nm-comparar__monto { color: var(--nm-acento); }
/* Agotada: el precio baja a terciario. Lo que hoy no se puede comprar no
   gana, y se ve que no. */
.nm-comparar__fila--agotada .nm-comparar__monto { color: var(--nm-texto-3); }

/* El trofeo: la única cifra en Bitter y en oro. Un momento serif por vista. */
.nm-trofeo { padding: 18px 18px 16px; }
.nm-trofeo__premisa { font-size: var(--nm-b2); color: var(--nm-texto-2); }
.nm-trofeo__cifra {
  font: 400 var(--nm-precio) / 1.05 var(--nm-serif); letter-spacing: var(--nm-precio-tracking);
  color: var(--nm-acento); font-variant-numeric: tabular-nums; margin: 2px 0 6px;
}
.nm-trofeo__cifra--entra { opacity: 1; transform: none; filter: blur(0); transition: opacity var(--nm-mov-entrada) var(--nm-curva-salida), transform var(--nm-mov-entrada) var(--nm-curva-salida), filter var(--nm-mov-entrada) ease; }
@starting-style { .nm-trofeo__cifra--entra { opacity: 0; transform: scale(0.95); filter: blur(4px); } }
/* La resta que verifica el ahorro a ojo —«S/ 628 → S/ 560»— (Gonzalo,
   2026-09-23): la referencia y el total, ambos sin centavos, para que la
   persona pueda restar de cabeza y le dé el mismo ahorro de arriba. La
   flechita es puramente visual (`aria-hidden`); el «hasta» que la
   acompaña para el lector de pantalla vive en `.sr-solo`. */
.reparto__trofeo-resta { margin-top: 2px; }

/* La variante compacta del trofeo de Carritos (Gonzalo, 2026-09-25: «una
   tonelada de texto» antes del primer carrito): menos aire vertical y
   «Ahorras» pegado a la cifra —el mismo trato que la resta, que ya vivía
   apretada contra el número—, para que en 390×800 el primer carrito quede
   dentro de la primera pantalla. */
.nm-trofeo--compacto { padding: 12px 16px 10px; }
.nm-trofeo--compacto .nm-trofeo__premisa--compacta { margin: 0 0 -2px; }
.nm-trofeo--compacto .nm-trofeo__cifra { margin: 0 0 2px; }

/* La fila compacta y plegada de lo que quedó fuera de los carritos
   (PANTALLAS §3 P4, pedido de Gonzalo 2026-09-25): reemplaza los dos
   recuadros largos de antes —«Hoy no se puede comprar» y «Quedaron fuera
   de los carritos»— por un solo `<details>` nativo, sin verde ni oro (es un
   aviso, no un logro ni un dato). El ícono es el triángulo de aviso de
   `nm-ico`, tinta terciaria como el resto de la fila cerrada. */
.fuera-panel {
  margin-top: var(--nm-esp-3); border: 1px solid var(--nm-borde-sutil);
  border-radius: var(--nm-radio-lg); background: var(--nm-superficie-1);
}
.fuera-panel__resumen {
  display: flex; align-items: center; gap: 8px; list-style: none;
  padding: 12px 14px; cursor: pointer; color: var(--nm-texto-2);
  font: 600 var(--nm-b2) / 1.3 var(--nm-cuerpo);
}
.fuera-panel__resumen::-webkit-details-marker { display: none; }
.fuera-panel__icono { width: 18px; height: 18px; color: var(--nm-texto-3); flex: none; }
.fuera-panel__titulo { flex: 1 1 auto; }
.fuera-panel__chevron { width: 16px; height: 16px; color: var(--nm-texto-3); flex: none; transition: transform var(--nm-mov-base) var(--nm-curva-salida); }
.fuera-panel[open] > .fuera-panel__resumen .fuera-panel__chevron { transform: rotate(90deg); }
.fuera-panel__lista { list-style: none; margin: 0; padding: 0 14px 12px; }
.fuera-panel__fila {
  display: flex; align-items: center; gap: 10px; justify-content: space-between;
  padding: 10px 0; border-top: 1px solid var(--nm-borde-sutil);
}
.fuera-panel__info { min-width: 0; }
.fuera-panel__nombre { font: 600 var(--nm-b2) / 1.3 var(--nm-cuerpo); overflow-wrap: break-word; }
.fuera-panel__razon { margin-top: 1px; }
.fuera-panel__acciones { display: flex; gap: 6px; flex: none; }
.fuera-panel__accion {
  border: 1px solid var(--nm-borde); border-radius: var(--nm-radio-full);
  background: transparent; color: var(--nm-texto-2); font: 600 var(--nm-b3) / 1 var(--nm-cuerpo);
  padding: 6px 12px; cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .fuera-panel__accion:hover { border-color: var(--nm-borde-fuerte); background: var(--nm-superficie-2); }
}

/* La etiqueta chica que abre una sección dentro del panel de carritos —
   «Plan recomendado», «Si prefieres todo en una sola tienda»—: mismo
   tratamiento que un `nm-meta`, solo con el aire propio del bloque que
   encabeza. No es un H2 ni lleva marcador de cuerda: son sub-secciones de
   una misma franja, no secciones mayores de la página. */
.reparto__titulo-seccion { padding: 0 var(--nm-esp-1); margin: var(--nm-esp-3) 0 var(--nm-esp-1); }
.reparto__titulo-seccion:first-child { margin-top: 0; }
/* «Plan recomendado» es el título principal del panel de carritos —encima
   de «Si prefieres todo en una sola tienda», que queda secundaria en su
   `nm-meta` de siempre—: negrita y tinta primaria, nunca oro (el oro es
   solo la cifra del ahorro). El ícono es la cuerda con dos paradas, el
   mismo trazo `nm-ico` que el resto de la UI. */
.reparto__titulo-seccion--plan {
  display: flex; align-items: center; gap: 8px;
  font: 700 var(--nm-b1) / 1.2 var(--nm-cuerpo);
  color: var(--nm-texto);
}
.reparto__titulo-seccion--plan .nm-ico { width: 20px; height: 20px; }

/* El carrusel chico "así te hubiera ido en una tienda sola", debajo del
   plan de carritos (no es su propia franja ni su propio marcador — es
   apoyo del plan). Reusa `.nm-carrusel`/`.nm-carrusel-caja`; solo la
   tarjeta es propia y más chica que `.nm-marca`: acá no hay foto de
   producto que pesar, solo el logo de la tienda. El total va en tinta,
   nunca en oro — es un total a pagar, no un ahorro (DESIGN.md, "el oro se
   gana"). Cada tarjeta dice UNA cosa por línea: qué cubre, y aparte, cuánto
   más cuesta (Gonzalo, 2026-09-23: antes se mezclaban y no se entendía). */
.nm-comparativa { margin-top: var(--nm-esp-2); }
/* Ancho: 156px partía «S/ 894.95» y «todo en Wong» en dos renglones cada
   uno (QA trofeo, 2026-09-23). El total va SOLO en su línea, la tienda
   debajo en secundario y sin recortarse —un nombre de tienda no se trunca—,
   y el terciario junta falta+diferencia en una sola frase («+13 en otras ·
   S/ 117 más») en vez de dos spans que se apretujaban. */
.nm-comparativa__tarjeta {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  width: 208px; padding: var(--nm-esp-2); border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-lg); background: var(--nm-superficie-1);
}
.nm-comparativa .nm-tienda__logo { width: 24px; height: 24px; margin-bottom: 2px; }
.nm-comparativa__total {
  font: 700 var(--nm-b1) / 1.2 var(--nm-cuerpo); color: var(--nm-texto);
}
.nm-comparativa__tienda {
  font-size: var(--nm-b3); color: var(--nm-texto-2);
}
.nm-comparativa__resto {
  display: flex; align-items: center; gap: 4px; margin-top: 1px;
  font-size: var(--nm-b3); color: var(--nm-texto-3);
}
.nm-comparativa__resto .nm-ico { width: 14px; height: 14px; flex: 0 0 auto; }
/* Desde el 2026-10-10 la tarjeta se puede elegir (ESPEC_UI §5.5, «Comprar en
   una sola tienda»): es un `<button aria-pressed>` que se ve igual que la
   tarjeta de siempre, más el borde de tinta cuando es el plan en pantalla
   —el mismo que marca la tarjeta ganadora, nunca oro: elegir no es ganar—. */
.nm-comparativa__tarjeta--elegible {
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .nm-comparativa__tarjeta--elegible:not(.nm-comparativa__tarjeta--elegida):hover { border-color: var(--nm-borde-fuerte); }
}
.nm-comparativa__tarjeta--elegida { border-color: var(--nm-texto); box-shadow: inset 0 0 0 .5px var(--nm-texto); }
.nm-comparativa__tienda .nm-ico { width: 14px; height: 14px; margin-right: 4px; vertical-align: -2px; }
.nm-comparativa__ajustes { margin: var(--nm-esp-2) 0 0; padding: 0 var(--nm-esp-1); }
.nm-comparativa__ajustes a { color: var(--nm-texto-2); text-underline-offset: 3px; }
.nm-comparativa__ajustes a:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: 2px; border-radius: 2px; }

/* El plan de una sola tienda, arriba de sus carritos: cuánto cuesta la
   conveniencia, la vuelta al más barato y —si a la tienda le falta algo—
   esos productos aparte con sus dos salidas (radios nativos). Tarjeta
   neutra, sin oro ni verde: es una decisión de la persona, no un logro. */
.tienda-unica {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px;
  margin: 0 0 var(--nm-esp-2); padding: 12px 14px;
  border: 1px solid var(--nm-borde-sutil); border-radius: var(--nm-radio-lg);
  background: var(--nm-superficie-1);
}
.tienda-unica__resumen {
  flex: 1 1 16em; margin: 0; color: var(--nm-texto);
  font: 600 var(--nm-b2) / 1.35 var(--nm-cuerpo);
}
.tienda-unica__volver { flex: none; gap: 6px; }
.tienda-unica__volver .nm-ico { width: 16px; height: 16px; }
/* La tarjeta que espera su plan: el mismo latido que un botón que espera,
   sin cambiar de tamaño. */
.nm-comparativa__tarjeta--cargando { opacity: .55; animation: nm-latido 1.2s linear infinite; cursor: progress; }
.nm-comparativa__error { margin: var(--nm-esp-2) 0 0; padding: 0 var(--nm-esp-1); color: var(--nm-texto-2); }
.tienda-unica__faltantes {
  flex: 1 1 100%; min-width: 0; margin: 6px 0 0; padding: 10px 0 0;
  border-top: 1px solid var(--nm-borde-sutil);
}
.tienda-unica__grupo { min-width: 0; margin: 0; padding: 0; border: 0; }
.tienda-unica__leyenda {
  padding: 0; margin-bottom: 2px; color: var(--nm-texto-2);
  font: 600 var(--nm-b3) / 1.3 var(--nm-cuerpo);
}
.tienda-unica__cuales > summary {
  display: inline-flex; padding: 4px 0; cursor: pointer; list-style: none;
  color: var(--nm-texto-2); font: 600 var(--nm-b3) / 1.3 var(--nm-cuerpo);
  text-decoration: underline; text-underline-offset: 3px;
}
.tienda-unica__cuales > summary::-webkit-details-marker { display: none; }
.tienda-unica__lista { list-style: none; margin: 4px 0 0; padding: 0; }
.tienda-unica__producto {
  display: flex; justify-content: space-between; gap: 10px; padding: 3px 0;
  font-size: var(--nm-b2); color: var(--nm-texto);
}
.tienda-unica__nombre { min-width: 0; overflow-wrap: anywhere; }
/* Lo que la tienda trae en otro tamaño: la frase del cambio es más larga que
   un precio y en 375 px baja a su propio renglón en vez de apretar el nombre. */
.tienda-unica__tamanos .tienda-unica__leyenda { margin: 0 0 2px; }
.tienda-unica__tamanos .tienda-unica__producto { flex-wrap: wrap; gap: 0 10px; }
.tienda-unica__tamanos .tienda-unica__producto .nm-meta { flex: 0 1 auto; }
.tienda-unica__producto .nm-meta { flex: none; }
.tienda-unica__opcion {
  display: flex; align-items: flex-start; gap: 10px; padding: 8px 0; cursor: pointer;
  font: 600 var(--nm-b2) / 1.3 var(--nm-cuerpo); color: var(--nm-texto);
}
.tienda-unica__opcion > span { display: flex; flex-direction: column; gap: 1px; }
.tienda-unica__opcion .nm-meta { font-weight: 400; }
.tienda-unica__opcion input {
  width: 18px; height: 18px; margin: 1px 0 0; flex: none; accent-color: var(--nm-primario);
}
.tienda-unica__opcion input:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: 2px; }

.nm-escalon { flex: 0 0 230px; padding: 12px 14px; }
.nm-escalon__titulo { font: 700 14px / 1.3 var(--nm-cuerpo); }
.nm-escalon__cifra { font: 700 18px / 1.3 var(--nm-cuerpo); font-variant-numeric: tabular-nums; }

/* ---------- segmentado ---------- */

/* Dos o tres partes de una misma pantalla, no navegación: la pastilla vive
   dentro del contenido y la parte activa sube un nivel tonal. Sin oro — el
   oro no marca dónde estás (PANTALLAS §8). */
.nm-segmentado {
  display: inline-flex; gap: 2px; padding: 3px;
  background: var(--nm-fondo); border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-full);
}
.nm-segmentado button {
  flex: 1 1 auto; height: 32px; padding: 0 18px; border: 0;
  border-radius: var(--nm-radio-full); background: transparent;
  color: var(--nm-texto-2); font: 600 var(--nm-b2) / 1 var(--nm-cuerpo);
  white-space: nowrap; cursor: pointer;
}
.nm-segmentado button[aria-current="page"] {
  background: var(--nm-superficie-2); color: var(--nm-texto); font-weight: 700;
}

/* ---------- la grilla de la semana ---------- */

/* Siete columnas por seis momentos, y cada celda es un vistazo: un nudo por
   plato, punteada cuando está vacía. No es donde se edita —eso pasa en el
   detalle del día, debajo— sino donde se ve la semana entera de un golpe,
   que es lo que se pidió: revisar no puede ser abrir setenta platos. Los
   nudos van en terciario: acá no hay nada focal, y el oro es del dato que
   gana (DESIGN.md). */
.nm-grilla {
  display: grid;
  grid-template-columns: var(--nm-grilla-rotulo, 92px) repeat(var(--nm-grilla-dias, 7), minmax(0, 1fr));
  gap: 4px;
}
/* El rótulo del día es un botón —elige el día del detalle— pero se ve como
   rótulo: sin relleno ni borde, y el elegido se marca en tinta. */
.nm-grilla__dia {
  font: 600 var(--nm-b3) / 1 var(--nm-cuerpo); letter-spacing: .06em;
  text-transform: uppercase; color: var(--nm-texto-3); text-align: center;
  padding: 0 0 6px; background: transparent; border: 0; cursor: pointer;
}
.nm-grilla__dia--hoy { color: var(--nm-texto-2); }
.nm-grilla__dia--elegido { color: var(--nm-texto); font-weight: 700; }
.nm-grilla__momento {
  display: flex; align-items: center;
  font-size: var(--nm-b3); line-height: 1.2; color: var(--nm-texto-2);
  padding-right: 8px; min-width: 0;
}
.nm-grilla__celda {
  position: relative; overflow: hidden;
  display: flex; align-items: center; justify-content: center; flex-wrap: wrap;
  gap: 3px; height: 40px; padding: 0 2px;
  background: var(--nm-superficie-2); border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-md); color: var(--nm-texto-3); cursor: pointer;
}
.nm-grilla__celda--vacia { background: transparent; border-style: dashed; }
.nm-grilla__celda--fuera { background: transparent; border-style: dashed; opacity: .75; }
.nm-grilla__celda--elegido { border-color: var(--nm-texto); }
.nm-grilla__celda .nm-ico { width: 15px; height: 15px; }
/* Ya no la pinta `menu.js` (el mosaico de fotos la reemplazó, 2026-09-23);
   se queda solo por la galería de componentes, que la sigue mostrando como
   referencia del estado antiguo. */
.nm-grilla__nudo { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
@media (hover: hover) and (pointer: fine) {
  .nm-grilla__celda:hover { border-color: var(--nm-borde-fuerte); }
}
/* Un plato puesto: la foto llena la celda entera (`fotoReceta`, sin verId
   propio — la celda YA es el control), pareja con el resto de fotos de
   receta pero sin su borde ni su cuadrado 1:1 propios, que acá los pone
   la celda. */
.nm-grilla__celda .nm-grilla__foto {
  position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto;
  border: 0; border-radius: inherit;
}

/* Dos platos o más (2026-09-23, reemplaza al nudo por plato — un puntito no
   dice nada, la foto sí): el mosaico reparte la celda entera entre las
   fotos, separadas por la línea de gap, que es el fondo de la celda
   asomando — nunca un color nuevo. 2 → mitades; 3 → una mitad y dos
   cuartos (el primer botón ocupa las dos filas); 4 (o el «+N» de sobra) →
   cuadrícula 2×2. El borde redondeado es el de la celda (`overflow:
   hidden` arriba); cada mosaico-tile es cuadrado por dentro. */
.nm-grilla__mosaico {
  position: absolute; inset: 0; display: grid; gap: 2px;
}
.nm-grilla__mosaico--2 { grid-template-columns: 1fr 1fr; }
.nm-grilla__mosaico--3, .nm-grilla__mosaico--4 {
  grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr;
}
.nm-grilla__mosaico--3 .nm-grilla__mosaico-tile:first-child { grid-row: 1 / span 2; }
.nm-grilla__mosaico-tile {
  position: relative; display: block; width: 100%; height: 100%;
  padding: 0; margin: 0; border: 0; background: var(--nm-fondo);
  color: var(--nm-texto-3); cursor: pointer; overflow: hidden;
}
.nm-grilla__mosaico-tile:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: -2px; }
.nm-grilla__mosaico-foto {
  position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto;
  border: 0; border-radius: 0;
}
.nm-grilla__mosaico-tile--hueca { display: flex; align-items: center; justify-content: center; }
.nm-grilla__mosaico-tile--hueca .nm-ico { width: 40%; height: 40%; }
.nm-grilla__mosaico-tile--mas {
  display: flex; align-items: center; justify-content: center;
  font: 700 12px/1 var(--nm-cuerpo);
}

/* ---------- motivo: cuerda y nudo ---------- */

.nm-marcador { display: flex; align-items: center; gap: 0; height: 12px; margin-bottom: 12px; }
.nm-marcador i { display: block; width: 34px; height: 3px; border-radius: 2px; background: var(--nm-borde-fuerte); }
.nm-marcador b { display: block; width: 12px; height: 12px; border-radius: 50%; background: var(--nm-texto-3); margin-left: -2px; }
.nm-marcador--focal b { background: var(--nm-nudo); }
.nm-nudo { display: inline-block; width: 14px; height: 14px; border-radius: 50%; background: var(--nm-texto-3); vertical-align: middle; }
.nm-nudo--focal { background: var(--nm-nudo); }

/* Tu quipu (PANTALLAS §3 P6): el quipu haciendo lo que hacía un quipu,
   registrar cantidades anudando. Cuerda madre horizontal, un nudo por compra
   confirmada en orden de tiempo, el de la compra que más ahorró en oro, y los
   hitos en soles como nudos grandes en tinta —punteados los que faltan— con
   la cifra debajo. Con muchas compras la cuerda se desplaza a lo largo: la
   cuerda solo crece, no se comprime. */
.nm-quipu-personal { position: relative; }
.nm-quipu-personal__carril { overflow-x: auto; overscroll-behavior-x: contain; scrollbar-width: none; }
.nm-quipu-personal__carril::-webkit-scrollbar { display: none; }
.nm-quipu-personal__carril svg { display: block; height: 86px; }
.nm-quipu-personal__cuerda { fill: none; stroke: var(--nm-borde-fuerte); stroke-width: 2.5; stroke-linecap: round; }
.nm-quipu-personal__pendiente { fill: none; stroke: var(--nm-borde-fuerte); stroke-width: 2.5; stroke-linecap: round; stroke-dasharray: 2 6; }
.nm-quipu-personal__nudo {
  fill: var(--nm-texto-3); cursor: pointer;
  transition: fill var(--nm-t-toque) ease;
}
.nm-quipu-personal__nudo--focal { fill: var(--nm-nudo); }
.nm-quipu-personal__nudo[aria-pressed="true"] { fill: var(--nm-texto); }
.nm-quipu-personal__nudo:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: 2px; }
.nm-quipu-personal__hito { fill: var(--nm-texto); }
.nm-quipu-personal__hito--pendiente { fill: none; stroke: var(--nm-texto-3); stroke-width: 2; stroke-dasharray: 2 3; }
.nm-quipu-personal__cifra {
  fill: var(--nm-texto-3); text-anchor: middle;
  font: 600 var(--nm-l1) / 1 var(--nm-cuerpo); letter-spacing: var(--nm-l1-tracking);
  font-variant-numeric: tabular-nums;
}
.nm-quipu-personal__tip {
  position: absolute; top: 0; transform: translate(-50%, -100%);
  padding: 6px 10px; border-radius: var(--nm-radio-md);
  background: var(--nm-texto); color: var(--nm-fondo);
  font: 600 var(--nm-b3) / 1.3 var(--nm-cuerpo); font-variant-numeric: tabular-nums;
  white-space: nowrap; pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .nm-quipu-personal__nudo:hover { fill: var(--nm-texto); }
}

/* El quipu del gasto (P6, «Gasto por semana»): el mismo lenguaje que el de
   arriba pero contando plata por período. Cuerda madre horizontal de tiempo
   y un colgante por semana o por mes, con el largo proporcional a lo gastado
   y un nudo por compra. El nudo maíz va en el colgante MÁS CORTO —la semana
   que menos gastaste—, y esa cuerda es la única con oro: el gasto no es un
   premio (PANTALLAS §8). */
.nm-quipu-gasto__cuerda { fill: none; stroke: var(--nm-borde-fuerte); stroke-width: 3; stroke-linecap: round; }
/* `non-scaling-stroke`: el quipu escala con su `viewBox` de 640 y en un
   teléfono queda al 55 %, donde un trazo escalado a 1.6 px borra la partición
   en segmentos justo donde más falta verla. El trazo se queda en 3 px de
   pantalla y las posiciones sí escalan. */
.nm-quipu-gasto__hilo {
  fill: none; stroke: var(--nm-texto-3); stroke-width: 3; stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}
.nm-quipu-gasto__nudo { fill: var(--nm-texto-3); }
.nm-quipu-gasto__nudo--focal { fill: var(--nm-nudo); }

/* R57: el colgante va partido en un segmento por TIENDA y cada tienda tiene
   su TONO, no su color: la paleta no tiene colores que repartir (DESIGN.md,
   95 % acromática) y meter uno por tienda sería inventar una paleta. Son
   cinco pasos de la tinta —el mismo recurso que usaba el quipu del gasto
   hasta `ee50b83`, y con SUS MISMOS VALORES, 1 · .68 · .46 · .3 · .18, que
   separan mucho más que la rampa apretada que probó R55—, escritos como
   opacidad sobre `--nm-texto` para que los cinco escalones sirvan igual en
   claro y en oscuro: la rampa de bordes se apaga contra el fondo profundo y
   el tono 5 desaparecería. El oro no entra
   en la rampa: sigue solo en el nudo maíz del colgante más corto. */
.nm-quipu-gasto__hilo--t1, .nm-quipu-gasto__hilo--t2, .nm-quipu-gasto__hilo--t3,
.nm-quipu-gasto__hilo--t4, .nm-quipu-gasto__hilo--t5 { stroke: var(--nm-texto); }
.nm-quipu-gasto__nudo--t1, .nm-quipu-gasto__nudo--t2, .nm-quipu-gasto__nudo--t3,
.nm-quipu-gasto__nudo--t4, .nm-quipu-gasto__nudo--t5 { fill: var(--nm-texto); }
.nm-quipu-gasto__hilo--t1 { stroke-opacity: 1; }
.nm-quipu-gasto__hilo--t2 { stroke-opacity: .68; }
.nm-quipu-gasto__hilo--t3 { stroke-opacity: .46; }
.nm-quipu-gasto__hilo--t4 { stroke-opacity: .3; }
.nm-quipu-gasto__hilo--t5 { stroke-opacity: .18; }
.nm-quipu-gasto__nudo--t1 { fill-opacity: 1; }
.nm-quipu-gasto__nudo--t2 { fill-opacity: .68; }
.nm-quipu-gasto__nudo--t3 { fill-opacity: .46; }
.nm-quipu-gasto__nudo--t4 { fill-opacity: .3; }
.nm-quipu-gasto__nudo--t5 { fill-opacity: .18; }
/* el maíz manda sobre su tono: es el nudo del colgante más corto */
.nm-quipu-gasto__nudo--focal { fill: var(--nm-nudo); fill-opacity: 1; }
.nm-quipu-gasto__hilo { transition: stroke-opacity var(--nm-mov-base) var(--nm-curva-salida); }
/* El nudo transiciona igual que su hilo: el resaltado por tienda de
   `.gasto__seg--activa` (`estilo.css`) los sube o los apaga juntos. */
.nm-quipu-gasto__nudo { transition: fill-opacity var(--nm-mov-base) var(--nm-curva-salida); }
@media (hover: hover) and (pointer: fine) {
  .nm-quipu-gasto__hilo:hover { stroke-opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .nm-quipu-gasto__hilo, .nm-quipu-gasto__nudo { transition: none; }
}

/* La muestra de tono de la leyenda: el mismo trocito de cuerda, en redondo. */
.nm-quipu-gasto__muestra {
  display: inline-block; width: 10px; height: 10px; flex: none;
  border-radius: var(--nm-radio-full);
  background: var(--nm-texto);
}
.nm-quipu-gasto__muestra--t1 { opacity: 1; }
.nm-quipu-gasto__muestra--t2 { opacity: .68; }
.nm-quipu-gasto__muestra--t3 { opacity: .46; }
.nm-quipu-gasto__muestra--t4 { opacity: .3; }
.nm-quipu-gasto__muestra--t5 { opacity: .18; }

/* ---------- ajustes: la lista de filas con chevrón ---------- */

/* Un bloque de ajustes es una tarjeta con filas adentro, a la manera de una
   lista de ajustes de sistema: nombre a la izquierda, el valor puesto debajo
   en terciario, y el chevrón que dice que hay algo más hondo. El valor se
   lee sin abrir nada, que es lo que hace que la pantalla se revise de un
   vistazo. Sin borde por fila: separan divisores de 1px. */
.nm-ajustes {
  background: var(--nm-superficie-1); border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-lg); overflow: hidden;
}
.nm-ajuste {
  display: flex; align-items: center; gap: 12px; width: 100%;
  min-height: 60px; padding: 12px 16px; text-align: left; cursor: pointer;
  border: 0; border-bottom: 1px solid var(--nm-borde-sutil);
  background: transparent; color: inherit; font: inherit; text-decoration: none;
}
.nm-ajuste:last-child { border-bottom: 0; }
.nm-ajuste__cuerpo { flex: 1; min-width: 0; }
.nm-ajuste__nombre { font: 600 15px / 1.3 var(--nm-cuerpo); }
.nm-ajuste__valor {
  font-size: var(--nm-b3); line-height: 1.4; color: var(--nm-texto-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nm-ajuste__chev { width: 18px; height: 18px; color: var(--nm-texto-3); flex: 0 0 auto; }
.nm-ajuste--peligro .nm-ajuste__nombre { color: var(--nm-error); }
@media (hover: hover) and (pointer: fine) {
  .nm-ajuste:hover { background: var(--nm-superficie-2); }
}

/* Segmentado: tres opciones excluyentes en una pastilla (el tema). El
   elegido sube al fondo de página, no a un color: el oro no marca estado. */
.nm-segmentado {
  display: inline-flex; padding: 3px; gap: 2px; border: 1px solid var(--nm-borde);
  border-radius: var(--nm-radio-full); background: var(--nm-superficie-2);
}
.nm-segmentado button {
  height: 30px; padding: 0 14px; border: 0; border-radius: var(--nm-radio-full);
  background: transparent; color: var(--nm-texto-2);
  font: 600 13px / 1 var(--nm-cuerpo); cursor: pointer;
}
.nm-segmentado button[aria-pressed="true"] {
  background: var(--nm-fondo); color: var(--nm-texto); box-shadow: var(--nm-sombra-1);
}

/* La cuenta, arriba de Ajustes: avatar grande, el nombre y el correo. */
.nm-cuenta { display: flex; align-items: center; gap: 14px; }
.nm-cuenta__avatar {
  width: 56px; height: 56px; border-radius: var(--nm-radio-full); flex: 0 0 auto;
  background: var(--nm-primario); color: var(--nm-primario-tinta);
  display: flex; align-items: center; justify-content: center;
  font: 700 22px / 1 var(--nm-cuerpo);
}
.nm-cuenta__quien { flex: 1; min-width: 0; }
.nm-cuenta__nombre { font: 700 19px / 1.2 var(--nm-cuerpo); letter-spacing: -0.01em; }
.nm-cuenta__correo {
  font-size: var(--nm-b3); color: var(--nm-texto-3);
  overflow: hidden; text-overflow: ellipsis;
}

/* ---------- cromo ---------- */

.nm-cabecera {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 12px;
  padding: 12px 20px 10px; min-height: 56px;
}
.nm-cabecera__titulo {
  margin: 0; font: 700 26px / 1.1 var(--nm-cuerpo); letter-spacing: -0.02em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nm-avatar {
  width: 36px; height: 36px; border-radius: var(--nm-radio-full); flex: 0 0 auto;
  background: var(--nm-primario); color: var(--nm-primario-tinta);
  display: flex; align-items: center; justify-content: center; font: 700 14px / 1 var(--nm-cuerpo);
}
/* Reseteo de apariencia de <button> UNA sola vez acá: hasta el 2026-09-23
   cada pantalla que usaba `.nm-volver` en un `<button>` cargaba su propia
   `background: none; border: 0; cursor: pointer` en una clase modificadora
   (`.ajustes__volver`, `.ficha__volver`, `.receta-lista__volver`) porque la
   base nunca lo traía. El «‹ Carritos» del push del carrito no tiene
   modificadora propia y se quedó con el botón gris de fábrica del
   navegador —el bug que Gonzalo reportó dos veces—. El reseteo va en la
   base para que ningún consumidor nuevo lo vuelva a olvidar. */
.nm-volver {
  display: inline-flex; align-items: center; gap: 4px; color: var(--nm-texto-2);
  font: 600 14px / 1 var(--nm-cuerpo); text-decoration: none;
  background: none; border: 0; padding: 0; cursor: pointer;
}
.nm-volver .nm-ico { width: 18px; height: 18px; }

/* Tab bar flotante de 5 + botón circular aparte. Dos capas fijas abajo como máximo. */
.nm-tabs { display: flex; align-items: center; gap: 10px; }
.nm-tabs__pastilla {
  flex: 1; display: flex; justify-content: space-around; align-items: center; height: 64px; padding: 6px;
  border-radius: var(--nm-radio-full); background: var(--nm-superficie-1); border: 1px solid var(--nm-borde);
  box-shadow: var(--nm-sombra-2);
}
.nm-tab {
  display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 6px 0; min-width: 54px;
  border-radius: var(--nm-radio-full); color: var(--nm-texto-3); border: 0; background: transparent;
  font: 500 10px / 1 var(--nm-cuerpo); cursor: pointer;
  transition: background-color var(--nm-t-capa) var(--nm-curva-mover), color var(--nm-t-toque) ease;
}
.nm-tab .nm-ico { width: 21px; height: 21px; }
.nm-tab[aria-current="page"] { background: var(--nm-superficie-2); color: var(--nm-texto); font-weight: 600; }
.nm-tabs--fija { position: fixed; left: 16px; right: 16px; bottom: max(18px, env(safe-area-inset-bottom)); z-index: 10; }

.nm-barra {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; height: 74px; padding: 12px 20px;
  background: var(--nm-superficie-1); border-top: 1px solid var(--nm-borde);
}
.nm-barra__cifra { font: 700 20px / 1.2 var(--nm-cuerpo); letter-spacing: -0.01em; font-variant-numeric: tabular-nums; transition: filter var(--nm-t-capa) ease, opacity var(--nm-t-capa) ease; }
.nm-barra__cifra[data-cambiando] { filter: blur(2px); opacity: .6; }

/* Toast: entra y sale por el mismo borde, transición (no keyframes), pausa con la pestaña oculta (JS).
   Vive en la capa superior (`popover="manual"`): nada de `z-index`, así se ve
   también sobre Buscar a pantalla completa y sobre la ficha. */
.nm-toast {
  position: fixed; inset: auto 20px max(28px, env(safe-area-inset-bottom)) 20px;
  width: auto; max-width: none; height: auto; max-height: none;
  margin: 0; border: 0; overflow: visible;
  display: none; align-items: center; justify-content: space-between; gap: 12px; padding: 12px 16px;
  border-radius: var(--nm-radio-lg); background: var(--nm-texto); color: var(--nm-fondo); box-shadow: var(--nm-sombra-2);
  font: 600 14px / 1.3 var(--nm-cuerpo);
  transform: translateY(calc(100% + 40px)); opacity: 0;
  transition: transform var(--nm-t-capa) var(--nm-curva-salida), opacity var(--nm-t-capa) ease,
              display var(--nm-t-capa) allow-discrete, overlay var(--nm-t-capa) allow-discrete;
}
.nm-toast:popover-open { display: flex; transform: translateY(0); opacity: 1; }
@starting-style {
  .nm-toast:popover-open { transform: translateY(calc(100% + 40px)); opacity: 0; }
}
.nm-toast__accion { color: var(--nm-p-maiz); font-weight: 700; white-space: nowrap; border: 0; background: transparent; cursor: pointer; }
.nm-toast .nm-ico { width: 18px; height: 18px; }

/* Popover anclado: escala desde su disparador. Vive en la capa superior del navegador. */
.nm-popover {
  margin: 0; padding: 6px; border: 1px solid var(--nm-borde); border-radius: var(--nm-radio-lg);
  background: var(--nm-superficie-2); color: var(--nm-texto); box-shadow: var(--nm-sombra-2);
  min-width: 220px; transform-origin: top left;
  opacity: 1; transform: scale(1);
  transition: opacity var(--nm-t-capa) var(--nm-curva-salida), transform var(--nm-t-capa) var(--nm-curva-salida), overlay var(--nm-t-capa) allow-discrete, display var(--nm-t-capa) allow-discrete;
}
.nm-popover:not(:popover-open) { opacity: 0; transform: scale(0.95); }
@starting-style { .nm-popover:popover-open { opacity: 0; transform: scale(0.95); } }
.nm-popover__item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; border: 0; background: transparent; color: inherit; border-radius: var(--nm-radio-md); font: 500 15px / 1 var(--nm-cuerpo); cursor: pointer; text-align: left; }
.nm-popover__item[aria-checked="true"] { font-weight: 700; }
@media (hover: hover) and (pointer: fine) { .nm-popover__item:hover { background: var(--nm-superficie-1); } }

/* La hoja: <dialog> nativo que sube desde abajo. Se arrastra para cerrar (JS). */
.nm-hoja {
  position: fixed; inset: auto 0 0 0; width: 100%; max-width: none; margin: 0; padding: 8px 20px 24px;
  border: 0; border-radius: var(--nm-radio-xl) var(--nm-radio-xl) 0 0;
  background: var(--nm-superficie-1); color: var(--nm-texto); box-shadow: var(--nm-sombra-2);
  max-height: calc(100dvh - 48px); overflow: auto; overscroll-behavior: contain;
  transform: translateY(0);
  transition: transform var(--nm-t-hoja-salida) var(--nm-curva-hoja), overlay var(--nm-t-hoja-salida) allow-discrete, display var(--nm-t-hoja-salida) allow-discrete;
}
.nm-hoja[open] { transition-duration: var(--nm-t-hoja); }
.nm-hoja:not([open]) { transform: translateY(100%); }
@starting-style { .nm-hoja[open] { transform: translateY(100%); } }
.nm-hoja::backdrop { background: var(--nm-velo); opacity: 1; transition: opacity var(--nm-t-capa) ease, overlay var(--nm-t-capa) allow-discrete, display var(--nm-t-capa) allow-discrete; }
.nm-hoja:not([open])::backdrop { opacity: 0; }
@starting-style { .nm-hoja[open]::backdrop { opacity: 0; } }
.nm-hoja__asa { width: 36px; height: 4px; border-radius: 2px; background: var(--nm-borde-fuerte); margin: 0 auto 14px; touch-action: none; }
.nm-hoja__titulo { font: 700 19px / 1.2 var(--nm-cuerpo); letter-spacing: -0.01em; }
.nm-hoja--alta { height: calc(100dvh / 6 * 5); }
.nm-hoja__pie { position: sticky; bottom: -24px; margin: 0 -20px -24px; padding: 12px 20px 28px; background: var(--nm-superficie-1); border-top: 1px solid var(--nm-borde); display: flex; align-items: center; gap: 12px; }

/* Modal: el único que escala desde el centro. */
.nm-modal {
  margin: auto; padding: 24px; border: 1px solid var(--nm-borde); border-radius: var(--nm-radio-xl);
  background: var(--nm-superficie-2); color: var(--nm-texto); box-shadow: var(--nm-sombra-2); max-width: 360px; width: calc(100% - 40px);
  transform-origin: center; opacity: 1; transform: scale(1);
  transition: opacity var(--nm-t-capa) var(--nm-curva-salida), transform var(--nm-t-capa) var(--nm-curva-salida), overlay var(--nm-t-capa) allow-discrete, display var(--nm-t-capa) allow-discrete;
}
.nm-modal:not([open]) { opacity: 0; transform: scale(0.95); }
@starting-style { .nm-modal[open] { opacity: 0; transform: scale(0.95); } }
.nm-modal::backdrop { background: var(--nm-velo); }
/* Título, consecuencia en una línea y dos salidas: Cancelar y el verbo, que
   es el único relleno de la capa. La consecuencia se dice concreta —«se van
   los 51 productos»— y nada más: lo legal vive en la política. */
.nm-modal__texto { margin: 8px 0 20px; color: var(--nm-texto-2); }
.nm-modal__acciones { display: flex; gap: 8px; justify-content: flex-end; }

/* Esqueleto: pulso de opacidad, lineal, nunca spinner. */
.nm-esqueleto { border-radius: var(--nm-radio-md); background: var(--nm-superficie-2); animation: nm-pulso 1.2s linear infinite; }
@keyframes nm-pulso { 0%, 100% { opacity: .6; } 50% { opacity: 1; } }

/* Estado vacío: una instrucción y dónde. */
.nm-vacio { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 14px; padding: 48px 36px; }
.nm-vacio .nm-ico { width: 40px; height: 40px; color: var(--nm-texto-3); }
.nm-vacio__titulo { font: 700 22px / 1.2 var(--nm-cuerpo); letter-spacing: -0.01em; }
.nm-vacio__texto { font-size: var(--nm-b2); line-height: 1.5; color: var(--nm-texto-2); max-width: 280px; }

/* ---------- Entrada («Escanea / Pega / Busca») ----------
   Tarjeta-botón para elegir entre varias maneras de empezar algo, todas del
   mismo peso: ícono grande arriba, etiqueta corta y, si cabe, una línea de
   apoyo. Es neutra a propósito —superficie de tarjeta, borde de contorno,
   tinta—: el único primario de una pantalla es otro, y el oro no entra
   (DESIGN.md «Components»: botón secundario, pill solo para botones; esta es
   una tarjeta y lleva el radio de tarjeta). Área táctil de sobra: nunca
   menos de 96 px de alto. Primer uso: el estado vacío de la lista
   (`.lista-vacia__entradas`, que decide la grilla). */
.nm-entrada {
  display: flex; flex-direction: column; align-items: center; justify-content: flex-start;
  gap: var(--nm-esp-2); min-width: 0; min-height: 96px;
  padding: var(--nm-esp-4) var(--nm-esp-2);
  background: var(--nm-superficie-1); color: var(--nm-texto);
  border: 1px solid var(--nm-borde); border-radius: var(--nm-radio-lg);
  font-family: var(--nm-cuerpo); text-align: center; cursor: pointer;
}
.nm-entrada .nm-entrada__ico { width: 32px; height: 32px; stroke-width: 1.5; color: var(--nm-texto-2); }
.nm-entrada__etiqueta { font-size: var(--nm-b2); font-weight: 700; line-height: 1.2; text-wrap: balance; }
.nm-entrada__apoyo { font-size: var(--nm-b3); line-height: 1.35; color: var(--nm-texto-3); text-wrap: balance; }
@media (hover: hover) and (pointer: fine) {
  .nm-entrada:hover { background: var(--nm-superficie-2); border-color: var(--nm-borde-fuerte); }
  .nm-entrada:hover .nm-entrada__ico { color: var(--nm-texto); }
}

/* El «¿quisiste decir…?» del camino de cero: mismo bloque que .nm-vacio, un
   botón real —nunca sustituye sola la consulta— en el único oro permitido
   por fila, el de texto (--nm-acento). */
.nm-quisiste-decir { font-size: var(--nm-b2); color: var(--nm-texto-2); max-width: 280px; }

/* La Bolsa, la mascota (DESIGN.md «Mascota: la Bolsa», `formatos.js`
   `bolsaHtml`): figura, nunca fondo — una por pantalla, en sus seis
   lugares y ninguno más. El marcado trae las dos variantes de tema
   siempre; acá se decide cuál se ve, con el mismo patrón de `[data-theme]`
   + `prefers-color-scheme` guardado que ya usa `.miniatura` más abajo en
   `estilo.css` (una media query sola no alcanza, porque no sabe de la
   elección explícita del conmutador). El tamaño y la posición de cada
   pose son de la superficie que la usa (`.lista-vacia__bolsa`,
   `.reparto__bolsa-calculando`…), no de acá. */
.nm-bolsa { display: inline-block; width: 180px; height: 180px; flex: none; }
.nm-bolsa picture { display: block; width: 100%; height: 100%; }
.nm-bolsa img { display: block; width: 100%; height: 100%; }
/* `picture.nm-bolsa__oscura` y no `.nm-bolsa__oscura` a secas: la regla de
   arriba (`.nm-bolsa picture`) tiene la especificidad de una clase MÁS un
   elemento, así que un selector de una sola clase pierde contra ella sin
   importar el orden — las dos variantes se veían juntas, superpuestas
   sobre el título (QA de esta entrega). Repitiendo el elemento se igualan
   y gana el orden, que es lo que hace falta. */
.nm-bolsa picture.nm-bolsa__oscura { display: none; }
:root[data-theme="oscuro"] .nm-bolsa picture.nm-bolsa__oscura { display: block; }
:root[data-theme="oscuro"] .nm-bolsa picture.nm-bolsa__clara { display: none; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="claro"]) .nm-bolsa picture.nm-bolsa__oscura { display: block; }
  :root:not([data-theme="claro"]) .nm-bolsa picture.nm-bolsa__clara { display: none; }
}
.nm-quisiste-decir__boton {
  font: 700 var(--nm-b2) / 1.5 var(--nm-cuerpo); color: var(--nm-acento); background: none; border: none;
  padding: 0; cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
@media (hover: hover) and (pointer: fine) {
  .nm-quisiste-decir__boton:hover { color: var(--nm-acento-hover); }
}
.nm-quisiste-decir__boton:focus-visible { outline: 2px solid var(--nm-acento); outline-offset: 2px; border-radius: 2px; }

/* El aviso de marcha blanca (Gonzalo, 2026-09-28): una franja fina arriba de
   TODO —antes de la cabecera de la app y antes del hero de la portada—,
   nunca dentro de una franja existente, así que la comparte `componentes.css`
   y no `estilo.css`: la portada carga la primera y no la segunda. Es tono,
   no acento: `superficie-1` sobre el fondo, jamás índigo ni oro, para no
   competir con el único primario de la franja de abajo (DESIGN.md,
   Components). El enlace es el mismo `data-comentario` de siempre
   (`seo.enlace_comentario`, `js/comentarios.js`) y se ve como «Avísanos» en
   la ficha: texto, no oro. Se cierra con una X terciaria; qué la recuerda
   por aparato es `js/aviso.js` en la app y un script propio en la portada,
   los dos con la MISMA clave versionada — subirle el número la vuelve a
   mostrar a todo el mundo. */
.nm-aviso-marcha {
  display: flex; align-items: center; gap: var(--nm-esp-3);
  padding: var(--nm-esp-2) calc(var(--nm-esp-4) + env(safe-area-inset-right))
           var(--nm-esp-2) calc(var(--nm-esp-4) + env(safe-area-inset-left));
  padding-top: calc(var(--nm-esp-2) + env(safe-area-inset-top));
  background: var(--nm-superficie-1);
  border-bottom: 1px solid var(--nm-borde-sutil);
}
.nm-aviso-marcha[hidden] { display: none; }
.nm-aviso-marcha__texto {
  margin: 0; flex: 1; min-width: 0;
  font-size: var(--nm-b3); line-height: var(--nm-lh-ui); color: var(--nm-texto-2);
}
.nm-aviso-marcha__texto a { color: var(--nm-texto); text-underline-offset: 2px; }
.nm-aviso-marcha__cerrar {
  flex: none; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--nm-radio-full); background: transparent;
  color: var(--nm-texto-3); font-size: 14px; line-height: 1; cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .nm-aviso-marcha__cerrar:hover { background: var(--nm-superficie-2); color: var(--nm-texto); }
}
.nm-aviso-marcha__cerrar:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: 2px; }

/* La invitación a entrar: una línea en el sitio de la acción (tras el primer
   «Guardar» o «Lo compré»), no un modal ni un toast. Superficie tonal y texto
   secundario, el enlace con subrayado; la × es terciaria. Sin oro, sin
   primario. */
.nm-invitacion {
  display: flex; align-items: center; gap: var(--nm-esp-3);
  margin: 0 0 var(--nm-esp-3); padding: var(--nm-esp-2) var(--nm-esp-2) var(--nm-esp-2) var(--nm-esp-3);
  background: var(--nm-superficie-1); border: 1px solid var(--nm-borde-sutil);
  border-radius: var(--nm-radio-md);
}
.nm-invitacion__texto {
  margin: 0; flex: 1; min-width: 0;
  font-size: var(--nm-b3); line-height: var(--nm-lh-ui); color: var(--nm-texto-2);
}
.nm-invitacion__enlace {
  color: var(--nm-texto); font-weight: 600; text-underline-offset: 2px; white-space: nowrap;
}
.nm-invitacion__cerrar {
  flex: none; width: 28px; height: 28px; display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: var(--nm-radio-full); background: transparent;
  color: var(--nm-texto-3); font-size: 16px; line-height: 1; cursor: pointer;
}
@media (hover: hover) and (pointer: fine) {
  .nm-invitacion__cerrar:hover { background: var(--nm-superficie-2); color: var(--nm-texto); }
}
.nm-invitacion__cerrar:focus-visible, .nm-invitacion__enlace:focus-visible { outline: 2px solid var(--nm-foco); outline-offset: 2px; }

/* Reducción de movimiento: se quitan los desplazamientos, quedan las opacidades. */
@media (prefers-reduced-motion: reduce) {
  .nm-presionable:active { transform: none; }
  .nm-hoja, .nm-hoja:not([open]), .nm-popover, .nm-modal, .nm-toast, .nm-fila--nueva, .nm-trofeo__cifra--entra { transform: none !important; filter: none !important; }
  .nm-esqueleto { animation: none; opacity: .8; }
}
