/* catalogo — estilos compartidos (mobile-only "phone-shaped"). Client-first.

   PALETA: sale del núcleo de marca de la bóveda (proyectos/MGX/identidad/NUCLEO.md),
   emitida en identidad/tokens/mgx.tokens.css. NO se muestrea de acá ni se elige a ojo:
   el canon se muestrea de las piezas reales de MGX. Ver DESIGN.md en la raíz del repo. */

/* Montserrat 800 — display, títulos y precios. El cuerpo denso se queda en el stack
   del sistema: 47 productos no tienen por qué pagar peso de fuente. UN SOLO PESO;
   la jerarquía dentro del display se resuelve con tamaño y color, no bajando otro
   archivo. `montserrat-800-latin.c57401a633.woff2` lo sustituye generate.py por el nombre hasheado. */
@font-face {
  font-family: 'Montserrat';
  font-style: normal;
  font-weight: 800;
  font-display: swap;
  src: url('/montserrat-800-latin.c57401a633.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
    U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
    U+FEFF, U+FFFD;
}

:root {
  /* --- CANON MegaMix: tres colores, tres trabajos (D-45) -------------------
     Son las tres bandas de la hoja de catálogo impresa. Un elemento toma el color
     de SU FUNCIÓN, no de su vecindad. */
  --mgx-orange: #FF710F;   /* IDENTIFICA — banda del header, sello de oferta */
  --mgx-cyan:   #0098C7;   /* ORIENTA    — barra de facetas (la de rubros en papel) */
  --mgx-blue:   #0069BA;   /* ACCIÓN     — botón de pedido, links de acción */

  /* Tonos de servicio: el MISMO color con la luminancia bajada hasta el contraste
     que hace falta. No son colores nuevos; son el canon hecho legible sobre blanco. */
  --mgx-orange-ink: #BC4D00;   /* 5,00:1 sobre #fff */
  --mgx-cyan-ink:   #00789E;   /* 5,02:1 sobre #fff */
  --mgx-orange-wash: #FFF1E8; --mgx-orange-edge: #FFD2B2;
  --mgx-blue-wash:   #F2F9FF; --mgx-blue-edge:   #B2DEFF;

  /* --brand = acento de la MARCA DE PRODUCTO de la ruta (Rigolleau, Lumilagro…), que
     el shell inyecta por marca. Cierra A-27 de raíz: antes --brand significaba a la vez
     "la casa" y "la marca del catálogo", los dos orígenes se pisaban y los badges
     colapsaban. Ahora la casa vive en --mgx-* y nadie la pisa; --brand solo pinta el
     badge de marca. Su default es el azul de la casa, no un azul ajeno. */
  --brand: var(--mgx-blue); --brand-dark: #0B4D8F;

  --bg: #f5f5f4; --surface: #fff; --ink: #1A1A1A; --ink-soft: #52525b;
  --ink-mute: #a1a1aa; --line: #e7e5e4;
  --ok: #15803d; --radius: 12px; --shadow: 0 1px 2px rgba(0,0,0,.04), 0 2px 8px rgba(0,0,0,.05);

  --display: 'Montserrat', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --body: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;

  /* Alto real del header, que app.js mide y escribe después del render. El sticky de
     la barra de facetas cuelga de acá: con el valor hardcodeado, cualquier cambio en
     el header dejaba las dos capas solapadas o separadas. El fallback es el alto
     típico con logo + título + meta + vigencia. */
  --hdr-h: 104px;
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--body);
  color: var(--ink); background: var(--bg); font-size: 16px; line-height: 1.4;
  -webkit-font-smoothing: antialiased; padding-bottom: 104px;
}
.wrap { max-width: 480px; margin: 0 auto; min-height: 100vh; background: var(--bg); }

/* Header — BANDA 1: IDENTIFICA.
   Naranja de marca con el logo oficial, igual que la banda superior de las hojas de
   catálogo de MGX. El comerciante viene de un PDF naranja: acá tiene que reconocer que
   llegó al lugar correcto.
   EL TEXTO VA EN TINTA, NO EN BLANCO: #FF710F es un naranja claro y el blanco encima da
   2,76:1 — no pasa AA ni siquiera para texto grande (mínimo 3:1). La tinta #1A1A1A da
   6,32:1. Que las piezas impresas usen blanco no lo autoriza: el papel no tiene WCAG. */
header {
  background: var(--mgx-orange); color: var(--ink); padding: 12px 16px 10px;
  position: sticky; top: 0; z-index: 10;
}
/* Envuelve a propósito: la vigencia vive DENTRO de .hdr-top para poder sentarse al lado
   del logo cuando el header colapsa. Expandido la mando a renglón propio con
   flex-basis:100%; colapsado la dejo fluir y queda en la misma línea. Un solo nodo, dos
   layouts, sin mover nada en el DOM. */
.hdr-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* El logo oficial de MGX tiene 94×61 px de contenido real. A 30 px de alto lo cubre 2×
   en retina y se ve nítido; por encima de 47×30 CSS px se pixela y el núcleo lo prohíbe
   hasta que llegue el vector (A-45). El alto está clavado a propósito: no crece. */
.hdr-logo { height: 30px; width: auto; display: block; flex: 0 0 auto; }
.hdr-title { font-family: var(--display); font-size: 20px; font-weight: 800; letter-spacing: -.02em; line-height: 1.1; }
/* Sin atenuar por color, y no por descuido: sobre #FF710F el tono más claro que
   todavía pasa AA (4,5:1) es #592400 — un marrón que a simple vista ya es la tinta.
   La banda naranja no deja margen para una jerarquía por atenuación, así que acá la
   hace el TAMAÑO y el PESO: título 20px/800 contra meta 12px/400. */
.hdr-meta { font-size: 12px; color: var(--ink); margin-top: 5px; }
/* Vigencia de las ofertas, en el header — que es sticky, así que acompaña todo el
   scroll. Pedido de Gustavo (13-ago) con dos motivos: acelerador y atajar reclamos
   ("le mandaste el catálogo el 30 de agosto, te contestó el 2 de septiembre").
   Antes era texto #fb923c sobre el header negro (7,7:1). Con el header naranja ese
   cálculo se cayó, y ningún tono de texto sobre naranja podía a la vez pasar AA y
   ganarle al resto del meta. Se resolvió invirtiendo la superficie: PÍLDORA BLANCA con
   el naranja de servicio encima (#BC4D00 sobre #fff = 5,00:1). La píldora destaca por
   forma y por polaridad, no por saturación — que era lo que el naranja ya no permitía. */
.hdr-vig {
  flex: 0 0 100%; margin-top: 7px; padding: 3px 10px; border-radius: 999px;
  background: #fff; color: var(--mgx-orange-ink);
  font-family: var(--display); font-size: 12px; font-weight: 800;
  align-self: flex-start; width: max-content; max-width: 100%;
}

/* ── Cromo colapsado ────────────────────────────────────────────────────────
   Medido el 19-ago en producción: header 104 + facetas 150 + footbar 113 = 367 px de
   cromo fijo, el 55,3% de un iPhone 13. Contra eso, una tarjeta de producto mide
   236 px: entraba 1,26 tarjetas, así que el comerciante NUNCA veía una entera y la
   siguiente empezando — miraba pedazos. El catálogo es la mercadería; el cromo tiene
   que cederle lugar apenas empieza a scrollear.
   Bajando: el header queda en 38 px con el logo y la vigencia, que es lo único que
   sigue haciendo falta a mitad de lista (la marca, y hasta cuándo valen los precios).
   Subiendo: vuelve entero. Es el gesto que el comerciante ya tiene aprendido. */
header.compact { padding: 4px 16px; }
header.compact .hdr-logo { height: 20px; }
header.compact .hdr-title,
header.compact .hdr-meta { display: none; }
header.compact .hdr-vig { flex: 0 1 auto; margin-top: 0; }
/* Dos textos en el mismo nodo, uno visible por vez. El largo dice la fecha completa
   arriba; el corto —"Válidas hasta 31/08"— es el que entra al lado del logo en 38 px.
   Con el largo la línea envolvía y el header colapsado medía 85, no 38. */
.hdr-vig .vig-corta { display: none; }
header.compact .hdr-vig .vig-larga { display: none; }
header.compact .hdr-vig .vig-corta { display: inline; }
header.compact .hdr-vig { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* SIN TRANSICIÓN, y es deliberado. Animar `padding` o `height` obliga al navegador a
   recalcular el layout en cada frame — y acá el elemento que cambia es sticky, con la
   lista entera de productos debajo. En el celular de gama media que usa un comerciante
   eso es jank visible justo mientras scrollea, que es el único momento en que esto
   ocurre. El colapso instantáneo se siente más rápido, no más brusco: lo que el ojo
   registra es que el contenido gana lugar, no cómo llegó. */
.hdr-logo { transition: none; }

/* Greeting / search */
.intro { padding: 16px; background: var(--surface); border-bottom: 1px solid var(--line); }
.intro h1 { margin: 0 0 4px; font-family: var(--display); font-size: 17px; font-weight: 800; }
.intro p { margin: 0 0 12px; color: var(--ink-soft); font-size: 14px; }
.search { position: relative; }
.search input {
  width: 100%; padding: 12px 14px 12px 40px; font-size: 15px; border: 1px solid var(--line);
  border-radius: 10px; font-family: inherit; background: #fafaf9; color: var(--ink);
}
.search input:focus { outline: none; border-color: var(--ink); background: #fff; }
.search::before {
  content: "🔍"; position: absolute; left: 13px; top: 50%; transform: translateY(-50%);
  font-size: 14px; opacity: .5;
}

/* Section chips (camino legacy xlsx) */
.cats {
  display: flex; gap: 8px; overflow-x: auto; padding: 12px 16px; scrollbar-width: none;
  background: var(--surface); border-bottom: 1px solid var(--line); position: sticky; top: var(--hdr-h); z-index: 9;
}
.cats::-webkit-scrollbar { display: none; }
.chip {
  flex: 0 0 auto; padding: 8px 14px; border-radius: 999px; border: 1px solid var(--line);
  background: #fff; font-family: var(--display); font-size: 13px; color: var(--ink-soft); font-weight: 800;
  white-space: nowrap; cursor: pointer;
}
.chip.active { background: var(--ink); color: #fff; border-color: var(--ink); }

/* Faceted filter bar — BANDA 2: ORIENTA.
   Es la barra de rubros de las hojas de catálogo de MGX, vuelta filtro. Cyan, igual que
   en el papel (#0098C7, dominante 89% de esa fila en las dos piezas medidas).
   Texto en TINTA otra vez: el blanco sobre cyan da 3,32:1 y no pasa AA para 12-13px; la
   tinta da 5,23:1. El chip blanco sobre la banda cyan se recorta a 3,32:1, que es lo que
   pide un elemento no textual (3:1). */
.facets-bar {
  background: var(--mgx-cyan); border-bottom: 1px solid rgba(0,0,0,.12);
  position: sticky; top: var(--hdr-h); z-index: 9; padding: 8px 0 6px;
}
/* Colapsada: las tres filas se van y queda UNA que dice qué filtros están puestos.
   150 px → 52 px. Lo que no se puede perder al colapsar es saber que la lista está
   filtrada: un catálogo filtrado que no lo avisa esconde mercadería, y el comerciante
   cree que no hay más y se va. Por eso el resumen nombra los filtros, no dice "3
   filtros activos". */
.facets-bar .facets-summary { display: none; }
.facets-bar.compact .facet { display: none; }
.facets-bar.compact { padding: 7px 16px; }
.facets-bar.compact .facets-summary {
  display: flex; align-items: center; gap: 8px; width: 100%; min-height: 34px;
}
.facets-summary > button {
  background: transparent; border: 0; cursor: pointer; color: var(--ink);
  font-family: var(--display); font-size: 13px; font-weight: 800; text-align: left;
}
.facets-summary .fs-txt {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  opacity: .72; padding: 0;
}
.facets-summary > button:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
/* Con filtros puestos el texto deja de ser una invitación y pasa a ser un estado: se
   pone opaco y en píldora blanca, que es como se ve un chip activo en esta pantalla. */
.facets-summary.fs-on .fs-txt {
  opacity: 1; background: #fff; color: var(--mgx-cyan-ink);
  border-radius: 999px; padding: 4px 11px; flex: 0 1 auto;
}
.facets-summary .fs-cta,
.facets-summary .fs-find {
  flex: 0 0 auto; background: #fff; color: var(--mgx-cyan-ink);
  border-radius: 999px; padding: 5px 12px; font-size: 12px;
  box-shadow: 0 1px 4px rgba(0,0,0,.28); white-space: nowrap;
}

/* ── Modo búsqueda de la barra sticky ──────────────────────────────────────
   El campo no se duplica: el <input> de la bienvenida se MUDA acá adentro (ver
   setSearching). Un segundo input serían dos valores para una misma búsqueda, y el día
   que se desincronicen el comerciante ve una lista filtrada por algo que su campo no
   dice. Este contenedor es el hueco donde aterriza. */
.facets-search { display: none; align-items: center; gap: 8px; padding: 0 16px; }
.facets-bar.searching .facets-search { display: flex; }
.facets-bar.searching .facets-summary,
.facets-bar.searching .facet { display: none; }
.facets-bar.searching { padding: 7px 0; }
.facets-search .fs-slot { flex: 1; min-width: 0; }
/* El input llega con los estilos de `.search input`, que lo dibujan sobre fondo claro.
   Acá el fondo es la banda cyan, así que se le da su propia superficie blanca y se le
   saca el padding-izquierdo que reservaba la lupa decorativa del bloque de bienvenida
   (esa lupa es un ::before de `.search` y no viaja con el input). */
.facets-search .fs-slot input {
  width: 100%; padding: 9px 12px; font-size: 15px; border: 0; border-radius: 999px;
  background: #fff; color: var(--ink); font-family: var(--body);
}
.facets-search .fs-slot input:focus { outline: 2px solid var(--ink); outline-offset: 1px; }
.fs-close {
  flex: 0 0 auto; width: 34px; height: 34px; border: 0; border-radius: 999px;
  background: rgba(255,255,255,.22); color: var(--ink);
  font-size: 17px; line-height: 1; cursor: pointer;
}
.facet { display: flex; align-items: center; gap: 8px; padding: 4px 16px; }
.facet + .facet { border-top: 1px dashed rgba(0,0,0,.18); padding-top: 8px; }
/* 92px y no 64: la faceta "Destacados" (04-ago-2026) es la etiqueta más larga y es UNA
   SOLA PALABRA — no tiene dónde cortar, así que no envuelve. Y como un flex item tiene
   `min-width: auto`, su ancho se CLAMPEA al min-content: con una base menor, esa fila
   quedaba más ancha que las otras dos (medido en Chromium: 89 px contra 80) y los chips
   arrancaban 9 px corridos respecto de Marca y Producto. Con 92 px las tres filas usan
   la base declarada y los chips alinean. El sobrante se lo come el scroll horizontal de
   los chips, que ya existía. Si alguna vez entra una etiqueta más larga, hay que subir
   este número o la fila se vuelve a desalinear sola. */
.facet-label {
  flex: 0 0 92px; font-family: var(--display); font-size: 11px; font-weight: 800; color: var(--ink);
  text-transform: uppercase; letter-spacing: .03em;
}
.facet-scroll { position: relative; flex: 1; min-width: 0; }
/* `crowded` = esta fila tiene chips fuera de pantalla, así que se le reserva el hueco
   del botón. Lo pone app.js DESPUÉS de medir sin él: una fila que entra justa no paga
   el hueco y no se rompe sola. El hueco va acá afuera y no en el que scrollea, para no
   inflar su scrollWidth ni dejar un chip debajo del botón. */
.facet-scroll.crowded { padding-right: 56px; }
/* Fundido corto en los bordes mientras haya chips ocultos de ese lado (lo togglea
   app.js). El degradado sale del cyan de la banda, no de --surface: el fondo cambió.
   ERA DE 32px Y ESE ERA EL PROBLEMA: desvanecía el chip cortado, que es justamente la
   pista de que hay más. Ahora son 14px — alcanza para que el corte no parezca un tajo
   y deja el chip ASOMAR, que es lo que se lee como "seguí deslizando". */
.facet-scroll::before, .facet-scroll::after {
  content: ""; position: absolute; top: 0; bottom: 2px; width: 14px;
  pointer-events: none; opacity: 0; transition: opacity .15s; z-index: 1;
}
.facet-scroll::before { left: 0; background: linear-gradient(90deg, var(--mgx-cyan) 30%, transparent); }
.facet-scroll::after { right: 56px; background: linear-gradient(270deg, var(--mgx-cyan) 30%, transparent); }
.facet-scroll.has-left::before { opacity: 1; }
.facet-scroll.has-right::after { opacity: 1; }
/* El botón que dice CUÁNTAS opciones quedan y las trae. Es la respuesta a las dos
   preguntas que la fila no contestaba: "¿hay más?" y "¿cómo llego?".
   Blanco con el cyan de servicio encima: 5,02:1 sobre #fff. No usa el cyan de la banda
   como fondo porque tiene que despegarse de ella, no fundirse. */
.facet-more {
  position: absolute; right: 0; top: 50%; transform: translateY(-50%); z-index: 2;
  min-width: 50px; height: 30px; padding: 0 9px; border: 0; border-radius: 999px;
  background: #fff; color: var(--mgx-cyan-ink);
  font-family: var(--display); font-size: 12px; font-weight: 800; line-height: 1;
  cursor: pointer; box-shadow: 0 1px 4px rgba(0,0,0,.28);
  display: flex; align-items: center; justify-content: center; gap: 1px;
}
.facet-more:active { transform: translateY(-50%) scale(.94); }
.facet-more:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }
.facet-chips { display: flex; gap: 8px; overflow-x: auto; scrollbar-width: none; padding-bottom: 2px; }
.facet-chips::-webkit-scrollbar { display: none; }
.facet .chip { padding: 6px 13px; font-size: 13px; border-color: transparent; }
/* El chip activo NO se pinta del mismo cyan de la banda: desaparecería dentro de ella.
   Se marca por inversión — tinta llena, texto blanco (17,4:1) — que es el mismo recurso
   que ya usaba el chip legacy activo. La banda dice "acá se orienta"; el chip dice
   "esto está puesto". */
.facet .chip.active { background: var(--ink); color: #fff; border-color: var(--ink); }
/* Badge de la marca del producto: el ÚNICO lugar que usa --brand, que es lo que --brand
   significa ahora (la marca del catálogo, no la casa). */
.marca-badge {
  display: inline-block; font-family: var(--display); font-size: 10px; font-weight: 800; letter-spacing: .03em;
  color: var(--brand); border: 1px solid var(--line); border-radius: 4px;
  padding: 1px 6px; margin-right: 6px; text-transform: uppercase;
}

/* List */
.section-head {
  padding: 18px 16px 6px; font-family: var(--display); font-size: 11px; font-weight: 800; color: var(--ink-soft);
  text-transform: uppercase; letter-spacing: .08em; background: var(--bg);
}
.item {
  display: grid; grid-template-columns: 80px minmax(0, 1fr); gap: 12px; padding: 12px 14px;
  background: var(--surface); border-bottom: 1px solid var(--line); align-items: start;
}
.item-img {
  width: 80px; height: 80px; background: #fff; border: 1px solid var(--line); border-radius: 10px;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
}
.item-img img { max-width: 100%; max-height: 100%; display: block; }
.item-img.empty::after { content: "📦"; font-size: 30px; opacity: .25; }
/* Solo las fotos con resolución suficiente se ofrecen para ampliar (app.js marca
   .zoomable al medirlas). La lupa es la única señal de que la miniatura se toca. */
.item-img.zoomable { cursor: zoom-in; position: relative; }
.item-img.zoomable::after {
  content: "⤢"; position: absolute; right: 2px; bottom: 1px; font-size: 11px; line-height: 1;
  color: #fff; background: rgba(0,0,0,.45); border-radius: 4px; padding: 2px 3px; pointer-events: none;
}
.item-img.zoomable:focus-visible { outline: 2px solid var(--mgx-blue); outline-offset: 2px; }
.item-body { min-width: 0; }
.item-tag {
  display: inline-block; font-family: var(--display); font-size: 10px; font-weight: 800; letter-spacing: .04em;
  color: #fff; background: var(--mgx-blue); padding: 2px 7px; border-radius: 4px; margin: 0 5px 5px 0;
}
/* Los dos badges llevan su color ESCRITO, no heredado (A-27) — pero ya no por la
   colisión de --brand, que quedó resuelta arriba: es que cada uno tiene su propio par
   de contraste medido y no debe moverse cuando cambie otra cosa.
   OFERTA pasa al NARANJA CANON con texto tinta (6,32:1). Antes era #ea580c con texto
   blanco: 3,56:1, que NO pasaba AA para 10px/800 — un defecto que ya estaba en
   producción y que este repinte corrige de paso. El naranja sigue siendo el que grita
   ("el fuerte de esta herramienta", Lucas), ahora además legible.
   NUEVO se queda en azul profundo con texto blanco (7,56:1 sobre #fff y 7,12:1 sobre la
   card con cantidad cargada). Se distingue de OFERTA por color Y por polaridad. */
.tag-offer { background: var(--mgx-orange); color: var(--ink); }
.tag-new { background: #075985; color: #fff; }
.item-name { font-weight: 700; font-size: 14px; line-height: 1.3; margin: 0 0 3px; color: var(--ink); }
.item-meta { font-size: 11px; color: var(--ink-mute); margin-bottom: 8px; }
.price-block { margin-bottom: 10px; }
/* Cifras tabulares en todo lo que se compara en columna: un precio que baila al
   scrollear es un precio más difícil de comparar. */
.price-unit {
  font-family: var(--display); font-size: 19px; font-weight: 800; color: var(--ink); line-height: 1.1;
  font-variant-numeric: tabular-nums;
}
.price-unit small { font-family: var(--body); font-size: 11px; font-weight: 600; color: var(--ink-mute); }
/* Precio de lista TACHADO, al lado del de oferta. Solo sale cuando el ítem trae
   price_old (zeus.py con --ofertas). Dos decisiones:
   - el color va ESCRITO (#78716c): el gris de --ink-mute (#a1a1aa) da 2,3:1 sobre el
     blanco de la card y no llega a AA. #78716c da 4,80:1 sobre #fff y 4,52:1 sobre el
     #F2F9FF de la card con cantidad cargada. Un precio no es decoración: hay que poder
     leerlo. (Ese 4,52 es el que fijó el tono exacto del wash azul: con un azul más
     saturado el tachado se caía por debajo de AA.)
   - `text-decoration-color` explícito y grosor fijo: sin eso el navegador tacha con
     el color heredado del contenedor (--ink, casi negro) y la línea pesa más que el
     número. */
.price-old {
  font-size: 13px; font-weight: 700; color: #78716c; margin-right: 4px;
  font-variant-numeric: tabular-nums;
  text-decoration: line-through; text-decoration-color: #78716c; text-decoration-thickness: 2px;
}
/* La misma vigencia, repetida en el resumen del checkout. No es redundancia: el
   header anuncia y el resumen CONGELA — es la línea que el comerciante tiene delante
   en el momento de confirmar, y la que después se puede citar. Acá el fondo es claro,
   así que el naranja va en su tono de servicio: #BC4D00 sobre el wash #FFF1E8 da
   4,52:1. */
.sum-vig {
  margin-top: 10px; padding: 8px 10px; border-radius: 8px;
  background: var(--mgx-orange-wash); border: 1px solid var(--mgx-orange-edge);
  font-family: var(--display); font-size: 12.5px; font-weight: 800; color: var(--mgx-orange-ink);
}
.price-calc { margin-top: 6px; padding-top: 6px; border-top: 1px dashed var(--line); font-size: 12px; color: var(--ink-soft); line-height: 1.5; }
.price-calc span { display: block; }
.price-calc b { color: var(--ink); font-weight: 800; font-size: 13px; font-variant-numeric: tabular-nums; }
.buy-row { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }
.buy-unit { font-size: 12px; color: var(--ink-soft); font-weight: 700; }
.buy-sub { font-size: 12px; color: var(--ok); font-weight: 800; margin-right: auto; text-align: right; font-variant-numeric: tabular-nums; }
.stepper { display: flex; align-items: center; border: 1px solid var(--line); border-radius: 999px; background: #fff; overflow: hidden; flex: 0 0 auto; }
.stepper button {
  width: 32px; height: 34px; border: 0; background: transparent; font-size: 19px; font-weight: 600;
  color: var(--ink); cursor: pointer; padding: 0; line-height: 1; font-family: inherit;
}
.stepper button:active { background: #f4f4f5; }
.stepper button:disabled { color: var(--ink-mute); }
.stepper-val { min-width: 26px; text-align: center; font-family: var(--display); font-weight: 800; font-size: 15px; font-variant-numeric: tabular-nums; }
/* Producto ya cargado en el pedido: toma el wash del AZUL DE ACCIÓN, porque eso es lo
   que significa — este ítem ya está en el pedido. */
.item.has-qty { background: var(--mgx-blue-wash); }
.item.has-qty .item-img { border-color: var(--mgx-blue); }
.empty-search { padding: 48px 24px; text-align: center; color: var(--ink-mute); }
/* Ayuda de la casa: wash naranja, que es el color de "esto lo dice Megamix". Texto
   ink-soft sobre #FFF1E8 = 6,99:1. */
.how { margin-top: 12px; font-size: 12.5px; color: var(--ink-soft); background: var(--mgx-orange-wash); border: 1px solid var(--mgx-orange-edge); border-radius: 10px; padding: 10px 12px; line-height: 1.45; }
.how b { color: var(--ink); }

/* Footbar — BANDA 3: ACCIÓN.
   Es la barra de CTA de las hojas de catálogo. Acá el azul NO llena la barra: llena el
   botón, y la barra lo anuncia con un filo azul de 2px. Si se pintara entera, el total
   (que es el número que el comerciante mira antes de confirmar) tendría que competir
   contra el fondo, y el botón dejaría de ser el elemento más fuerte de la pantalla. */
.footbar {
  position: fixed; bottom: 0; left: 0; right: 0; background: #fff; border-top: 2px solid var(--mgx-blue);
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); box-shadow: 0 -4px 16px rgba(0,0,0,.07); z-index: 20;
}
.footbar-inner { max-width: 480px; margin: 0 auto; }
.footbar-row { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 10px; }
.footbar-label { font-size: 12px; color: var(--ink-soft); font-weight: 600; }
.footbar-total { font-family: var(--display); font-size: 20px; font-weight: 800; color: var(--ink); font-variant-numeric: tabular-nums; }
.btn {
  display: block; width: 100%; padding: 15px 16px; border-radius: 12px; border: 0;
  font-family: var(--display); font-size: 16px; font-weight: 800; cursor: pointer;
  transition: transform .08s ease;
}
.btn:active { transform: scale(.985); }
/* El ÚNICO botón lleno de la pantalla, y la única inversión a texto blanco sobre color
   de marca (5,63:1). Esa inversión es la señal más fuerte que tiene el sistema y por eso
   se reserva para lo que cierra el pedido. */
.btn-primary { background: var(--mgx-blue); color: #fff; }
/* Gris, no azul pálido: un azul apagado se lee como acción disponible. */
.btn-primary:disabled { background: var(--ink-mute); cursor: not-allowed; }

/* Sheet */
.sheet-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,.5); z-index: 50; opacity: 0; pointer-events: none; transition: opacity .2s; }
.sheet-backdrop.open { opacity: 1; pointer-events: auto; }
.sheet {
  position: fixed; bottom: 0; left: 50%; transform: translateX(-50%) translateY(100%); width: 100%; max-width: 480px;
  background: var(--bg); z-index: 51; max-height: 92vh; border-radius: 18px 18px 0 0; transition: transform .25s ease;
  display: flex; flex-direction: column;
}
.sheet.open { transform: translateX(-50%) translateY(0); }
.sheet-hdr { display: flex; align-items: center; justify-content: space-between; padding: 16px; border-bottom: 1px solid var(--line); background: #fff; border-radius: 18px 18px 0 0; }
.sheet-hdr h2 { margin: 0; font-family: var(--display); font-size: 17px; font-weight: 800; }
.sheet-close { width: 32px; height: 32px; border: 0; background: transparent; font-size: 26px; color: var(--ink-soft); cursor: pointer; line-height: 1; }
.sheet-body { flex: 1; overflow-y: auto; padding: 16px; }
.summary { background: #fff; border-radius: var(--radius); border: 1px solid var(--line); padding: 4px 0; margin-bottom: 16px; }
.sum-row { display: grid; grid-template-columns: 46px 1fr auto; gap: 10px; padding: 10px 14px; align-items: center; border-bottom: 1px solid var(--line); }
.sum-row:last-child { border-bottom: 0; }
.sum-img { width: 46px; height: 46px; background: #fff; border: 1px solid var(--line); border-radius: 8px; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.sum-img img { max-width: 100%; max-height: 100%; }
.sum-name { font-size: 13px; font-weight: 600; line-height: 1.3; }
.sum-qty { font-size: 11px; color: var(--ink-mute); margin-top: 2px; }
.sum-price { font-family: var(--display); font-size: 13px; font-weight: 800; white-space: nowrap; font-variant-numeric: tabular-nums; }
.sum-totals { padding: 12px 14px; background: #fafaf9; border-top: 1px solid var(--line); font-size: 13px; }
.sum-tot-row { display: flex; justify-content: space-between; margin-bottom: 4px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
.sum-tot-row.grand { font-family: var(--display); font-size: 16px; font-weight: 800; color: var(--ink); margin-top: 6px; padding-top: 6px; border-top: 1px solid var(--line); margin-bottom: 0; }

/* Form */
.form-group { margin-bottom: 14px; }
.form-group label { display: block; font-size: 12px; font-weight: 700; color: var(--ink-soft); margin-bottom: 6px; letter-spacing: .02em; }
/* El asterisco de obligatorio va en naranja de servicio (5,00:1), no en el azul de
   acción: señala, no invita a tocar. */
.form-group label .req { color: var(--mgx-orange-ink); }
.form-group input, .form-group textarea {
  width: 100%; padding: 12px 14px; font-size: 15px; border: 1px solid var(--line); border-radius: 10px;
  font-family: inherit; background: #fff; color: var(--ink);
}
.form-group input:focus, .form-group textarea:focus { outline: none; border-color: var(--ink); }
.form-group textarea { resize: vertical; min-height: 60px; }
/* Ayuda bajo el campo: el email dejó de ser obligatorio y sin decir PARA QUÉ sirve
   nadie lo completa (pedido explícito de Lucas, 04-ago-2026). */
.field-hint { margin: 6px 0 0; font-size: 12px; color: var(--ink-soft); line-height: 1.35; }
.prefill-card { background: var(--mgx-blue-wash); border: 1px solid var(--mgx-blue-edge); border-radius: var(--radius); padding: 14px; margin-bottom: 14px; }
.prefill-label { font-family: var(--display); font-size: 10px; color: var(--ink-soft); text-transform: uppercase; letter-spacing: .06em; font-weight: 800; margin-bottom: 6px; }
.prefill-main { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.prefill-main strong { display: block; font-family: var(--display); font-size: 15px; color: var(--ink); font-weight: 800; }
.prefill-main small { display: block; color: var(--ink-soft); font-size: 12px; margin-top: 2px; word-break: break-all; }
.link-btn { background: none; border: 0; color: var(--mgx-blue); font-weight: 700; font-size: 12px; padding: 4px 0; cursor: pointer; font-family: inherit; white-space: nowrap; }
.hidden { display: none !important; }
.sheet-foot { padding: 12px 16px calc(12px + env(safe-area-inset-bottom)); border-top: 1px solid var(--line); background: #fff; }

/* Done */
.done { text-align: center; padding: 32px 20px; }
.done-icon { width: 64px; height: 64px; margin: 0 auto 16px; border-radius: 50%; background: var(--ok); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 32px; }
.done h2 { margin: 0 0 8px; font-family: var(--display); font-size: 22px; font-weight: 800; }
.done p { color: var(--ink-soft); margin: 0 0 16px; }
.done-detail { background: #fff; border: 1px solid var(--line); border-radius: var(--radius); padding: 14px; text-align: left; font-size: 13px; color: var(--ink-soft); margin-top: 16px; }
.done-detail strong { color: var(--ink); }
/* Verde de WhatsApp: color de un tercero. Se respeta y no se re-tinta. */
.wa-btn { background: #25d366; color: #fff; text-decoration: none; margin-top: 18px; box-sizing: border-box; }
.done-hint { font-size: 12px; color: var(--ink-mute); margin: 10px 0 0; }

.foot-note { text-align: center; font-size: 11px; color: var(--ink-mute); padding: 16px; }
.loading { padding: 60px 20px; text-align: center; color: var(--ink-mute); }

/* Visor de foto — por encima del sheet (51) y de la footbar (20).
   max-width/max-height solo ACHICAN: la foto nunca se estira más allá de su
   tamaño nativo, así que ampliar no la puede pixelar. */
.zoom {
  position: fixed; inset: 0; background: rgba(0,0,0,.92); z-index: 60;
  display: none; align-items: center; justify-content: center; padding: 20px;
  cursor: zoom-out;
}
.zoom.open { display: flex; }
.zoom-fig { margin: 0; text-align: center; max-width: 100%; }
.zoom-fig img { max-width: min(92vw, 900px); max-height: 76vh; display: block; margin: 0 auto; border-radius: 8px; background: #fff; }
.zoom-fig figcaption { color: #fff; font-size: 13px; line-height: 1.4; margin-top: 14px; opacity: .85; }
.zoom-close {
  position: absolute; top: calc(10px + env(safe-area-inset-top)); right: 14px; width: 40px; height: 40px;
  border: 0; border-radius: 50%; background: rgba(255,255,255,.15); color: #fff; font-size: 26px;
  line-height: 1; cursor: pointer;
}
/* `position: fixed` y no `overflow: hidden`: Safari iOS ignora el segundo en el
   scroll táctil. app.js guarda y restituye el scrollY (ver lockScroll). */
body.no-scroll { position: fixed; left: 0; right: 0; width: 100%; overflow: hidden; }

/* Landing */
.landing { padding: 48px 20px; text-align: center; }
.landing h1 { font-family: var(--display); font-size: 26px; font-weight: 800; margin: 0 0 8px; }
.landing p { color: var(--ink-soft); margin: 0 0 28px; }
.landing-cards { display: grid; gap: 12px; max-width: 380px; margin: 0 auto; }
.landing-card { display: flex; align-items: center; gap: 14px; padding: 18px; background: #fff; border: 1px solid var(--line); border-radius: var(--radius); text-decoration: none; color: var(--ink); text-align: left; transition: border-color .15s; }
.landing-card:hover { border-color: var(--ink); }
.landing-card .lc-dot { width: 40px; height: 40px; border-radius: 10px; flex: 0 0 auto; }
.landing-card b { font-family: var(--display); font-size: 17px; font-weight: 800; }
.landing-card small { display: block; color: var(--ink-mute); font-weight: 400; font-size: 12px; margin-top: 2px; }
.spinner { display: inline-block; width: 16px; height: 16px; border: 2px solid rgba(255,255,255,.3); border-top-color: #fff; border-radius: 50%; animation: spin .8s linear infinite; vertical-align: middle; margin-right: 8px; }
@keyframes spin { to { transform: rotate(360deg); } }
