/* Línea Outlet — design tokens & base styles */
:root {
  /* [LINEA-OUTLET · BLOQUE 6] Paleta 1 · carbón, hueso, rojo outlet, verde nivel, gris piedra.
     Los nombres de token se conservan (kb-orange, kb-pink, kb-b2b…) para no tocar 4 000 reglas;
     lo que cambia es el color que pintan. */

  /* [LINEA-OUTLET · BLOQUE 22] EL HUESO SE VA. La escala entera pasa a neutra de verdad.
     El dueño lo dijo mirando el teléfono: «el beige en buscador y en menú hace que se vean sin
     vida». Medido, tenía razón y por un motivo más preciso que el gusto:

       hueso  #F4F1EB contra el papel → 1,13:1     línea  #E8E4DC contra el papel → 1,27:1
       franja #F6EFE0 contra el papel → 1,14:1     borde  #D3CEC4 contra el papel → 1,57:1

     1,13:1 es demasiado poco para leerse como una superficie distinta y demasiado desplazado de
     tono para leerse como blanco: se percibe como suciedad, no como capa. El beige estaba pagando
     el precio de un color (ensuciar) sin dar el beneficio (estructurar).

     El motivo de fondo es que TODA la escala vivía en el mismo matiz —cream 40°, yellow-soft 41°,
     line 40°, line-strong 40°, mute2 37°, dorado 39°— y un neutro cálido sólo se lee «cálido»
     al lado de un neutro de verdad; solo, se lee «blanco que amarilleó». Y entre #D3CEC4 (1,57:1)
     y #8D8880 (3,52:1) la escala saltaba sin pasar por ningún gris que llegara a 3:1, que es lo que
     hace falta para DIBUJAR un campo. Todo estaba relleno y nada estaba dibujado.

     Lo que sustituye al beige, en este orden: aire, filete decorativo, un paso de valor en paneles
     grandes y, cuando hace falta peso, inversión a tinta. Nunca sombra ni degradado.
     Las medidas de aquí abajo NO son de adorno: tests/paleta-neutra.test.js las calcula. */
  --kb-orange: #B8322F;        /* rojo outlet (primario de la marca) */
  --kb-orange-deep: #8E2522;
  /* Variante para TEXTO pequeño sobre fondo claro: #A32C29 da 6.8:1 sobre hueso (AA). */
  --kb-orange-text: #A32C29;
  /* [BLOQUE 22] El dorado NO es un neutro y se queda como está. La propuesta lo convertió
     en latón oscuro #7A5C1E porque como RELLENO sobre blanco mide 1,99:1 — es verdad, y sigue
     pendiente. Pero medido en la hoja, este token es sobre todo el ACENTO SOBRE CARBÓN: unas
     cincuenta reglas del tipo `background: var(--kb-ink); color: var(--kb-yellow)`, el pie, las
     pestañas activas, el punto de venta. Ahí da 9,18:1, que está bien. Cambiarlo de golpe habría
     tumbado ese acento a 2,94:1 en todas ellas para arreglar el sello. Son dos hallazgos
     distintos y el segundo se arregla aparte, con su propio token para superficie clara. */
  --kb-yellow: #D9B26A;        /* dorado · acento sobre carbón */
  --kb-yellow-soft: #F4F4F5;   /* [BLOQUE 22] alias de cream; la franja ya no lo usa */
  --kb-cream: #F4F4F5;         /* [BLOQUE 22] superficie suave, sólo en paneles grandes */
  --kb-paper: #FFFFFF;
  --kb-ink: #15141A;           /* carbón */
  --kb-charcoal: #15141A;
  --kb-line: #E3E3E5;          /* filete DECORATIVO (1,28:1): divisores, filas, no controles */
  /* [BLOQUE 22] El arreglo central: por fin existe un gris con el que dibujar. Borde FUNCIONAL
     a 3,44:1 sobre el papel y 3,13:1 sobre la superficie suave — input del buscador, botón
     secundario, chips, casillas. El anterior daba 1,57:1: en la práctica, no había borde. */
  --kb-line-strong: #8A8A8E;
  --kb-muted: #5C5A60;
  /* [BLOQUE 22] Venía documentado como si cumpliera y daba 3,52:1 sobre blanco y 3,12:1 sobre
     el hueso: incumplía la regla de 4,5:1 para texto desde antes de esta paleta, y era parte de
     lo que se veía desvaído. Ahora 5,38:1 sobre papel y 4,89:1 sobre la superficie suave.
     La propuesta decía #6E6E73 (5,07:1 y 4,61:1). Se usa un punto más oscuro a propósito: con
     4,61:1 el margen sobre la superficie suave es de una centésima larga, y esta hoja pinta
     paneles con ese fondo por todas partes. Mismo tono neutro, más margen. */
  --kb-mute2: #6A6A6F;         /* gris de texto terciario */
  /* Acentos */
  --kb-red: #B8322F;
  --kb-green: #0E6B5E;
  /* [LINEA-OUTLET · BLOQUE 13] En esta marca NO hay azul. Los cuatro tokens
     "azules" que quedaban del clon (--kb-blue, --kb-blue-deep y --kb-info en
     #1F5BA8, y el fondo --kb-blue-soft #E4ECF7) pintaban en realidad TRES
     papeles: fondo suave, borde de acento y texto de información. Se traducen a
     la escala neutra de la paleta 1, que es la única que no miente: un neutro no
     promete "nivel" (verde) ni "acción" (rojo) donde no los hay. Los sitios de
     esta hoja que SÍ hablan de nivel o de acción dejaron de colgar de estos
     tokens y piden el suyo (--kb-b2b, --kb-orange-text) uno por uno, más abajo;
     lo que sigue usándolos es JSX que no es de este encargo (la portada vieja,
     page-builder, growth-features, admin-crud).
     El NOMBRE se conserva, como en el bloque 6: lo que cambia es el color.
       --kb-blue-soft = superficie suave · fondo suave (tinta #15141A: 16,66:1 ✓)
       --kb-blue      = gris de borde · borde/acento (3,44:1 sobre blanco ✓,
                        mínimo 3:1 de objeto gráfico)
       [BLOQUE 22] Los dos seguían al hueso y al gris piedra; ahora siguen a la escala neutra
       (#F4F4F5 y #8A8A8E). El papel que pintan no cambia; el tono, sí.
       --kb-blue-deep = carbón #15141A · texto de información (18,31:1 ✓)
       --kb-info      = gris #5C5A60 · texto secundario (6,80:1 ✓)
     [MEDIDO · el único sitio donde --kb-blue-deep no es texto] La barra de
     estado del pedido (admin-crud, ST_SEG_COLOR) lo usa como tramo de en medio
     entre --kb-yellow y --kb-success. Entre tramos vecinos el mínimo es 3:1:
     con el azul daban 3,37:1 (dorado) y 1,05:1 (verde); con el carbón dan
     9,18:1 y 2,86:1. Ningún neutro de la paleta despega del verde de nivel —su
     luminancia cae justo en medio de la escala— así que el carbón es el mejor
     par posible y el conjunto mejora mucho; además cada tramo lleva su rótulo
     al lado, así que el color no es lo único que lo dice.
     Se borra --kb-brown (#6B4E2E): cero referencias en todo el repo y el café
     tampoco está en la paleta — un token que nadie usa no significa nada. */
  --kb-blue: #8A8A8E;
  --kb-pink: #E3E3E5;
  --kb-pink-deep: #B8322F;
  --kb-pink-soft: #F4F4F5;
  --kb-blue-deep: #15141A;
  --kb-blue-soft: #F4F4F5;
  --kb-success: #0E6B5E;       /* verde nivel (mayoreo / distribuidor alcanzado) */
  --kb-danger: #B8322F;
  --kb-info: #5C5A60;
  --kb-b2b: #0E6B5E;           /* nivel mayoreo/distribuidor */
  --kb-b2b-soft: #E3F1EE;

  /* [LINEA-OUTLET · BLOQUE 22] EL VERDE, UN PELDAÑO MÁS CLARO, SÓLO PARA FONDOS OSCUROS.
     El dueño pidió —con captura— la etiqueta de distribuidores en verde en el cajón del menú.
     El cajón pasa a carbón, y ahí el verde de la casa cae a 2,86:1 como objeto: no llega al
     mínimo de 3:1 y la etiqueta se vería como una mancha. La corrección NO es quitarle el
     color: es subirlo un peldaño de claridad sin moverlo de sitio. Es LITERALMENTE el mismo
     verde —mismo matiz 172°, misma saturación 77 %, claridad 24 % → 27 %— y da 3,50:1 contra
     el carbón con el blanco encima a 5,23:1. Sobre fondo claro NO se usa: ahí manda --kb-b2b. */
  --kb-b2b-on-dark: #107A6B;

  /* [LINEA-OUTLET · BLOQUE 23] LOS DOS BLOQUES DE LA ESCALERA DE PRECIO.
     El dueño los pidió "naranja con blanco" y "azul cielo con blanco". Medidos, ni uno ni otro
     aguantan letra blanca: el naranja #F57C00 da 2,70:1 y el azul cielo #87CEEB da 1,74:1,
     cuando el mínimo para texto es 4,5. Se le enseñaron las dos salidas —conservar el color y
     poner la letra en tinta, u oscurecer el color hasta que el blanco se lea— y eligió la
     segunda: el naranja hasta óxido y el azul cielo hasta marino.
       --kb-nivel-aviso  blanco encima 5,18:1 ✓ · como objeto sobre el papel 5,18:1 ✓
       --kb-nivel-accion blanco encima 5,93:1 ✓ · como objeto sobre el papel 5,93:1 ✓
     El azul es el único de toda la hoja, y entra por decisión expresa del dueño: el bloque 13
     había retirado los cuatro azules heredados con la regla "en esta marca NO hay azul". Vive
     sólo aquí, en el botón que sube el pedido al siguiente nivel. */
  --kb-nivel-aviso: #C2410C;   /* óxido · "te faltan N piezas para bajar a X" */
  --kb-nivel-accion: #0369A1;  /* marino · "agregar N piezas y bajar el precio" */
  --kb-nivel-accion-hover: #075985;  /* el mismo marino pulsado · blanco encima 7,56:1 */

  /* [LINEA-OUTLET · BLOQUE 25] UN COLOR POR ACORDE OLFATIVO.
     Encargo del dueño: "ponle color a la ficha, se ve muy simple". Son DOCE y están cerrados
     en ACORDE_LABEL (product-sensory.jsx): ni uno más puede aparecer sin pasar por ahí, así
     que la lista se puede exigir entera — y tests/acordes-con-color.test.js la exige.

     El color aquí es REDUNDANTE: el nombre del acorde va escrito en tinta al lado. Quien no
     distinga "especiado" de "dulce" por el tono los lee igual. Por eso doce cálidos vecinos no
     rompen nada: el color ayuda a barrer la lista, no carga la información.

     Todos miden 3:1 o más contra el riel de la barra, que es el mínimo de objeto gráfico
     (el más flojo es cítrico con 4,61:1), y 4,5:1 o más como texto sobre el papel por si alguna
     vez se usan escritos.

     El VERDE es el único que tenía que negociarse: el verde de esta casa significa "nivel de
     mayoreo alcanzado" y nada más. El acorde verde va en oliva, a 97° de matiz del de nivel. */
  --kb-ac-citrico: #8A6A00;       /* cítrico */
  --kb-ac-frutal: #A3283F;        /* frutal */
  --kb-ac-floral: #A83E63;        /* floral */
  --kb-ac-amaderado: #6B4423;     /* amaderado */
  --kb-ac-especiado: #9C3B1E;     /* especiado */
  --kb-ac-dulce: #7D5320;         /* dulce */
  --kb-ac-resinoso: #8A5300;      /* resinoso */
  --kb-ac-almizclado: #6B6156;    /* almizclado */
  --kb-ac-cuero: #4E3524;         /* cuero */
  --kb-ac-verde: #5C6B2E;         /* verde */
  --kb-ac-acuatico: #0E6285;      /* acuático */
  --kb-ac-polvoso: #6F5F7A;       /* polvoso */

  /* [LINEA-OUTLET · BLOQUE 22] LO QUE SE PINTA ENCIMA DEL CARBÓN.
     La hoja ya tenía superficies oscuras —el pie, el azulejo grande de la vitrina, el total
     del punto de venta— pero ninguna familia que dijera qué color va encima: se resolvían con
     --kb-line-strong (un color de BORDE haciendo de texto) o con literales. Ahora hay una, y
     la usan la franja de aviso y el cajón del menú. Medido contra la tinta #15141A:
       --kb-ink-fg    18,31:1   texto principal
       --kb-ink-fg2    7,55:1   texto secundario, rótulos de grupo, el pie del cajón
       --kb-ink-line   3,19:1   divisor — objeto gráfico, no texto
       --kb-ink-hover  1,17:1   fila pulsada: es un ESTADO pasajero, no un borde ni un rótulo,
                                y nunca es la única señal de nada */
  --kb-ink-fg: #FFFFFF;
  --kb-ink-fg2: #A8A5AE;
  --kb-ink-line: #67656D;
  --kb-ink-hover: #24222A;
  /* El velo es lo que despega el cajón de la página, y por eso el cajón no lleva sombra.
     Con el panel en carbón la separación contra la página velada es de 5,32:1; con el panel
     blanco que proponía la paleta A habría sido de 3,44:1. */
  --kb-scrim: rgba(21, 20, 26, 0.5);
  --kb-focus: #15141A;         /* :focus-visible en tinta: sin azul de navegador, sin resplandor */

  /* Density (overridden by JS) */
  --kb-row-py: 10px;
  --kb-row-px: 14px;
  --kb-card-pad: 14px;
  --kb-gap: 14px;
  --kb-fs-base: 13.5px;

  /* [LINEA-OUTLET · BLOQUE 27] EL TAMAÑO DE LAS REJILLAS DE PRODUCTO, EN DOS NÚMEROS.
     Pregunta del dueño: "¿dónde se puede ajustar el tamaño de grid en el sitio?". La respuesta
     honesta era: en seis sitios y ninguno evidente — la portada decía 5 columnas, 4 bajo
     1100px y 3 bajo 900px; el catálogo 4, 3 y 2 repartidos en cuatro bloques @media.

     Ahora el número de columnas lo decide el ANCHO MÍNIMO DE FICHA, que es lo que de verdad se
     quiere controlar: "que la ficha no baje de tanto". El navegador mete las que quepan.
     Subir el número = fichas más grandes y menos por fila. Bajarlo = al revés. Y las pantallas
     intermedias dejan de necesitar excepciones. */
  --lo-tile-min: 260px;   /* azulejo de la portada */
  --kb-card-min: 260px;   /* ficha del catálogo */

  --kb-radius: 6px;
  --kb-radius-lg: 12px;
  --kb-shadow-sm: 0 1px 0 rgba(21,17,11,0.04), 0 1px 2px rgba(21,17,11,0.06);
  --kb-shadow: 0 2px 6px rgba(21,17,11,0.06), 0 8px 24px rgba(21,17,11,0.06);
  --kb-shadow-lg: 0 12px 40px rgba(21,17,11,0.18);

  /* [LINEA-OUTLET · BLOQUE 22] LA VOZ DE LA CASA.
     Lo que había era heredado del clon: Bricolage Grotesque, una grotesca de tamaños ópticos
     anchos y dibujo juguetón —la voz del antojo, no la de una perfumería—; Cormorant Garamond,
     una garalda de ojo muy pequeño que a 15-17 px en el teléfono se vuelve filiforme; y Hanken
     Grotesk de interfaz, una CUARTA familia que no añadía jerarquía: dos grotescas parecidas a
     la vez dan ritmo turbio, porque el ojo no sabe si el cambio significa algo.

     Ahora son tres, y la jerarquía la hacen el peso y el tamaño dentro de una familia.

     OJO, y queda escrito porque no da error en ninguna parte: Instrument Sans es variable de
     400 a 700 y no tiene 800 ni 900. La hoja los declara en 65 sitios y el JSX en 143 más,
     heredados de una familia que sí llegaba a 800. El navegador recorta al máximo disponible,
     así que lo que se pinta es 700 — el mismo resultado se declare 700, 800 o 900. No se
     reescriben las 208 declaraciones: sería un cambio enorme, sin ninguna diferencia en
     pantalla y con riesgo de tocar de paso media docena de vistas del panel. La consecuencia
     de verdad es otra y hay que verla en pantalla, no en el código: la voz de la casa se
     aligera un paso arriba. Era parte de lo que se buscaba. */
  --kb-font-display: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
  /* Serif de la vitrina (nombres de perfume). EB Garamond es una Garamond de TEXTO: trazo más
     firme y ojo mayor que la anterior, que en la retícula de dos columnas del móvil se rompía.
     Nunca para cifras — ver la regla de los precios más abajo. */
  --kb-font-serif: "EB Garamond", Georgia, "Times New Roman", serif;
  /* La misma que display, a propósito: una familia menos que descargar. */
  --kb-font-ui: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
  --kb-font-mono: "JetBrains Mono", ui-monospace, Menlo, monospace;
}

[data-density="compact"] {
  --kb-row-py: 6px;
  --kb-row-px: 10px;
  --kb-card-pad: 10px;
  --kb-gap: 10px;
  --kb-fs-base: 12.5px;
}
[data-density="comfy"] {
  --kb-row-py: 14px;
  --kb-row-px: 18px;
  --kb-card-pad: 18px;
  --kb-gap: 20px;
  --kb-fs-base: 14.5px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--kb-font-ui);
  font-size: var(--kb-fs-base);
  color: var(--kb-ink);
  background: var(--kb-paper);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

/* App shell */
.kb-app { min-height: 100vh; display: flex; flex-direction: column; }

/* Top utility bar (negro) */
.kb-topbar {
  background: var(--kb-ink);
  color: rgba(255,255,255,0.78);
  font-family: var(--kb-font-mono);
  font-size: 11px;
  letter-spacing: 0.04em;
  padding: 6px 22px;
  display: flex; align-items: center; justify-content: space-between; gap: 18px;
  /* Con los accesos editables el ancho deja de ser fijo. Sin `wrap`, los ítems
     de flex no bajan de su min-content y el sobrante lo recorta en SILENCIO el
     overflow-x: clip de html/body/.kb-app (más abajo en este archivo).
     Prefiero que la barra crezca hacia abajo antes que cortar una palabra a
     la mitad. */
  flex-wrap: wrap;
}
.kb-topbar-tools { display: flex; gap: 18px; flex-wrap: wrap; min-width: 0; }
.kb-topbar-tool { display: inline-flex; gap: 6px; align-items: center; cursor: pointer; padding: 2px 4px; }
.kb-topbar-tool:hover { color: var(--kb-yellow); }
.kb-topbar-tool b { color: #fff; font-weight: 600; }
.kb-topbar-meta { display: flex; gap: 14px; opacity: 0.7; }
.kb-topbar-meta b { color: var(--kb-yellow); }
/* [CLS-TOPBAR-ROTA] El anuncio de la barra rota cada 6 s entre los mensajes que
   el dueño separa con "||" en Admin (chrome.jsx → AnnounceRotator), y esos
   mensajes no miden lo mismo. Cuando se pintaba solo el del turno, el hueco
   medía lo que midiera ÉL: la franja pasaba de un renglón a dos y volvía en
   cada vuelta, empujando toda la página hacia abajo. Medido en Chrome real
   contra producción a 1550 px de ancho: alturas de `.kb-topbar` [30, 63] y CLS
   acumulado 1,5154 en 22 s (el umbral "bueno" de Google es 0,1).
   Aquí van TODOS los mensajes uno encima de otro en la MISMA celda del grid:
   el navegador la mide con el más grande y ya no la vuelve a medir, así que el
   alto deja de depender de cuál esté visible — en CUALQUIER ancho y con
   CUALQUIER par de mensajes. Rotar solo cambia la `visibility`.
   [CLS-TOPBAR-HUECO] `justify-items` cumple DOS cosas y por eso no puede
   faltar ni ser cualquiera:
   1. Que NO sea el valor inicial (`stretch`): si los mensajes se estiran al
      ancho de la celda, la pastilla de color de un tema —el `style` con fondo
      que pone AnnounceRotator— deja de ceñirse a su texto y pinta una barra del
      ancho del mensaje más largo.
   2. `end` y no `start`, porque reservar el mensaje más largo deja ~62 px
      sobrando cuando se ve el CORTO y hay que elegir de qué lado caen. Esta
      celda va DENTRO de `.kb-topbar-meta`, el grupo de la derecha (anuncio ·
      ES-MX · MXN $): con `start` el sobrante quedaba entre el mensaje y su "·"
      y partía en dos un texto que se lee seguido —medido con Chrome real a
      1920 px con el mensaje corto a la vista: 76 px hasta el "·" en vez de los
      14 px del `gap`, con el "·" flotando solo en mitad del renglón—. Con `end`
      el mensaje vuelve a pegarse a su separador y los 62 px se van al hueco que
      YA existe entre los accesos de la izquierda y el grupo de la derecha,
      donde no se ven. En el ancho donde la barra parte en dos renglones el
      sobrante queda como una sangría del anuncio, que es lo mismo que ya hace
      el espacio en blanco de al lado.
      (`end` es lógico, no "derecha": sigue la dirección del texto.) */
.kb-announce-rot { display: inline-grid; justify-items: end; }
.kb-announce-rot > .kb-announce-msg { grid-area: 1 / 1; }

/* Mega menu */
.kb-megabox { position: relative; display: flex; align-items: stretch; }
.kb-mega {
  position: absolute; top: 100%; left: 0; right: auto;
  background: #fff; border: 1px solid var(--kb-line); border-top: 3px solid var(--kb-orange);
  box-shadow: var(--kb-shadow);
  padding: 22px; min-width: 720px; z-index: 60;
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 22px;
  border-radius: 0 0 12px 12px;
}
.kb-mega h6 { margin: 0 0 8px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--kb-orange-deep); }
.kb-mega ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 4px; }
.kb-mega li { font-size: 12.5px; padding: 4px 0; cursor: pointer; }
.kb-mega li:hover { color: var(--kb-orange-deep); padding-left: 4px; }
.kb-mega .kb-mega-feat { background: var(--kb-cream); border-radius: 8px; padding: 14px; }
.kb-mega .kb-mega-feat h5 { font-family: var(--kb-font-display); font-size: 14px; margin: 0 0 4px; }
.kb-mega .kb-mega-feat p { font-size: 11.5px; color: var(--kb-muted); margin: 0; line-height: 1.4; }

/* Split screen B2C/B2B */
.kb-split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  margin-top: 22px;
}
.kb-split-card {
  position: relative; overflow: hidden; border-radius: 14px; padding: 28px;
  min-height: 220px; display: flex; flex-direction: column; justify-content: space-between;
  cursor: pointer; transition: transform .15s; border: 1px solid var(--kb-line);
}
.kb-split-card:hover { transform: translateY(-2px); }
/* [LINEA-OUTLET · BLOQUE 13] Las dos mitades hablaban con colores prestados:
   el naranja flúor #FFA500 y el cian #0099D8. Ahora cada una dice lo suyo con la
   paleta: menudeo = dorado (distinción), mayoreo = verde nivel apagándose en
   carbón. Medido: tinta #15141A sobre #D9B26A 9,18:1 ✓ y sobre #F6EFE0 15,99:1 ✓;
   blanco sobre #0E6B5E 6,40:1 ✓ y sobre #15141A 18,31:1 ✓. El "em" del titular
   (26px, grande: mínimo 3:1) va en #8E2522 sobre el dorado: 4,32:1 ✓. */
.kb-split-card.b2c {
  background: linear-gradient(135deg, var(--kb-yellow) 0%, var(--kb-yellow-soft) 100%);
  color: var(--kb-ink);
  border: 0;
}
.kb-split-card.b2b {
  background: linear-gradient(135deg, var(--kb-b2b) 0%, var(--kb-ink) 100%);
  color: #fff;
  border: 0;
}
.kb-split-card.b2b h3 em { color: #D9B26A; font-style: normal; -webkit-text-stroke: 1px var(--kb-ink); }
.kb-split-card.b2b p { color: rgba(255,255,255,0.92); }
.kb-split-card h3 { font-family: var(--kb-font-display); font-size: 26px; font-weight: 800; margin: 0; line-height: 1.05; letter-spacing: -0.01em; }
.kb-split-card.b2b h3 em { color: var(--kb-yellow); font-style: normal; }
.kb-split-card.b2c h3 em { color: var(--kb-orange-deep); font-style: normal; }
.kb-split-card p { font-size: 13.5px; line-height: 1.45; max-width: 38ch; margin: 0; opacity: 0.85; }
.kb-split-card::after {
  content: ""; position: absolute; right: -40px; bottom: -40px; width: 200px; height: 200px;
  background: repeating-linear-gradient(45deg, transparent 0 12px, rgba(255,255,255,0.12) 12px 13px);
  pointer-events: none;
}

/* Quick Order matrix */
.kb-qo-table { width: 100%; border-collapse: collapse; font-size: 12.5px; background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; overflow: hidden; }
.kb-qo-table th { text-align: left; padding: 10px 14px; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--kb-muted); border-bottom: 1px solid var(--kb-line); background: var(--kb-cream); }
.kb-qo-table td { padding: 10px 14px; border-bottom: 1px solid var(--kb-line); vertical-align: middle; }
.kb-qo-table tr:last-child td { border-bottom: 0; }
.kb-qo-thumb { width: 40px; height: 40px; border-radius: 4px; overflow: hidden; flex-shrink: 0; }
.kb-qo-qty { width: 80px; }
.kb-qo-qty input { width: 100%; height: 32px; text-align: center; border: 1px solid var(--kb-line-strong); border-radius: 4px; outline: none; font-weight: 600; }
.kb-qo-qty input:focus { border-color: var(--kb-orange); }

/* Mix & Match builder */
.kb-mix { display: grid; grid-template-columns: 1fr 320px; gap: 18px; }
.kb-mix-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.kb-mix-tile { background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; padding: 10px; cursor: pointer; transition: all .12s; position: relative; }
.kb-mix-tile:hover { border-color: var(--kb-orange); transform: translateY(-1px); }
.kb-mix-tile.on { border-color: var(--kb-orange); border-width: 2px; padding: 9px; background: var(--kb-cream); }
.kb-mix-tile .kb-tile-stripe-count { position: absolute; top: 6px; right: 6px; background: var(--kb-orange); color: #fff; width: 22px; height: 22px; border-radius: 50%; font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; font-family: var(--kb-font-mono); }
.kb-mix-tile h5 { font-size: 12px; font-weight: 600; margin: 6px 0 0; line-height: 1.25; }
.kb-mix-tile .kb-tile-pp { font-family: var(--kb-font-display); font-weight: 700; font-size: 13px; }

.kb-mix-box {
  background: #fff; border: 2px dashed var(--kb-line-strong); border-radius: 12px; padding: 18px;
  position: sticky; top: 110px;
}
.kb-mix-slots { display: grid; grid-template-columns: repeat(5, 1fr); gap: 6px; margin: 12px 0; }
/* [LINEA-OUTLET · BLOQUE 13] El "+" del hueco vacío del armador de kits iba en gris piedra
   --kb-mute2 #8D8880 sobre el hueso #F4F1EB: 3,12:1. A 18px sin negrita el mínimo sigue
   siendo 4,5:1 (el de "texto grande" empieza en 18,66px, o en 14px si va en negrita), así
   que se queda corto por poco. Con el gris de texto --kb-muted #5C5A60 son 6,37:1 ✓ y el
   hueco además se ve como lo que es: una invitación a tocarlo. */
.kb-mix-slot { aspect-ratio: 1; border-radius: 6px; background: var(--kb-cream); border: 1px dashed var(--kb-line-strong); display: flex; align-items: center; justify-content: center; font-size: 18px; color: var(--kb-muted); position: relative; overflow: hidden; }
.kb-mix-slot.filled { border-style: solid; border-color: var(--kb-orange); }
.kb-mix-slot.filled::after { content: ""; position: absolute; inset: 0; background: linear-gradient(135deg, var(--kb-yellow), var(--kb-orange)); opacity: 0.85; }
.kb-mix-slot.filled span { position: relative; z-index: 1; color: #fff; font-size: 9px; font-weight: 700; font-family: var(--kb-font-mono); padding: 0 4px; text-align: center; line-height: 1.1; }
/* Responsive: barra inferior móvil del kit (total + CTA visibles al apilar) */
.kb-mix-mobilebar { display: none; }
/* Barra de compra pegajosa de la FICHA (solo móvil): mismo patrón que kb-mix-mobilebar,
   fija sobre la barra de pestañas (.kb-bottomnav, 56px, z-index 80). */
.kb-pdp-stickybuy { display: none; }
@media (max-width: 900px) {
  .kb-pdp-stickybuy {
    display: flex; position: fixed; left: 0; right: 0; bottom: 56px; z-index: 70;
    gap: 10px; align-items: center;
    background: rgba(255,255,255,0.97); border-top: 2px solid var(--kb-pink-deep, #A32C29);
    padding: 10px 14px calc(10px + env(safe-area-inset-bottom) * 0);
    box-shadow: 0 -4px 16px rgba(0,0,0,.12);
  }
  .kb-pdp-stickybuy .kb-btn { white-space: nowrap; }
  /* Que la barra no tape el final del contenido de la ficha. */
  .kb-pdp { padding-bottom: 84px; }
}
@media (max-width: 1024px) {
  .kb-mix { grid-template-columns: 1fr 280px; }
}
@media (max-width: 860px) {
  .kb-mix { grid-template-columns: 1fr; padding-bottom: 140px; }
  .kb-mix-box { position: static; top: auto; }
  .kb-mix-grid { grid-template-columns: repeat(3, 1fr); }
  /* bottom: 56px = altura de .kb-bottomnav (la barra de pestañas móvil, z-index 80) */
  .kb-mix-mobilebar {
    display: flex; position: fixed; left: 0; right: 0; bottom: 56px; z-index: 70;
    gap: 10px; align-items: center; justify-content: space-between;
    background: #fff; border-top: 2px solid var(--kb-orange);
    padding: 10px 14px;
    box-shadow: 0 -4px 16px rgba(0,0,0,.12);
  }
  .kb-mix-mobilebar .kb-btn { white-space: nowrap; }
}
@media (max-width: 560px) {
  .kb-mix-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .kb-mix-tile h5 { font-size: 11.5px; }
  .kb-mix-tile button { width: 28px !important; height: 28px !important; }
}

/* ROI calc */
.kb-roi {
  background: linear-gradient(135deg, var(--kb-cream) 0%, #FFF 100%);
  border: 1px solid var(--kb-orange);
  border-radius: 10px; padding: 14px;
  display: flex; flex-direction: column; gap: 10px;
}
.kb-roi-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.kb-roi-row { background: #fff; border-radius: 6px; padding: 8px 10px; }
.kb-roi-row small { font-family: var(--kb-font-mono); font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--kb-muted); }
.kb-roi-row b { font-family: var(--kb-font-display); font-size: 16px; font-weight: 700; display: block; }
.kb-roi-out { background: var(--kb-ink); color: #fff; border-radius: 6px; padding: 10px 12px; display: flex; justify-content: space-between; align-items: center; }
.kb-roi-out b { color: var(--kb-yellow); font-family: var(--kb-font-display); font-size: 22px; font-weight: 800; }

/* Trust badge — fábrica */
.kb-factory-badge {
  display: inline-flex; align-items: center; gap: 8px;
  background: var(--kb-ink); color: var(--kb-yellow);
  padding: 6px 10px; border-radius: 999px;
  font-family: var(--kb-font-mono); font-size: 10.5px; letter-spacing: 0.04em; text-transform: uppercase; font-weight: 600;
}

/* Sensory filter chips */
.kb-chip-row { display: flex; flex-wrap: wrap; gap: 6px; }
.kb-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 5px 10px; border-radius: 999px; cursor: pointer;
  background: #fff; border: 1px solid var(--kb-line-strong);
  font-size: 11.5px; font-weight: 500;
}
.kb-chip:hover { border-color: var(--kb-orange); }
.kb-chip.on { background: var(--kb-ink); color: var(--kb-yellow); border-color: var(--kb-ink); }
.kb-chip .swatch { width: 10px; height: 10px; border-radius: 50%; border: 1px solid rgba(0,0,0,0.1); }

/* Old promo (kept compatible) */
.kb-promo {
  background: var(--kb-ink);
  color: var(--kb-yellow);
  font-family: var(--kb-font-mono);
  font-size: 11.5px;
  letter-spacing: 0.04em;
  padding: 7px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  text-transform: uppercase;
}
.kb-promo .kb-promo-track { display: flex; gap: 28px; overflow: hidden; flex: 1; }
.kb-promo-item { white-space: nowrap; opacity: 0.85; }
.kb-promo-item b { color: var(--kb-orange); font-weight: 700; }
.kb-promo .kb-lang { display: flex; gap: 10px; opacity: 0.7; }
.kb-promo .kb-lang span { cursor: pointer; }
.kb-promo .kb-lang .on { color: #fff; opacity: 1; }

/* Header */
.kb-header { background: #fff; border-bottom: 1px solid var(--kb-line); position: sticky; top: 0; z-index: 50; }
.kb-header-row {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 18px;
  align-items: center;
  padding: 12px 22px;
  max-width: 1480px; margin: 0 auto;
}
.kb-logo {
  display: flex; align-items: center; gap: 10px;
  font-family: var(--kb-font-display); font-weight: 800; font-size: 22px; letter-spacing: -0.01em;
  color: var(--kb-ink);
}
.kb-logo-mark {
  width: 38px; height: 38px; border-radius: 9px;
  /* [LINEA-OUTLET · BLOQUE 13] La cinta traía el arcoíris del clon: cian #0099D8
     y dos cafés (#2D1810, #8B4513) que no existen en esta marca. Ahora son las
     CINCO bandas de la paleta 1 —rojo, dorado, verde nivel, carbón, gris piedra—
     en el mismo reparto de anchos. Es decoración: no lleva texto encima. */
  background: linear-gradient(90deg, #B8322F 0%, #B8322F 28%, #D9B26A 28%, #D9B26A 42%, #0E6B5E 42%, #0E6B5E 57%, #15141A 57%, #15141A 71%, #8A8A8E 71%);
  position: relative; overflow: hidden;
  box-shadow: 0 2px 6px rgba(15,14,11,0.12), inset 0 0 0 1px rgba(0,0,0,0.06);
}
.kb-logo-mark::before {
  content: "K"; position: absolute; inset: 4px; background: #fff; border-radius: 6px;
  font-family: "Arial Black", system-ui, sans-serif; font-weight: 900; font-size: 22px;
  color: #15141A; display: grid; place-items: center;
  -webkit-text-stroke: 1.5px #B8322F;
  line-height: 1;
}
.kb-logo-mark::after {
  content: "★"; position: absolute; top: 2px; right: 2px; font-size: 11px;
  color: #D9B26A; text-shadow: 0 1px 2px rgba(0,0,0,0.4);
}
.kb-logo small { font-family: var(--kb-font-mono); font-size: 10px; font-weight: 500; color: var(--kb-orange-deep); letter-spacing: 0.12em; display: block; line-height: 1; margin-top: 2px; }

/* [LINEA-OUTLET · BLOQUE 22] EL BUSCADOR SE DIBUJA, NO SE RELLENA.
   Es la primera de las dos superficies que el dueño señaló. Medido, el campo no tenía ni
   color ni canto: el relleno #F4F1EB daba 1,13:1 contra el papel y el borde #E8E4DC daba
   1,12:1 contra ese relleno — y en la vitrina, además, `border-color: transparent`. O sea que
   el campo se definía ENTERO por una mancha que casi no se distingue de la página. Por eso se
   leía como blanco sucio y no como un sitio donde escribir.
   Ahora: mismo papel de fondo y un borde de 1 px a 3,44:1, que es el mínimo de la casa para
   un objeto gráfico — y el campo ES el objeto gráfico. La esquina baja de píldora a 2 px:
   píldora más relleno claro es el gesto de una app de antojo, no el de una tienda.
   El tamaño de letra sube a 16 px y NO va dentro de una @media a propósito: Safari en iPhone
   amplía la página entera al enfocar un campo de menos de 16 px, y un teléfono en horizontal
   pasa de 768 px, así que un punto de ruptura dejaría el fallo vivo justo al girar el aparato. */
.kb-search {
  display: flex; align-items: center;
  background: var(--kb-paper);
  border: 1px solid var(--kb-line-strong);
  border-radius: 2px;
  padding: 0 6px 0 14px;
  height: 40px;
  transition: border-color .15s;
}
/* El foco va en tinta. Antes se pintaba con el rojo de la marca, que en esta casa ya significa
   acción y error: un mismo color no puede significar además "aquí está el cursor". */
.kb-search:focus-within { border-color: var(--kb-focus); background: var(--kb-paper); }
.kb-search input { flex: 1; border: none; outline: none; background: transparent; font-size: 16px; height: 100%; }
.kb-search input:focus-visible { outline: 2px solid var(--kb-focus); outline-offset: 2px; }
/* Nadie lo declaraba: lo pintaba el gris de fábrica de cada navegador, distinto en Chrome, en
   Safari y en Firefox, y por lo tanto sin medir. 6,80:1 sobre el papel. */
.kb-search input::placeholder { color: var(--kb-muted); opacity: 1; }
.kb-search .kb-search-cat { border: none; background: transparent; font-size: 12px; color: var(--kb-muted); padding: 0 12px; border-right: 1px solid var(--kb-line); height: 22px; outline: none; }
.kb-search button.kb-search-go {
  height: 32px; padding: 0 18px; border: none; border-radius: 999px;
  background: var(--kb-ink); color: var(--kb-yellow);
  font-weight: 600; font-size: 12.5px; letter-spacing: 0.02em;
}
.kb-search button.kb-search-go:hover { background: var(--kb-orange); color: #fff; }

.kb-header-actions { display: flex; align-items: center; gap: 4px; }
.kb-iconbtn {
  display: flex; align-items: center; gap: 8px;
  background: transparent; border: 1px solid transparent; border-radius: 8px;
  padding: 8px 12px;
  font-size: 12.5px; color: var(--kb-charcoal); font-weight: 500;
  position: relative;
}
.kb-iconbtn:hover { background: var(--kb-cream); border-color: var(--kb-line); }
.kb-iconbtn .kb-icon { font-size: 16px; line-height: 1; }
.kb-iconbtn .kb-badge {
  position: absolute; top: 2px; right: 4px;
  background: var(--kb-orange); color: #fff; font-size: 10px; font-weight: 700;
  border-radius: 999px; padding: 1px 5px; min-width: 16px; text-align: center;
  font-family: var(--kb-font-mono);
}
.kb-iconbtn small { display: block; font-size: 10px; color: var(--kb-muted); font-weight: 500; line-height: 1; margin-top: 2px; }
.kb-iconbtn .lbl-stack { text-align: left; line-height: 1.15; }
.kb-iconbtn .lbl-stack b { display: block; font-weight: 600; }

/* Header variants */
[data-header="split"] .kb-header-row { grid-template-columns: auto 1fr auto; }
[data-header="centered"] .kb-header-row { grid-template-columns: 1fr auto 1fr; }
[data-header="centered"] .kb-search { max-width: 560px; margin: 0 auto; }
[data-header="centered"] .kb-logo-side { display: flex; justify-content: flex-start; }
[data-header="minimal"] .kb-promo { display: none; }
[data-header="minimal"] .kb-nav { display: none; }
[data-header="minimal"] .kb-header-row { padding: 10px 22px; }

/* Primary nav */
.kb-nav {
  background: #fff; border-bottom: 1px solid var(--kb-line);
}
.kb-nav-row { display: flex; align-items: stretch; gap: 0; padding: 0 22px; max-width: 1480px; margin: 0 auto; }
.kb-nav-item {
  appearance: none; -webkit-appearance: none; background: none; border: 0;
  margin: 0; font-family: inherit; text-align: left;
  padding: 10px 14px; font-size: 12.5px; font-weight: 500; color: var(--kb-charcoal);
  border-bottom: 2px solid transparent; cursor: pointer;
  display: flex; align-items: center; gap: 6px;
  white-space: nowrap;
}
.kb-nav-item:hover { color: var(--kb-orange-deep); }
.kb-nav-item.on { color: var(--kb-orange-deep); border-bottom-color: var(--kb-orange); }
.kb-nav-item .kb-pill {
  font-family: var(--kb-font-mono); font-size: 9.5px; font-weight: 700;
  background: var(--kb-yellow-soft); color: var(--kb-charcoal);
  padding: 2px 6px; border-radius: 3px; letter-spacing: 0.04em;
}
.kb-nav-item .kb-pill.b2b { background: var(--kb-b2b-soft); color: var(--kb-b2b); }
.kb-nav-spacer { flex: 1; }
.kb-nav-meta { display: flex; align-items: center; gap: 14px; font-family: var(--kb-font-mono); font-size: 11px; color: var(--kb-muted); }
.kb-nav-meta b { color: var(--kb-charcoal); }

/* Layout */
.kb-main { max-width: 1480px; width: 100%; margin: 0 auto; padding: 22px; flex: 1; }

/* Buttons */
.kb-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  height: 38px; padding: 0 16px; border-radius: 6px; border: 1px solid transparent;
  font-weight: 600; font-size: 13px; cursor: pointer; transition: all .12s;
  white-space: nowrap;
}
/* CTA en #A32C29 (rosa de marca): contraste 5.87:1 con blanco → cumple WCAG AA
   (el #B8322F anterior daba 4.35:1, por debajo del mínimo 4.5:1). */
.kb-btn-primary { background: #A32C29; color: #fff; }
.kb-btn-primary:hover { background: #8E2522; }

/* Foco de teclado visible (solo navegación por teclado, no afecta al mouse).
   Antes había varias reglas outline:none sin reemplazo accesible. */
:focus-visible { outline: 2px solid #A32C29; outline-offset: 2px; }
a:focus-visible, button:focus-visible, [role="button"]:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid #A32C29; outline-offset: 2px;
}
.kb-btn-dark { background: var(--kb-ink); color: var(--kb-yellow); }
.kb-btn-dark:hover { background: #000; }
.kb-btn-ghost { background: #fff; border-color: var(--kb-line-strong); color: var(--kb-ink); }
.kb-btn-ghost:hover { border-color: var(--kb-orange); color: var(--kb-orange-deep); }
/* [LINEA-OUTLET · BLOQUE 13] Un enlace es ACCIÓN, y la acción de esta marca es
   el rojo outlet — no el azul de "información" que traía (--kb-info). Con
   --kb-orange-text: 7,12:1 sobre blanco ✓ (el #1F5BA8 anterior daba 6,73:1, pero
   era azul). --kb-info se queda para lo que de verdad es informativo. */
.kb-btn-link { background: transparent; color: var(--kb-orange-text); border-color: transparent; padding: 0 6px; height: auto; }
.kb-btn-link:hover { text-decoration: underline; }
.kb-btn-sm { height: 30px; padding: 0 12px; font-size: 12px; }
.kb-btn-lg { height: 46px; padding: 0 22px; font-size: 14px; }
.kb-btn-block { width: 100%; }
.kb-btn[disabled] { opacity: .45; cursor: not-allowed; }

/* Cards */
.kb-card {
  background: #fff;
  border: 1px solid var(--kb-line);
  border-radius: var(--kb-radius-lg);
  padding: var(--kb-card-pad);
}
.kb-card-flat { background: var(--kb-cream); border-color: var(--kb-line); }
.kb-card-section { padding: 0; overflow: hidden; }
.kb-card-section > .head {
  padding: 12px 16px; border-bottom: 1px solid var(--kb-line);
  font-weight: 600; font-size: 13px; display: flex; align-items: center; justify-content: space-between;
}
.kb-card-section > .body { padding: 16px; }

/* Tags / chips / badges */
.kb-tag {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--kb-font-mono); font-size: 10.5px; font-weight: 600;
  padding: 3px 7px; border-radius: 3px; letter-spacing: 0.03em;
  background: var(--kb-cream); color: var(--kb-charcoal);
  border: 1px solid var(--kb-line);
}
.kb-tag.orange { background: var(--kb-yellow-soft); color: var(--kb-orange-deep); border-color: var(--kb-yellow); }
/* [LINEA-OUTLET · BLOQUE 13] El dorado no contrasta: el texto sobre él va en el
   bronce de la paleta, #8A5300 (5,53:1 sobre #F6EFE0 ✓). El #6B5220 que había
   pasaba, pero era un café suelto — un dorado oscuro más de los seis que tenía
   la hoja. */
.kb-tag.yellow { background: var(--kb-yellow-soft); color: #8A5300; border-color: var(--kb-yellow); }
.kb-tag.pink { background: var(--kb-pink-soft); color: var(--kb-pink-deep); border-color: var(--kb-pink); }
/* [LINEA-OUTLET · BLOQUE 13] El único sitio que pinta este tag es "Ejecutivo debe
   contactar" (admin-crud): no es información, es un AVISO con trabajo pendiente.
   Pasa al ámbar de estado de la casa: #8A5300 sobre #FFF7E6 → 5,94:1 ✓
   (el par azul anterior, #1F5BA8 sobre #E4ECF7, decía "dato" y no "atiéndeme"). */
.kb-tag.blue { background: var(--kb-cola-ambar-bg); color: var(--kb-cola-ambar); border-color: var(--kb-cola-ambar-borde); }
.kb-tag.dark { background: var(--kb-ink); color: var(--kb-yellow); border-color: var(--kb-ink); }
/* [LINEA-OUTLET · BLOQUE 13] Tres arreglos en tres renglones:
   · b2b llevaba un BORDE lila (#C9BFE9), herencia del morado del clon, alrededor
     de un chip que ya era verde por dentro. El borde pasa al verde nivel.
   · green era el cuarto verde casi igual de la hoja (#14613A sobre #DCEFE2,
     borde #B7DCC2). Ahora es el ÚNICO verde de la casa: #0E6B5E sobre #E3F1EE
     → 5,51:1 ✓ (el par anterior daba 6,24:1 pero con otro verde).
   · red es un ESTADO DE ERROR, así que usa los tokens de estado de la paleta en
     vez de su rojo suelto: #B3261E sobre #FDECEA → 5,72:1 ✓. */
.kb-tag.b2b { background: var(--kb-b2b-soft); color: var(--kb-b2b); border-color: var(--kb-b2b); }
.kb-tag.green { background: var(--kb-b2b-soft); color: var(--kb-b2b); border-color: var(--kb-b2b); }
.kb-tag.red { background: var(--kb-cola-rojo-bg); color: var(--kb-cola-rojo); border-color: var(--kb-cola-rojo-borde); }

/* Striped product placeholder */
.kb-prod-img {
  position: relative; width: 100%; aspect-ratio: 1 / 1; border-radius: 8px; overflow: hidden;
  background: var(--kb-cream);
  border: 1px solid var(--kb-line);
}
.kb-prod-img svg { width: 100%; height: 100%; display: block; }
.kb-prod-img .kb-prod-label {
  position: absolute; left: 8px; bottom: 8px; right: 8px;
  font-family: var(--kb-font-mono); font-size: 10px; color: var(--kb-charcoal);
  text-transform: uppercase; letter-spacing: 0.06em; line-height: 1.2;
  background: rgba(255,255,255,0.85); padding: 4px 6px; border-radius: 3px;
  backdrop-filter: blur(2px);
}

/* Product card */
.kb-pcard {
  background: #fff; border: 1px solid var(--kb-line); border-radius: 10px;
  padding: 12px; display: flex; flex-direction: column; gap: 8px;
  transition: transform .15s, box-shadow .15s, border-color .15s;
  cursor: pointer; position: relative;
}
.kb-pcard:hover { transform: translateY(-2px); box-shadow: var(--kb-shadow); border-color: var(--kb-line-strong); }
/* UX-02: enlace superpuesto ("stretched link") — toda la tarjeta navega, pero los
   controles del pie y el aviso B2B viven ENCIMA para no anidar interactivos. */
/* UX-02/UX-14: enlace superpuesto ("stretched link"). Selector NO scopeado a
   .kb-pcard para reutilizarlo también en las mini-tarjetas del carrito. La clase
   solo se usa en overlays, así que position:absolute/inset:0 es inocuo fuera. */
.kb-pcard-link { position: absolute; inset: 0; z-index: 1; border-radius: inherit; }
.kb-pcard .kb-pcard-foot { position: relative; z-index: 2; }
.kb-pcard .kb-pcard-tags { position: absolute; top: 16px; left: 16px; display: flex; flex-direction: column; gap: 4px; }
/* CONV-1BC/1b · ♡ como overlay circular sobre la imagen (antes compartía la fila
   del pie). z-index 3 y NO 2: tiene que ganarle al stretched link .kb-pcard-link
   (z-index 1) —si no, el overlay que cubre la tarjeta se come el click y el
   control es inalcanzable con el mouse— y también al listón de tema
   (.kb-pcard::after, z-index 2), que si no lo taparía en la misma esquina. */
.kb-pcard .kb-pcard-fav {
  position: absolute; top: 6px; right: 6px; z-index: 3;
  width: 28px; height: 28px; padding: 0; min-height: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid var(--kb-line-strong);
  background: rgba(255,255,255,0.92); color: var(--kb-muted);
  cursor: pointer; line-height: 0; box-shadow: 0 1px 3px rgba(15,14,11,0.12);
}
/* El círculo se ve de 28px (lo que pide el handoff) pero el área de toque es de
   44px: el mínimo táctil del propio handoff, y sin él las esquinas de la caja
   caían en el stretched link — un roce al intentar guardar en favoritos abría
   la ficha. El ::before no pinta nada, solo agranda el blanco de golpeo. */
.kb-pcard .kb-pcard-fav::before { content: ""; position: absolute; inset: -8px; border-radius: 50%; }
.kb-pcard .kb-pcard-fav:hover { color: var(--kb-pink-deep); border-color: var(--kb-pink-deep); }
.kb-pcard .kb-pcard-fav svg { width: 15px; height: 15px; }
.kb-pcard .kb-pcard-meta { font-family: var(--kb-font-mono); font-size: 10px; color: var(--kb-muted); text-transform: uppercase; letter-spacing: 0.06em; }
/* CONV-1BC/1b · el nombre es información, no acción: tinta en vez del rosa de
   marca (que aquí competía con el CTA del pie). */
.kb-pcard h4 { margin: 0; font-size: 13.5px; font-weight: 700; color: var(--kb-ink); line-height: 1.3; min-height: 2.6em; }
.kb-pcard .kb-price-row { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.kb-pcard .price { font-family: var(--kb-font-display); font-weight: 700; font-size: 20px; color: var(--kb-ink); letter-spacing: -0.01em; }
.kb-pcard .price-old { color: var(--kb-mute2); text-decoration: line-through; font-size: 12px; }
/* [LINEA-OUTLET · BLOQUE 16] La línea de reputación de la tarjeta. Venía con sus
   colores y su tamaño escritos a mano dentro del JSX; ahora manda la clase, como
   en el resto de la tarjeta. Solo se pinta si hay reseñas o ventas de verdad.
   Contrastes sobre el blanco de .kb-pcard:
     · estrella --kb-orange #B8322F  → 5,94:1 ✓ (y es un objeto gráfico: le basta 3:1)
     · nota --kb-charcoal            → 12,6:1 ✓
     · conteo y ventas --kb-muted #5C5A60 → 7,00:1 ✓ */
.kb-pcard-rate { display: flex; align-items: center; gap: 4px; font-size: 11px; color: var(--kb-orange); }
.kb-pcard-rate b { color: var(--kb-charcoal); font-weight: 600; }
.kb-pcard-rate span { color: var(--kb-muted); }
.kb-pcard .price-save { color: var(--kb-success); font-size: 11.5px; font-weight: 600; }
/* [LINEA-OUTLET · BLOQUE 16] Aquí vivía .kb-b2b-hook: el bloque-botón amarillo de
   44px "Mayoreo $X −N% · Regístrate GRATIS →" de la tarjeta. Se fue porque el
   descuento por volumen dejó de necesitar cuenta (backend/orders.js se lo aplica a
   cualquiera) y porque el precio que enseñaba salía de los tiers del PRODUCTO, que
   son el precio de una cuenta y no el que iba a pagar quien miraba la tarjeta.
   En su lugar, una línea con el peldaño real y su condición — el mismo dibujo que
   usa la vitrina en .lo-tile-pl: el precio en la grotesca, el −% en la pastilla
   verde de NIVEL ALCANZADO y la condición debajo.
   Contrastes sobre el blanco de .kb-pcard:
     · rótulo y condición --kb-muted #5C5A60 → 7,00:1 ✓
     · precio --kb-ink #15141A              → 18,31:1 ✓
     · −% --kb-b2b #0E6B5E sobre --kb-b2b-soft #E3F1EE → 5,40:1 ✓
   Se va con el bloque su último verde literal (#0E6B5E escrito a mano porque sobre
   el amarillo del gancho el token no llegaba a 4,5:1): sin amarillo detrás, el
   token de la paleta vale y no hace falta la excepción. */
.kb-pcard-may { display: flex; align-items: baseline; flex-wrap: wrap; gap: 5px; font-size: 11.5px; line-height: 1.35; color: var(--kb-muted); }
/* nowrap SOLO en la cifra: en el catálogo de 2 columnas, "Mayoreo $4,900" entero
   no cabe en un renglón y antes se desbordaba. Así "Mayoreo" puede bajar de línea
   y la cantidad nunca se parte por la mitad. */
.kb-pcard-may b { font-family: var(--kb-font-display); font-size: 14px; font-weight: 800; color: var(--kb-ink); font-variant-numeric: tabular-nums; white-space: nowrap; }
.kb-pcard-may i { font-style: normal; padding: 1px 6px; border-radius: 5px; background: var(--kb-b2b-soft); color: var(--kb-b2b); font-size: 11px; font-weight: 700; }
.kb-pcard-may span { flex-basis: 100%; font-size: 11px; color: var(--kb-muted); }
/* CONV-1BC/1b · el pie pasa de fila (botón + ♡) a COLUMNA: el control de compra
   ocupa el ancho completo a 44px en sus tres estados (paquete, stepper, agregar). */
.kb-pcard .kb-pcard-foot { display: flex; flex-direction: column; gap: 6px; margin-top: auto; align-items: stretch; }
.kb-pcard .kb-pcard-foot .kb-btn { width: 100%; height: 44px; font-size: 13px; }
.kb-pcard .kb-pcard-foot .kb-pcard-stepper { width: 100%; height: 44px; min-height: 44px; }

.kb-locked-price {
  background: var(--kb-b2b-soft); color: var(--kb-b2b);
  padding: 6px 8px; border-radius: 4px;
  font-size: 11.5px; font-weight: 500;
  display: flex; align-items: center; gap: 6px;
  /* [LINEA-OUTLET · BLOQUE 13] El borde era lila (#B5A8E0): morado del clon
     rodeando un bloque que ya habla de NIVEL por dentro (fondo y texto verdes).
     Con --kb-b2b el borde dice lo mismo que el relleno. */
  border: 1px dashed var(--kb-b2b);
}

/* Inputs */
.kb-input {
  width: 100%; height: 40px; padding: 0 12px;
  border: 1px solid var(--kb-line-strong); border-radius: 6px;
  background: #fff; font-size: 13.5px; outline: none;
  transition: border-color .12s, box-shadow .12s;
}
/* [LINEA-OUTLET · BLOQUE 13] El halo del foco seguía en el rgba del magenta
   viejo (#E91E63). Pasa al rojo outlet (#B8322F = rgb 184,50,47), que es el
   mismo color del borde al que acompaña. */
.kb-input:focus { border-color: var(--kb-orange); box-shadow: 0 0 0 3px rgba(184,50,47,0.15); }
.kb-input.error { border-color: var(--kb-danger); }
.kb-label { display: block; font-size: 11.5px; font-weight: 600; color: var(--kb-charcoal); margin-bottom: 6px; letter-spacing: 0.01em; }
.kb-help { font-size: 11px; color: var(--kb-muted); margin-top: 4px; }
.kb-error { font-size: 11px; color: var(--kb-danger); margin-top: 4px; }

textarea.kb-input { height: auto; min-height: 80px; padding: 10px 12px; resize: vertical; }
select.kb-input { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--kb-charcoal) 50%), linear-gradient(135deg, var(--kb-charcoal) 50%, transparent 50%); background-position: calc(100% - 18px) center, calc(100% - 13px) center; background-size: 5px 5px; background-repeat: no-repeat; padding-right: 32px; }

/* Toast */
.kb-toast-stack { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; gap: 8px; z-index: 1000; pointer-events: none; }
.kb-toast { background: var(--kb-charcoal); color: #fff; padding: 10px 16px; border-radius: 6px; font-size: 13px; box-shadow: var(--kb-shadow); display: flex; align-items: center; gap: 10px; pointer-events: auto; animation: kbToast .25s ease both; }
.kb-toast.success { background: #0E6B5E; }
.kb-toast.warn { background: var(--kb-orange-deep); color: #fff; }
@keyframes kbToast { from { opacity: 0; transform: translateY(8px);} to { opacity: 1; transform: translateY(0);} }

/* Utility */
.kb-row { display: flex; align-items: center; gap: 12px; }
.kb-stack { display: flex; flex-direction: column; gap: 12px; }
.kb-spread { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.kb-grid { display: grid; gap: var(--kb-gap); }
.kb-mono { font-family: var(--kb-font-mono); }
.kb-muted { color: var(--kb-muted); }
.kb-divider { height: 1px; background: var(--kb-line); border: 0; margin: 4px 0; }

.kb-h1 { font-family: var(--kb-font-display); font-size: 38px; font-weight: 800; line-height: 1.05; letter-spacing: -0.02em; margin: 0; }
.kb-h2 { font-family: var(--kb-font-display); font-size: 26px; font-weight: 700; line-height: 1.1; letter-spacing: -0.01em; margin: 0; }
.kb-h3 { font-family: var(--kb-font-display); font-size: 18px; font-weight: 700; line-height: 1.2; margin: 0; }
.kb-eyebrow { font-family: var(--kb-font-mono); font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--kb-orange-deep); }

/* Hero */
.kb-hero {
  /* [LINEA-OUTLET · BLOQUE 13] La cinta traía el arcoíris del clon: cian #0099D8
     y dos cafés (#2D1810, #8B4513) que no existen en esta marca. Ahora son las
     CINCO bandas de la paleta 1 —rojo, dorado, verde nivel, carbón, gris piedra—
     en el mismo reparto de anchos. Es decoración: no lleva texto encima. */
  background: linear-gradient(90deg, #B8322F 0%, #B8322F 28%, #D9B26A 28%, #D9B26A 42%, #0E6B5E 42%, #0E6B5E 57%, #15141A 57%, #15141A 71%, #8A8A8E 71%);
  color: #fff; padding: 6px;
  border-radius: 18px; overflow: hidden;
  position: relative; min-height: 380px;
}
.kb-hero .kb-hero-content,
.kb-hero .kb-hero-art {
  background: #fff;
}
.kb-hero {
  display: grid; grid-template-columns: 1.1fr 1fr; gap: 6px;
}
.kb-hero .kb-hero-content { padding: 36px; display: flex; flex-direction: column; gap: 18px; justify-content: center; position: relative; z-index: 1; border-radius: 14px 0 0 14px; }
.kb-hero h1 {
  color: var(--kb-ink);
  font-family: var(--kb-font-display); /* [BLOQUE 22] era "Arial Black" con la grotesca vieja de respaldo: el único font-family de la hoja que no pasaba por una variable */
  font-weight: 900; font-size: 44px; line-height: 1; letter-spacing: -0.02em;
  -webkit-text-stroke: 2px #FFFFFF;
  text-shadow: 3px 3px 0 var(--kb-ink);
}
.kb-hero h1 em { color: var(--kb-pink-deep); font-style: normal; -webkit-text-stroke: 2px #FFFFFF; }
.kb-hero .tagline {
  font-family: "Arial Black", sans-serif; font-weight: 900; font-size: 13px;
  color: #D9B26A; -webkit-text-stroke: 1px #B8322F;
  letter-spacing: 0.05em; text-transform: uppercase;
}
.kb-hero p { color: var(--kb-muted); font-size: 15px; max-width: 46ch; line-height: 1.5; margin: 0; }

/* ═══ HERO · variante "Pulido" (V1) en sección izquierda ═══ */
.kb-hero .kb-hero-pulido {
  padding: 0; gap: 0; overflow: hidden;
  border: 6px solid var(--kb-red);
  border-radius: 14px 0 0 14px;
  background: #fff;
}
.kb-hero-pulido .kb-hero-stripe { display: flex; height: 8px; flex: 0 0 auto; }
.kb-hero-pulido .kb-hero-stripe i { flex: 1; }
.kb-hero-pulido .kb-hero-stripe i:nth-child(1) { background: var(--kb-pink-deep); }
.kb-hero-pulido .kb-hero-stripe i:nth-child(2) { background: var(--kb-yellow); }
.kb-hero-pulido .kb-hero-stripe i:nth-child(3) { background: var(--kb-green); }
.kb-hero-pulido .kb-hero-inner {
  padding: clamp(26px, 4vw, 46px);
  display: flex; flex-direction: column; gap: 18px;
  flex: 1; justify-content: center;
}
.kb-hero-pulido .kb-hero-eyebrow {
  font-family: var(--kb-font-display); font-weight: 800;
  color: var(--kb-pink-deep); letter-spacing: 0.14em;
  font-size: 12px; text-transform: uppercase;
  display: inline-flex; align-items: center; gap: 0.6em;
}
.kb-hero-pulido .kb-hero-eyebrow .dot {
  width: 8px; height: 8px; border-radius: 50%; background: var(--kb-green); flex: 0 0 auto;
}
.kb-hero-pulido h1 {
  font-family: var(--kb-font-display);
  font-weight: 900; font-size: clamp(40px, 5vw, 72px);
  line-height: 0.94; letter-spacing: -0.01em;
  color: var(--kb-ink); text-transform: uppercase;
  -webkit-text-stroke: 0; text-shadow: none; margin: 0;
}
.kb-hero-pulido h1 em {
  font-style: normal; color: var(--kb-pink-deep); position: relative;
  -webkit-text-stroke: 0;
}
.kb-hero-pulido h1 em::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0.04em; height: 0.08em;
  background: var(--kb-yellow); border-radius: 2px;
}
.kb-hero-pulido .kb-hero-sub {
  color: var(--kb-muted); font-size: 16px; line-height: 1.5;
  font-weight: 500; max-width: 32em; margin: 0;
}
.kb-hero-pulido .kb-hero-trust {
  display: flex; align-items: stretch; flex-wrap: wrap;
  border-top: 1px solid var(--kb-line); padding-top: 16px; margin-top: 2px;
}
.kb-hero-pulido .kb-hero-trust .stat {
  display: flex; flex-direction: column; gap: 3px; padding-right: clamp(18px, 3vw, 32px);
}
.kb-hero-pulido .kb-hero-trust .stat + .stat {
  border-left: 1px solid var(--kb-line); padding-left: clamp(18px, 3vw, 32px);
}
.kb-hero-pulido .kb-hero-trust .stat b {
  font-family: var(--kb-font-display); font-size: 21px; color: var(--kb-ink); line-height: 1;
}
.kb-hero-pulido .kb-hero-trust .stat b .star { font-size: 0.85em; }
.kb-hero-pulido .kb-hero-trust .stat span {
  font-size: 12px; color: var(--kb-muted); font-weight: 600; letter-spacing: 0.02em;
}
@media (max-width: 560px) {
  .kb-hero-pulido .kb-hero-trust { flex-direction: column; gap: 12px; }
  .kb-hero-pulido .kb-hero-trust .stat { padding-right: 0; }
  .kb-hero-pulido .kb-hero-trust .stat + .stat {
    border-left: none; padding-left: 0; border-top: 1px solid var(--kb-line); padding-top: 12px;
  }
}

/* ═══ HERO Distribuidores · responsive (apilar columnas en móvil) ═══ */
@media (max-width: 860px) {
  .kb-dist-hero { grid-template-columns: 1fr !important; min-height: auto !important; }
  .kb-dist-hero-left { padding: 34px 22px 28px !important; }
  .kb-dist-hero-right {
    /* [LINEA-OUTLET · BLOQUE 13] #2a2a2a era un gris frío suelto sobre un fondo
       oscuro que no es suyo; una línea blanca al 14% se apoya en el fondo real,
       sea cual sea. */
    border-left: none !important; border-top: 1px solid rgba(255,255,255,0.14);
    padding: 30px 18px 36px !important;
  }
}
@media (max-width: 480px) {
  .kb-dist-hero-left { padding: 28px 18px 24px !important; }
  .kb-dist-kpis { grid-template-columns: repeat(2, 1fr) !important; gap: 16px 0 !important; }
  .kb-dist-kpi { border-left: none !important; padding-left: 0 !important; }
  .kb-dist-kpi:nth-child(even) { border-left: 1px solid rgba(255,255,255,0.14) !important; padding-left: 16px !important; }
}

.kb-hero .kb-hero-cta { display: flex; gap: 10px; flex-wrap: wrap; }
.kb-hero .kb-hero-art {
  position: relative;
  /* [LINEA-OUTLET · BLOQUE 13] El degradado cerraba en el azul suave heredado;
     ahora cierra en hueso y el arte queda entero en la gama cálida de la marca. */
  background: linear-gradient(135deg, var(--kb-yellow-soft) 0%, var(--kb-pink-soft) 50%, var(--kb-cream) 100%);
  border-radius: 0 14px 14px 0;
  overflow: hidden;
}
.kb-hero .kb-hero-art::after {
  content: ""; position: absolute; inset: 0;
  /* [LINEA-OUTLET · BLOQUE 13] El rayado iba en el rgba del ámbar viejo #C9A227;
     ahora en el dorado de la paleta (#D9B26A = rgb 217,178,106). */
  background-image: repeating-linear-gradient(45deg, transparent 0 14px, rgba(217,178,106,0.10) 14px 15px);
}
.kb-hero .kb-hero-stat {
  position: absolute; bottom: 20px; right: 20px;
  background: var(--kb-ink); color: var(--kb-yellow);
  padding: 12px 16px; border-radius: 8px;
  font-family: var(--kb-font-mono); font-size: 11px;
  letter-spacing: 0.08em; text-transform: uppercase;
}
.kb-hero .kb-hero-stat b { display: block; font-family: var(--kb-font-display); font-size: 24px; color: #fff; }

/* Category strip */
.kb-cats { display: grid; grid-template-columns: repeat(7, 1fr); gap: 10px; margin-top: 22px; }
.kb-cat {
  background: #fff; border: 1px solid var(--kb-line); border-radius: 10px;
  padding: 12px; display: flex; flex-direction: column; gap: 6px; cursor: pointer;
  transition: border-color .12s, transform .12s;
}
.kb-cat:hover { border-color: var(--kb-orange); transform: translateY(-2px); }
/* UX-12: la tarjeta de categoría es un <a> — neutraliza subrayado/color de link
   (los textos internos ya fijan su color). */
a.kb-cat { text-decoration: none; color: inherit; }
.kb-cat .kb-cat-thumb { width: 100%; aspect-ratio: 1.2 / 1; border-radius: 6px; background: var(--kb-cream); position: relative; overflow: hidden; }
.kb-cat .kb-cat-thumb svg { width: 100%; height: 100%; }
.kb-cat .kb-cat-name { font-size: 12.5px; font-weight: 600; color: var(--kb-charcoal); }
.kb-cat .kb-cat-count { font-family: var(--kb-font-mono); font-size: 10.5px; color: var(--kb-muted); }

/* Section header */
.kb-section-head { display: flex; align-items: flex-end; justify-content: space-between; margin: 30px 0 14px; gap: 16px; }
.kb-section-head h2 { font-family: var(--kb-font-display); font-size: 22px; font-weight: 700; margin: 0; letter-spacing: -0.01em; }
.kb-section-head .kb-section-meta { font-family: var(--kb-font-mono); font-size: 11px; color: var(--kb-muted); text-transform: uppercase; letter-spacing: 0.08em; }

/* Footer */
.kb-footer {
  background: var(--kb-ink); color: rgba(255,255,255,0.7);
  margin-top: 50px; padding: 40px 22px 22px;
  font-size: 12.5px;
}
.kb-footer-grid { max-width: 1480px; margin: 0 auto; display: grid; grid-template-columns: 1.4fr repeat(4, 1fr); gap: 32px; }
.kb-footer h5 { color: #fff; font-family: var(--kb-font-display); font-size: 13px; margin: 0 0 12px; letter-spacing: 0.02em; }
.kb-footer ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 6px; }
.kb-footer li { cursor: pointer; }
.kb-footer li:hover { color: var(--kb-yellow); }
/* [LINEA-OUTLET · BLOQUE 14] La franja de abajo del pie (aviso de derechos y la línea de
   la empresa) iba en blanco al 40 % sobre el carbón --kb-ink: compuesto da #737276, que
   contra #15141A son 3,83:1 — por debajo del 4,5:1 que pide un texto de 10,5 px, y encima
   por debajo del piso de 11 px de la casa. Es el ÚNICO par del pie que no llegaba: el
   cuerpo (blanco al 70 % → 9,34:1), los títulos (blanco → 18,31:1), el hover dorado
   (9,18:1), los enlaces legales (7,01:1) y el "Síguenos:" (5,18:1) pasan de sobra.
   Al 62 % el compuesto es #a6a6a8 → 7,53:1 ✓, y sigue leyéndose como letra chica: el pie
   no cambia de aspecto, cambia que se pueda leer. */
.kb-footer-bot { max-width: 1480px; margin: 30px auto 0; padding-top: 18px; border-top: 1px solid rgba(255,255,255,0.08); display: flex; justify-content: space-between; gap: 12px; font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: rgba(255,255,255,0.62); }

/* Catalog */
.kb-catalog { display: grid; grid-template-columns: 240px 1fr; gap: 22px; align-items: start; }
.kb-filters { background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; position: sticky; top: 110px; }
.kb-filter-grp { padding: 14px 16px; border-bottom: 1px solid var(--kb-line); }
.kb-filter-grp:last-child { border-bottom: 0; }
.kb-filter-grp h6 { margin: 0 0 10px; font-size: 11.5px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--kb-charcoal); }
.kb-filter-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 4px 0; cursor: pointer; font-size: 12.5px; }
.kb-filter-row input { accent-color: var(--kb-orange); }
.kb-filter-row .count, .kb-chip .count { color: var(--kb-muted); font-family: var(--kb-font-mono); font-size: 10.5px; }

.kb-catalog-toolbar { display: flex; align-items: center; justify-content: space-between; padding: 10px 14px; background: #fff; border: 1px solid var(--kb-line); border-radius: 8px; margin-bottom: 14px; gap: 12px; flex-wrap: wrap; }
.kb-catalog-toolbar .kb-catalog-count { font-size: 12.5px; color: var(--kb-muted); }
.kb-catalog-toolbar .kb-catalog-count b { color: var(--kb-ink); }
.kb-catalog-toolbar .kb-toolbar-right { display: flex; gap: 10px; align-items: center; }

.kb-product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--kb-card-min), 1fr)); gap: 14px; }

/* PDP */
/* [B1] minmax(0, …) y no `1.05fr 0.95fr` a secas: `1fr` es `minmax(AUTO, 1fr)`,
   y ese `auto` significa "mínimo = min-content del contenido", así que CUALQUIER
   hijo con min-width propio (el carril del camino de volumen era el caso) hacía
   crecer la columna, y con html/body/.kb-main en overflow-x: clip el sobrante no
   sacaba barra: se cortaba. Con minmax(0, …) la columna nunca crece por su
   contenido; quien tenga que desplazarse lo hace en su propio contenedor. */
.kb-pdp { display: grid; grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr); gap: 30px; }
.kb-pdp-gallery { display: grid; grid-template-columns: 80px 1fr; gap: 12px; align-items: start; }
.kb-pdp-thumbs { display: flex; flex-direction: column; gap: 8px; }
/* UX-02: thumbs y dots ahora son <button> — reset del chrome nativo (no hay
   reset global de button). Los thumbs miden 80px, no les afecta el mínimo
   táctil de 44px; los dots lo neutralizan con min-height:0. */
.kb-pdp-thumb { width: 80px; aspect-ratio: 1; border: 1px solid var(--kb-line); border-radius: 6px; overflow: hidden; cursor: pointer; appearance: none; -webkit-appearance: none; padding: 0; background: none; }
.kb-pdp-thumb.on { border-color: var(--kb-orange); border-width: 2px; }
/* Dot visible de 8px con área táctil de 16px (borde transparente + background-clip). */
.kb-pdp-dot {
  appearance: none; -webkit-appearance: none; box-sizing: border-box;
  width: 16px; height: 16px; min-height: 0; min-width: 0;
  padding: 0; margin: 0; border: 4px solid transparent; border-radius: 50%;
  background: rgba(0,0,0,.25); background-clip: padding-box; cursor: pointer;
}
.kb-pdp-dot.on { background-color: var(--kb-pink-deep); }
.kb-pdp-main { aspect-ratio: 1; border: 1px solid var(--kb-line); border-radius: 12px; overflow: hidden; }
.kb-pdp-info { display: flex; flex-direction: column; gap: 14px; min-width: 0; }
/* ═══ LA VOZ DE LA CASA EN LA FICHA ═════════════════════════════
   [LINEA-OUTLET · BLOQUE 16] LA REGLA DE LA CASA, LEÍDA DE LO QUE YA EXISTE:

     · la SERIF nombra           → .lo-tile-nm (el nombre del perfume en la
       vitrina, 19px/600) y .kb-olf-tit (la familia olfativa);
     · la GROTESCA cuenta        → TODOS los precios y totales de la tienda:
       .kb-pcard .price (catálogo), .kb-summary-row.tot (carrito),
       .kb-oc-total (pedidos), .kb-pos-cobro-total (mostrador);
     · y los encabezados de sección de las pantallas de compra van en
       .kb-section-head h2 (grotesca 22/700), iguales en catálogo y ficha.

   Este bloque nació pasando TAMBÉN los precios de la ficha a la serif, y eso
   rompía la regla por dos sitios. Uno: la ficha quedaba como la única pantalla
   de la tienda con los precios en otra letra. Dos, y peor: la serif de la casa
   dibuja CIFRAS DE ESTILO ANTIGUO —el 9 baja de la línea base, el 6 sube—, así
   que "$1,900" salía haciendo escalera. Eso es precioso en un párrafo de libro y
   es ilegible en un precio de 42px que además cambia al mover el contador.
   `tabular-nums` fija el ANCHO de la cifra, no su altura: no podía arreglarlo.

   Lo que SÍ se queda de aquel cambio, porque sí es la regla: el nombre del
   perfume en serif (como en la vitrina), su tamaño en la hoja de estilos y no
   escrito a mano en el JSX, y `tabular-nums` en todo número que se mueva — sin
   él el precio salta de sitio a cada pieza que agrega el cliente.

   Contrastes (blanco #FFFFFF de la caja):
     · nombre --kb-ink #15141A  → 18,3:1 ✓
     · precio --kb-red #B8322F   → 5,94:1 ✓ */
.kb-pdp-h1 { font-family: var(--kb-font-serif); font-size: clamp(30px, 3.4vw, 44px); font-weight: 600; line-height: 1.05; letter-spacing: -0.01em; color: var(--kb-ink); margin: 0; }
.kb-pdp-tot { margin-left: auto; font-family: var(--kb-font-display); font-size: 22px; font-weight: 700; line-height: 1; color: var(--kb-ink); font-variant-numeric: tabular-nums; }
/* La barra pegajosa móvil: el total manda ahí dentro, y el "· N pz" lo acompaña en
   la de interfaz a 11px. Los dos venían escritos a mano en el JSX. */
.kb-pdp-stickybuy b { font-family: var(--kb-font-display); font-size: 18px; font-weight: 700; font-variant-numeric: tabular-nums; }
.kb-pdp-stickybuy b span { font-family: var(--kb-font-ui); font-size: 11px; font-weight: 500; color: var(--kb-muted); }

/* CONV-2A · Caja de precio de la ficha (propuesta 2a del handoff de conversión).
   La clase MANDA: el degradado rosa/azul con borde rosa de 2px que traía el
   componente como style inline desapareció, y esta regla dejó de estar pisada.
   [CONTRADICCIÓN DEL HANDOFF] Su regla transversal fija el mínimo de texto de
   datos en 11px, pero más abajo pide "mono 10.5px" para el eyebrow y 10.5px para
   la nota de volumen. Gana la regla general: ningún dato DE LOS QUE TOCA ESTA
   PROPUESTA baja de 11px (por eso el .kb-tag de la fila superior y las
   etiquetas del bloque de caja, que estaban en 10 / 10.5px, suben a 11px solo
   dentro de esta caja). [ESCALERA-B] La rejilla de volumen (.kb-volume-*), que
   era la última excepción con texto de 9–10.5px, desapareció: la sustituye el
   módulo .kb-vol-* del final de este bloque. Ya no queda nada bajo 11px en la
   caja de precio.
   Los hex del handoff #8E2522 y #B8322F NO se escriben literales: son los tokens
   --kb-orange-deep y --kb-red, que ya existen en la paleta. Usar el token además
   conserva el comportamiento previo — el .kb-tag.orange al que sustituye el
   eyebrow ya se repintaba con el acento del motor de temas. */
.kb-pricebox { background: #fff; border: 1px solid var(--kb-line-strong); border-radius: 16px; padding: 16px; display: flex; flex-direction: column; gap: 10px; }
/* El precio grande: la grotesca de la casa, la misma con la que la tarjeta del
   catálogo y el total del carrito dicen sus cifras. `tabular-nums` porque este
   número CAMBIA al mover el contador y sin ancho fijo bailaría de sitio. */
.kb-pricebox .price-main { font-family: var(--kb-font-display); font-size: 34px; font-weight: 800; color: var(--kb-red); letter-spacing: -0.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.kb-pricebox .price-old { color: var(--kb-mute2); text-decoration: line-through; font-size: 14px; }
/* a · Fila superior: eyebrow del tipo de precio + pill de $/kg de fábrica */
.kb-pb-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.kb-pricebox .kb-pb-top .kb-tag { font-size: 11px; }
/* [HALLAZGO DE AUDITORÍA — mismo agujero que .kb-bm-lbl, ver comentario allá]
   --kb-orange-deep lo repinta el motor de temas (theme-engine.jsx:27) con el
   acento de temporada; en "Buen Fin" este "PRECIO DETALLE" quedaría amarillo
   sobre blanco. --kb-orange-text es estable y cumple AA. */
.kb-pb-eyebrow { font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--kb-orange-text); }
/* [LINEA-OUTLET · BLOQUE 13] La pastilla traía tres ámbares propios (#FFF9F0 de
   fondo, #E9C46A de borde, #8B6D00 de rótulo) cuando la paleta ya tiene el
   dorado y su suave. Con los de la casa: rótulo #8A5300 sobre #F6EFE0 → 5,53:1 ✓
   y la cifra #15141A sobre #F6EFE0 → 15,99:1 ✓. */
.kb-pb-kg { display: inline-flex; align-items: center; gap: 6px; background: var(--kb-yellow-soft); border: 1.5px dashed var(--kb-yellow); border-radius: 999px; padding: 3px 10px; font-size: 11px; }
.kb-pb-kg b { font-size: 13px; color: #15141A; }
.kb-pb-kg-lbl { color: #8A5300; font-weight: 700; }
/* b · Precio grande */
.kb-pb-price { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.kb-pb-unit { font-size: 13px; color: var(--kb-muted); }
/* c · Gancho B2B (solo invitado). #E3F1EE y #0E6B5E del handoff SON los tokens
   --kb-b2b-soft / --kb-b2b y se usan como tales. */
/* [LINEA-OUTLET · BLOQUE 16] El fondo era un degradado de 135° que iba del verde
   suave al blanco. La casa no usa degradados decorativos, y este no informaba de
   nada: el bloque ya se distingue por su borde de 1,5px. Plano, además, el
   contraste del texto es UNO y no un rango que hay que medir en los dos extremos. */
.kb-pricebox .kb-locked-price {
  display: block; padding: 12px; border-radius: 12px; font-size: 12px; font-weight: 500;
  background: var(--kb-b2b-soft);
  border: 1.5px solid var(--kb-b2b); color: var(--kb-ink);
}
.kb-pb-b2b-cols { display: flex; align-items: stretch; gap: 14px; }
.kb-pb-b2b-col { flex: 1; min-width: 0; }
/* [LINEA-OUTLET · BLOQUE 13] El separador era lila (#C9BFE9) dentro del bloque
   de NIVEL: pasa al verde de nivel, como el borde y el texto del mismo bloque. */
.kb-pb-b2b-sep { flex: 0 0 1px; width: 1px; align-self: stretch; background: var(--kb-b2b); }
.kb-pb-b2b-lbl { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: var(--kb-muted); }
.kb-pb-b2b-val { font-family: var(--kb-font-display); font-size: 22px; font-weight: 800; line-height: 1.15; color: var(--kb-b2b); font-variant-numeric: tabular-nums; }
.kb-pb-b2b-pz { font-family: var(--kb-font-ui); font-size: 12px; font-weight: 500; color: var(--kb-muted); }
/* Verde propio, NO --kb-success (#0E6B5E): sobre el morado claro del bloque da
   3.6:1, bajo el mínimo AA de 4.5:1 para texto pequeño. Este da 5.1:1. Es el
   mismo verde que ya usa el gancho de la tarjeta por la misma razón. */
.kb-pb-b2b-off { font-size: 11.5px; font-weight: 800; color: #0E6B5E; }
/* [LINEA-OUTLET · BLOQUE 16] Aquí vivía .kb-pb-b2b-cta: un botón primario rojo de
   44px y ancho completo con la palabra GRATIS dentro, dibujado ANTES que "Agregar al
   carrito" y más pesado que él. En una ficha de producto el único botón rojo tiene
   que ser comprar. Queda una línea con su enlace: mismo destino (abrir sesión o
   cuenta), un octavo del peso visual.
   Contrastes sobre el fondo del bloque (#E3F1EE, el extremo más oscuro del degradado):
     · texto --kb-ink #15141A  → 17,4:1 ✓
     · enlace --kb-orange-text #A32C29 → 6,19:1 ✓ (subrayado, así que no depende del color)
   El enlace es inline-flex con min-height 44px: el área táctil de la casa sin sacarlo
   del renglón ni convertirlo en un botón de bloque. */
.kb-pb-b2b-pie { margin: 10px 0 0; font-size: 12px; line-height: 1.5; color: var(--kb-ink); }
.kb-pb-b2b-link {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 2px;
  background: none; border: 0; cursor: pointer;
  font-family: inherit; font-size: 12.5px; font-weight: 700;
  color: var(--kb-orange-text); text-decoration: underline;
}
/* d · Referencia por caja/bolsa (mismo bloque rosa de siempre, radio y padding
   del handoff; la separación la da ahora el gap del contenedor). */
/* [LINEA-OUTLET · BLOQUE 13] Este bloque tenía DOS colores que no son de la
   marca: #b08 (o sea #BB0088, magenta puro) en la referencia y #7a4a5a (malva)
   en el subtítulo. El degradado pasa a los dos rosas que SÍ son tokens
   (--kb-pink → --kb-pink-soft) y cada texto dice lo que es, medido contra el
   extremo más oscuro del degradado (#F5DCDA):
     · rótulo · --kb-orange-text #A32C29 → 5,47:1 ✓ (rojo de marca, 11px)
     · la ref es un DATO · --kb-ink #15141A → 14,07:1 ✓
     · el sub es secundario · --kb-muted #5C5A60 → 5,23:1 ✓
   El total (.kb-pb-caja-tot, 30px) se queda en --kb-pink-deep: a ese tamaño el
   mínimo es 3:1 y da 4,56:1 ✓. */
.kb-pb-caja { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 11px 12px; border-radius: 12px; background: var(--kb-pink-soft); border: 1.5px solid var(--kb-pink); }
.kb-pb-caja-lbl { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--kb-orange-text); }
.kb-pb-caja-ref { color: var(--kb-ink); font-weight: 500; }
.kb-pb-caja-sub { font-size: 13px; color: var(--kb-muted); }
.kb-pb-caja-tot { font-size: 30px; font-weight: 900; line-height: 1; color: var(--kb-pink-deep); font-variant-numeric: tabular-nums; }
/* e · Pie · f · Nota de volumen */
.kb-pb-foot { display: flex; justify-content: space-between; gap: 10px; padding-top: 9px; border-top: 1px solid var(--kb-line); font-size: 11px; color: var(--kb-muted); white-space: nowrap; }
/* [HALLAZGO DE AUDITORÍA] --kb-muted (#5C5A60, 5.95:1) y NO --kb-mute2
   (#8D8880, 3.15:1) aunque el handoff liste mute2 para notas: a 11px eso queda
   bajo el mínimo AA de 4.5:1. Es el mismo criterio con el que aquí mismo se
   descartó --kb-success para el −%; aplicarlo solo a un color y no al otro era
   inconsistente. Además esta nota explica CÓMO se calculan los descuentos por
   volumen — es información que el mayorista necesita leer, no letra chica. */
.kb-pb-nota { font-size: 11px; line-height: 1.4; color: var(--kb-muted); }

/* ═══ LA ESCALERA DE PRECIO DE LA FICHA (.po-*) ══════════════════════
   [LINEA-OUTLET · BLOQUE 16] Aquí vivía .kb-vol-* (135 líneas): el "camino al
   precio" con su carril, sus paradas alternadas arriba/abajo, su bandera de
   "estás aquí" y una tabla desplegable con un renglón por peldaño. Estaba muy
   trabajado y resolvía un problema que esta tienda no tiene: OCHO peldaños que
   no caben. Línea Outlet tiene DOS descuentos —mayoreo y distribución— y punto
   (regla del dueño). Con tres cifras, un carril con paradas es un gráfico para
   un dato que cabe en tres columnas.
   Y lo que de verdad importaba: ese módulo solo se pintaba para cuentas B2B
   (volOn && showB2B), mientras el servidor le cobra la misma escalera a
   cualquiera. El público —el 100 % de quien entra sin cuenta— no veía el
   argumento de venta de este negocio. Estas columnas las ve todo el mundo.

   Contrastes, medidos sobre el blanco de la caja de precio (#FFFFFF):
     · precio de columna --kb-ink #15141A            → 18,31:1 ✓
     · etiqueta y condición --kb-muted #5C5A60        → 7,00:1 ✓
     · "Tu precio" en nivel --kb-b2b #0E6B5E          → 5,86:1 ✓
     · sobre el verde suave #E3F1EE de la columna activa: #0E6B5E → 5,40:1 ✓
     · el botón de agregar --kb-orange-text #A32C29   → 7,12:1 ✓
   Ningún tamaño baja de 11px y el botón mide 44px de alto. */
.po { display: grid; gap: 10px; padding-top: 10px; border-top: 1px solid var(--kb-line); }
.po-top { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; }
.po-eyebrow { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--kb-muted); }
/* El acumulado del PEDIDO: la regla que hace funcionar la escalera (suman las
   piezas y el importe de todo el carrito) leída sin buscarla. */
.po-pedido { font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700; letter-spacing: .04em; color: var(--kb-ink); }
/* Una columna por peldaño. auto-fit + minmax: con dos niveles son tres columnas,
   con ocho se reparten en varias filas — nunca desbordan ni encogen la letra,
   que era justo lo que el carril tenía que resolver con alternancia y scroll. */
.po-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(118px, 1fr)); gap: 8px; }
.po-col { display: grid; gap: 2px; align-content: start; padding: 10px; border: 1px solid var(--kb-line); border-radius: 10px; background: var(--kb-paper); }
/* La columna del precio que se paga. En el peldaño de lista se marca con tinta
   —no con verde— porque en esta casa el verde significa NIVEL ALCANZADO, y el
   precio público no es un nivel que nadie haya alcanzado. */
/* [LINEA-OUTLET · BLOQUE 23] EL CHIP ELEGIDO SE RELLENA Y SUS CIFRAS VAN EN BLANCO.
   Encargo del dueño: antes el elegido sólo se marcaba con un filete y no saltaba a la vista.
   El COLOR del relleno lo dice el estado, no el capricho: rojo cuando lo que está vigente es el
   precio de lista, verde cuando el pedido YA cruzó el umbral y ganó su descuento. El verde de
   esta casa significa exactamente eso —nivel alcanzado— y es la única señal que tiene el
   cliente de que lo consiguió: pintar de rojo un nivel alcanzado sería borrarla.
   Blanco sobre el rojo 5,94:1 ✓, sobre el verde 6,40:1 ✓. El rótulo y la condición van en
   blanco al 88 %, que compone 4,92:1 sobre el rojo y 5,35:1 sobre el verde: baja el peso sin
   bajar de la regla, que es lo que antes hacía el gris. */
.po-col.on { border: 1.5px solid var(--kb-orange); background: var(--kb-orange); padding: 9.5px; }
.po-col.on .po-col-pr, .po-col.on .po-col-tu { color: var(--kb-paper); }
.po-col.on .po-col-lbl, .po-col.on .po-col-cond { color: rgba(255, 255, 255, 0.88); }
.po-col.on.nivel { border-color: var(--kb-b2b); background: var(--kb-b2b); }
.po-col-lbl { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--kb-muted); }
.po-col-pr { font-family: var(--kb-font-display); font-size: 21px; font-weight: 800; line-height: 1.05; letter-spacing: -0.01em; color: var(--kb-ink); font-variant-numeric: tabular-nums; }
.po-col-cond { font-size: 11px; line-height: 1.35; color: var(--kb-muted); }
.po-col-tu { display: flex; align-items: center; gap: 4px; margin-top: 2px; font-size: 11px; font-weight: 700; color: var(--kb-ink); }
.po-col.on.nivel .po-col-pr, .po-col.on.nivel .po-col-tu { color: var(--kb-paper); }
.po-col.on.nivel .po-col-lbl, .po-col.on.nivel .po-col-cond { color: rgba(255, 255, 255, 0.88); }
/* La brecha: UNA frase con lo que falta, y un botón que la cierra. */
.po-falta { display: grid; gap: 8px; }
/* [LINEA-OUTLET · BLOQUE 23] Las dos líneas de abajo eran texto suelto y un enlace que parecía
   uno más. Pasan a ser dos bloques del mismo peso: el de arriba AVISA de lo que falta, el de
   abajo lo RESUELVE. Los dos a 44 px, que es el mínimo táctil de la casa. */
.po-falta-txt {
  margin: 0; display: flex; align-items: center; min-height: 44px; padding: 10px 12px;
  border-radius: 10px; background: var(--kb-nivel-aviso); color: var(--kb-paper);
  font-size: 14px; font-weight: 700; line-height: 1.35;
}
/* El precio del siguiente nivel iba en verde de nivel. Sobre el óxido ese verde da 1,17:1 y
   desaparece — y además aquí el nivel todavía NO se ha alcanzado, que es justo lo que el
   bloque está diciendo. Va en blanco, y lo destaca el peso. */
.po-falta-txt b { color: var(--kb-paper); font-weight: 800; }
.po-cta {
  display: inline-flex; align-items: center; justify-content: center; width: 100%; min-height: 44px;
  padding: 10px 12px; border-radius: 10px; font-family: inherit; font-size: 13px; font-weight: 700;
  text-align: center; color: var(--kb-paper); background: var(--kb-nivel-accion); border: 0; cursor: pointer;
}
.po-cta:hover { background: var(--kb-nivel-accion-hover); }
.po-max { margin: 0; font-size: 12.5px; font-weight: 700; color: var(--kb-b2b); }
.po-regla { margin: 0; font-size: 11.5px; line-height: 1.45; color: var(--kb-muted); }
.po-margen { margin: 0; font-size: 12.5px; line-height: 1.45; padding: 9px 11px; border-radius: 10px; background: var(--kb-yellow-soft); color: var(--kb-ink); }
/* El ahorro, dicho UNA vez, junto al precio grande. Era .kb-vol-ahorro con sus
   hex sueltos (#E3F1EE / #0E6B5E), que son los tokens de nivel de la paleta 1. */
.kb-pb-ahorro { font-size: 12px; font-weight: 800; border-radius: 999px; padding: 3px 10px; background: var(--kb-b2b-soft); color: var(--kb-b2b); white-space: nowrap; }

/* CONV-2B · Botones de compra de la ficha (propuesta 2b del handoff de
   conversión). Antes compartían fila con flex:1 cada uno: mismo tamaño, mismo
   peso visual y ninguno se leía como LA acción. Ahora se apilan a ancho
   completo — primario rosa de 48px con el total pegado, "Comprar ahora →" como
   outline debajo — y la barra pegajosa móvil repite la misma etiqueta.
   La clase MANDA: el `style` inline con flex:1 de cada botón desapareció, igual
   que se hizo en .kb-pricebox (CONV-2A) y .kb-pcard (CONV-1BC).
   [CONTRADICCIÓN DEL HANDOFF] La propuesta 2b pide 42px de alto para el
   secundario, pero la regla transversal del MISMO handoff fija el mínimo de
   botón táctil en 44px. Gana la regla general (mismo criterio que se aplicó en
   la caja de precio): el secundario va a 44px, no a 42px.
   Los selectores llevan .kb-btn delante a propósito: el @media de 480px de más
   abajo pisa `padding`/`font-size` de .kb-btn para que quepan los botones en
   fila, y estos ya ocupan el ancho completo — no tienen ese problema. */
.kb-pdp-cta { display: flex; flex-direction: column; gap: 8px; }
.kb-btn.kb-pdp-cta-primary { width: 100%; height: 48px; padding: 0 18px; font-size: 14px; }
.kb-btn.kb-pdp-cta-secondary {
  width: 100%; height: 44px; padding: 0 18px; font-size: 13px;
  background: #fff; color: var(--kb-ink); border: 1.5px solid var(--kb-ink);
}
.kb-btn.kb-pdp-cta-secondary:hover { background: var(--kb-ink); color: #fff; }
/* El total nunca se trunca: si el ancho no alcanza, el que cede es el texto. */
.kb-pdp-cta-lbl { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.kb-pdp-cta-tot { flex: 0 0 auto; }
/* Barra pegajosa (solo móvil): mismo texto y mismo total que el primario, en un
   botón que puede encoger para no empujar el nombre/precio fuera de la barra.
   El reparto del ancho es a propósito: el bloque de la izquierda (nombre +
   total + piezas) cede PRIMERO —mayor flex-shrink que el botón— porque su nombre ya
   se recorta con ellipsis y su total lo repite el propio botón; solo si aun así
   no cabe encoge el botón, y ahí lo que se recorta es la etiqueta, nunca la
   cifra. Sin esto el botón se quedaba en "Agregar al carr… · $1,299" incluso en
   un 390px. El selector es `> div` porque el único hijo <div> de la barra es
   ese bloque (el otro hijo es el <button>).
   [HALLAZGO DE AUDITORÍA] El shrink era 999 y resultó DEMASIADO agresivo: a
   320px con un total de 6 cifras el bloque izquierdo se comprimía por debajo de
   lo que mide su propio precio y el "· N pz" bajaba de renglón, creciendo la
   barra de 66 a 85px. Con 8 sigue cediendo primero (que es lo que se buscaba)
   pero ya no se aplasta por debajo de su contenido; el precio además lleva
   `white-space: nowrap` en el JSX, que es la garantía dura de que no se parte. */
.kb-pdp-stickybuy > div { flex: 1 8 auto; min-width: 0; overflow: hidden; }
.kb-pdp-stickybuy .kb-pdp-cta-sticky { margin-left: auto; flex: 0 1 auto; min-width: 0; height: 44px; padding: 0 12px; }
.kb-pdp-stickybuy .kb-pdp-cta-sticky svg { flex: 0 0 auto; }
@media (max-width: 380px) {
  /* En 320px el ícono se lleva los 24px que necesita la cifra. No se pierde
     información: el texto del botón ya dice qué hace. */
  .kb-pdp-stickybuy .kb-pdp-cta-sticky svg { display: none; }
}

/* CONV-2D · Legibilidad de los datos (propuesta 2d del handoff de conversión).
   Alcance ACOTADO por el dueño a DOS bloques: las cuatro métricas del paquete
   en el carrito (screens-shop.jsx, tarjeta del bundle) y la CABECERA de
   PkgCard (packages-catalog.jsx). El "admin-*" que el handoff también lista
   queda FUERA. Regla dura: ninguna etiqueta de dato de lo que se toca baja de
   11px (venían en mono de 9px en el carrito y de 10px en PkgCard).

   ── Rejilla: 1 / 2 / 4 columnas, nunca 3 ──────────────────────────────────
   El handoff describe una rejilla de TRES columnas, pero en el carrito hay
   CUATRO métricas y no se quita ninguna (no se elimina información): con 3
   columnas una quedaría siempre huérfana en su renglón. Los cortes no son los
   breakpoints de costumbre porque el ancho de estas métricas NO es el del
   viewport: viven en la columna izquierda del encabezado del paquete, que
   comparte fila con la pila de botones (Ver detalle / Modificar / Quitar,
   flex-shrink 0). Medido en Chrome sobre el carrito real (360×780, fuentes
   cargadas): esa pila ocupa 150.7px con "(5 SKUs)" y ~164px con "(120 SKUs)".
   Descontando .kb-main (14px×2 en ≤900px), el borde de .kb-cart-list (1px×2),
   el borde de la tarjeta (2px×2), el padding del encabezado (16px×2) y el gap
   de la fila (12px), a 360px quedan 131px para las cuatro métricas. La cifra
   más ancha del alcance —"$999,999" a 18px, medido con la grotesca anterior— mide 81.3px y no se
   parte, así que un chip necesita ~95px: a 360px SOLO cabe UNA columna (el
   "2×2 en móvil" que sugería el encargo se midió y no entra). Dos columnas
   caben desde 460px y cuatro desde 680px, con margen incluso en el peor caso
   de la pila de botones.

   ── Color de la cifra: se abandona tierColor ──────────────────────────────
   Las cifras usaban `tierColor` (#B8322F paquete / #0099D8 pedido B2B), que
   distinguía el tier. Sobre el chip rosa dan 3.94:1 y 2.91:1 — y ni siquiera
   sobre BLANCO llegan a AA (4.35:1 y 3.21:1): tierColor no es color válido
   para texto pequeño sobre NINGÚN fondo claro, así que conservarlo no era una
   opción real. La señal de tier no se pierde: el borde de la tarjeta, su
   degradado de fondo, la cinta "LÍNEA COMERCIAL · PAQUETE" / "PEDIDO B2B" y
   los botones Ver detalle / Modificar siguen pintados con tierColor. Además
   una cifra azul sobre un chip rosa habría sido una señal falsa.

   ── Contraste · un solo criterio en TODO el cambio: 4.5:1 (texto pequeño) ──
   Mismo precedente ya sentado en .kb-pricebox y .kb-pcard, donde se descartó
   --kb-success (#0E6B5E) sobre fondo claro por quedar bajo 4.5:1:
     · etiqueta rosa  --kb-orange-deep #8E2522 sobre #FBEDEC → 6.24:1  ✓
       (con la paleta por defecto: el motor de temas repinta este token con el
       acento del admin, exactamente igual que en .kb-pb-eyebrow)
     · cifra rosa: el handoff pide --kb-pink-deep #B8322F → 3.94:1  ✗. Se usa
       --kb-orange-text (#A32C29 → 5.37:1), el token que este repo ya creó
       exactamente para "texto pequeño sobre fondo claro" cuando #B8322F falla.
       Además el motor de temas repinta --kb-pink-deep con el primario del
       admin (theme-engine.jsx) → su contraste ni siquiera sería estable;
       --kb-orange-text no se repinta.
     · cifra verde: el handoff pide --kb-success #0E6B5E sobre #ECFDF3 →
       4.16:1 ✗ → #0E6B5E (5.84:1), el MISMO verde y la misma razón que
       .kb-pb-b2b-off. (El #1FA577 que usaba el carrito daba 2.97:1 sobre el
       chip y 3.13:1 hasta sobre blanco.)
     · chip neutro del total: --kb-muted sobre --kb-cream → 5.82:1 ✓ y cifra
       --kb-ink → 18.6:1 ✓. El handoff no pide chip para esta cuarta métrica:
       conserva su tinta de siempre ("tinta = información") y solo se le da el
       mismo envase para que la rejilla no quede con tres chips y un hueco.
   #FBEDEC y #F5DCDA SÍ existen como tokens (--kb-pink-soft / --kb-pink) y se
   usan como tales. [LINEA-OUTLET · BLOQUE 13] #ECFDF3 y #B7DCC2 ya no van
   literales: eran el verde menta y su borde del clon, y la paleta tiene un solo
   verde de nivel con su suave. El chip pasa a --kb-b2b-soft / --kb-b2b y la
   cifra mantiene su #0E6B5E: 5,51:1 sobre #E3F1EE ✓ (sobre el #ECFDF3 anterior
   daba 6,06:1, pero con un fondo que no era de nadie). */
.kb-bundle-metrics { display: grid; grid-template-columns: 1fr; gap: 6px; margin-top: 8px; }
@media (min-width: 460px) { .kb-bundle-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 680px) { .kb-bundle-metrics { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
.kb-bundle-metric { min-width: 0; border-radius: 10px; padding: 8px 6px; text-align: center; }
.kb-bundle-metric .kb-bm-lbl { font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.06em; line-height: 1.25; }
/* La etiqueta SÍ puede repartirse en dos renglones ("TOTAL / INVERSIÓN"); la
   cifra no debería, pero `overflow-wrap: anywhere` es la red de seguridad para
   un importe de 7+ dígitos: parte antes que desbordar la tarjeta. */
.kb-bundle-metric .kb-bm-val { font-family: var(--kb-font-display); font-size: 18px; font-weight: 900; line-height: 1.15; overflow-wrap: anywhere; }
/* El chip cuesta 14px de ancho (padding + borde) que la fila flex anterior no
   gastaba. A ≤400px eso es justo lo que separa un "$999,999" de partirse en
   dos renglones, así que ahí la cifra baja a 16px (72.2px medidos, contra los
   81.3px de 18px). Sigue muy por encima del mínimo de 11px de la propuesta. */
@media (max-width: 400px) { .kb-bundle-metric .kb-bm-val { font-size: 16px; } }
.kb-bm-pink { background: var(--kb-pink-soft); border: 1px solid var(--kb-pink); }
/* [HALLAZGO DE AUDITORÍA] --kb-orange-text y NO --kb-orange-deep aunque el
   handoff pida #8E2522: theme-engine.jsx:27 REPINTA --kb-orange-deep con el
   acento del tema de temporada, y backend/themes.js trae temas sembrados,
   enabled y programados, con auto_enabled true por defecto. En "Buen Fin"
   (13–18 nov, accent #D9B26A) esta etiqueta quedaría amarilla sobre rosa claro:
   1.28:1, ilegible — en la semana de más venta del año. En "Día de Muertos"
   (#FF8F00) da 2.07:1. --kb-orange-text no se repinta y da 5.37:1. */
.kb-bm-pink .kb-bm-lbl { color: var(--kb-orange-text); }
.kb-bm-pink .kb-bm-val { color: var(--kb-orange-text); }
.kb-bm-ink { background: var(--kb-cream); border: 1px solid var(--kb-line-strong); }
.kb-bm-ink .kb-bm-lbl { color: var(--kb-muted); }
.kb-bm-ink .kb-bm-val { color: var(--kb-ink); }
.kb-bm-green { background: var(--kb-b2b-soft); border: 1px solid var(--kb-b2b); }
.kb-bm-green .kb-bm-lbl, .kb-bm-green .kb-bm-val { color: #0E6B5E; }

/* CONV-2D · b · Cabecera de PkgCard. Solo tamaño y tachado: el color lo hereda
   del encabezado (blanco sobre pkg.gradient) y el degradado viene de la tabla
   `packages`, fuera de este alcance.
   [HALLAZGO DE AUDITORÍA · queda ABIERTO] Ese blanco sobre el extremo CLARO de
   los degradados no cumple AA y no puede cumplirlo sin tocarlos. Quitar la
   opacidad decorativa es lo máximo que puede hacer un cambio de tipografía, y
   sí mueve la aguja (medido sobre los 4 degradados de `packages`):
     · badge, sobre el degradado, extremo claro: .8 → 2.01–3.09:1;
       a opacidad 1 → 2.39–4.02:1.
     · "INVERSIÓN TOTAL" (.8) y "Antes" (.7), sobre la caja
       rgba(0,0,0,.2): 2.90–4.30:1 y 2.57–3.65:1 en el extremo claro;
       a opacidad 1 → 3.65–5.83:1 en el claro y 6.65–10.65:1 en el oscuro.
   Sigue por debajo de 4.5:1 en los paquetes de degradado claro (#FF8533 del
   Inicial y #2DB4E6 del Distribuidor+). Es un problema PREEXISTENTE de TODO el
   encabezado —afecta igual al h3 y al tagline, que esta propuesta no toca— y
   corregirlo pide oscurecer los degradados, que son dato de negocio de la
   tabla `packages`: otra propuesta, no esta. */
.kb-pkgcard-badge { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: 0.1em; }
.kb-pkgcard-invlbl { font-size: 11px; letter-spacing: 0.06em; }
.kb-pkgcard-antes { font-size: 12px; text-decoration: line-through; }

/* Quantity stepper
   [ESCALERA-B] 40px de alto y botones de 36px estaban por debajo del mínimo
   táctil de la casa (44px): suben a 44. El componente <Stepper> (shared.jsx) NO
   usa esta clase —dibuja sus controles de 48px con estilo inline— y por eso la
   ficha y el carrito no cambian de aspecto; esta regla es la que heredaría
   cualquier stepper que se enganche a la clase, y ahora nace ya conforme. */
.kb-stepper { display: inline-flex; align-items: center; border: 1px solid var(--kb-line-strong); border-radius: 6px; overflow: hidden; height: 44px; min-height: 44px; }
.kb-stepper button { width: 44px; min-width: 44px; height: 100%; min-height: 44px; background: #fff; border: 0; font-size: 16px; color: var(--kb-charcoal); }
.kb-stepper button:hover { background: var(--kb-cream); }
.kb-stepper input { width: 56px; height: 100%; text-align: center; border: 0; border-left: 1px solid var(--kb-line); border-right: 1px solid var(--kb-line); font-weight: 600; outline: none; }

/* Cart */
.kb-cart { display: grid; grid-template-columns: 1fr 360px; gap: 22px; align-items: start; }
.kb-cart-list { background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; }
.kb-cart-item { display: grid; grid-template-columns: 80px 1fr auto auto auto; gap: 14px; padding: 14px; border-bottom: 1px solid var(--kb-line); align-items: center; }
.kb-cart-item:last-child { border-bottom: 0; }
.kb-cart-item .kb-prod-img { width: 80px; height: 80px; aspect-ratio: 1; }
.kb-cart-item h5 { margin: 0; font-size: 13.5px; font-weight: 600; line-height: 1.3; }
.kb-cart-item .kb-cart-meta { font-family: var(--kb-font-mono); font-size: 10.5px; color: var(--kb-muted); text-transform: uppercase; }
.kb-cart-item .kb-cart-line { font-family: var(--kb-font-display); font-size: 16px; font-weight: 700; min-width: 80px; text-align: right; }
.kb-cart-item .kb-cart-rm { background: transparent; border: 0; color: var(--kb-mute2); font-size: 11px; cursor: pointer; }
.kb-cart-item .kb-cart-rm:hover { color: var(--kb-danger); }

.kb-summary { background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; padding: 18px; display: flex; flex-direction: column; gap: 12px; position: sticky; top: 110px; }
.kb-summary h4 { margin: 0; font-family: var(--kb-font-display); font-size: 17px; font-weight: 700; }
.kb-summary-row { display: flex; justify-content: space-between; font-size: 13px; }
.kb-summary-row.tot { font-family: var(--kb-font-display); font-size: 22px; font-weight: 800; padding-top: 10px; border-top: 1px solid var(--kb-line); }
.kb-summary-row .saved { color: var(--kb-success); font-weight: 600; }

/* [LINEA-OUTLET · BLOQUE 13] Rosa suave → azul suave pasa a rosa suave → hueso.
   El texto de dentro (.kb-progress-msg, --kb-charcoal) da 16,07:1 sobre #FBEDEC
   y 16,24:1 sobre #F4F1EB ✓. */
.kb-progress { background: linear-gradient(135deg, var(--kb-pink-soft), var(--kb-cream)); border-radius: 8px; padding: 12px; border: 1px dashed var(--kb-line-strong); }
.kb-progress .kb-progress-bar { height: 6px; background: var(--kb-line); border-radius: 999px; overflow: hidden; margin-top: 8px; }
.kb-progress .kb-progress-bar > div { height: 100%; background: linear-gradient(90deg, var(--kb-orange), var(--kb-yellow)); border-radius: 999px; transition: width .35s ease; }
.kb-progress .kb-progress-msg { font-size: 12px; color: var(--kb-charcoal); }
.kb-progress .kb-progress-msg b { color: var(--kb-orange-deep); }

/* Login modal */
.kb-modal-bg { position: fixed; inset: 0; background: rgba(21,17,11,0.6); backdrop-filter: blur(4px); z-index: 100; display: flex; align-items: center; justify-content: center; padding: 22px; animation: kbFade .2s; }
@keyframes kbFade { from {opacity:0;} to {opacity:1;} }
.kb-modal { background: #fff; border-radius: 14px; max-width: 880px; width: 100%; overflow: hidden; box-shadow: var(--kb-shadow-lg); display: grid; grid-template-columns: 1fr 1fr; max-height: 90vh; }
.kb-modal-side { background: var(--kb-ink); color: #fff; padding: 30px; display: flex; flex-direction: column; gap: 18px; position: relative; overflow: hidden; }
.kb-modal-side::after { content: ""; position: absolute; right: -80px; bottom: -80px; width: 240px; height: 240px; border-radius: 50%; background: radial-gradient(circle, var(--kb-orange), transparent 70%); }
.kb-modal-side h3 { font-family: var(--kb-font-display); font-size: 28px; font-weight: 800; margin: 0; line-height: 1.05; letter-spacing: -0.01em; }
.kb-modal-side h3 em { color: var(--kb-yellow); font-style: normal; }
.kb-modal-side ul { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 10px; font-size: 13px; color: rgba(255,255,255,0.85); }
.kb-modal-side li::before { content: "→"; color: var(--kb-orange); margin-right: 8px; font-weight: 700; }
.kb-modal-body { padding: 30px; display: flex; flex-direction: column; gap: 14px; overflow: auto; }
.kb-modal-tabs { display: flex; gap: 0; border-bottom: 1px solid var(--kb-line); }
.kb-modal-tab { flex: 1; padding: 10px; font-size: 13px; font-weight: 600; color: var(--kb-muted); border: 0; background: transparent; cursor: pointer; border-bottom: 2px solid transparent; }
.kb-modal-tab.on { color: var(--kb-orange-deep); border-bottom-color: var(--kb-orange); }
.kb-modal-close { position: absolute; top: 14px; right: 14px; background: rgba(255,255,255,0.1); border: 0; color: #fff; width: 30px; height: 30px; border-radius: 50%; cursor: pointer; font-size: 16px; z-index: 2; }
.kb-modal-close:hover { background: rgba(255,255,255,0.2); }

/* Dashboard */
/* [ADMIN-PEDIDOS-SCROLL] "1fr" es minmax(auto, 1fr): el AUTO deja que el mínimo
   del contenido (ej. la tabla de Pedidos con white-space:nowrap) suba y
   ensanche esta columna en vez de desbordarse dentro de ella — mismo error ya
   documentado y corregido para .kb-side (línea ~3668) y .kb-vol-track (línea
   ~919), pero esa corrección solo vivía dentro de @media (max-width:900px).
   A ancho de escritorio la columna seguía creciendo sin límite y, como
   html/body/.kb-main llevan overflow-x:clip, el sobrante se recortaba
   invisible — cualquier overflow-x:auto puesto DENTRO de esta columna (como
   el de la tabla de Pedidos) nunca llegaba a activarse porque nada desbordaba
   la propia columna, ya había crecido para caber. minmax(0, 1fr) corta esa
   cadena en todos los anchos: el contenido ahora sí se desborda DENTRO de la
   columna, donde el overflow-x:auto de cada tabla puede absorberlo de verdad. */
.kb-dash-grid { display: grid; grid-template-columns: 240px minmax(0, 1fr); gap: 22px; align-items: start; }
/* minmax(0,1fr) arriba solo corrige el TRACK; el ITEM (el <section>/<div> de
   contenido que cada pantalla admin pone como segunda columna) sigue con su
   min-width:auto por defecto — que en Grid vale "min-content de lo que haya
   adentro" salvo que el propio item sea un contenedor de scroll (overflow
   distinto de visible/clip). Como ese wrapper no lo es, seguía empujando la
   columna. min-width:0 aquí es la segunda mitad del mismo arreglo (ver .kb-vol-track). */
.kb-dash-grid > :not(.kb-side) { min-width: 0; }
.kb-side { background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; padding: 6px; position: sticky; top: 110px; max-height: calc(100vh - 130px); overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin; scrollbar-color: var(--kb-line-strong) transparent; }
.kb-side::-webkit-scrollbar { width: 8px; }
.kb-side::-webkit-scrollbar-track { background: transparent; }
/* [LINEA-OUTLET · BLOQUE 13] Los fallback de estos var() eran de la paleta vieja
   (#d8c9c0, #b8a99f): si el token faltara, la barra se pintaría con un color que
   ya no existe. Ahora el respaldo es el valor REAL del token. */
.kb-side::-webkit-scrollbar-thumb { background: var(--kb-line-strong); border-radius: 8px; border: 2px solid #fff; }
.kb-side::-webkit-scrollbar-thumb:hover { background: var(--kb-muted, #5C5A60); }
.kb-side-subhead { margin-top: 14px; padding-bottom: 4px; }
.kb-side-subhead:first-of-type { margin-top: 4px; }
/* [MENU-TECLADO] Cada opción del menú es un <button type="button"> de verdad
   —teclado, Enter/Espacio y lector de pantalla—, no un div con onClick. Mismo
   patrón que .kb-side-grp de aquí abajo: se apaga el cromo del botón (borde 3D,
   fondo ButtonFace, fuente del sistema, texto centrado) para que el menú se vea
   EXACTAMENTE igual que antes.
   width: 100% no es decorativo: un <button> con width:auto se encoge a su
   contenido aunque sea display:flex, y en la columna de escritorio el fondo del
   ítem activo dejaría de llegar al borde de la barra. En la tira móvil se
   devuelve a `auto` (bloque CONV-4C del final). */
.kb-side-item { display: flex; align-items: center; gap: 10px; width: 100%; padding: 10px 12px; border: 0; border-radius: 6px; background: transparent; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 500; text-align: left; color: var(--kb-charcoal); }
.kb-side-item:hover { background: var(--kb-cream); }
.kb-side-item.on { background: var(--kb-ink); color: #fff; }
.kb-side-item.on .kb-icon { color: var(--kb-yellow); }
.kb-side-item .kb-icon { width: 18px; opacity: 0.7; }
.kb-side-divider { height: 1px; background: var(--kb-line); margin: 6px 8px; }
.kb-side-head { padding: 8px 12px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--kb-muted); }

/* ─── [ADM-MENU-SOL] Menú admin: accesos fijos, buscador y grupos plegables ───
   NINGÚN color de aquí usa un token que theme-engine.jsx repinta
   (--kb-orange, --kb-orange-deep, --kb-pink-deep, --kb-theme-*): son rótulos y
   cifras del menú y en noviembre (tema "Buen Fin", acento amarillo) tienen que
   seguir leyéndose. Contrastes calculados sobre el fondo real de la barra
   (#fff) y sobre el rosa del hover del panel pro (#FBEDEC):
     · --kb-muted   #5C5A60 → 6.02:1 (#fff) · 5.46:1 (#FBEDEC)
     · --kb-charcoal #15141A → 17.7:1 (#fff)
     · [BLOQUE 13] --kb-cola-rojo #B3261E sobre --kb-cola-rojo-bg #FDECEA → 5,72:1
       (era #96261F, un rojo suelto más: 7,08:1, pero de nadie)
   Ningún dato baja de 11px. */

/* Los TRES accesos fijos. Separados del resto por una línea, no por color:
   el color de marca es justo el que puede repintarse. */
.kb-side-pinned { padding-bottom: 6px; margin-bottom: 2px; border-bottom: 1px solid var(--kb-line); }

/* Buscador "Ir a módulo…" */
.kb-side-search { padding: 6px 4px 8px; }
.kb-side-search-input {
  width: 100%; box-sizing: border-box;
  padding: 8px 10px; border: 1px solid var(--kb-line-strong); border-radius: 8px;
  background: #fff; font-family: inherit; font-size: 13px; font-weight: 500;
  color: var(--kb-charcoal);
}
.kb-side-search-input::placeholder { color: var(--kb-muted); opacity: 1; }
/* [HALLAZGO DE AUDITORÍA] El anillo de foco va en el rosa de acción, NO en
   --kb-line-strong. Esta regla (0,2,0) le ganaba a la accesible del repo
   `input:focus-visible` (0,1,1, styles.css:402) y dejaba el foco en #D3CEC4
   sobre blanco = 1.40:1 — invisible, y justo en el ÚNICO control que este
   cambio agrega para navegar el panel sin ratón. Con #A32C29 da 5.87:1.
   Se usa :focus-visible para no dibujar el anillo al hacer clic con el mouse. */
.kb-side-search-input:focus-visible { outline: 2px solid #A32C29; outline-offset: 1px; }
/* Grupo al que pertenece cada resultado del buscador (contexto: sin esto, el
   usuario ve una lista plana y no sabe de dónde salió cada módulo). */
.kb-side-hit-grp { flex-shrink: 0; font-size: 11px; font-weight: 600; color: var(--kb-muted); text-transform: none; letter-spacing: 0; }
.kb-side-empty { padding: 10px 12px; font-size: 12px; color: var(--kb-muted); }

/* Cabecera de grupo plegable. Es un <button> de verdad (teclado + lector de
   pantalla con aria-expanded), no un div con onClick. */
.kb-side-grp {
  display: flex; align-items: center; gap: 7px; width: 100%;
  background: transparent; border: 0; cursor: pointer;
  font-family: inherit; font-size: 11px; font-weight: 700;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--kb-muted);
}
.kb-side-grp:hover { color: var(--kb-charcoal); }
.kb-side-grp-chev { font-size: 11px; opacity: 0.7; }

/* Contadores del menú. El neutro (pedidos activos) y el de alerta (stock por
   debajo del mínimo) se distinguen por color Y por fondo, no solo por color. */
.kb-side-badge {
  flex-shrink: 0; font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700;
  padding: 1px 6px; border-radius: 999px; background: var(--kb-cream); color: var(--kb-muted);
}
.kb-side-badge-alert {
  flex-shrink: 0; font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700;
  padding: 1px 6px; border-radius: 999px; background: var(--kb-cola-rojo-bg); color: var(--kb-cola-rojo);
}

.kb-stat-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.kb-stat { background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; padding: 14px; }
.kb-stat .kb-stat-label { font-family: var(--kb-font-mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--kb-muted); }
.kb-stat .kb-stat-val { font-family: var(--kb-font-display); font-size: 26px; font-weight: 700; letter-spacing: -0.01em; margin-top: 4px; }
.kb-stat .kb-stat-delta { font-size: 11.5px; font-weight: 600; margin-top: 4px; }
.kb-stat .kb-stat-delta.up { color: var(--kb-success); }
.kb-stat .kb-stat-delta.down { color: var(--kb-danger); }

.kb-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.kb-table th { text-align: left; padding: 10px 14px; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--kb-muted); border-bottom: 1px solid var(--kb-line); background: var(--kb-cream); }
.kb-table td { padding: 12px 14px; border-bottom: 1px solid var(--kb-line); vertical-align: middle; }
.kb-table tr:last-child td { border-bottom: 0; }
/* [LINEA-OUTLET · BLOQUE 13] El hover de fila era el azul suave heredado; el
   hueso hace el mismo trabajo y es de la casa. */
.kb-table tr:hover td { background: var(--kb-cream); }
.kb-table .num { font-family: var(--kb-font-mono); text-align: right; }

/* Admin tabs (sub-navigation inside cards) */
.kb-tab {
  background: transparent; border: 1px solid transparent;
  padding: 6px 12px; border-radius: 999px;
  font-size: 11.5px; font-weight: 600; cursor: pointer;
  color: var(--kb-charcoal); white-space: nowrap;
  font-family: inherit;
}
.kb-tab:hover { background: var(--kb-cream); }
.kb-tab.on { background: var(--kb-ink); color: var(--kb-yellow); border-color: var(--kb-ink); }

/* Toggle switch */
.kb-toggle { position: relative; display: inline-block; width: 36px; height: 20px; cursor: pointer; }
.kb-toggle input { opacity: 0; width: 0; height: 0; }
.kb-toggle span {
  position: absolute; inset: 0;
  background: var(--kb-line-strong); border-radius: 999px; transition: .2s;
}
.kb-toggle span::before {
  content: ""; position: absolute; left: 2px; top: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: white; transition: .2s;
}
.kb-toggle input:checked + span { background: var(--kb-orange); }
.kb-toggle input:checked + span::before { left: 18px; }

@keyframes kbPulse { 0% { background: var(--kb-yellow-soft); } 100% { background: transparent; } }

/* Track timeline */
.kb-track { display: flex; flex-direction: column; gap: 0; padding: 4px 0; }
.kb-track-step { display: grid; grid-template-columns: 28px 1fr; gap: 14px; padding: 10px 0; position: relative; }
.kb-track-step::before { content: ""; position: absolute; top: 28px; bottom: -10px; left: 13px; width: 2px; background: var(--kb-line); }
.kb-track-step:last-child::before { display: none; }
.kb-track-step .dot { width: 28px; height: 28px; border-radius: 50%; background: var(--kb-cream); border: 2px solid var(--kb-line); display: flex; align-items: center; justify-content: center; font-size: 11px; color: var(--kb-muted); position: relative; z-index: 1; }
.kb-track-step.done .dot { background: var(--kb-success); border-color: var(--kb-success); color: #fff; }
/* [LINEA-OUTLET · BLOQUE 13] El anillo iba en el rgba del magenta viejo; ahora
   en el rojo outlet (rgb 184,50,47), que es el color del propio punto. */
.kb-track-step.now .dot { background: var(--kb-orange); border-color: var(--kb-orange); color: #fff; box-shadow: 0 0 0 6px rgba(184,50,47,0.18); }
.kb-track-step.now ~ .kb-track-step::before { background: var(--kb-line); }
.kb-track-step .ts-title { font-weight: 600; font-size: 13px; }
/* CONV-3B · 10.5px → 11px. Esta línea dejó de ser decorativa: ahora lleva DATO
   real del pedido (la fecha en que se recibió, la paquetería y el número de
   guía), así que le aplica la regla del handoff de que ninguna etiqueta de dato
   baje de 11px. --kb-muted sobre blanco da 6.02:1 y no lo repinta el motor de
   temas. */
.kb-track-step .ts-meta { font-family: var(--kb-font-mono); font-size: 11px; color: var(--kb-muted); text-transform: uppercase; letter-spacing: 0.06em; margin-top: 2px; }

/* Bar chart */
.kb-bars { display: grid; grid-template-columns: repeat(12, 1fr); align-items: end; gap: 6px; height: 180px; padding: 8px 0; }
.kb-bar { display: flex; flex-direction: column-reverse; gap: 2px; height: 100%; }
.kb-bar .seg { border-radius: 3px 3px 0 0; }
.kb-bar .seg.det { background: var(--kb-orange); }
.kb-bar .seg.may { background: var(--kb-yellow); }
.kb-bar .seg.dis { background: var(--kb-b2b); }
.kb-bar small { font-family: var(--kb-font-mono); font-size: 9.5px; color: var(--kb-muted); text-align: center; margin-top: 4px; }

/* B2B banner / hero */
/* [LINEA-OUTLET · BLOQUE 13] Este banner habla de MAYOREO, así que su color es el
   verde de nivel — y además tenía un agujero de legibilidad: el degradado subía
   al azul fuerte #1F5BA8 en el 60%, justo por debajo de un titular y un párrafo
   escritos en --kb-ink (2,72:1 en esa zona, ilegible). Ahora el fondo se queda
   claro de lado a lado y la tinta se lee en todo el ancho:
     · --kb-ink sobre --kb-b2b-soft #E3F1EE → 15,77:1 ✓
     · --kb-ink sobre --kb-pink-soft #FBEDEC → 16,07:1 ✓
     · el "em" del titular, --kb-orange-deep #8E2522 sobre #E3F1EE → 7,42:1 ✓ */
.kb-b2b-banner {
  background: linear-gradient(135deg, var(--kb-b2b-soft) 0%, var(--kb-pink-soft) 100%);
  color: var(--kb-ink); border-radius: 14px; padding: 30px;
  border: 1px solid var(--kb-b2b);
  display: grid; grid-template-columns: 1fr auto; gap: 24px; align-items: center;
  margin-top: 30px; position: relative; overflow: hidden;
}
.kb-b2b-banner::after {
  content: ""; position: absolute; right: -60px; top: -60px;
  width: 300px; height: 300px;
  background-image: repeating-linear-gradient(45deg, transparent 0 12px, rgba(255,255,255,0.05) 12px 13px);
}
.kb-b2b-banner h2 { color: var(--kb-ink); font-family: var(--kb-font-display); font-size: 26px; font-weight: 800; margin: 0; letter-spacing: -0.01em; }
.kb-b2b-banner h2 em { color: var(--kb-orange-deep); font-style: normal; }
.kb-b2b-banner p { color: var(--kb-charcoal); font-size: 13.5px; margin: 8px 0 0; max-width: 60ch; }
.kb-b2b-banner .badges { display: flex; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.kb-b2b-banner .badge { background: rgba(255,255,255,0.7); border: 1px solid rgba(15,14,11,0.12); padding: 4px 10px; border-radius: 999px; font-size: 11px; font-family: var(--kb-font-mono); letter-spacing: 0.04em; color: var(--kb-charcoal); }

/* AI Assistant FAB + panel */
.kb-ai-fab {
  position: fixed; bottom: 22px; right: 22px; z-index: 90;
  height: 52px; padding: 0 18px 0 14px; border-radius: 999px; border: 0;
  /* [LINEA-OUTLET · BLOQUE 13] El degradado cerraba en #2a2418, un pardo suelto.
     El botón es la pastilla oscura de la casa: carbón liso, con el dorado encima
     (9,18:1 ✓). */
  background: var(--kb-ink);
  color: var(--kb-yellow); display: flex; align-items: center; gap: 10px;
  box-shadow: 0 14px 40px rgba(15,14,11,0.35), 0 0 0 1px var(--kb-orange) inset;
  font-family: var(--kb-font-ui); font-weight: 600; font-size: 13.5px;
  cursor: pointer; transition: transform .15s, box-shadow .2s;
}
.kb-ai-fab:hover { transform: translateY(-2px); }
.kb-ai-fab.open { background: var(--kb-orange); color: #fff; }
.kb-ai-fab svg { animation: kbSlowSpin 8s linear infinite; }
@keyframes kbSlowSpin { to { transform: rotate(360deg); } }
.kb-ai-fab-label { letter-spacing: 0.02em; }
.kb-ai-fab.pulse::after {
  content: ""; position: absolute; inset: -2px; border-radius: 999px;
  border: 2px solid var(--kb-orange); animation: kbPulse 1.8s ease-out infinite;
  pointer-events: none;
}
@keyframes kbPulse { 0% { opacity: 0.8; transform: scale(1); } 100% { opacity: 0; transform: scale(1.25); } }

.kb-ai-panel {
  position: fixed; bottom: 88px; right: 22px; z-index: 91;
  width: 380px; max-width: calc(100vw - 44px); height: 540px; max-height: calc(100vh - 120px);
  background: #fff; border-radius: 16px; border: 1px solid var(--kb-line);
  box-shadow: 0 30px 80px rgba(15,14,11,0.25);
  display: flex; flex-direction: column; overflow: hidden;
  animation: kbAiIn .25s cubic-bezier(.2,.8,.2,1);
}
@keyframes kbAiIn { from { opacity: 0; transform: translateY(12px) scale(0.98);} to { opacity: 1; transform: translateY(0) scale(1);} }
.kb-ai-head { padding: 14px 16px; border-bottom: 1px solid var(--kb-line); display: flex; align-items: center; gap: 10px;
  background: linear-gradient(180deg, var(--kb-yellow-soft), #fff); }
.kb-ai-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--kb-ink); color: var(--kb-yellow); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
/* [LINEA-OUTLET · BLOQUE 13] El latido salía en rgba(30,138,74) —el verde
   heredado #1E8A4A— mientras el punto ya se pintaba con --kb-success. Ahora las
   dos cosas son el mismo verde de nivel (#0E6B5E = rgb 14,107,94). */
.kb-ai-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--kb-success); display: inline-block; box-shadow: 0 0 0 0 rgba(14,107,94,0.6); animation: kbDot 1.6s ease-out infinite; }
@keyframes kbDot { 0% { box-shadow: 0 0 0 0 rgba(14,107,94,0.6);} 100% { box-shadow: 0 0 0 8px rgba(14,107,94,0);} }
.kb-ai-close { width: 28px; height: 28px; border-radius: 50%; border: 0; background: var(--kb-cream); color: var(--kb-charcoal); font-size: 16px; cursor: pointer; }
.kb-ai-close:hover { background: var(--kb-line); }
.kb-ai-body { flex: 1; overflow-y: auto; padding: 14px; display: flex; flex-direction: column; gap: 10px; }
.kb-ai-msg { display: flex; }
.kb-ai-msg.user { justify-content: flex-end; }
.kb-ai-bubble { max-width: 84%; padding: 10px 12px; border-radius: 12px; font-size: 13px; line-height: 1.45; word-break: break-word; }
.kb-ai-msg.assistant .kb-ai-bubble { background: var(--kb-cream); border-bottom-left-radius: 3px; color: var(--kb-ink); }
.kb-ai-msg.user .kb-ai-bubble { background: var(--kb-ink); color: var(--kb-yellow); border-bottom-right-radius: 3px; }
.kb-ai-bubble b { color: var(--kb-orange-deep); }
.kb-ai-msg.user .kb-ai-bubble b { color: var(--kb-yellow); }
.kb-ai-thinking { display: inline-flex; gap: 4px; padding: 14px 14px; }
.kb-ai-thinking span { width: 7px; height: 7px; border-radius: 50%; background: var(--kb-orange); animation: kbBlink 1.4s ease-in-out infinite; }
.kb-ai-thinking span:nth-child(2) { animation-delay: 0.2s; }
.kb-ai-thinking span:nth-child(3) { animation-delay: 0.4s; }
@keyframes kbBlink { 0%, 80%, 100% { opacity: 0.3; transform: scale(0.85);} 40% { opacity: 1; transform: scale(1);} }
.kb-ai-quick { padding: 0 14px 10px; display: flex; flex-direction: column; gap: 6px; }
.kb-ai-chip { background: #fff; border: 1px solid var(--kb-line); border-radius: 999px; padding: 8px 14px; font-size: 12px; text-align: left; cursor: pointer; color: var(--kb-charcoal); transition: border-color .12s; }
.kb-ai-chip:hover { border-color: var(--kb-orange); }
.kb-ai-input { display: flex; gap: 8px; padding: 12px; border-top: 1px solid var(--kb-line); background: #fff; }
.kb-ai-input input { flex: 1; height: 38px; padding: 0 14px; border: 1px solid var(--kb-line-strong); border-radius: 999px; outline: none; font-size: 13px; }
.kb-ai-input input:focus { border-color: var(--kb-orange); }
.kb-ai-input button { width: 38px; height: 38px; border-radius: 50%; border: 0; background: var(--kb-orange); color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; }
.kb-ai-input button:disabled { opacity: 0.4; cursor: not-allowed; }

/* SEO Lab */
/* [LINEA-OUTLET · BLOQUE 13] Panel de trabajo, no bloque de marca: rosa suave →
   hueso, y el borde con la línea fuerte de la paleta en vez del azul heredado. */
.kb-seo-lab { background: linear-gradient(135deg, var(--kb-pink-soft) 0%, var(--kb-cream) 100%); border-color: var(--kb-line-strong); }
.kb-seo-lab .head { background: rgba(255,255,255,0.6); backdrop-filter: blur(8px); }
.kb-seo-score { display: inline-flex; align-items: center; justify-content: center; position: relative; width: 34px; height: 34px; color: var(--kb-success); }
.kb-seo-score[data-score="ok"] { color: var(--kb-orange-text); }
.kb-seo-score[data-score="bad"] { color: var(--kb-danger); }
.kb-seo-score span { position: absolute; font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700; color: var(--kb-charcoal); }
.kb-seo-tab { background: transparent; border: 1px solid var(--kb-line); border-radius: 4px; padding: 4px 8px; font-size: 10.5px; cursor: pointer; color: var(--kb-muted); font-family: var(--kb-font-mono); letter-spacing: 0.04em; }
.kb-seo-tab.on { background: var(--kb-ink); color: var(--kb-yellow); border-color: var(--kb-ink); }
.kb-seo-google { display: grid; grid-template-columns: 1fr; gap: 14px; }
.kb-seo-google-card { background: #fff; border-radius: 10px; padding: 14px; border: 1px solid var(--kb-line); }
.kb-seo-favicon { width: 28px; height: 28px; border-radius: 50%; background: var(--kb-orange); color: #fff; display: flex; align-items: center; justify-content: center; font-weight: 700; font-family: var(--kb-font-display); font-size: 14px; }
/* [LINEA-OUTLET · BLOQUE 13] Estos DOS colores no se tocan y no son un descuido:
   son el azul de enlace y el gris de descripción de Google. Este bloque simula un
   resultado de búsqueda para que el dueño vea cómo se verá su ficha ahí fuera —
   repintarlo con la paleta convertiría la simulación en una mentira. Mismo
   criterio que los distintivos de pago (.kb-pm-badge-*). Legibles los dos:
   #1a0dab sobre blanco 12,43:1 ✓ · #4d5156 sobre blanco 7,99:1 ✓. */
.kb-seo-google-title { color: #1a0dab; font-size: 18px; line-height: 1.3; margin-top: 6px; cursor: pointer; }
.kb-seo-google-title:hover { text-decoration: underline; }
.kb-seo-google-desc { color: #4d5156; font-size: 13.5px; line-height: 1.45; margin-top: 4px; }
.kb-seo-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.kb-seo-fields .kb-seo-field:nth-child(2),
.kb-seo-fields .kb-seo-field:nth-child(4) { grid-column: span 2; }
.kb-seo-field label { display: flex; justify-content: space-between; font-size: 11px; font-weight: 600; color: var(--kb-charcoal); margin-bottom: 4px; font-family: var(--kb-font-mono); letter-spacing: 0.04em; text-transform: uppercase; }
.kb-seo-field label span { font-weight: 500; opacity: 0.7; }
.kb-seo-field label span.ok { color: var(--kb-success); }
.kb-seo-field label span.warn { color: var(--kb-orange-deep); }
.kb-seo-field label span.bad { color: var(--kb-danger); }
.kb-seo-slug { display: flex; align-items: center; background: #fff; border: 1px solid var(--kb-line-strong); border-radius: 6px; padding: 0 10px; height: 36px; font-family: var(--kb-font-mono); font-size: 12px; }
.kb-seo-slug span { color: var(--kb-muted); }
.kb-seo-slug input { flex: 1; border: 0; outline: none; font-family: inherit; font-size: inherit; padding: 0 4px; background: transparent; color: var(--kb-orange-deep); }
.kb-seo-code { background: var(--kb-ink); color: var(--kb-ink-fg2); padding: 14px; border-radius: 8px; font-family: var(--kb-font-mono); font-size: 11px; line-height: 1.55; max-height: 320px; overflow: auto; margin: 0; }
/* [LINEA-OUTLET · BLOQUE 13] Era un verde lima de resaltador de sintaxis, ajeno
   a la marca. El dorado sobre el carbón del bloque de código da 9,18:1 ✓. */
.kb-seo-code .str { color: var(--kb-yellow); } /* sin uso hoy; queda para cuando lo haya */
.kb-seo-og-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.kb-seo-og-card { background: #fff; border-radius: 8px; overflow: hidden; border: 1px solid var(--kb-line); }
.kb-seo-og-img { aspect-ratio: 1.91/1; display: flex; align-items: center; justify-content: center; }
.kb-seo-og-meta { padding: 10px 12px; }

/* Performance panel */
.kb-perf-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.kb-vital { background: var(--kb-cream); border-radius: 8px; padding: 12px; border-left: 3px solid var(--kb-success); }
.kb-vital.ok { border-left-color: var(--kb-orange); }
.kb-vital.bad { border-left-color: var(--kb-danger); }
.kb-vital-label { font-family: var(--kb-font-mono); font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; color: var(--kb-muted); }
.kb-vital-val { font-family: var(--kb-font-display); font-size: 22px; font-weight: 800; color: var(--kb-ink); }
.kb-vital-meta { font-size: 10.5px; color: var(--kb-muted); margin-top: 2px; }
.kb-status-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.kb-status-dot.good { background: var(--kb-success); box-shadow: 0 0 0 0 rgba(14,107,94,0.6); animation: kbDot 2s ease-out infinite; }
.kb-status-dot.ok { background: var(--kb-orange); }
.kb-indexed-row { display: flex; align-items: center; gap: 10px; font-size: 13px; }
.kb-perf-row { display: flex; justify-content: space-between; padding: 8px 0; border-bottom: 1px dashed var(--kb-line); font-size: 13px; }
.kb-perf-row:last-child { border-bottom: 0; }
/* [LINEA-OUTLET · BLOQUE 13] Una sugerencia es algo DESTACADO, no "información
   azul": pasa al dorado suave con borde dorado, que es como esta hoja marca lo
   destacado en todas partes. El texto de dentro es --kb-ink sobre #F6EFE0:
   15,99:1 ✓. */
.kb-ai-suggest { display: flex; align-items: center; gap: 10px; padding: 10px; border: 1px solid var(--kb-yellow); background: var(--kb-yellow-soft); border-radius: 8px; }
.kb-ai-suggest-mark { width: 28px; height: 28px; border-radius: 50%; background: var(--kb-orange); color: #fff; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }

/* AI sparkle indicator */
.kb-ai-sparkle-tag {
  display: inline-flex; align-items: center; gap: 4px;
  /* [LINEA-OUTLET · BLOQUE 13] Rosa → azul pasa a rosa → dorado suave: los dos
     extremos de la marca. El texto (--kb-charcoal) da 14,07:1 sobre #F5DCDA y
     15,99:1 sobre #F6EFE0 ✓. */
  background: linear-gradient(135deg, var(--kb-pink) 0%, var(--kb-yellow-soft) 100%);
  color: var(--kb-charcoal); padding: 3px 8px; border-radius: 999px;
  font-family: var(--kb-font-mono); font-size: 10px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase;
  border: 1px solid rgba(15,14,11,0.08);
}
.kb-ai-sparkle-tag::before { content: "✨"; font-size: 11px; }

/* Misc visual */
.kb-spinner { width: 14px; height: 14px; border: 2px solid var(--kb-yellow); border-top-color: transparent; border-radius: 50%; animation: kbSpin .8s linear infinite; }
@keyframes kbSpin { to { transform: rotate(360deg);} }

@media (max-width: 1100px) {
  .kb-catalog { grid-template-columns: 1fr; }
  .kb-filters { position: static; }
/* [BLOQUE 27] lo decide --kb-card-min */
  .kb-cart { grid-template-columns: 1fr; }
  .kb-pdp { grid-template-columns: minmax(0, 1fr); }
  .kb-dash-grid { grid-template-columns: 1fr; }
  .kb-cats { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 700px) {
/* [BLOQUE 27] lo decide --kb-card-min */
  .kb-stat-grid { grid-template-columns: repeat(2, 1fr); }
  .kb-cats { grid-template-columns: repeat(3, 1fr); }
  .kb-hero { grid-template-columns: 1fr; }
  .kb-hero .kb-hero-art { min-height: 180px; }
  .kb-modal { grid-template-columns: 1fr; }
  .kb-modal-side { display: none; }
}

/* ─── Mobile bottom navigation (PWA app-like) ─── */
.kb-bottomnav {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 80;
  background: rgba(255,255,255,0.94);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-top: 1px solid var(--kb-line);
  padding: 4px 4px calc(4px + env(safe-area-inset-bottom));
  grid-template-columns: repeat(5, 1fr);
  gap: 2px;
}
.kb-bottomnav button {
  background: transparent; border: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 7px 4px 5px;
  color: var(--kb-muted); font-size: 10px; font-weight: 500; border-radius: 10px;
  font-family: inherit;
}
.kb-bottomnav button.on { color: var(--kb-ink); background: var(--kb-yellow-soft); }
.kb-bottomnav button.on svg { color: var(--kb-orange-deep); }

@media (max-width: 900px) {
  .kb-bottomnav { display: grid; }
  body { padding-bottom: 68px; }
  .kb-topbar { display: none; }
  /* [LINEA-OUTLET · BLOQUE 18] En el teléfono la tira de navegación NO se pinta: sus
     destinos viven en el cajón de la hamburguesa, que los lleva todos y con sitio para
     leerlos. Antes se "arreglaba" con scroll horizontal y el cliente veía una barra de
     arrastre bajo el buscador, con Distribuidores y Blog cortados fuera de pantalla — tres
     navegaciones a la vez (barra de abajo, cajón y tira) para una pantalla de 390 px. */
  .kb-nav { display: none; }
  /* Header móvil: logo centrado arriba, search en su propia fila */
  .kb-header-row {
    display: grid !important;
    /* "cart" más ancha que "acct" (92px vs 40px): .kb-header-actions le mete
       DOS botones (cuenta + carrito) en esa columna — con 40px cada uno
       quedaba en 17px de ancho real, muy por debajo del mínimo táctil
       (hallazgo real de Lighthouse). */
    grid-template-columns: 40px 1fr 92px !important;
    grid-template-areas: "acct logo cart" "search search search" !important;
    align-items: center; padding: 10px 14px; gap: 10px;
  }
  .kb-header-row .kb-logo { grid-area: logo; justify-self: center; }
  .kb-header-row .kb-logo .kb-logo-img { height: 40px !important; }
  /* [LINEA-OUTLET · BLOQUE 18] El margen lateral es del escritorio; en la rejilla del
     encabezado móvil se suma al ancho completo de la columna y la píldora se sale de la
     pantalla (medido a 375 px: iba de 32 a 379, con su esquina derecha cortada). Va con el
     selector de la vitrina incluido porque body.lo-vitrina .kb-search gana por especificidad. */
  .kb-header-row .kb-search,
  body.lo-vitrina .kb-header-row .kb-search { grid-area: search; margin: 0; width: auto; max-width: 100%; }
  /* [BLOQUE 22] 44 px es el mínimo táctil de la casa. En el escritorio el campo mide 40. */
  .kb-search { height: 44px; }
  .kb-header-row .kb-header-actions { grid-area: cart; display: flex; justify-content: flex-end; gap: 6px; }
  .kb-header-actions .kb-iconbtn .lbl-stack { display: none; }
  .kb-header-actions .kb-iconbtn { padding: 6px; min-width: 40px; justify-content: center; }
  .kb-main { padding: 14px; }
  /* Footer responsive: 2 columnas en tablet/móvil, texto sin cortar */
  .kb-footer { padding-left: 16px; padding-right: 16px; }
  .kb-footer-grid { grid-template-columns: 1fr 1fr !important; gap: 22px !important; }
  .kb-footer-grid > *:first-child { grid-column: 1 / -1; }
  .kb-h1 { font-size: 28px !important; }
  .kb-h2 { font-size: 22px !important; }
  /* [BLOQUE 27] En el teléfono, dos fichas por fila: se redefine el NÚMERO, no la rejilla.
     Va dentro de :root y no suelto en el @media: una declaración de variable fuera de una regla
     es CSS inválido y el navegador se la come sin decir nada. */
  :root { --kb-card-min: 150px; }
  .kb-product-grid { gap: 10px !important; }
  .kb-cats { grid-template-columns: repeat(2, 1fr) !important; }
  .kb-hero { grid-template-columns: 1fr; min-height: auto; }
  .kb-hero .kb-hero-art { display: none; }
  .kb-hero .kb-hero-content { padding: 24px; }
  .kb-hero .kb-hero-pulido { padding: 0; }
  .kb-hero-pulido .kb-hero-inner { padding: 24px; }
  .kb-split { grid-template-columns: 1fr; }
  .kb-cart { grid-template-columns: 1fr; }
  .kb-pdp { grid-template-columns: minmax(0, 1fr); }
  .kb-dash-grid { grid-template-columns: 1fr; }
  .kb-side { position: static; overflow-x: auto; overflow-y: visible; max-height: none; display: flex; flex-direction: row; gap: 4px; padding: 6px; }
  .kb-side-item { white-space: nowrap; }
  .kb-side-divider, .kb-side-head { display: none; }
  .kb-ai-fab { bottom: 76px; right: 14px; }
  .kb-ai-panel { bottom: 140px; right: 14px; width: calc(100vw - 28px); height: min(70vh, 540px); }
}


/* ─── Calculadora ruta · responsiva ─── */
.kb-route-calc-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
.kb-route-calc-chart-row { display: grid; grid-template-columns: 60px 1fr; gap: 10px; align-items: stretch; height: 240px; }
.kb-route-calc-bars { display: grid; grid-template-columns: repeat(12, 1fr); gap: 6px; align-items: stretch; }
.kb-route-calc-metas { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-top: 18px; }
@media (max-width: 768px) {
  .kb-route-calc-grid { grid-template-columns: 1fr; gap: 12px; }
  .kb-route-calc-chart-row { height: 180px; }
  .kb-route-calc-bars { gap: 3px; }
  .kb-route-calc-metas { grid-template-columns: 1fr; gap: 10px; }
}
@media (max-width: 480px) {
  .kb-route-calc-chart-row { grid-template-columns: 40px 1fr; height: 160px; }
}


/* ─── Calculadora Home · Estilo elegante k-botanas.com ─── */
.kb-calc-pkg-cards { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
.kb-calc-pkg-card {
  position: relative; padding: 20px 18px; border-radius: 16px; cursor: pointer; text-align: left;
  background: white; border: 1px solid var(--kb-line);
  transition: transform .2s, box-shadow .2s, border-color .2s;
  box-shadow: 0 1px 3px rgba(15,14,11,.04);
  overflow: hidden;
}
.kb-calc-pkg-card::before {
  content: ""; position: absolute; top: 0; left: 0; right: 0; height: 4px;
  background: var(--card-color, var(--kb-line));
  opacity: .35; transition: opacity .2s;
}
.kb-calc-pkg-card:hover { transform: translateY(-3px); box-shadow: 0 12px 30px rgba(15,14,11,.10); }
.kb-calc-pkg-card:hover::before { opacity: 1; }
.kb-calc-pkg-card.active {
  border-color: var(--card-color);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--card-color) 18%, transparent), 0 12px 30px rgba(15,14,11,.10);
  background: linear-gradient(180deg, color-mix(in srgb, var(--card-color) 8%, white) 0%, white 60%);
}
.kb-calc-pkg-card.active::before { opacity: 1; height: 6px; }

.kb-calc-pkg-badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-family: var(--kb-font-mono); font-size: 9.5px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  background: color-mix(in srgb, var(--card-color) 12%, white);
  color: var(--card-color); padding: 3px 8px; border-radius: 99px;
}
.kb-calc-pkg-name {
  font-family: var(--kb-font-display); font-size: 18px; font-weight: 800;
  margin: 10px 0 4px; letter-spacing: -.01em; line-height: 1.1;
}
.kb-calc-pkg-price {
  font-family: var(--kb-font-display); font-size: 28px; font-weight: 900;
  color: var(--kb-ink); letter-spacing: -.02em;
}
.kb-calc-pkg-price small { font-size: 12px; color: var(--kb-muted); font-weight: 600; }
.kb-calc-pkg-detail {
  font-family: var(--kb-font-mono); font-size: 10.5px;
  color: var(--kb-muted); margin-top: 4px;
}
.kb-calc-pkg-divider {
  height: 1px; background: var(--kb-line); margin: 12px 0;
}
.kb-calc-pkg-stat { display: flex; justify-content: space-between; font-size: 11.5px; margin-top: 4px; }
.kb-calc-pkg-stat b { color: var(--kb-success); font-family: var(--kb-font-display); font-size: 13px; }

/* Proyección container */
.kb-calc-projection {
  margin-top: 18px; padding: 24px; border-radius: 18px;
  background:
    radial-gradient(circle at 0% 0%, color-mix(in srgb, var(--card-color, var(--kb-pink-deep)) 8%, transparent), transparent 50%),
    radial-gradient(circle at 100% 100%, color-mix(in srgb, var(--card-color, var(--kb-pink-deep)) 6%, transparent), transparent 50%),
    linear-gradient(135deg, var(--kb-yellow-soft), var(--kb-pink-soft));
  border: 1px solid color-mix(in srgb, var(--card-color, var(--kb-pink-deep)) 18%, white);
}
.kb-calc-proj-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; margin-bottom: 16px; flex-wrap: wrap; }
.kb-calc-proj-eyebrow {
  font-family: var(--kb-font-mono); font-size: 10.5px; letter-spacing: .12em;
  color: var(--card-color, var(--kb-pink-deep)); font-weight: 700;
}
.kb-calc-proj-title { font-family: var(--kb-font-display); font-size: 22px; font-weight: 800; margin: 6px 0 0; }
.kb-calc-proj-cta {
  background: var(--kb-ink); color: var(--kb-yellow);
  padding: 12px 18px; border-radius: 99px; border: 0;
  font-weight: 700; font-size: 13px; cursor: pointer;
  box-shadow: 0 4px 14px rgba(15,14,11,.18);
  transition: transform .15s;
}
.kb-calc-proj-cta:hover { transform: translateY(-2px); }

.kb-calc-route { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.kb-calc-route-card {
  background: white; padding: 18px; border-radius: 14px;
  box-shadow: 0 1px 3px rgba(15,14,11,.04);
}
.kb-calc-route-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
.kb-calc-route-head h4 {
  font-family: var(--kb-font-display); font-size: 15px; font-weight: 800; margin: 0;
  display: flex; align-items: center; gap: 8px;
}
.kb-calc-route-head h4 .num {
  background: var(--kb-cream); padding: 3px 8px; border-radius: 6px;
  font-family: var(--kb-font-mono); font-size: 10px; color: var(--kb-muted);
}
.kb-calc-route-tag {
  font-family: var(--kb-font-mono); font-size: 9.5px; letter-spacing: .08em;
  border: 1px solid currentColor; padding: 3px 10px; border-radius: 99px; font-weight: 600;
}

/* [LINEA-OUTLET · BLOQUE 13] La cinta iba en dos verdes propios (#0F3D2A y
   #1A5C42) que no son el de la casa, y encima llevaba rótulo y cifra en dorado.
   Medido, ESA era la combinación imposible: #D9B26A sobre el verde de nivel
   #0E6B5E da 3,21:1, por debajo del 4,5:1 que pide el rótulo de 10,5px. Aquí no
   se habla de nivel sino de COSTO, así que la cinta usa la pastilla oscura de la
   casa (la misma de .kb-roi-out y .kb-pos-total): carbón con dorado encima →
   9,18:1 ✓, y el filete lateral dorado se queda. */
.kb-calc-cost-strip {
  background: var(--kb-ink);
  color: white; padding: 14px 16px; border-radius: 10px;
  margin-bottom: 16px;
  display: flex; justify-content: space-between; align-items: center;
  border-left: 4px solid var(--kb-yellow);
  box-shadow: 0 2px 8px rgba(0,0,0,.12);
}
.kb-calc-cost-strip .label {
  color: var(--kb-yellow); font-family: var(--kb-font-mono); font-size: 10.5px;
  letter-spacing: .08em; font-weight: 700;
}
.kb-calc-cost-strip .value {
  font-family: var(--kb-font-display); font-size: 22px; font-weight: 900;
  color: var(--kb-yellow); letter-spacing: -.01em;
}

.kb-calc-slider-row { margin-bottom: 14px; }
.kb-calc-slider-row .slider-head {
  display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;
}
.kb-calc-slider-row label { font-size: 12px; font-weight: 600; color: var(--kb-charcoal); }
.kb-calc-slider-row .pill {
  background: var(--kb-cream); padding: 4px 12px; border-radius: 99px;
  font-family: var(--kb-font-display); font-size: 14px; font-weight: 800;
  color: var(--kb-success); min-width: 60px; text-align: center;
}
.kb-calc-slider-row input[type=range] {
  width: 100%; accent-color: var(--kb-success); height: 6px;
}
.kb-calc-slider-row .range-labels {
  display: flex; justify-content: space-between;
  font-family: var(--kb-font-mono); font-size: 9.5px; color: var(--kb-muted); margin-top: 2px;
}

/* [LINEA-OUTLET · BLOQUE 13] Cuatro ámbares en cuatro renglones (#FFFBEC,
   #FFF7D6, #C9A227 y #8C6F12) para decir "destacado", que es exactamente lo que
   ya dice el dorado de la paleta. Con los de la casa: rótulo #8A5300 sobre
   --kb-yellow-soft #F6EFE0 → 5,53:1 ✓. */
.kb-calc-route-summary {
  background: var(--kb-yellow-soft);
  border-left: 4px solid var(--kb-yellow); padding: 12px 14px; border-radius: 8px;
  margin-top: 8px;
}
.kb-calc-route-summary .tag {
  font-family: var(--kb-font-mono); font-size: 9.5px; color: #8A5300;
  font-weight: 700; letter-spacing: .08em;
}
.kb-calc-route-summary .text {
  font-family: var(--kb-font-mono); font-size: 11.5px; color: var(--kb-charcoal); margin-top: 4px;
}

.kb-calc-output-row {
  display: flex; justify-content: space-between; padding: 9px 0;
  border-bottom: 1px dashed var(--kb-line); font-size: 12.5px;
}
.kb-calc-output-row b { font-family: var(--kb-font-mono); color: var(--kb-ink); }
.kb-calc-output-row.margin {
  border-top: 2px solid #D9B26A; margin-top: 4px;
  font-weight: 600;
}
.kb-calc-output-row.margin b { color: var(--kb-success); font-family: var(--kb-font-display); font-size: 15px; }

.kb-calc-big-result {
  padding: 16px; border-radius: 12px; margin-top: 12px;
  box-shadow: 0 4px 14px rgba(15,14,11,.10);
  position: relative; overflow: hidden;
}
/* [LINEA-OUTLET · BLOQUE 13] Dos arreglos:
   · .green repetía los verdes sueltos de la cinta. Pasa al verde de NIVEL —aquí
     sí se habla de lo que gana el distribuidor— apagándose en carbón. Blanco
     sobre #0E6B5E 6,40:1 ✓ y sobre #15141A 18,31:1 ✓.
   · .gold era dorado CON LETRAS BLANCAS, que es justo lo que la marca prohíbe:
     blanco sobre #C9A227 daba 2,42:1 y sobre #D9B26A da 1,99:1 — ilegible en
     los dos. Se aplica la regla ("el dorado nunca lleva texto encima sin
     oscurecer"): fondo dorado de la paleta y texto en carbón, 9,18:1 sobre
     #D9B26A y 15,99:1 sobre #F6EFE0 ✓. */
.kb-calc-big-result.green {
  background: linear-gradient(135deg, var(--kb-b2b) 0%, var(--kb-ink) 100%);
  color: white;
}
.kb-calc-big-result.green .label { color: var(--kb-yellow-soft); }
.kb-calc-big-result.gold {
  background: linear-gradient(135deg, var(--kb-yellow) 0%, var(--kb-yellow-soft) 100%);
  color: var(--kb-ink);
}
.kb-calc-big-result.dark {
  background: var(--kb-ink); color: var(--kb-yellow); text-align: center;
}
.kb-calc-big-result .label {
  font-family: var(--kb-font-mono); font-size: 10.5px; letter-spacing: .08em;
  color: var(--kb-yellow); font-weight: 700;
}
/* El rótulo del dorado se oscurece con el resto del bloque (era blanco al 85%:
   1,99:1 sobre #D9B26A). --kb-ink sobre el dorado → 9,18:1 ✓. */
.kb-calc-big-result.gold .label { color: var(--kb-ink); }
.kb-calc-big-result .value {
  font-family: var(--kb-font-display); font-size: 32px; font-weight: 900;
  letter-spacing: -.02em; line-height: 1.1; margin-top: 2px;
}
.kb-calc-big-result .badge-anual {
  position: absolute; top: 12px; right: 14px;
  font-family: var(--kb-font-mono); font-size: 9px; opacity: .8;
  letter-spacing: .1em; font-weight: 700;
}

@media (max-width: 900px) {
  .kb-calc-pkg-cards { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 768px) {
  .kb-calc-route { grid-template-columns: 1fr; gap: 10px; }
  .kb-calc-projection { padding: 18px; border-radius: 14px; }
  .kb-calc-proj-cta { width: 100%; text-align: center; }
  .kb-calc-big-result .value { font-size: 26px; }
}
@media (max-width: 480px) {
  .kb-calc-pkg-cards { grid-template-columns: 1fr; }
  .kb-calc-pkg-price { font-size: 24px; }
}


/* ─── YouTube Section ─── */
.kb-yt-grid {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px;
}
.kb-yt-card {
  position: relative; aspect-ratio: 9 / 16; border-radius: 12px; overflow: hidden;
  cursor: pointer; text-decoration: none; color: white;
  display: block; transition: transform .2s, box-shadow .2s;
  box-shadow: 0 4px 14px rgba(0,0,0,.1);
}
.kb-yt-card:hover { transform: translateY(-3px) scale(1.02); box-shadow: 0 14px 30px rgba(0,0,0,.2); }
.kb-yt-thumb {
  position: absolute; inset: 0; display: grid; place-items: center;
  font-size: 80px; opacity: 0.65;
}
.kb-yt-play {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  background: rgba(0,0,0,.7); color: white; width: 50px; height: 50px;
  border-radius: 50%; display: grid; place-items: center; font-size: 18px;
  transition: transform .2s, background .2s;
}
/* [LINEA-OUTLET · BLOQUE 13] Este rojo NO es de la paleta y se queda: es el rojo
   de YouTube en su botón de reproducir, o sea identificación de un tercero — el
   mismo criterio que los distintivos de pago. El triángulo blanco encima mide
   4,00:1, por encima del 3:1 que pide un objeto gráfico. */
.kb-yt-card:hover .kb-yt-play { background: #FF0000; transform: translate(-50%, -50%) scale(1.15); }
.kb-yt-overlay {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 12px;
  background: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.85) 100%);
}
.kb-yt-title { font-size: 11.5px; font-weight: 700; line-height: 1.3; }
.kb-yt-views { font-family: var(--kb-font-mono); font-size: 9.5px; opacity: .85; margin-top: 4px; letter-spacing: .04em; }

@media (max-width: 900px) {
  .kb-yt-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 480px) {
  .kb-yt-grid { grid-template-columns: repeat(2, 1fr); }
}


/* ─── Imagen del paquete con hover swap ─── */
.kb-calc-pkg-img {
  position: relative;
  aspect-ratio: 9 / 16;
  max-height: 280px;
  border-radius: 10px;
  overflow: hidden;
  margin-bottom: 14px;
  background: linear-gradient(135deg, var(--kb-cream), white);
  box-shadow: 0 4px 14px rgba(15,14,11,.08);
}
.kb-calc-pkg-img img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  transition: opacity .4s ease, transform .5s ease;
}
.kb-calc-pkg-img .img-secondary {
  opacity: 0;
  transform: scale(1.05);
}
.kb-calc-pkg-card:hover .kb-calc-pkg-img .img-primary {
  opacity: 0;
  transform: scale(1.05);
}
.kb-calc-pkg-card:hover .kb-calc-pkg-img .img-secondary {
  opacity: 1;
  transform: scale(1);
}
.kb-calc-pkg-card.active .kb-calc-pkg-img {
  box-shadow: 0 8px 20px color-mix(in srgb, var(--card-color) 25%, transparent);
}


/* ─── B2B Catalog (PWA style) ─── */
.kb-mkt-strip {
  background: linear-gradient(90deg, #B8322F 0%, #A32C29 100%);
  color: white; padding: 10px 0; margin: 14px -14px 0;
  overflow: hidden; white-space: nowrap;
  font-family: var(--kb-font-display); font-weight: 800; font-size: 13px; letter-spacing: .04em;
  position: relative;
  display: flex; gap: 60px;
  animation: kbMktScroll 30s linear infinite;
}
@keyframes kbMktScroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* Secciones por categoría (orden de catálogo) en el armador B2B */
.kb-b2b-section { margin-top: 22px; }
.kb-b2b-section:first-of-type { margin-top: 14px; }
.kb-b2b-section-title {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 0;
  padding: 0 0 9px 12px;
  position: relative;
  font-size: 15.5px;
  font-weight: 800;
  letter-spacing: 0.01em;
  color: var(--kb-charcoal);
  border-bottom: 1px solid var(--kb-line);
}
.kb-b2b-section-title::before {
  content: "";
  position: absolute;
  left: 0; top: 1px; bottom: 10px;
  width: 4px; border-radius: 99px;
  background: linear-gradient(180deg, var(--kb-orange), #A32C29);
}
.kb-b2b-section-emoji { font-size: 18px; line-height: 1; }
.kb-b2b-section-name { flex: 0 0 auto; }
.kb-b2b-section-count {
  margin-left: auto;
  min-width: 22px;
  padding: 2px 9px;
  border-radius: 99px;
  background: var(--kb-cream);
  border: 1px solid var(--kb-line);
  color: var(--kb-muted);
  font-size: 11.5px;
  font-weight: 700;
  text-align: center;
}
.kb-b2b-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 12px;
  margin-top: 18px;
}
.kb-b2b-section .kb-b2b-grid { margin-top: 12px; }
@media (max-width: 600px) {
  .kb-b2b-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; }
  .kb-b2b-section-title { font-size: 14px; }
}
.kb-b2b-card {
  background: white;
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 2px 10px rgba(15,14,11,.06);
  display: flex; flex-direction: column;
  transition: transform .15s, box-shadow .15s;
  border: 1px solid var(--kb-line);
}
.kb-b2b-card:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(15,14,11,.10); }
/* [BLOQUE 22] Era un degradado del rosa heredado al blanco. Fuera el degradado —la casa no
   usa degradados decorativos— y fuera el rosa: lo que dice "esto ya está en el carrito" es el
   filete rojo de 2 px, no el relleno. */
.kb-b2b-card.in-cart { border: 2px solid var(--kb-orange); background: var(--kb-pink-soft); }

.kb-b2b-thumb {
  position: relative;
  aspect-ratio: 1;
  display: grid; place-items: center;
  color: white;
  overflow: hidden;
}
.kb-b2b-label {
  position: absolute; top: 8px; left: 8px;
  background: rgba(0,0,0,.45); color: white;
  padding: 4px 8px; border-radius: 4px;
  font-family: var(--kb-font-mono); font-size: 8px; font-weight: 700; letter-spacing: .06em;
  line-height: 1.3;
}
.kb-b2b-dot {
  position: absolute;
  top: 50%; left: 50%; transform: translate(-50%, -50%);
  background: rgba(255,255,255,.95);
  width: 56px; height: 56px; border-radius: 50%;
  display: grid; place-items: center; font-size: 28px;
  box-shadow: 0 4px 14px rgba(0,0,0,.2);
}
.kb-b2b-badge {
  position: absolute; top: 8px; right: 8px;
  background: #B8322F; color: white;
  min-width: 26px; height: 26px; padding: 0 8px; border-radius: 99px;
  display: grid; place-items: center;
  font-weight: 800; font-size: 12px;
  border: 2px solid white;
}

.kb-b2b-info {
  padding: 12px;
  display: flex; flex-direction: column; gap: 4px;
  flex: 1;
}
.kb-b2b-info h4 {
  margin: 0; font-size: 13px; font-weight: 700; line-height: 1.3;
  color: var(--kb-ink); min-height: 34px;
}
.kb-b2b-meta { font-size: 11px; color: var(--kb-muted); }
.kb-b2b-price {
  font-family: var(--kb-font-display); font-size: 24px; font-weight: 900;
  color: #B8322F; margin-top: 2px;
}

.kb-b2b-btn-add {
  width: 100%; margin-top: 8px;
  background: #B8322F; color: white; border: 0;
  padding: 10px; border-radius: 8px;
  font-weight: 700; font-size: 13px; cursor: pointer;
  transition: background .15s;
}
.kb-b2b-btn-add:hover { background: #A32C29; }

.kb-b2b-stepper {
  margin-top: 8px;
  display: grid; grid-template-columns: 36px 1fr 36px;
  align-items: center;
  background: white; border: 2px solid #B8322F; border-radius: 8px; overflow: hidden;
}
.kb-b2b-stepper button {
  background: white; border: 0; padding: 8px 0; cursor: pointer;
  font-size: 16px; font-weight: 800; color: #B8322F;
}
.kb-b2b-stepper button:hover { background: var(--kb-pink-soft); }
.kb-b2b-stepper span {
  text-align: center; font-weight: 800; font-size: 14px;
  font-family: var(--kb-font-display);
}

.kb-b2b-checkout {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  padding: 14px;
  background: linear-gradient(180deg, transparent, rgba(0,0,0,.05) 30%);
}
/* Armador embebido en /paquetes: en móvil la barra se apoya SOBRE la nav
   inferior (.kb-bottomnav, 56px) para no quedar tapada por ella. */
@media (max-width: 768px) {
  .kb-b2b-checkout--embedded { bottom: 56px; }
}
.kb-b2b-checkout button {
  display: flex; align-items: center; gap: 10px;
  width: 100%; max-width: 1480px; margin: 0 auto;
  background: linear-gradient(135deg, #B8322F, #A32C29);
  color: white; border: 0;
  padding: 16px 20px; border-radius: 99px;
  font-family: var(--kb-font-display);
  font-size: 16px; font-weight: 800;
  cursor: pointer;
  /* [LINEA-OUTLET · BLOQUE 13] La sombra seguía tintada con el magenta viejo
     (#E91E63) bajo un botón que ya es rojo outlet: ahora es su propio color. */
  box-shadow: 0 10px 30px rgba(184,50,47,.4);
}
.kb-b2b-checkout button .qty-badge {
  background: rgba(255,255,255,.25);
  width: 32px; height: 32px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 14px; font-weight: 800;
}


/* Quick add chips B2B */
.kb-b2b-quickadd {
  display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px;
  margin-top: 8px;
}
.kb-b2b-chip {
  background: var(--kb-cream); border: 1px solid var(--kb-line);
  padding: 6px 0; border-radius: 6px;
  font-weight: 700; font-size: 11px; cursor: pointer;
  color: #B8322F; transition: all .12s;
  font-family: var(--kb-font-mono);
}
.kb-b2b-chip:hover { background: #B8322F; color: white; border-color: #B8322F; }

.kb-b2b-stepper { grid-template-columns: 48px 1fr 48px !important; }
.kb-b2b-stepper button { font-size: 12px !important; font-weight: 800 !important; font-family: var(--kb-font-mono) !important; }
.kb-b2b-stepper span small { display: block; font-size: 9px; color: var(--kb-muted); font-weight: 500; }


/* Calc package card · imagen centrada + CTAs */
.kb-calc-pkg-img {
  display: flex;
  align-items: center;
  justify-content: center;
  /* [LINEA-OUTLET · BLOQUE 13] #FAFAFA era un gris frío; el hueso de la casa
     hace de fondo neutro cálido y es token. */
  background: linear-gradient(135deg, var(--kb-cream), white);
  aspect-ratio: 9 / 16;
  max-height: 320px;
  position: relative;
  overflow: hidden;
  border-radius: 10px;
  margin-bottom: 14px;
}
.kb-calc-pkg-img img {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: 100%; height: 100%;
  object-fit: contain;
  object-position: center;
  padding: 6px;
  transition: opacity .4s ease, transform .5s ease;
  inset: auto;
}
.kb-calc-pkg-img .img-secondary {
  opacity: 0;
  transform: translate(-50%, -50%) scale(1.05);
}
.kb-calc-pkg-card:hover .kb-calc-pkg-img .img-primary {
  opacity: 0;
  transform: translate(-50%, -50%) scale(1.05);
}
.kb-calc-pkg-card:hover .kb-calc-pkg-img .img-secondary {
  opacity: 1;
  transform: translate(-50%, -50%) scale(1);
}

.kb-calc-pkg-ctas {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-top: 12px;
  padding: 0;
}
.kb-calc-pkg-btn {
  padding: 10px 8px;
  border-radius: 8px;
  border: 0;
  font-weight: 700;
  font-size: 12px;
  cursor: pointer;
  transition: transform .12s, box-shadow .12s;
}
.kb-calc-pkg-btn:hover { transform: translateY(-1px); }
.kb-calc-pkg-btn.primary {
  color: white;
  box-shadow: 0 4px 12px rgba(0,0,0,.15);
}
.kb-calc-pkg-btn.primary:hover {
  box-shadow: 0 8px 20px rgba(0,0,0,.25);
}
.kb-calc-pkg-btn.secondary {
  background: white;
  border: 1.5px solid;
  color: var(--card-color);
}
.kb-calc-pkg-btn.secondary:hover {
  background: var(--card-color);
  color: white !important;
}


/* Calc package card · imagen COMPLETA centrada */
.kb-calc-pkg-img {
  display: flex !important;
  align-items: center;
  justify-content: center;
  background: white !important;
  width: 100%;
  height: 360px;
  max-height: none !important;
  aspect-ratio: auto !important;
  position: relative;
  overflow: hidden;
  border-radius: 10px;
  margin-bottom: 14px;
  border: 1px solid var(--kb-line);
}
.kb-calc-pkg-img img {
  position: absolute !important;
  top: 0; left: 0;
  width: 100% !important;
  height: 100% !important;
  object-fit: contain !important;
  object-position: center !important;
  padding: 8px;
  transform: none !important;
  transition: opacity .4s ease, transform .5s ease;
  inset: 0;
}
.kb-calc-pkg-img .img-secondary {
  opacity: 0;
  transform: scale(1.04) !important;
}
.kb-calc-pkg-card:hover .kb-calc-pkg-img .img-primary {
  opacity: 0;
  transform: scale(1.04) !important;
}
.kb-calc-pkg-card:hover .kb-calc-pkg-img .img-secondary {
  opacity: 1;
  transform: scale(1) !important;
}
@media (max-width: 900px) {
  .kb-calc-pkg-img { height: 320px; }
}
@media (max-width: 600px) {
  .kb-calc-pkg-img { height: 280px; }
}


/* ─── Logo oficial K-Botanas en sitio ─── */
.kb-logo-img { display: block; height: 44px; width: auto; max-width: 100%; object-fit: contain; }
.kb-logo-img.lg { height: 64px; }
.kb-logo-img.sm { height: 32px; }
.kb-logo-img.xs { height: 24px; }

/* Reemplaza mini-logo SVG por img cuando exista .kb-logo-real */
.kb-logo-real { display: flex; align-items: center; gap: 8px; }
.kb-logo-real img { height: 40px; width: auto; }

/* En hero/banners donde necesite versión grande */
.kb-logo-hero { max-width: 480px; width: 100%; height: auto; display: block; }


/* ─── RESPONSIVE GLOBAL · breakpoints estandarizados ─── */
/* Tablet */
@media (max-width: 1024px) {
  .kb-yt-grid { grid-template-columns: repeat(3, 1fr); }
  .kb-calc-pkg-cards { grid-template-columns: repeat(2, 1fr); }
  .kb-route-calc-grid { grid-template-columns: 1fr; }
/* [BLOQUE 27] lo decide --kb-card-min */
  .kb-hero { grid-template-columns: 1fr; }
  .kb-hero-content { padding: 28px !important; }
  .kb-hero-pulido { padding: 0 !important; }
  .kb-hero-pulido .kb-hero-inner { padding: 28px; }
  .kb-hero-art { display: none !important; }
}

/* Mobile */
@media (max-width: 768px) {
  .kb-yt-grid { grid-template-columns: repeat(2, 1fr); }
/* [BLOQUE 27] lo decide --kb-card-min */
  .kb-section-head { flex-direction: column; align-items: flex-start; gap: 8px; }
  .kb-section-head h2 { font-size: 20px !important; }
  h1 { font-size: 28px !important; }
  h2 { font-size: 22px !important; }
  .kb-hero-content h1 { font-size: 32px !important; }
  .kb-logo-img { height: 36px !important; }
  .kb-b2b-grid { grid-template-columns: repeat(2, 1fr); }
  .kb-calc-pkg-cards { grid-template-columns: 1fr; }
  .kb-route-calc-bars { gap: 2px; }
  .kb-b2b-quickadd { grid-template-columns: repeat(2, 1fr) !important; }
  .kb-cart-item {
    grid-template-columns: 64px 1fr auto !important;
    grid-template-areas:
      "img info  info"
      "img qty   price"
      "img qty   rm" !important;
    column-gap: 10px !important; row-gap: 6px !important;
    align-items: center !important;
  }
  .kb-cart-item .kb-prod-img { grid-area: img; align-self: start; width: 64px; height: 64px; }
  .kb-cart-item .kb-cart-info { grid-area: info; min-width: 0; }
  .kb-cart-item .kb-cart-qty { grid-area: qty; justify-self: start; }
  .kb-cart-item .kb-cart-line { grid-area: price; min-width: 0; }
  .kb-cart-item .kb-cart-rm { grid-area: rm; justify-self: end; }
}

/* Móvil pequeño */
@media (max-width: 480px) {
  .kb-yt-grid { grid-template-columns: 1fr 1fr; gap: 8px; }
  .kb-hero-content h1 { font-size: 26px !important; line-height: 1.1; }
  .kb-section-head h2 { font-size: 18px !important; }
  .kb-modal { max-width: calc(100vw - 20px) !important; }
  .kb-modal-side { display: none; }
  table { font-size: 11px !important; }
  th, td { padding: 8px 6px !important; }
  .kb-btn { padding: 10px 12px; font-size: 12px; }
  .kb-btn-lg { padding: 12px 16px; font-size: 13px; }
}

/* ─── PERFORMANCE optimizations ─── */
img { content-visibility: auto; }
.kb-product-grid > *, .kb-b2b-grid > *, .kb-calc-pkg-cards > * { content-visibility: auto; contain-intrinsic-size: 320px; }
* { -webkit-tap-highlight-color: transparent; }
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
}

/* Touch-friendly buttons on mobile */
@media (hover: none) {
  button, .kb-btn, a.kb-btn { min-height: 44px; }
  .kb-b2b-chip { min-height: 36px; }
}


/* ═══ RESPONSIVE GLOBAL · override de grids inline (atributo) ═══ */
/* Los grids con estilo inline no responden a media queries salvo con !important + selector de atributo */
@media (max-width: 768px) {
  [style*="repeat(6, 1fr)"],
  [style*="repeat(5, 1fr)"],
  [style*="repeat(4, 1fr)"],
  [style*="repeat(3, 1fr)"] { grid-template-columns: repeat(2, 1fr) !important; }
  /* asimétricos a 1 columna */
  [style*="1.4fr 1fr"],
  [style*="1.6fr 1fr"],
  [style*="1.05fr 1fr"],
  [style*="1.1fr 1fr"],
  [style*="320px 1fr"],
  [style*="1.4fr 1fr"] { grid-template-columns: 1fr !important; }
  /* contenedores grandes con sidebar a 1 col */
  [style*="gridTemplateColumns: \"320px 1fr\""] { grid-template-columns: 1fr !important; }
}
@media (max-width: 480px) {
  [style*="repeat(6, 1fr)"],
  [style*="repeat(5, 1fr)"],
  [style*="repeat(4, 1fr)"],
  [style*="repeat(3, 1fr)"],
  [style*="repeat(2, 1fr)"],
  [style*="1fr 1fr"] { grid-template-columns: 1fr !important; }
  /* Excepciones: badges/stats compactos se quedan en 2 col para no alargar demasiado */
  [style*="repeat(6, 1fr)"][style*="gap: 8"],
  [style*="repeat(4, 1fr)"][style*="gap: 4"] { grid-template-columns: repeat(2, 1fr) !important; }
}

/* Página de checkout y documentos: sidebar/preview a 1 columna en móvil */
@media (max-width: 768px) {
  .kb-modal { grid-template-columns: 1fr !important; }
}


/* ─── Prevenir zoom/desplazamiento accidental con gestos en móvil ─── */
html { touch-action: pan-x pan-y; -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
body { overscroll-behavior: none; }
/* Inputs a 16px+ para que iOS no haga zoom al enfocar */
@media (max-width: 768px) {
  input, select, textarea { font-size: 16px !important; }
}


/* ─── Fix overflow horizontal en móvil (elementos que se salen a la derecha) ─── */
html, body { max-width: 100%; overflow-x: clip; }
.kb-app, .kb-main, section { max-width: 100%; overflow-x: clip; }
/* Search autocomplete y dropdowns nunca exceden viewport */
.kb-smart-search, .kb-smart-search > div { max-width: 100%; }
@media (max-width: 768px) {
  /* Cualquier contenedor con min-width fijo grande se relaja en móvil */
  [style*="minWidth: 540"], [style*="min-width: 540"],
  [style*="minWidth: 480"], [style*="min-width: 480"] { min-width: 0 !important; }
  /* StickyMobileCart no debe empujar el ancho */
  .kb-main { overflow-x: clip; }
}


/* ═══════════════════════════════════════════════════════════════
   PASE FINAL RESPONSIVE · alineación global y anti-distorsión
   ═══════════════════════════════════════════════════════════════ */

/* Imágenes y media nunca desbordan ni se distorsionan */
img, svg, video, iframe { max-width: 100%; height: auto; }
img[style*="object-fit"] { height: 100%; } /* respeta covers explícitos */

/* Tablas con scroll horizontal propio en vez de romper el layout */
@media (max-width: 768px) {
  table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap; }
  table th, table td { white-space: nowrap; }
}

/* Botones e inputs full-width naturales en móvil dentro de formularios */
@media (max-width: 600px) {
  .kb-hero-cta { flex-direction: column; }
  .kb-hero-cta .kb-btn { width: 100%; justify-content: center; }
  /* CTAs dobles que se aprietan → apilan */
  [style*="display: flex"][style*="gap: 8"] > .kb-btn { flex: 1 1 auto; }
}

/* Section headers: título + meta no se encinan raro */
@media (max-width: 600px) {
  .kb-section-head { flex-wrap: wrap; gap: 6px; }
  .kb-section-meta { font-size: 11px; }
}

/* Texto display gigante se ajusta para no desbordar en móvil chico */
@media (max-width: 480px) {
  .kb-h1, h1 { font-size: clamp(24px, 8vw, 34px) !important; line-height: 1.1 !important; word-break: break-word; }
  .kb-h2, h2 { font-size: clamp(20px, 6vw, 26px) !important; }
  .kb-hero .tagline { font-size: 10px !important; }
}

/* Cards de producto: alto consistente y contenido alineado */
.kb-product-grid { align-items: stretch; }
.kb-product-grid > * { display: flex; flex-direction: column; }

/* Chips/tags que envuelven sin romper */
[style*="flexWrap"], [style*="flex-wrap"] { min-width: 0; }
.kb-chip, .kb-tag { white-space: nowrap; }

/* Inputs de número/range a ancho completo en su celda */
input[type="range"] { width: 100%; }

/* Modales: padding seguro y scroll interno en móvil */
@media (max-width: 600px) {
  .kb-modal, .kb-modal-bg > div { max-height: 92vh; overflow-y: auto; }
  .kb-modal { width: calc(100vw - 24px) !important; }
}

/* Sticky bottom bars no tapan contenido final */
@media (max-width: 900px) {
  body { padding-bottom: 80px; }
}

/* Footer columnas a 1-2 en móvil */
@media (max-width: 768px) {
  footer [style*="repeat"], .kb-footer [style*="repeat"] { grid-template-columns: repeat(2, 1fr) !important; }
}
@media (max-width: 440px) {
  footer [style*="repeat"], .kb-footer [style*="repeat"] { grid-template-columns: 1fr !important; }
}

/* Texto largo con guiones suaves para no desbordar */
p, h1, h2, h3, h4, .kb-card { overflow-wrap: anywhere; }


/* ─── Tablas dentro de tarjetas: scroll contenido, no desbordan ─── */
.kb-card-section { overflow: hidden; }
.kb-card-section .kb-table { width: 100%; }
@media (max-width: 768px) {
  .kb-card-section .kb-table {
    display: block; overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
  .kb-card-section .kb-table thead,
  .kb-card-section .kb-table tbody { display: table; width: max-content; min-width: 100%; }
}

/* ─── Header sticky: que nunca lo tape el autocomplete ni overflow ─── */
.kb-header { position: relative; z-index: 60; }
.kb-smart-search > div[style*="position: absolute"] { z-index: 70; max-width: calc(100vw - 28px); }

/* ─── Cart dynamic tiers: ancho seguro en móvil ─── */
@media (max-width: 600px) {
  [style*="repeat(5, 1fr)"] { grid-template-columns: repeat(2, 1fr) !important; }
}


/* ═══ Red de seguridad anti scroll horizontal en móvil ═══ */
@media (max-width: 600px) {
  [style*="width: 380"], [style*="width:380"],
  [style*="width: 360"], [style*="width: 400"],
  [style*="width: 420"], [style*="width: 440"],
  [style*="width: 460"], [style*="width: 480"],
  [style*="width: 500"], [style*="width: 520"] {
    width: 100% !important;
    max-width: 100% !important;
  }
  /* Nada empuja más allá del viewport */
  html, body { overflow-x: clip; max-width: 100%; }
  .kb-app, .kb-main, section, .kb-card { max-width: 100%; }
}


/* ─── Cards de producto SIEMPRE caben 2-up en móvil (anti overflow) ─── */
.kb-product-grid > *, .kb-b2b-grid > * { min-width: 0; }
.kb-product-card, .kb-product-grid > * { min-width: 0 !important; max-width: 100%; }
.kb-product-card img, .kb-product-card .kb-thumb { width: 100%; }
@media (max-width: 700px) {
  .kb-product-grid { grid-template-columns: repeat(2, 1fr) !important; gap: 8px !important; }
  .kb-product-grid .kb-product-card { font-size: 13px; }
  /* texto y precios dentro de card sin desbordar */
  .kb-product-card h3, .kb-product-card h4 { font-size: 13px; line-height: 1.25; }
}
@media (max-width: 360px) {
  .kb-product-grid { gap: 6px !important; }
}


/* Footer 1 columna en móvil chico */
@media (max-width: 480px) {
  .kb-footer-grid { grid-template-columns: 1fr !important; gap: 18px !important; }
  .kb-footer { overflow-x: clip; }
  .kb-footer p, .kb-footer li, .kb-footer a { white-space: normal; overflow-wrap: anywhere; }
  .kb-footer [style*="repeat"] { grid-template-columns: 1fr 1fr !important; }
}


/* Búsqueda móvil: ocultar selector "Todo el catálogo" para que "Buscar" se vea completo */
@media (max-width: 768px) {
  .kb-search .kb-search-cat { display: none !important; }
  .kb-search { width: 100%; }
  .kb-search input { flex: 1; min-width: 0; }
  .kb-search .kb-search-go { flex-shrink: 0; }
}


/* ─── Menú lateral (drawer) móvil ─── */
.kb-burger { display: none; }
@media (max-width: 768px) {
  .kb-burger {
    display: flex; flex-direction: column; gap: 4px; justify-content: center;
    position: absolute; top: 14px; left: 14px; z-index: 62;
    width: 40px; height: 40px; background: transparent; border: 0; cursor: pointer; padding: 8px;
  }
  .kb-burger span { display: block; height: 2.5px; width: 100%; background: var(--kb-pink-deep); border-radius: 2px; }
  /* Empuja el logo para no chocar con la hamburguesa. La columna "cart" es más
     ancha que "acct" (92px vs 40px) porque .kb-header-actions le mete DOS
     botones (cuenta + carrito) — con 40px cada uno quedaba en 17px de ancho
     real, muy por debajo del mínimo táctil (hallazgo real de Lighthouse). */
  .kb-header-row { grid-template-columns: 40px 1fr 92px !important; }
}
/* [PERF-DRAWER-PORTAL] La caja donde vive el menú lateral. Se renderiza
   SIEMPRE (chrome.jsx la pinta abierta o cerrada), y ahí está la gracia: lo que
   aparece al pulsar la hamburguesa ya no se inserta contra el documento, sino
   dentro de esta caja, que existía desde el primer render y tiene tamaño propio
   (el del viewport). `contain: layout paint` es la declaración explícita de que
   nada de lo que pase aquí dentro cambia nada de fuera: es lo que permite al
   motor cortar ahí el ámbito del relayout. Antes, overlay y panel eran
   position: fixed dentro de .kb-header, y un elemento fixed tiene como bloque
   contenedor el LayoutView, así que montarlos ensuciaba el documento ENTERO —
   medido: Layout marcado COMPLETO sobre 1964 objetos, 183 ms en una corrida y
   682 ms en otra (Playwright, Pixel 5 emulado, CPU 4x, contra producción).

   pointer-events: none NO ES OPCIONAL. Esto es un div fijo a pantalla completa
   que nunca se desmonta: sin esa línea se come TODOS los clics del sitio, sin
   dar error y sin verse. visibility: hidden va por lo mismo, para lo que pueda
   llegar a montarse dentro estando cerrado (ambas se heredan). El estado
   abierto las devuelve.

   z-index: `contain: paint` crea contexto de apilamiento propio, así que los
   998/999 de overlay y panel ya sólo ordenan PUERTAS ADENTRO y quien compite
   con el resto del sitio es el portal. Sin un z-index explícito quedaría en
   `auto` y le pintarían encima .kb-header (60), .kb-modal-bg (100) y los
   filtros del catálogo (110/120): el menú se abriría por debajo del sitio.
   Con 998 pasa lo contrario de lo que pasaba hasta ahora, que es lo correcto:
   estando dentro de .kb-header el drawer valía 60 y esos tres lo tapaban. */
.kb-drawer-portal {
  position: fixed; inset: 0; z-index: 998;
  contain: layout paint;
  pointer-events: none; visibility: hidden;
}
.kb-drawer-portal[data-open="1"] { pointer-events: auto; visibility: visible; }

/* [PERF-DRAWER-WILLCHANGE] Cada uno de estos dos anima UNA sola propiedad
   (el overlay opacity vía kbFade, el panel transform vía kbDrawerIn) y las dos
   son de las que el compositor puede llevar sin repintar. Declararlo con
   will-change deja al navegador preparar la capa en vez de decidirlo en el
   mismo fotograma del toque: en el home, pulsar la hamburguesa medía
   Layerize 267 ms y Paint 174 ms (Playwright, Pixel 5 emulado, CPU 4x, contra
   producción). OJO con lo que esto NO hace: will-change no es frontera de
   relayout, así que el Layout COMPLETO de ese mismo clic se ataca aparte
   (.kb-drawer-portal, más abajo). Se promete sólo la propiedad que de verdad
   anima cada uno — prometer de más se paga en memoria de GPU. */
/* absolute, no fixed: los dos viven dentro de .kb-drawer-portal, que YA es
   fixed a pantalla completa — la geometría sale idéntica (inset:0 contra el
   viewport) y así ninguno de los dos vuelve a tener el LayoutView por bloque
   contenedor, que es justo lo que arrastraba el relayout a todo el documento. */
.kb-drawer-overlay {
  position: absolute; inset: 0; background: var(--kb-scrim); z-index: 998; animation: kbFade .2s;
  will-change: opacity;
}
/* [LINEA-OUTLET · BLOQUE 22] EL CAJÓN SE VA A CARBÓN.
   La segunda superficie que señaló el dueño. Era #fff sobre una página blanca: no se leía como
   una capa que se abre sino como que la página se había ensuciado. Invertido, la separación
   contra la página velada sube de 3,44:1 a 5,32:1 — y por eso se le quita la sombra: lo que
   despega el cajón es el velo, no un desenfoque de 30 px.
   El `color` va aquí a propósito, para que TODO lo de dentro herede blanco: lo que no herede
   se queda con la tinta del body y desaparece sobre el carbón. */
.kb-drawer {
  position: absolute; top: 0; left: 0; bottom: 0; width: min(82vw, 340px);
  background: var(--kb-ink); color: var(--kb-ink-fg); z-index: 999;
  display: flex; flex-direction: column;
  transform: translateX(0);
  animation: kbDrawerIn .25s ease both;
  will-change: transform;
  overflow-y: auto;
}
@keyframes kbDrawerIn { from { transform: translateX(-100%); } to { transform: translateX(0); } }
/* [LINEA-OUTLET · BLOQUE 22] LA CABECERA SE QUEDA EN PAPEL, Y EL CARBÓN EMPIEZA DEBAJO.
   El cajón entero en carbón dejaba media marca invisible: el archivo del logotipo lleva
   "LÍNEA" en tinta sobre fondo transparente, así que en pantalla se leía sólo "OUTLET".
   El logotipo NO se toca — es la superficie la que se adapta a él. La cabecera conserva el
   papel para el que está dibujado y el panel de navegación arranca en carbón justo debajo:
   se lee como una hoja con membrete, y el salto de valor entre las dos zonas hace de línea
   sin necesidad de pintar ninguna. */
.kb-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 16px; border-bottom: 1px solid var(--kb-line); position: sticky; top: 0; background: var(--kb-paper); }
/* Medía 34×34, por debajo del mínimo táctil de 44 de la casa. No lo traía la paleta —venía de
   antes— pero es el botón de la pantalla que se está rehaciendo. */
.kb-drawer-close { background: transparent; border: 1px solid var(--kb-line-strong); width: 44px; height: 44px; border-radius: 50%; font-size: 15px; cursor: pointer; color: var(--kb-ink); }
.kb-drawer-nav { display: flex; flex-direction: column; padding: 8px 0; }
.kb-drawer-nav > a, .kb-drawer-acc {
  padding: 14px 20px; font-size: 15px; font-weight: 600; color: var(--kb-ink-fg);
  text-decoration: none; cursor: pointer; border: 0; background: transparent; text-align: left;
  display: flex; justify-content: space-between; align-items: center; border-bottom: 1px solid var(--kb-ink-line);
}
.kb-drawer-nav > a:active, .kb-drawer-acc:active { background: var(--kb-ink-hover); }
.kb-drawer-sub { background: var(--kb-ink-hover); }
.kb-drawer-sub a { display: block; padding: 12px 20px 12px 36px; font-size: 13.5px; color: var(--kb-ink-fg2); text-decoration: none; cursor: pointer; border-bottom: 1px solid var(--kb-ink-line); }
/* [LINEA-OUTLET · BLOQUE 21] Etiquetas de descuento del menu movil. Naranja para
   mayoreo y verde para distribucion, los dos de la paleta de la casa y con letra blanca
   encima: 5,94:1 y 6,40:1 de contraste, por encima del 4,5:1 que pide la casa para texto
   pequeno. Van a la derecha de su entrada y no empujan el texto. */
.kb-drawer-nav a { position: relative; }
.kb-drawer-badge {
  float: right; font-style: normal; font-size: 10px; font-weight: 800; letter-spacing: .04em;
  color: #fff; padding: 3px 8px; border-radius: 999px; white-space: nowrap; margin-left: 8px;
}
.kb-drawer-badge.may  { background: var(--kb-orange); }
/* [BLOQUE 22] El verde de la casa sobre el carbón da 2,86:1 y el mínimo de objeto es 3:1: la
   etiqueta se vería como una mancha. Sube un peldaño de claridad SIN cambiar de color (mismo
   matiz 172°, misma saturación 77 %): 3,50:1 de relleno y 5,23:1 la letra blanca encima. */
.kb-drawer-badge.dist { background: var(--kb-b2b-on-dark); }

.kb-drawer-divider { height: 8px; background: var(--kb-ink-line); }
/* Accesos de la barra superior dentro del drawer: en móvil ESTA es su única
   forma, así que van a tamaño táctil, no al 11px de la barra de escritorio. */
.kb-drawer-tool {
  display: flex; align-items: center; gap: 8px;
  min-height: 44px; padding: 10px 4px; cursor: pointer; font-size: 14px;
}
/* El acceso de WhatsApp conserva en el drawer la presencia que tenía cuando
   estaba escrito a mano; lo que cambia es de dónde sale su número.
   [LINEA-OUTLET · BLOQUE 13] El criterio de WhatsApp ya está resuelto en
   .kb-wa-card: el #25D366 de marca vale para BORDE o icono, nunca para texto
   (daba 1,98:1 sobre blanco). Aquí el texto estaba en #1B7F45 —5,04:1, pasaba,
   pero era un séptimo verde—: pasó al único verde de la casa, #0E6B5E → 6,40:1
   sobre blanco ✓.
   [LINEA-OUTLET · BLOQUE 22] Con el cajón en carbón ese verde cae a 2,86:1 y no llega al
   4,5:1 de texto. Para salvarlo haría falta un TERCER verde, sólo para este enlace, y en esta
   casa el verde significa una sola cosa: nivel alcanzado. Aquí no se ha alcanzado nada. Así
   que la entrada va en blanco como las demás y quien lleva la marca es el icono. */
.kb-drawer-tool[href*="wa.me"] { color: var(--kb-ink-fg); font-weight: 600; }
.kb-drawer-foot { margin-top: auto; padding: 18px 20px; font-size: 11.5px; color: var(--kb-ink-fg2); border-top: 1px solid var(--kb-ink-line); line-height: 1.5; }


/* Marca propia + Multimarca: SIEMPRE 2 productos por fila en móvil (como Más vendidos) */
@media (max-width: 768px) {
  /* las tarjetas contenedoras se apilan (1 col) pero los productos dentro quedan 2-up */
  .kb-brand-prods { grid-template-columns: repeat(2, 1fr) !important; gap: 8px !important; }
  .kb-brand-prods > * { min-width: 0; }
}
@media (max-width: 360px) {
  .kb-brand-prods { gap: 6px !important; }
}


/* ─── Carrusel de banners ─── */
.kb-banner-carousel { margin: 14px 0; }
.kb-banner-slide {
  position: relative; border-radius: 18px; overflow: hidden;
  min-height: 260px; display: flex; align-items: center;
  padding: 36px 48px; box-shadow: 0 8px 30px rgba(0,0,0,.12);
}
.kb-banner-content { position: relative; z-index: 2; max-width: 60%; }
.kb-banner-badge { display: inline-block; font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700; letter-spacing: .08em; padding: 4px 12px; border-radius: 99px; margin-bottom: 12px; }
.kb-banner-title { font-family: var(--kb-font-display); font-size: 38px; font-weight: 900; line-height: 1.05; letter-spacing: -.02em; margin: 0; text-shadow: 0 2px 4px rgba(0,0,0,.12); }
.kb-banner-sub { font-size: 16px; margin: 10px 0 0; opacity: .95; }
.kb-banner-cta { margin-top: 20px; border: 0; padding: 14px 26px; border-radius: 99px; font-weight: 800; font-size: 14px; cursor: pointer; box-shadow: 0 6px 18px rgba(0,0,0,.18); transition: transform .15s; }
.kb-banner-cta:hover { transform: translateY(-2px); }
.kb-banner-emoji { position: absolute; right: 6%; top: 50%; transform: translateY(-50%); font-size: 150px; opacity: .5; pointer-events: none; }
.kb-banner-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 40px; height: 40px; border-radius: 50%; border: 0; cursor: pointer;
  background: rgba(255,255,255,.85); color: var(--kb-ink); font-size: 22px; line-height: 1;
  display: grid; place-items: center; box-shadow: 0 2px 10px rgba(0,0,0,.2);
}
.kb-banner-arrow.left { left: 14px; }
.kb-banner-arrow.right { right: 14px; }
.kb-banner-dots { display: flex; gap: 8px; justify-content: center; margin-top: 14px; }
.kb-banner-dot { width: 9px; height: 9px; border-radius: 50%; border: 0; background: var(--kb-line-strong); cursor: pointer; padding: 0; transition: all .2s; }
.kb-banner-dot.on { background: var(--kb-pink-deep); width: 26px; border-radius: 99px; }

@media (max-width: 768px) {
  .kb-banner-slide { min-height: 200px; padding: 24px 22px; border-radius: 14px; }
  .kb-banner-content { max-width: 78%; }
  .kb-banner-title { font-size: 24px; }
  .kb-banner-sub { font-size: 13px; }
  .kb-banner-emoji { font-size: 90px; right: 2%; opacity: .35; }
  .kb-banner-arrow { width: 32px; height: 32px; font-size: 18px; }
  .kb-banner-cta { padding: 11px 18px; font-size: 13px; }
}
@media (max-width: 440px) {
  .kb-banner-content { max-width: 85%; }
  .kb-banner-title { font-size: 20px; }
  .kb-banner-emoji { font-size: 70px; opacity: .25; }
}

/* ─── Anti-parpadeo de imágenes al hacer scroll en móvil ───
   Promueve logo y banners a su propia capa GPU para que el navegador
   no re-rasterice (y "parpadee") cada cuadro durante el scroll. */
.kb-logo-img,
.kb-logo-real img,
.kb-banner-slide,
.kb-banner-slide--image img {
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
  -webkit-transform: translateZ(0);
  transform: translateZ(0);
  -webkit-perspective: 1000;
  perspective: 1000;
}

/* Drawer mini-texto utilidades */
.kb-drawer-mini { padding: 12px 20px; font-size: 11px; color: var(--kb-ink-fg2); font-family: var(--kb-font-mono); }


/* Banner: sin flechas de rotación (se controla por intervalo en admin) */
.kb-banner-arrow { display: none !important; }


/* ─── Header flotante en web ─── */
@media (min-width: 901px) {
  .kb-header {
    position: sticky; top: 0; z-index: 60;
    box-shadow: 0 4px 20px rgba(15,14,11,.08);
    backdrop-filter: saturate(180%) blur(6px);
  }
}


/* ─── Banda destacada · Calculadora / conversión distribuidor ─── */
/* [LINEA-OUTLET · BLOQUE 13] La banda entera venía del clon: un degradado a
   #6A0F2E (vino magenta), una sombra en rgba(201,16,70) y una cinta que remataba
   en el cian #0099D8. Ahora va de carbón a rojo outlet profundo —los dos oscuros
   de la marca— con sombra neutra, y la cinta cierra en carbón. Blanco sobre
   #15141A 18,31:1 ✓ y sobre #8E2522 8,61:1 ✓ en todo el recorrido.
   [REVISIÓN · BLOQUE 13] El extremo del degradado iba escrito como
   var(--kb-orange-deep), y ese es uno de los TRES tokens que theme-engine.jsx
   reasigna en caliente con el acento de temporada (la regla que esta misma hoja
   deja escrita en la cola de trabajo). En Buen Fin el acento es #D9B26A: toda
   esta banda —titular de 34px, párrafo y cifras, TODO en blanco— se pintaría
   1,99:1 en su mitad derecha, o sea ilegible, y el 8,61:1 que promete el
   comentario de arriba dejaría de ser cierto. El rojo profundo va literal, como
   el resto de los colores que no pueden moverse con la temporada. */
.kb-calc-band {
  position: relative; border-radius: 20px; overflow: hidden;
  background: linear-gradient(135deg, #15141A 0%, #8E2522 100%);
  color: white; text-align: center; padding: 0;
  box-shadow: 0 12px 40px rgba(21,20,26,.25);
}
.kb-calc-band-stripe {
  height: 8px; background: linear-gradient(90deg, #B8322F, #B8322F, #D9B26A, #0E6B5E, #15141A);
}
.kb-calc-band-inner { padding: 34px 28px 30px; max-width: 760px; margin: 0 auto; }
.kb-calc-band-tag {
  display: inline-block; font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700;
  /* La pastilla iba en rgba(255,212,0) —amarillo flúor—; ahora en el dorado de
     la paleta (rgb 217,178,106) al mismo alfa. El texto #D9B26A sobre el fondo
     real de la banda da 9,18:1 (carbón) y 4,32:1 (rojo profundo) ✓. */
  letter-spacing: .12em; color: #D9B26A; background: rgba(217,178,106,.12);
  border: 1px solid rgba(217,178,106,.35); padding: 5px 16px; border-radius: 99px;
}
.kb-calc-band-title {
  font-family: var(--kb-font-display); font-weight: 900; font-size: 34px; line-height: 1.1;
  letter-spacing: -.02em; margin: 14px 0 10px; color: white;
}
/* [LINEA-OUTLET · BLOQUE 13] #FF3D7F era rosa flúor: no hay magenta en esta
   marca. El dorado es el color de la distinción y además se lee sobre la banda
   (9,18:1 sobre carbón · 4,32:1 sobre el rojo profundo; el titular mide 34px,
   texto grande, mínimo 3:1) ✓. */
.kb-calc-band-title em { color: var(--kb-yellow); font-style: normal; }
.kb-calc-band-sub { font-size: 15px; opacity: .9; max-width: 600px; margin: 0 auto; line-height: 1.55; }
.kb-calc-band-sub b { color: #D9B26A; }
.kb-calc-band-stats {
  display: flex; justify-content: center; gap: 14px; margin-top: 22px; flex-wrap: wrap;
}
.kb-calc-band-stats > div {
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.14);
  border-radius: 14px; padding: 12px 20px; min-width: 90px; backdrop-filter: blur(6px);
}
.kb-calc-band-stats b { display: block; font-family: var(--kb-font-display); font-size: 26px; font-weight: 900; color: #D9B26A; line-height: 1; }
.kb-calc-band-stats span { font-size: 11px; opacity: .8; font-family: var(--kb-font-mono); letter-spacing: .04em; }

@media (max-width: 600px) {
  .kb-calc-band-inner { padding: 26px 18px 24px; }
  .kb-calc-band-title { font-size: 24px; }
  .kb-calc-band-sub { font-size: 13px; }
  .kb-calc-band-stats { gap: 8px; }
  .kb-calc-band-stats > div { padding: 10px 14px; min-width: 70px; flex: 1 1 calc(50% - 8px); }
  .kb-calc-band-stats b { font-size: 20px; }
}


/* ─── Stepper +/− en card de producto (cuando ya está en carrito) ─── */
.kb-pcard-stepper {
  flex: 1; display: grid; grid-template-columns: 38px 1fr 38px; align-items: center;
  background: var(--kb-ink); border-radius: 8px; overflow: hidden; height: 36px;
}
.kb-pcard-stepper button {
  background: var(--kb-ink); color: var(--kb-yellow); border: 0; height: 100%;
  font-size: 18px; font-weight: 800; cursor: pointer; line-height: 1;
}
.kb-pcard-stepper button:active { background: #000; }
.kb-pcard-stepper span {
  text-align: center; color: white; font-weight: 800; font-size: 15px;
  font-family: var(--kb-font-display);
}


/* ─── Descripción colapsable en PDP ─── */
.kb-pdp-desc {
  margin-top: 16px; background: white; border: 1px solid var(--kb-line);
  border-radius: 12px; padding: 4px 18px;
}
.kb-pdp-desc summary {
  font-family: var(--kb-font-display); font-weight: 700; font-size: 15px;
  cursor: pointer; padding: 12px 0; list-style: none; display: flex; justify-content: space-between; align-items: center;
}
.kb-pdp-desc summary::-webkit-details-marker { display: none; }
.kb-pdp-desc summary::after { content: "▾"; color: var(--kb-pink-deep); transition: transform .2s; }
.kb-pdp-desc[open] summary::after { transform: rotate(180deg); }
.kb-pdp-desc p { font-size: 13.5px; line-height: 1.6; color: var(--kb-muted); margin: 0 0 10px; padding-bottom: 0; }
.kb-pdp-desc[open] { padding-bottom: 16px; }


/* Descripción PDP · ancho completo (no en columna angosta) */
.kb-pdp-desc-body p { font-size: 13.5px; line-height: 1.6; color: var(--kb-muted); margin: 0 0 10px; }
.kb-pdp-desc-body ul { margin: 6px 0 0; padding-left: 18px; font-size: 13px; line-height: 1.7; color: var(--kb-charcoal); }
.kb-pdp-desc-body b { color: var(--kb-ink); }


/* ──────────────────────────────────────────────────────────────
   Dashboard PRO · "Rosa Editorial"  (scope: .kb-dash-pro)
   Tema rosa para el panel (admin + cuenta). No afecta la tienda.
   No redefine grid-template-columns → conserva responsive.
   ────────────────────────────────────────────────────────────── */
.kb-dash-pro {
  --kbp-pink: var(--kb-pink-deep);   /* #B8322F · paleta 1 */
  --kbp-pink-700: #A32C29;           /* [BLOQUE 22] 6,48:1 sobre la superficie suave; ningún tema lo repinta */
  --kbp-soft: var(--kb-pink-soft);   /* [BLOQUE 22] #F4F4F5: el rosa heredado se neutralizó */
  --kbp-100: var(--kb-pink);         /* #F5DCDA */
  /* [LINEA-OUTLET · BLOQUE 13] El degradado abría en #B8322F y CLAREABA a
     #D4534F, y encima van letras blancas (tarjeta destacada, botones del panel):
     blanco sobre ese extremo claro da 4,08:1 — por debajo del 4,5:1 del texto
     pequeño. Va al revés, del rojo de texto al rojo profundo, los dos de la
     paleta: blanco sobre #A32C29 7,12:1 ✓ y sobre #8E2522 8,61:1 ✓. */
  --kbp-grad: linear-gradient(135deg, #A32C29 0%, #8E2522 100%);
  gap: 26px;
}

/* Fondo crema con tinte rosa, contenido respira */
/* [LINEA-OUTLET · BLOQUE 13] Los dos halos del fondo eran rgba del magenta
   heredado (#E91E63) y de un rosa flúor (#FF5C93). Pasan al rojo outlet
   (rgb 184,50,47) con los mismos alfas: el tinte del panel se mantiene, el color
   ya es de la marca. */
.kb-dash-pro::before {
  content: "";
  position: fixed; inset: 0; z-index: -1;
  background:
    radial-gradient(1100px 520px at 88% -8%, rgba(184,50,47,0.07), transparent 60%),
    radial-gradient(900px 480px at -6% 4%, rgba(184,50,47,0.05), transparent 58%),
    var(--kb-cream);
  pointer-events: none;
}

/* ── Sidebar refinada ───────────────────────────────────────── */
/* [LINEA-OUTLET · BLOQUE 13] #F3D9E3 y #F1E2E8 eran dos rosas malva del clon
   usados como línea; --kb-pink (#F5DCDA) es el rosa suave que SÍ es de la casa y
   hace el mismo papel. Las sombras iban tintadas en rgba(194,24,91) —magenta— y
   pasan al rojo outlet (rgb 184,50,47). Son bordes y sombras: no llevan texto. */
.kb-dash-pro .kb-side {
  border: 1px solid var(--kb-pink);
  border-radius: 16px;
  padding: 10px;
  box-shadow: 0 10px 30px -22px rgba(184,50,47,0.45);
}
.kb-dash-pro .kb-side-head {
  color: var(--kbp-pink-700);
  font-weight: 800;
}
.kb-dash-pro .kb-side-item {
  border-radius: 10px;
  transition: background .15s, color .15s, transform .12s;
}
.kb-dash-pro .kb-side-item:hover { background: var(--kbp-soft); transform: translateX(2px); }
.kb-dash-pro .kb-side-item.on {
  background: var(--kbp-soft);
  color: var(--kbp-pink-700);
  font-weight: 700;
  box-shadow: inset 3px 0 0 var(--kbp-pink);
}
/* [HALLAZGO DE AUDITORÍA · el icono del chip activo] El icono se pintaba con
   --kbp-pink, que es var(--kb-pink-deep) — uno de los tokens que theme-engine.jsx
   REPINTA con el color de temporada. Y el fondo de este estado es #FBEDEC
   (--kbp-soft, estable) tanto en la columna de escritorio como en la píldora de
   ≤900px, así que el icono acababa claro sobre claro. Medido sobre #FBEDEC
   contra el `primary` de los NUEVE temas que declara backend/themes.js (ocho con
   enabled:true y "Mundial" con enabled:false, que igual se puede forzar a mano
   desde el admin: manual_id gana sobre fechas y sobre enabled), más el estado
   sin tema. Fallan 3 de los 9 — y el sin-tema, que es el caso más común:
     · sin tema      #B8322F → 3.94:1  ✗ (ya por debajo del 4.5:1 de la casa)
     · San Valentín  #B8322F → 3.94:1  ✗
     · Halloween     #F57C00 → 2.45:1  ✗
     · Año Nuevo     #C9A227 → 2.19:1  ✗
   (Los otros seis pasan por el color que les tocó, no por diseño: Buen Fin
   16.05:1 con su #15141A casi negro, Día de Muertos 8.51:1, Día de las Madres
   6.31:1, Fiestas Patrias y Mundial 6.20:1, Navidad 5.91:1. El
   informe apuntaba a .kb-side-item.on .kb-icon → var(--kb-yellow); esa regla
   existe pero aquí NO gana — este selector la supera en especificidad. Donde sí
   manda —la hoja "Más" del admin y las listas de brands/cotizaciones— el fondo
   activo sigue siendo --kb-ink y ahí #D9B26A da 13.48:1, que está bien.)
   Se ancla al MISMO literal que ya usa el texto del chip: #A32C29 vía
   --kbp-pink-700, que ningún tema toca → 5.32:1 sobre #FBEDEC EN LOS NUEVE y
   sin tema (es un literal: el número no depende del tema, por eso es uno solo),
   y el icono y su etiqueta dejan de ir por caminos distintos. */
.kb-dash-pro .kb-side-item.on .kb-icon { color: var(--kbp-pink-700); opacity: 1; }
.kb-dash-pro .kb-side-divider { background: var(--kb-pink); }
/* [ADM-MENU-SOL] La cabecera de grupo hereda el rosa del panel pro, pero desde
   --kbp-pink-700 (#A32C29, literal fijo → 5.87:1 sobre #fff) y NUNCA desde
   --kbp-pink, que es var(--kb-pink-deep) y el motor de temas sí repinta. */
.kb-dash-pro .kb-side-grp { color: var(--kbp-pink-700); }
.kb-dash-pro .kb-side-pinned { border-bottom-color: var(--kb-pink); }

/* ── Encabezado editorial ───────────────────────────────────── */
.kb-dash-pro .kb-eyebrow { color: var(--kbp-pink-700); }
.kb-dash-pro .kb-h2 { font-size: 32px; letter-spacing: -0.02em; }

/* ── Tarjetas KPI ───────────────────────────────────────────── */
.kb-dash-pro .kb-stat-grid { gap: 16px; }
.kb-dash-pro .kb-stat {
  border-radius: 18px;
  padding: 20px;
  border: 1px solid var(--kb-pink);
  box-shadow: 0 8px 26px -22px rgba(27,24,18,0.5);
  transition: transform .14s, box-shadow .14s;
}
.kb-dash-pro .kb-stat:hover {
  transform: translateY(-3px);
  box-shadow: 0 16px 36px -24px rgba(184,50,47,0.5);
}
.kb-dash-pro .kb-stat .kb-stat-val { font-size: 30px; }

/* Tarjeta destacada en degradado rosa */
.kb-dash-pro .kb-stat-feat {
  background: var(--kbp-grad);
  border-color: transparent;
  box-shadow: 0 16px 40px -18px rgba(184,50,47,0.6);
  position: relative; overflow: hidden;
}
.kb-dash-pro .kb-stat-feat::after {
  content: ""; position: absolute; right: -40px; top: -40px;
  width: 150px; height: 150px; border-radius: 50%;
  background: radial-gradient(circle, rgba(255,255,255,0.22), transparent 70%);
  pointer-events: none;
}
.kb-dash-pro .kb-stat-feat .kb-stat-label,
.kb-dash-pro .kb-stat-feat .kb-stat-val,
.kb-dash-pro .kb-stat-feat .kb-stat-delta { color: #fff !important; }
.kb-dash-pro .kb-stat-feat .kb-stat-label { opacity: 0.85; }
.kb-dash-pro .kb-stat-feat .kb-stat-delta { opacity: 0.95; }

/* ── Paneles / secciones ────────────────────────────────────── */
.kb-dash-pro .kb-card-section,
.kb-dash-pro .kb-card {
  border-radius: 18px;
  border: 1px solid var(--kb-pink);
  box-shadow: 0 8px 26px -24px rgba(27,24,18,0.5);
}
.kb-dash-pro .kb-card-section > .head {
  background: linear-gradient(180deg, var(--kbp-soft), #fff);
  border-bottom: 1px solid var(--kb-pink);
}
.kb-dash-pro .kb-card-section > .head h3,
.kb-dash-pro .kb-card-section > .head .kb-h3 {
  font-family: var(--kb-font-display);
}

/* Botones primarios del panel → rosa */
.kb-dash-pro .kb-btn-dark {
  background: var(--kbp-grad);
  border-color: transparent;
}
.kb-dash-pro .kb-btn-dark:hover { filter: brightness(1.05); }

/* Tabla: hover rosado en vez de azul */
.kb-dash-pro .kb-table tr:hover td { background: var(--kbp-soft); }
.kb-dash-pro .kb-table th { background: var(--kbp-soft); color: var(--kbp-pink-700); }

/* Sub-tabs activos en rosa */
.kb-dash-pro .kb-tab.on {
  background: var(--kbp-pink);
  color: #fff;
  border-color: var(--kbp-pink);
}


/* ──────────────────────────────────────────────────────────────
   BLOG · diseño editorial  (lista .kb-blog + artículo .kb-article)
   ────────────────────────────────────────────────────────────── */

/* ── Cabecera de la lista ───────────────────────────────────── */
.kb-blog { padding: 40px 0 76px; }
.kb-blog-head { padding: 4px 0 28px; border-bottom: 1px solid var(--kb-line); margin-bottom: 36px; }
.kb-blog-eyebrow { font-family: var(--kb-font-mono); font-size: 12px; text-transform: uppercase; letter-spacing: .14em; color: var(--kb-pink-deep); font-weight: 600; }
.kb-blog-title { font-family: var(--kb-font-display); font-size: clamp(30px, 5vw, 46px); line-height: 1.06; letter-spacing: -.02em; margin: 10px 0 12px; }
.kb-blog-sub { color: var(--kb-muted); font-size: 17px; max-width: 580px; line-height: 1.5; margin: 0; }
.kb-blog-crumb { margin-top: 18px; font-size: 12.5px; color: var(--kb-muted); display: flex; gap: 8px; }
.kb-blog-crumb span { cursor: pointer; }
.kb-blog-crumb .sep { opacity: .5; cursor: default; }
.kb-blog-crumb .cur { color: var(--kb-ink); cursor: default; font-weight: 600; }
.kb-blog-crumb span:not(.sep):not(.cur):hover { color: var(--kb-pink-deep); }

.kb-blog-empty { text-align: center; color: var(--kb-muted); padding: 64px 0; font-size: 15px; }
.kb-blog-empty-emoji { font-size: 40px; margin-bottom: 8px; }

.kb-blog-kicker { font-family: var(--kb-font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--kb-pink-deep); font-weight: 600; }

/* Avatar con iniciales */
/* [LINEA-OUTLET · BLOQUE 13] Mismo caso que --kbp-grad: iniciales BLANCAS sobre
   un degradado que clareaba hasta #D4534F (4,08:1). Se invierte a los dos rojos
   de la paleta: blanco sobre #A32C29 7,12:1 ✓ y sobre #8E2522 8,61:1 ✓. */
.kb-blog-avatar { width: 42px; height: 42px; border-radius: 50%; flex: none; display: flex; align-items: center; justify-content: center; font-family: var(--kb-font-display); font-weight: 700; font-size: 15px; color: #fff; background: linear-gradient(135deg, #A32C29 0%, #8E2522 100%); }
.kb-blog-avatar.sm { width: 26px; height: 26px; font-size: 11px; }

/* ── Artículo destacado ─────────────────────────────────────── */
.kb-blog-feature { display: grid; grid-template-columns: 1.1fr 1fr; border: 1px solid var(--kb-line); border-radius: 22px; overflow: hidden; background: #fff; cursor: pointer; margin: 8px 0 40px; transition: transform .18s, box-shadow .18s; }
.kb-blog-feature:hover { transform: translateY(-4px); box-shadow: 0 26px 56px -32px rgba(184,50,47,.5); }
.kb-blog-feature-media { position: relative; min-height: 340px; }
.kb-blog-chip { position: absolute; top: 16px; left: 16px; background: var(--kb-ink); color: #fff; font-family: var(--kb-font-mono); font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em; padding: 6px 12px; border-radius: 999px; }
.kb-blog-feature-body { padding: 34px 36px; display: flex; flex-direction: column; justify-content: center; }
.kb-blog-feature-title { font-family: var(--kb-font-display); font-size: 28px; line-height: 1.14; letter-spacing: -.01em; margin: 10px 0 12px; }
.kb-blog-feature-excerpt { color: var(--kb-muted); line-height: 1.6; font-size: 15px; margin: 0; }
.kb-blog-meta { display: flex; align-items: center; gap: 12px; margin-top: 22px; }
.kb-blog-meta strong { display: block; font-size: 13.5px; }
.kb-blog-meta small { display: block; color: var(--kb-muted); font-size: 12px; }
.kb-blog-readmore { margin-left: auto; color: var(--kb-pink-deep); font-weight: 700; font-size: 13.5px; white-space: nowrap; }

/* ── Grid de tarjetas ───────────────────────────────────────── */
.kb-blog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 24px; }
.kb-blog-card { display: flex; flex-direction: column; border: 1px solid var(--kb-line); border-radius: 18px; overflow: hidden; background: #fff; cursor: pointer; transition: transform .16s, box-shadow .16s; }
.kb-blog-card:hover { transform: translateY(-4px); box-shadow: 0 22px 46px -32px rgba(0,0,0,.45); }
.kb-blog-card-media { height: 190px; }
.kb-blog-card-body { padding: 18px 20px 20px; display: flex; flex-direction: column; flex: 1; }
.kb-blog-card-title { font-family: var(--kb-font-display); font-size: 19px; line-height: 1.25; margin: 8px 0; }
.kb-blog-card-excerpt { color: var(--kb-muted); font-size: 13.5px; line-height: 1.55; flex: 1; margin: 0 0 14px; }
.kb-blog-card-foot { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--kb-muted); }
.kb-blog-card-foot .dot { opacity: .5; }

/* ── Artículo individual ────────────────────────────────────── */
.kb-article { padding: 0 0 76px; }
.kb-article-crumb { padding-top: 28px; max-width: 760px; margin: 0 auto; font-size: 12.5px; color: var(--kb-muted); display: flex; gap: 8px; flex-wrap: wrap; }
.kb-article-crumb span { cursor: pointer; }
.kb-article-crumb .sep { opacity: .5; cursor: default; }
.kb-article-crumb .cur { color: var(--kb-ink); cursor: default; }
.kb-article-crumb span:not(.sep):not(.cur):hover { color: var(--kb-pink-deep); }

.kb-article-head { max-width: 760px; margin: 20px auto 0; }
.kb-article-kicker { font-family: var(--kb-font-mono); font-size: 11.5px; text-transform: uppercase; letter-spacing: .12em; color: var(--kb-pink-deep); font-weight: 600; }
.kb-article-title { font-family: var(--kb-font-display); font-size: clamp(30px, 5.2vw, 46px); line-height: 1.08; letter-spacing: -.02em; margin: 12px 0 16px; }
.kb-article-lead { font-size: 19px; line-height: 1.55; color: var(--kb-muted); margin: 0; }
.kb-article-meta { display: flex; align-items: center; gap: 12px; margin-top: 24px; }
.kb-article-meta strong { display: block; font-size: 14px; }
.kb-article-meta small { display: block; color: var(--kb-muted); font-size: 12.5px; }

/* Hero */
.kb-article-hero { margin: 30px 0 42px; }
/* [PSI-CLS-BLOG] aspect-ratio panorámico reserva el espacio de la portada antes
   de que la imagen cargue, evitando el salto de layout grande medido por PSI.
   Solo la portada: .kb-article-body/.kb-article-gallery llevan fotos de tamaño
   variable dentro del cuerpo del artículo y forzarles un aspect-ratio fijo las
   deformaría sin conocer sus proporciones reales (fuera de alcance de este
   arreglo — ver nota en el commit). */
.kb-article-hero img { display: block; width: 100%; max-width: 940px; margin: 0 auto; border-radius: 20px; box-shadow: 0 34px 64px -34px rgba(15,14,11,.45); aspect-ratio: 16/9; object-fit: cover; }

/* Cuerpo del artículo · tipografía rica */
/* [LINEA-OUTLET · BLOQUE 13] El cuerpo del artículo iba en #211E18, un casi
   negro pardo que no es de nadie. El carbón de la marca da 18,31:1 sobre blanco
   ✓ y es el mismo con el que se escribe el resto del sitio. */
.kb-article-body { max-width: 760px; margin: 0 auto; font-size: 17.5px; line-height: 1.8; color: var(--kb-ink); }
.kb-article-body > *:first-child { margin-top: 0; }
.kb-article-body p { margin: 0 0 22px; }
.kb-article-body > p:first-child::first-letter { font-family: var(--kb-font-display); font-weight: 700; font-size: 58px; line-height: .82; float: left; padding: 6px 12px 0 0; color: var(--kb-pink-deep); }
.kb-article-body h2 { font-family: var(--kb-font-display); font-size: 28px; line-height: 1.2; letter-spacing: -.01em; margin: 46px 0 14px; color: var(--kb-ink); }
.kb-article-body h3 { font-family: var(--kb-font-display); font-size: 21px; line-height: 1.25; margin: 34px 0 10px; color: var(--kb-ink); }
.kb-article-body a { color: var(--kb-pink-deep); text-decoration: underline; text-underline-offset: 3px; text-decoration-thickness: 1.5px; }
.kb-article-body a:hover { color: #8E2522; }
.kb-article-body strong, .kb-article-body b { color: var(--kb-ink); font-weight: 700; }
.kb-article-body ul, .kb-article-body ol { margin: 0 0 24px; padding: 0; list-style: none; }
.kb-article-body li { position: relative; padding-left: 28px; margin-bottom: 11px; line-height: 1.7; }
.kb-article-body ul li::before { content: ""; position: absolute; left: 5px; top: 11px; width: 8px; height: 8px; border-radius: 2px; background: var(--kb-pink-deep); transform: rotate(45deg); }
.kb-article-body ol { counter-reset: kbli; }
.kb-article-body ol li { padding-left: 34px; }
.kb-article-body ol li::before { counter-increment: kbli; content: counter(kbli); position: absolute; left: 0; top: 2px; width: 22px; height: 22px; border-radius: 50%; background: var(--kb-pink-soft); color: var(--kb-pink-deep); font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.kb-article-body blockquote { margin: 30px 0; padding: 6px 0 6px 24px; border-left: 3px solid var(--kb-pink-deep); font-style: italic; font-size: 19px; line-height: 1.6; color: var(--kb-ink); }
.kb-article-body img { width: 100%; border-radius: 14px; margin: 26px 0; }
.kb-article-body hr { border: 0; border-top: 1px solid var(--kb-line); margin: 36px 0; }

/* Galería extra */
.kb-article-gallery { max-width: 760px; margin: 38px auto 0; display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px; }
.kb-article-gallery img { width: 100%; border-radius: 12px; border: 1px solid var(--kb-line); }

/* CTA final */
/* [LINEA-OUTLET · BLOQUE 13] Bloque de cierre con TODO el texto en blanco sobre
   el degradado que clareaba a #D4534F (4,08:1 en ese extremo, y el párrafo va a
   14px). Mismos dos rojos de la paleta que el avatar: 7,12:1 y 8,61:1 ✓. La
   sombra deja el magenta viejo. */
.kb-article-cta { max-width: 760px; margin: 52px auto 0; background: linear-gradient(135deg, #A32C29 0%, #8E2522 100%); color: #fff; border-radius: 22px; padding: 30px 34px; display: flex; align-items: center; justify-content: space-between; gap: 22px; flex-wrap: wrap; box-shadow: 0 24px 50px -28px rgba(184,50,47,.6); }
.kb-article-cta-kicker { font-family: var(--kb-font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: .08em; opacity: .85; }
.kb-article-cta h3 { font-family: var(--kb-font-display); font-size: 22px; line-height: 1.2; margin: 6px 0 6px; color: #fff; }
.kb-article-cta p { margin: 0; font-size: 14px; opacity: .92; max-width: 460px; }
.kb-article-cta .kb-btn-primary { background: #fff; color: var(--kb-pink-deep); border: none; font-weight: 700; white-space: nowrap; }
.kb-article-cta .kb-btn-primary:hover { background: var(--kb-cream); }

.kb-article-foot { max-width: 760px; margin: 36px auto 0; padding-top: 24px; border-top: 1px solid var(--kb-line); }

/* Responsive */
@media (max-width: 760px) {
  .kb-blog-feature { grid-template-columns: 1fr; }
  .kb-blog-feature-media { min-height: 210px; }
  .kb-blog-feature-body { padding: 24px 22px; }
  .kb-article-body { font-size: 16.5px; }
  .kb-article-body > p:first-child::first-letter { font-size: 48px; }
  .kb-article-cta { padding: 24px 22px; }
}

/* [PSI-BLOG-CLS-SKELETON] Esqueleto de carga del artículo ──────────────────
   Reemplaza el antiguo "Cargando…" (una línea de texto) por bloques que
   reservan ~el mismo alto que el contenido real, para minimizar el CLS al
   llegar el fetch de /api/posts/:slug. Cada alto de barra sale de la tipo-
   grafía real que sustituye (ver reglas .kb-article-* arriba), no de números
   al azar:
   · título   → clamp(30px,5.2vw,46px) × line-height 1.08 ≈ clamp(32px,5.6vw,50px)
   · resumen  → 19px × line-height 1.55 ≈ 29.5px
   · cuerpo   → 17.5px × line-height 1.8 ≈ 31.5px
   La animación usa solo `opacity` (barata de renderizar) — nada de
   background-position en movimiento. */
.kb-skel-bar {
  display: block;
  background: var(--kb-line);
  border-radius: 7px;
  animation: kbSkelPulse 1.4s ease-in-out infinite;
}
@keyframes kbSkelPulse { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .kb-skel-bar { animation: none; opacity: .75; } }

.kb-skel-crumb { width: 140px; height: 12px; }
.kb-skel-kicker { width: 90px; height: 11px; }

.kb-skel-title { display: flex; flex-direction: column; gap: 10px; margin: 12px 0 16px; }
.kb-skel-title-line { height: clamp(32px, 5.6vw, 50px); width: 100%; }
.kb-skel-title-line.short { width: 58%; }

.kb-skel-lead { display: flex; flex-direction: column; gap: 8px; }
.kb-skel-lead-line { height: 29.5px; width: 100%; }
.kb-skel-lead-line.short { width: 68%; }

.kb-skel-meta-line { width: 120px; height: 14px; margin-bottom: 6px; }
.kb-skel-meta-line.sm { width: 90px; height: 12.5px; margin-bottom: 0; }
.kb-skel-avatar { width: 42px; height: 42px; border-radius: 50%; flex: none; }

/* Portada: mismo valor de aspect-ratio (16/9) que .kb-article-hero img trae
   en la regla de arriba — NO se inventa un ratio nuevo, se duplica el mismo
   número para que el hueco reservado sea idéntico al de la foto real. No se
   comparte la regla vía selector porque .kb-article-hero img es un <img> y
   este placeholder es un <span>; mismo valor, misma caja. */
.kb-skel-hero-img { display: block; width: 100%; max-width: 940px; margin: 0 auto; border-radius: 20px; aspect-ratio: 16/9; }

.kb-skel-body { max-width: 760px; margin: 0 auto; display: flex; flex-direction: column; gap: 14px; }
.kb-skel-body-line { height: 31.5px; width: 100%; }
.kb-skel-body-line.short { width: 45%; }

/* [CLS-BLOG-LISTA] Esqueleto de la LISTA del blog (BlogListSkeleton) ────────
   Los bloques de IMAGEN no llevan alto propio aquí: el destacado usa
   .kb-blog-feature-media (min-height: 340px) y las tarjetas
   .kb-blog-card-media (height: 190px) — las MISMAS reglas de arriba que usa
   la lista real, para que el hueco reservado sea el de verdad. Lo único que
   agrega .kb-skel-media es quitar el radio de .kb-skel-bar: esas cajas van
   pegadas al borde de una tarjeta que ya redondea con overflow: hidden.
   Los altos de TEXTO salen de la tipografía real que sustituyen (mismo método
   que el esqueleto del artículo, arriba):
   · título destacado  → 28px   × line-height 1.14 ≈ 32px
   · resumen destacado → 15px   × line-height 1.6  ≈ 24px
   · título tarjeta    → 19px   × line-height 1.25 ≈ 24px
   · resumen tarjeta   → 13.5px × line-height 1.55 ≈ 21px
   · pie de tarjeta    → 12.5px
   Los márgenes de cada grupo copian los de la regla real que reemplazan. */
.kb-skel-media { border-radius: 0; }
.kb-skel-lines { display: flex; flex-direction: column; gap: 8px; }

.kb-skel-feature-title { margin: 10px 0 12px; }          /* = margen de .kb-blog-feature-title */
.kb-skel-feature-title-line { height: 32px; width: 100%; }
.kb-skel-feature-title-line.short { width: 62%; }
.kb-skel-feature-excerpt-line { height: 24px; width: 100%; }
.kb-skel-feature-excerpt-line.short { width: 70%; }

.kb-skel-card-title { margin: 8px 0; }                    /* = margen de .kb-blog-card-title */
.kb-skel-card-title-line { height: 24px; width: 100%; }
.kb-skel-card-title-line.short { width: 64%; }
.kb-skel-card-excerpt { margin: 0 0 14px; }               /* = margen de .kb-blog-card-excerpt */
.kb-skel-card-excerpt-line { height: 21px; width: 100%; }
.kb-skel-card-excerpt-line.short { width: 55%; }
.kb-skel-foot-line { height: 12.5px; width: 120px; }
.kb-skel-avatar.sm { width: 26px; height: 26px; }         /* = .kb-blog-avatar.sm */

/* El esqueleto no lleva a ninguna parte: ni cursor de mano ni el hover que
   levanta la tarjeta real (esa animación sobre un placeholder sugiere que se
   puede pulsar algo que todavía no existe). */
.kb-blog-skel .kb-blog-feature, .kb-blog-skel .kb-blog-card { cursor: default; }
.kb-blog-skel .kb-blog-feature:hover, .kb-blog-skel .kb-blog-card:hover { transform: none; box-shadow: none; }

/* Solo para lectores de pantalla: los bloques placeholder de arriba son
   decorativos (aria-hidden), este aviso es el único texto que un lector de
   pantalla anuncia mientras carga el artículo. */
.kb-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* [LINEA-OUTLET · BLOQUE 12] Aquí vivía la decoración de los temas de temporada
   (.kb-decor, .kb-bunting, .kb-fall, la cinta de esquina de las tarjetas y la franja de 16px
   que los banderines le robaban al header). Los temas se eliminaron y con ellos su CSS. */

/* ════ UX móvil del cliente (auditoría 2026-06) ════ */
/* Drawer de filtros del catálogo: en escritorio, controles ocultos. */
.kb-filters-toggle, .kb-filters-head, .kb-filters-overlay { display: none; }
@media (max-width: 1100px) {
  /* Botón "Filtros": los PRODUCTOS quedan visibles primero; los filtros van a un panel. */
  .kb-filters-toggle {
    display: block; width: 100%; margin-bottom: 12px; padding: 11px 14px;
    /* [LINEA-OUTLET · BLOQUE 13] Los fallback de estos var() eran grises
       genéricos (#ccc, #eee, #888) de antes de la paleta. Ahora el respaldo es
       el valor real del token al que acompañan. */
    border: 1px solid var(--kb-line-strong); border-radius: 10px; background: #fff;
    font-weight: 700; font-size: 14px; cursor: pointer; text-align: left;
  }
  .kb-catalog > .kb-filters {
    position: fixed; top: 0; left: 0; bottom: 0; width: min(86vw, 340px); z-index: 120;
    background: #fff; margin: 0; padding: 16px; overflow-y: auto; max-height: 100vh;
    transform: translateX(-100%); transition: transform .25s ease; box-shadow: 2px 0 22px rgba(0,0,0,.18);
  }
  .kb-catalog > .kb-filters.kb-filters-open { transform: translateX(0); }
  .kb-filters-overlay { display: block; position: fixed; inset: 0; background: rgba(0,0,0,.45); z-index: 110; }
  .kb-filters-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding-bottom: 10px; border-bottom: 1px solid var(--kb-line); }
  .kb-filters-close { background: none; border: 0; font-size: 22px; line-height: 1; cursor: pointer; color: var(--kb-muted, #5C5A60); padding: 4px 8px; }
  .kb-filter-row { padding: 10px 0; }
}
/* Formularios de 2 columnas (Mi perfil, Direcciones) → 1 columna en móvil. */
.kb-form-2col { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 560px) { .kb-form-2col { grid-template-columns: 1fr; } }
/* Re-compra rápida: grid responsive (4 → 2 → 1). */
.kb-rebuy-grid { grid-template-columns: repeat(4, 1fr); }
@media (max-width: 900px) { .kb-rebuy-grid { grid-template-columns: repeat(2, 1fr) !important; } }
@media (max-width: 440px) { .kb-rebuy-grid { grid-template-columns: 1fr !important; } }
/* Catálogo en pantallas muy angostas → 1 columna. */
@media (max-width: 400px) { .kb-product-grid { grid-template-columns: 1fr !important; gap: 10px !important; } }
/* Hamburguesa con tap target accesible (44×44). */
@media (max-width: 900px) { .kb-burger { width: 44px; height: 44px; } }
/* FAB del bot: no tapar los CTA inferiores en móvil (queda sobre la barra inferior). */
@media (max-width: 700px) { .kb-ai-fab { bottom: 84px; } }
/* Tablas del panel del cliente: scroll horizontal táctil en móvil. */
@media (max-width: 768px) {
  .kb-dash-grid .kb-table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; }
}
/* Checkout (pago): el grid datos+resumen usa 1.4fr/1fr en escritorio; en móvil baja a 1
   columna y el resumen deja de ser sticky. Estaba inline y no colapsaba → en el teléfono el
   formulario y el resumen quedaban aplastados y se cortaba la información. */
/* [CO-RECORTE] La MISMA receta que .kb-pdp (l. 794): `1fr` es `minmax(AUTO, 1fr)`, y ese
   AUTO significa "mínimo = min-content del contenido". Medido en Chrome a 375px con tres
   renglones de 101 caracteres: el nombre del producto del resumen —un <div> con
   white-space:nowrap— pedía 571px de min-content, el <aside> subía a 685.56px y la pista
   crecía con él (grid-template-columns computado: 685.562px en una pantalla de 375). Como
   html/body/.kb-main llevan overflow-x: clip, ese sobrante NO sacaba barra: se recortaba —
   119 elementos fuera de pantalla y 18 de los 20 campos del formulario inalcanzables.
   `overflow:hidden` + `text-overflow:ellipsis` NO bajan el min-content, y el `min-width:0`
   que ya traía el envoltorio del nombre tampoco: hay que cortar la cadena en los DOS sitios
   de la receta — minmax(0, …) en las pistas Y min-width:0 en los hijos directos del grid.
   Sin lo segundo, el `min-width:auto` de un ítem de grid vuelve a poner su min-content como
   mínimo y el aside desbordaría la pista igual. Con la cadena cortada el nombre por fin usa
   su ellipsis y los sellos de confianza envuelven en vez de partirse a media palabra. */
.kb-checkout-grid { grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); }
.kb-checkout-grid > * { min-width: 0; }
@media (max-width: 760px) {
  .kb-checkout-grid { grid-template-columns: minmax(0, 1fr) !important; }
  .kb-checkout-grid > aside { position: static !important; top: auto !important; }
}
/* Full-bleed del checkout: cancela EXACTAMENTE el padding de .kb-main para ir
   borde a borde. El margen negativo DEBE igualar ese padding en cada breakpoint;
   antes estaba fijo en -22px inline y en móvil (.kb-main baja a 14px en ≤900px)
   se pasaba 8px por lado → como .kb-main tiene overflow-x:clip, las columnas se
   CORTABAN a la derecha en el teléfono. Mantener en sync con .kb-main (:371,:1073). */
.kb-checkout-page { margin: -22px; }         /* .kb-main padding: 22px (escritorio) */
@media (max-width: 900px) {
  .kb-checkout-page { margin: -14px; }       /* .kb-main padding: 14px (≤900px) */
}

/* ─── CONV-2E · Distintivos de los métodos de pago (propuesta 2e del handoff
   design_handoff_kbotanas_conversion). Cambio PURAMENTE COSMÉTICO: el botón de
   cada método, su onClick y el estado de selección quedan igual; solo se le
   añade a la derecha una pastilla de TEXTO con el nombre de la red/marca.
   Badges de texto y nada más: sin logos de terceros descargados, sin <img> ni
   SVG de marca (lo pide el handoff explícitamente).

   ── Por qué el mapa vive en JSX y no aquí ──
   La lista de métodos es DINÁMICA (bandera del admin × pasarela conectada):
   puede venir vacía o con cualquier subconjunto, y ya hoy incluye "deposito",
   que no lleva distintivo. Por eso el JSX resuelve por id con hasOwnProperty y
   omite el <span> entero cuando no hay entrada — estas clases nunca se aplican
   "a medias" ni dependen de la posición del método en la lista.

   ── CONTRASTE · un solo criterio, 4.5:1 (texto pequeño, 11px) ──
   Todas las cifras son ratios WCAG 2.x calculados sobre el color FIJO del
   propio badge, no sobre el fondo del botón. Esto último es deliberado: el
   botón del método cambia de blanco a --kb-pink-soft (#FBEDEC) al quedar
   seleccionado, así que un badge sin fondo propio tendría DOS contrastes
   distintos según el estado. Las tres pastillas de red llevan fondo blanco
   fijado (no `transparent`) justo para que el estado seleccionado no pueda
   degradarlas — el mismo agujero que el mockup del handoff sí tiene.
     · VISA  #1A1F71 sobre #FFFFFF → 14.21:1  ✓ (color del mockup)
     · AMEX  #016FD0 sobre #FFFFFF →  5.02:1  ✓ (color del mockup)
     · MC    #C8102E sobre #FFFFFF →  5.88:1  ✓ — SUSTITUYE al #EB001B del
       mockup, que da 4.61:1 sobre blanco (pasa raspando) y 4.18:1 ✗ si el
       badge heredara el rosa del estado seleccionado, como hace el mockup.
       Mismo criterio ya sentado en .kb-pb-b2b-off y .kb-bm-pink: cuando el
       color del handoff no llega, se cambia por el más cercano que sí, y se
       documenta. Sigue siendo rojo Mastercard de un vistazo.
     · OXXO  #FFFFFF sobre #E20A16 →  4.90:1  ✓ (el hex EXACTO del handoff)
     · SPEI  #FFFFFF sobre #12306B → 12.61:1  ✓ (el hex EXACTO del handoff)
     · PayPal #FFFFFF sobre #003087 → 11.84:1 ✓ (ver decisión abajo)
   [HALLAZGO DE AUDITORÍA · tercera vez que se documenta lo mismo] Ni un solo
   color de aquí sale de un token que theme-engine.jsx:27 repinte
   (--kb-orange, --kb-pink-deep, --kb-orange-deep, --kb-theme-*): son hex
   literales de marca, y el único var() es --kb-line-strong para un BORDE, no
   para texto. En "Buen Fin" (accent #D9B26A) estos badges se ven idénticos.
   Lo cubre tests/legibilidad-datos-conversion.test.js.

   [DECISIÓN · PayPal] El handoff no lo cubre (solo nombra tarjeta, OXXO y
   SPEI). Se le da badge sólido "PayPal" en su azul #003087 en vez de dejarlo
   sin distintivo: la propuesta es "que cada método se reconozca de un vistazo"
   y dejar un hueco en la única fila sin marca rompe la rejilla justo donde se
   decide el pago. La regla que queda escrita: el badge lleva color de marca
   cuando NOMBRA al método/proveedor (OXXO, SPEI, PayPal) y va en pastilla
   neutra cuando nombra las REDES que acepta la tarjeta (VISA/MC/AMEX), que no
   son el método.

   [CONTRADICCIÓN DEL HANDOFF] El mockup dibuja estos badges a 9px, pero la
   regla transversal del MISMO handoff fija "texto de datos ≥ 11px". Gana la
   regla general — igual que en CONV-2D (10px → 11px) y en CONV-2B (42 → 44px).

   ── Móvil (≤900px, la prioridad) ──
   El grupo no encoge (flex-shrink:0) pero se topa en 42% del ancho del botón y
   ENVUELVE: con las tres redes en un teléfono de 320-360px pasa a dos renglones
   en vez de empujar el nombre del método fuera de la tarjeta. Al bloque de
   texto se le da min-width:0 + overflow-wrap para que "crédito/débito" parta en
   vez de desbordar. El radio es hermano flex con flex-shrink:0 y el badge va
   DESPUÉS del texto: no hay posicionamiento absoluto, así que no puede taparlo. */
.kb-pm-info { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.kb-pm-badges {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end;
  gap: 4px; flex-shrink: 0; max-width: 42%;
}
/* Medido en Chrome sobre el checkout real a 320px: con el 42% de arriba el
   bloque de texto se queda en 88px y "crédito/débito" (≈100px a 13.5px/700)
   parte a media palabra ("crédito/débi | to"). Bajando el tope a 34% el grupo
   de badges cabe igual —VISA+MC en un renglón, AMEX en el siguiente— y el
   nombre del método recupera ~18px, los justos para no partirse. Solo baja el
   ANCHO del grupo: el tamaño del badge sigue en 11px (regla del handoff). */
@media (max-width: 400px) { .kb-pm-badges { max-width: 34%; } }
.kb-pm-badge {
  font-family: var(--kb-font-ui);
  font-size: 11px; font-weight: 800; line-height: 1.1; letter-spacing: 0.02em;
  padding: 4px 6px; border-radius: 4px; border: 1px solid transparent;
  white-space: nowrap;
}
/* Redes de tarjeta: pastilla blanca FIJA (ver nota de contraste arriba). */
.kb-pm-badge-visa, .kb-pm-badge-mc, .kb-pm-badge-amex { background: #FFFFFF; border-color: var(--kb-line-strong); }
.kb-pm-badge-visa { color: #1A1F71; font-style: italic; }
.kb-pm-badge-mc { color: #C8102E; }
.kb-pm-badge-amex { color: #016FD0; }
/* Métodos con marca propia: relleno sólido + texto blanco. */
.kb-pm-badge-oxxo { background: #E20A16; color: #FFFFFF; font-weight: 900; letter-spacing: 0.04em; }
.kb-pm-badge-spei { background: #12306B; color: #FFFFFF; }
.kb-pm-badge-paypal { background: #003087; color: #FFFFFF; }

/* CONV-2E · El botón final ya decía "🔒 Pagar {mxn(total)} →" y ya era de ancho
   completo (.kb-btn-block). Lo único que NO cumplía era el alto: .kb-btn-lg
   mide 46px y la propuesta pide 48px. Se ajusta por clase, sin tocar
   .kb-btn-lg (lo usan decenas de botones del sitio). La especificidad
   (.kb-btn.kb-co-pay) gana sobre .kb-btn-lg, y el @media de ≤480px que pisa
   padding/font-size de .kb-btn-lg no toca la altura (box-sizing: border-box). */
.kb-btn.kb-co-pay { width: 100%; height: 48px; }

/* ─── CONV-PAY-TRUST · Sellos de métodos de pago en la FICHA de producto ───
   El dueño, tras ver la ficha: "después de envío gratis también agrega en
   pequeño y bien estructurado los banners de medios y/o métodos de pago, eso
   genera confianza". Va justo debajo del <TermometroEnvio variant="pdp">.

   ── Tira, no tarjeta ──
   "En pequeño": una sola línea sin fondo, sin borde y sin sombra — nada que
   compita con el aviso de envío que tiene justo encima ni con los CTAs.

   ── Sin margin propio, a propósito ──
   La tira es hija directa de .kb-pdp-info, que es flex en columna con gap:14px:
   ese gap YA separa cada bloque de la ficha. Un margin aquí se SUMARÍA al gap
   (en flex los márgenes no colapsan) y la tira quedaría flotando más lejos del
   aviso de envío que el resto de la columna entre sí — justo lo contrario de lo
   que pidió el dueño ("después de envío gratis"). Se descartó también un
   margin-top negativo para cancelar el margen propio del termómetro: cuando la
   cotización falla el termómetro devuelve null y ese −8px se comería el espacio
   del bloque anterior. El ritmo lo manda el contenedor.

   ── Por qué REUSA .kb-pm-badge-* del checkout ──
   Son las mismas pastillas que el cliente ve en el paso de pago, con los mismos
   colores y los mismos ratios ya calculados en el bloque CONV-2E de arriba. La
   variante compacta .kb-paytrust-badge SOLO baja el padding (6px→5px de lado,
   4px→3px arriba): NO toca color, NO toca background y NO toca font-size, así
   que no hay ningún contraste que recalcular — los de CONV-2E siguen siendo los
   válidos. Un método nuevo se define una vez y se ve igual en las dos pantallas.

   ── Contraste del rótulo (lo ÚNICO nuevo con texto) ──
   .kb-paytrust-lbl usa --kb-muted (#5C5A60) sobre --kb-paper (#FFFFFF, el fondo
   del body y de la columna de la ficha) → 5.95:1 ✓ para 11px. Es el mismo
   criterio ya sentado en .kb-pb-nota: --kb-muted y NO --kb-mute2 (#8D8880,
   3.15:1), que a este tamaño no llega a AA.
   [HALLAZGO DE AUDITORÍA · cuarta vez] --kb-muted NO está entre los tokens que
   theme-engine.jsx repinta (--kb-orange, --kb-pink-deep, --kb-orange-deep,
   --kb-theme-*), así que en "Buen Fin" (acento amarillo #D9B26A) el rótulo se
   ve idéntico. Queda cubierto en tests/legibilidad-datos-conversion.test.js.

   ── 11px, no 9px ──
   Misma regla transversal del handoff que ya ganó en CONV-2B (42→44px), CONV-2D
   (10→11px) y CONV-2E: el texto de datos no baja de 11px. Aquí eso vale para el
   rótulo Y para los sellos, que heredan los 11px de .kb-pm-badge.

   ── Móvil (≤900px, la prioridad) ──
   Todo envuelve (flex-wrap en la tira y en el grupo de sellos): con seis sellos
   en un teléfono de 320px pasa a dos o tres renglones en vez de desbordar. La
   tira va en FLUJO NORMAL, sin position ni z-index, así que no puede empujar ni
   tapar la barra de compra pegajosa (.kb-pdp-stickybuy, que es position:fixed y
   vive fuera de este flujo). */
.kb-paytrust {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 4px 7px;
}
.kb-paytrust-lbl {
  font-family: var(--kb-font-ui);
  font-size: 11px; font-weight: 700; line-height: 1.1;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--kb-muted);
}
.kb-paytrust-sellos { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; min-width: 0; }
/* Variante compacta: solo geometría (ver nota de contraste arriba). */
.kb-paytrust-badge { padding: 3px 5px; }
/* ─── App admin móvil · barra inferior del staff (AdminBottomNav) ─── */
.kb-adminnav {
  display: none;
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 85;
  background: rgba(255,255,255,0.96);
  backdrop-filter: saturate(180%) blur(20px);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  border-top: 1px solid var(--kb-line);
  padding: 4px 4px calc(4px + env(safe-area-inset-bottom));
  grid-auto-flow: column; grid-auto-columns: 1fr; gap: 2px;
}
.kb-adminnav button {
  background: transparent; border: 0; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 7px 4px 5px;
  color: var(--kb-muted); font-size: 10px; font-weight: 600; border-radius: 10px;
  font-family: inherit;
}
.kb-adminnav button.on { color: var(--kb-ink); background: var(--kb-yellow-soft); }
.kb-adminnav button.on svg { color: var(--kb-orange-deep); }
.kb-adminnav-sheet { position: fixed; inset: 0; z-index: 84; background: rgba(15,14,11,0.45); }
.kb-adminnav-panel {
  position: absolute; left: 0; right: 0;
  bottom: calc(54px + env(safe-area-inset-bottom));
  max-height: 70vh; overflow-y: auto; background: #fff;
  border-radius: 16px 16px 0 0; padding: 10px 8px 14px;
  box-shadow: 0 -8px 30px rgba(0,0,0,.18);
}
/* ─── [ADM-MENU-SOL] Panel · Solicitudes de distribuidor ───
   Datos de PROSPECTOS: se leen de un vistazo o no sirven. Nada por debajo de
   11px y ningún token que theme-engine.jsx repinta. Contrastes calculados:
     · --kb-muted   #5C5A60 sobre #fff → 6.02:1
     · --kb-charcoal #15141A sobre #fff → 17.7:1
     · [BLOQUE 13] chip nuevo      #A32C29 sobre #FBEDEC → 6,25:1
     · [BLOQUE 13] chip contactado #8A5300 sobre #FFF7E6 → 5,94:1
     · [BLOQUE 13] chip aprobado   #0E6B5E sobre #E3F1EE → 5,51:1
     · [BLOQUE 13] chip descartado #5C5A60 sobre #F4F1EB → 6,04:1 */
.kb-sol-filtros { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.kb-sol-lista { display: grid; gap: 12px; }
.kb-sol-card { padding: 16px; }
.kb-sol-nombre { font-family: var(--kb-font-display); font-size: 17px; font-weight: 700; color: var(--kb-charcoal); overflow-wrap: anywhere; }
.kb-sol-meta { font-size: 11px; font-weight: 500; color: var(--kb-muted); margin-top: 2px; }
.kb-sol-lbl { font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--kb-muted); }
.kb-sol-dato { font-size: 13px; font-weight: 500; color: var(--kb-charcoal); margin-top: 2px; overflow-wrap: anywhere; }
.kb-sol-dato a { color: var(--kb-charcoal); text-decoration: underline; }
.kb-sol-vacio { font-size: 13px; color: var(--kb-muted); font-style: italic; }
.kb-sol-vacio-caja { border: 1px dashed var(--kb-line-strong); border-radius: 12px; padding: 26px 20px; text-align: center; font-size: 13px; color: var(--kb-muted); background: #fff; }
.kb-sol-grid { display: grid; grid-template-columns: 1fr; gap: 12px; margin-top: 12px; }
.kb-sol-acciones { display: grid; grid-template-columns: 1fr; gap: 10px; margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--kb-line); align-items: end; }
.kb-sol-chip { flex-shrink: 0; font-size: 11px; font-weight: 700; letter-spacing: 0.03em; padding: 3px 10px; border-radius: 999px; white-space: nowrap; }
/* [LINEA-OUTLET · BLOQUE 13] Los cuatro chips venían del clon con cuatro pares
   ajenos: azul, ámbar, verde y gris frío. Cada uno pasa al color que le toca por
   lo que SIGNIFICA en esta marca, no por costumbre:
   · NUEVO       = hay que atenderlo → rojo outlet, que es la acción de la casa
                   (el azul decía "dato", no "atiéndeme").
   · CONTACTADO  = trabajo a medias → ámbar de aviso de la paleta.
   · APROBADO    = el prospecto YA es distribuidor → verde de NIVEL. Este es el
                   único de los cuatro donde el verde dice la verdad.
   · DESCARTADO  = apagado → gris y hueso de la paleta (el #F2F3F5 anterior era
                   un gris azulado).
   Los ratios están arriba, medidos uno por uno. */
.kb-sol-chip-nuevo      { background: var(--kb-pink-soft); color: var(--kb-orange-text); }
.kb-sol-chip-contactado { background: var(--kb-cola-ambar-bg); color: var(--kb-cola-ambar); }
.kb-sol-chip-aprobado   { background: var(--kb-b2b-soft); color: var(--kb-b2b); }
.kb-sol-chip-descartado { background: var(--kb-cream); color: var(--kb-muted); }
/* [SOL-DIST-USER] Aviso de APROBACIÓN A MEDIAS (aprobada, rol sin cambiar).
   Mismo par ámbar del chip "contactado" porque dice lo mismo: hay trabajo
   pendiente. [LINEA-OUTLET · BLOQUE 13] Ese par ahora es el ámbar de estado de
   la paleta: #8A5300 sobre #FFF7E6 → 5,94:1 ✓ (antes #7A4A06 sobre #FFF4E5,
   6,88:1, y el filete en un tercer ámbar #B26A00). Siguen siendo colores fijos:
   ningún token de los que repintaba el motor de temas de la marca heredada. */
.kb-sol-aviso { margin-top: 6px; font-size: 12px; font-weight: 600; line-height: 1.35; color: var(--kb-cola-ambar); background: var(--kb-cola-ambar-bg); border-left: 3px solid var(--kb-cola-ambar); border-radius: 0 8px 8px 0; padding: 6px 10px; }
@media (min-width: 760px) {
  .kb-sol-grid { grid-template-columns: repeat(3, 1fr); }
  .kb-sol-acciones { grid-template-columns: 200px 1fr auto; }
}

/* [ADM-MENU-SOL] Dentro de la hoja "Más" los títulos de grupo SÍ se ven: la
   regla de ≤900px apaga .kb-side-head para el sidebar del cliente (que ahí se
   vuelve una tira horizontal), y de paso dejaba la hoja del staff como una
   lista de 52 opciones sin un solo separador. Especificidad 0,2,0 → gana sin
   depender del orden. */
.kb-adminnav-panel .kb-side-head { display: block; }
.kb-adminnav-panel .kb-side-search { padding: 2px 4px 8px; position: sticky; top: -10px; background: #fff; z-index: 1; }
@media (max-width: 900px) {
  body.kb-admin-app .kb-adminnav { display: grid; }
  body.kb-admin-app .kb-bottomnav { display: none; }
  body.kb-admin-app .kb-side { display: none; }
}

/* ─── Widget de chat "K•BOT IA" (index-prod.html, plataforma externa) ───
   Se inyecta directo en document.body (fuera de React) con position:fixed y
   se ve en TODA página, incluido el panel interno de Admin — tapando
   contenido (p.ej. "Rentabilidad (interno)" en la ficha de un pedido). Es
   para vender al cliente en el storefront; se oculta en rutas de admin/vendedor
   (body.kb-admin-route, ver screens-account.jsx DashboardScreen). Selector por
   prefijo (no solo .kbot-fab/.kbot-panel): el widget también agrega un popup
   proactivo (.kbot-bubble) y podría sumar más piezas a futuro — todas comparten
   el prefijo "kbot-". !important porque el widget de terceros puede aplicar
   estilos inline. */
body.kb-admin-route [class^="kbot-"] { display: none !important; }

/* ─── AdminPedidoDetalle (ficha de un pedido) · grid de 2 columnas ───
   Columna de Resumen PRIMERO en el DOM (queda arriba en mobile, 1 columna);
   en desktop ocupa los 320px de la izquierda — evita reordenar con CSS.
   Base a 1 columna aquí (no en el style inline de admin-crud.jsx): un
   gridTemplateColumns inline le gana SIEMPRE a esta media query, sin
   importar el ancho — la clase debe ser la única dueña de esta propiedad. */
.kb-pedido-grid { grid-template-columns: 1fr; }
@media (min-width: 900px) {
  .kb-pedido-grid { grid-template-columns: 320px 1fr; align-items: start; }
}

/* [ADMIN-PEDIDOS-SCROLL] La tabla de Pedidos (Pedido/Cliente/Fecha/Canal/Art./
   Método/Estado pago/Estado/Guía/Total/acciones) no cabe completa a 100% de
   zoom del navegador; ya tenía overflow-x:auto (admin-crud.jsx) pero con el
   scrollbar nativo delgado, fácil de pasar por alto — el dueño terminaba
   haciendo zoom out del navegador en vez de deslizar. Mismo tratamiento visual
   que .kb-side (barra lateral del dashboard), pero horizontal y más visible
   (scrollbar-width:auto en vez de thin) para que sea obvio que se puede deslizar.
*/
.kb-orders-scroll { scrollbar-width: auto; scrollbar-color: var(--kb-line-strong) var(--kb-cream); }
.kb-orders-scroll::-webkit-scrollbar { height: 12px; }
.kb-orders-scroll::-webkit-scrollbar-track { background: var(--kb-cream); border-radius: 8px; }
.kb-orders-scroll::-webkit-scrollbar-thumb { background: var(--kb-line-strong); border-radius: 8px; border: 2px solid var(--kb-cream); }
.kb-orders-scroll::-webkit-scrollbar-thumb:hover { background: var(--kb-muted); }

/* ─── App admin móvil · Nivel 1 ─── */
.kb-geo-grid { display: grid; grid-template-columns: 1.3fr 1fr; gap: 14px; }

@media (max-width: 700px) {
  /* Águila: mapa arriba, ranking abajo */
  .kb-geo-grid { grid-template-columns: 1fr; }

  /* Genéricas del panel en modo app: nada desborda la pantalla */
  body.kb-admin-app .kb-dash-grid > div { min-width: 0; }
  body.kb-admin-app .kb-card-section { overflow-x: auto; }
  body.kb-admin-app .kb-stat-grid { grid-template-columns: 1fr 1fr; }

  /* Pedidos: la tabla se vuelve tarjetas */
  .kb-orders-cards thead { display: none; }
  .kb-orders-cards, .kb-orders-cards tbody { display: block; }
  .kb-orders-cards tr {
    display: grid; grid-template-columns: 1fr 1fr; gap: 2px 12px;
    padding: 12px 14px; border-bottom: 1px solid var(--kb-line);
  }
  .kb-orders-cards td {
    display: flex; flex-direction: column; gap: 1px;
    border: 0 !important; padding: 2px 0 !important; font-size: 13px;
  }
  .kb-orders-cards td::before {
    content: attr(data-label);
    font-size: 9.5px; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; color: var(--kb-muted);
  }
  .kb-orders-cards td[data-label=""]::before { content: none; }
  .kb-orders-cards td[data-label=""] { flex-direction: row; gap: 8px; grid-column: 1 / -1; }
}

/* ═══ CONV-3AB · Panel del cliente: historial en tarjetas y resumen honesto ═══
   Propuestas 3a y 3b del handoff design_handoff_kbotanas_conversion. Cambio
   COSMÉTICO: ni una cifra cambia de fórmula (ytdSpend, orderCount, savedYtd,
   activeOrders y el mapeo de /api/orders quedan intactos); lo que cambia es
   cómo se cuentan y qué se deja de afirmar sin dato.

   ── 3a · El historial deja de ser tabla ────────────────────────────────────
   La .kb-table del tab "Mis pedidos" tenía SIETE columnas y bajo 768px este
   mismo archivo la pone en overflow-x:auto: en un teléfono el cliente veía
   folio y fecha, y tenía que arrastrar de lado para llegar al total y a
   "Recomprar" — el botón que más vende de esa pantalla. Se sustituye por
   tarjetas en TODOS los anchos (el handoff permitía "bajo 900px o siempre"):
   dos marcados con los mismos datos serían dos sitios donde equivocarse.
   Ninguna columna se pierde — folio, fecha, productos/piezas, canal, estado,
   total y las tres acciones siguen ahí.

   ── Botones: 44px, no los 40px del handoff ─────────────────────────────────
   El handoff pide "Recomprar" de 40px; la regla transversal del mismo handoff
   (y el precedente ya aplicado en CONV-2B, 42→44px) manda que el objetivo
   táctil no baje de 44px. Gana la regla: los tres botones de la tarjeta miden
   44px.

   ── Contraste · criterio único 4.5:1 (texto pequeño), calculado ────────────
     · .kb-oc-folio / .kb-oc-estado / .kb-oc-total → --kb-ink #15141A sobre el
       blanco de la tarjeta: 19.30:1  ✓
     · .kb-oc-meta / .kb-oc-empty / .kb-fav-note   → --kb-muted #5C5A60 sobre
       blanco: 6.02:1  ✓
     · .kb-oc-eyebrow → --kb-orange-text #A32C29 sobre blanco: 5.93:1  ✓
   [HALLAZGO DE AUDITORÍA aplicado] Ninguna de estas clases usa un token que
   theme-engine.jsx:27 repinte (--kb-orange, --kb-pink-deep, --kb-orange-deep,
   --kb-theme-*). Por eso el eyebrow va con --kb-orange-text y NO con el
   --kb-orange-deep de .kb-eyebrow: en "Buen Fin" el acento es amarillo #D9B26A
   (1.41:1 sobre blanco) y ese rótulo quedaría ilegible en la semana de más
   venta del año. Los chips de estado y canal conservan .kb-tag tal cual —
   mismas clases y mismos colores que en la tabla, para no cambiar nada que no
   pida el encargo; la frase de .kb-oc-estado repite esa misma información en
   tinta estable, así que el dato ya no depende del color del chip. */
.kb-oc-list { display: grid; gap: 10px; padding: 14px; }
.kb-oc-empty { margin: 0; padding: 28px 16px; text-align: center; font-size: 13px; color: var(--kb-muted); }
.kb-oc {
  display: grid; gap: 8px; min-width: 0;
  background: #fff; border: 1px solid var(--kb-line); border-radius: 12px; padding: 12px;
}
.kb-oc-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 10px; }
.kb-oc-idcol { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.kb-oc-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* El folio es un identificador que el cliente dicta por teléfono o pega en
   WhatsApp: mono, 12px (no baja de 11px) y puede partirse antes que desbordar. */
.kb-oc-folio { font-size: 12px; color: var(--kb-ink); overflow-wrap: anywhere; }
.kb-oc-total { font-family: var(--kb-font-display); font-size: 18px; font-weight: 800; color: var(--kb-ink); white-space: nowrap; }
.kb-oc-meta { font-size: 12px; color: var(--kb-muted); }
.kb-oc-estado { font-size: 12.5px; font-weight: 600; color: var(--kb-ink); overflow-wrap: anywhere; }
.kb-oc-acts { display: flex; flex-wrap: wrap; gap: 8px; }
.kb-btn.kb-oc-btn { height: 44px; padding: 0 14px; font-size: 13px; flex: 1 1 auto; min-width: 0; }
.kb-oc-share { background: var(--kb-cream); border: 1px solid var(--kb-line); border-radius: 10px; padding: 12px; }
@media (min-width: 700px) {
  /* Con sitio de sobra los botones dejan de estirarse: una fila compacta a la
     izquierda lee mejor que tres botones enormes repartidos a lo ancho. */
  .kb-oc-acts .kb-btn.kb-oc-btn { flex: 0 0 auto; }
}

/* ── 3b · Resumen del cliente detalle ───────────────────────────────────────
   Un cliente de mostrador no tiene crédito ni ahorro por volumen: la rejilla de
   4 KPIs le salía en ceros y "Compras YTD" con un "↑ 18% vs. 2025" escrito a
   mano. En su lugar ve su pedido en curso (misma tarjeta que el historial, sin
   inventar un componente nuevo) y tres productos para volver a pedir.
   Mayoreo/Distribuidor conservan la rejilla: ahí las cuatro cifras sí existen.
   Una sola columna a propósito: apilado, las tarjetas de re-compra reciben todo
   el ancho y no hay que apretar el selector de piezas en móvil. */
/* Los rótulos de .kb-stat venían en mono de 10.5px. Como esta propuesta reescribe
   justo esos rótulos (el falso "Compras YTD" y el crédito), les toca la regla de
   los 11px — pero .kb-stat-label la comparten trece tarjetas del panel ADMIN, que
   el dueño dejó fuera del encargo. Por eso el bump va detrás de .kb-stat-cliente,
   una clase que solo lleva la rejilla del cliente: el admin no se entera.
   --kb-muted sobre blanco = 6.02:1 (y sobre la tarjeta destacada el texto es
   blanco sobre el degradado rosa, como siempre). */
.kb-stat-cliente .kb-stat-label { font-size: 11px; }

.kb-resumen-cliente { display: grid; gap: 12px; }
.kb-oc-curso { border-color: var(--kb-line-strong); }
.kb-oc-eyebrow { font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--kb-orange-text); }
.kb-fav-block { background: #fff; border: 1px solid var(--kb-line); border-radius: 12px; padding: 12px; }
.kb-fav-head { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: baseline; gap: 8px; margin-bottom: 10px; }
.kb-fav-head b { font-size: 14px; }
.kb-fav-note { font-size: 11px; color: var(--kb-muted); }
/* Una columna en móvil y tres a partir de 640px. Nunca dos: con TRES productos
   dejaría uno huérfano. El corte está en 640 y no antes porque cada tarjeta
   lleva el selector de piezas (−/38px/+ = 90px) más el botón "Agregar": por
   debajo de ~180px de tarjeta ese botón no cabe sin desbordar. */
.kb-fav-grid { display: grid; grid-template-columns: 1fr; gap: 10px; }
@media (min-width: 640px) { .kb-fav-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ── 3b · Rastreo: la columna del mapa solo cuando hay mapa ─────────────────
   El ancho de columnas lo decide un style inline en screens-account.jsx (1.2fr
   1fr con ruta activa, 1fr sin ella) porque depende del dato. Un inline le gana
   SIEMPRE a una media query —el mismo motivo documentado en .kb-pedido-grid—,
   así que la red de seguridad móvil necesita !important: en un teléfono el
   rastreo es de una sola columna aunque algún día haya ruta activa. */
@media (max-width: 900px) { .kb-track-cols { grid-template-columns: 1fr !important; } }

/* ═══ CONV-4C · Encabezado de Mi Cuenta ═══════════════════════════════════════
   Propuesta 4c del handoff design_handoff_kbotanas_conversion, sobre
   screens-account.jsx (DashboardScreen).

   ── Contraste · criterio único 4.5:1 (texto pequeño), CALCULADO ─────────────
     · .kb-acc-ava   #8E2522 sobre #F8E4E2  →  5.72:1  ✓ (los dos del handoff)
     · .kb-acc-nivel.may   #8A5300 sobre #F6EFE0  →  5,53:1  ✓  [BLOQUE 13]
     · .kb-acc-nivel.dist  #0E6B5E sobre #E3F1EE  →  5,51:1  ✓
     · .kb-acc-nivel.det   #15141A sobre #F4F1EB  → 17.11:1  ✓
     · .kb-up-tit    #15141A sobre #F6EFE0  → 18.17:1  ✓
     · .kb-up-sub    #5C5A60 sobre #F6EFE0  →  5.67:1  ✓
     · .kb-up-cta    #FFFFFF sobre #A32C29  →  5.87:1  ✓
   NINGUNA de estas reglas usa un token que theme-engine.jsx:27 repinte
   (--kb-orange, --kb-orange-deep, --kb-pink-deep, --kb-theme-*). Es
   deliberado: hoy backend/themes.js siembra "buen-fin" (13–18 nov) con acento
   #D9B26A y auto_enabled, y esta banda es AMARILLA — con esos tokens quedaría
   amarillo sobre amarillo justo en la semana de más venta del año. Todos los
   colores de aquí son literales o tokens estables. */

/* El bloque de identidad de la barra lateral (nivel + cerrar sesión). Vive en
   una clase y no en un style inline porque en móvil hay que poder ocultarlo
   desde la media query, y un inline le gana siempre. */
.kb-side-user { padding: 0 12px 8px; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

/* ── El encabezado: avatar + nombre + chip de nivel ───────────────────────── */
.kb-acc-head { flex-wrap: wrap; }
.kb-acc-id { display: flex; align-items: center; gap: 12px; min-width: 0; }
/* El avatar NO puede desbordar ni deformarse pase lo que pase:
     · 42px clavados (los del handoff) con flex: 0 0 auto → no lo encoge el
       flex aunque el nombre de al lado sea larguísimo;
     · las iniciales son DOS caracteres como máximo (kbIniciales), pero aun así
       overflow: hidden es la red por si algún día entra un emoji ancho;
     · line-height: 1 para que el círculo no crezca por la caja de la fuente. */
.kb-acc-ava {
  flex: 0 0 auto; width: 42px; height: 42px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  /* [LINEA-OUTLET · BLOQUE 13] Este par NO se toca en el barrido de paleta:
     #F8E4E2 es el rosado que el bloque 7 fijó para el avatar (un tinte del rojo
     de la marca, no un color heredado) y tests/cuenta-encabezado-4c.test.js lo
     ancla con su medida: #8E2522 encima da 7,05:1 ✓. */
  background: #F8E4E2; color: #8E2522;
  font-family: var(--kb-font-display); font-size: 16px; font-weight: 800;
  line-height: 1; letter-spacing: 0.01em; overflow: hidden; user-select: none;
}
/* Un nombre sin espacios (correo de 40 caracteres como "nombre") no puede
   ensanchar la columna: parte antes de desbordar. */
.kb-acc-nombre { overflow-wrap: anywhere; }
/* El chip de nivel: 11px (regla de la casa), no los 10.5px de .kb-tag. */
.kb-acc-nivel {
  display: inline-block; margin-top: 6px;
  font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px; border: 1px solid var(--kb-line);
  background: var(--kb-cream); color: #15141A; white-space: nowrap;
}
/* [LINEA-OUTLET · BLOQUE 13] Dos retoques a la escalera de niveles:
   · "may" escribía en #6B5220, un café suelto; el bronce de la paleta (#8A5300)
     hace lo mismo y es el color con el que se escribe sobre dorado en todo el
     sitio → 5,53:1 ✓ (el anterior daba 6,43:1, pero era de nadie).
   · "dist" tenía un borde LILA (#C9BFE9) alrededor de un chip verde: resto del
     morado del clon. Pasa a --kb-b2b, el mismo verde de su texto. */
/* [BLOQUE 22] El chip de nivel usaba el beige heredado con el ámbar de estado encima. El
   ámbar ya tiene su propio trio en el :root y es el que dice "atención, todavía no": #8A5300
   sobre #FFF7E6 da 5,94:1, mejor que los 5,94 de antes y sin arrastrar el hueso. */
.kb-acc-nivel.may  { background: var(--kb-cola-ambar-bg); color: var(--kb-cola-ambar); border-color: var(--kb-cola-ambar-borde); }
.kb-acc-nivel.dist { background: #E3F1EE; color: #0E6B5E; border-color: var(--kb-b2b); }
.kb-acc-nivel.det  { background: var(--kb-cream); color: var(--kb-ink); border-color: var(--kb-line); }

/* ── La banda de ascenso ──────────────────────────────────────────────────── */
.kb-up {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 12px;
  margin-bottom: 14px; padding: 12px 14px; min-width: 0;
  background: var(--kb-cola-ambar-bg); border: 1.5px solid var(--kb-cola-ambar-borde); border-radius: 12px;
  animation: kb-up-in 180ms ease-out both;
}
.kb-up-espera { background: var(--kb-cream); border-color: var(--kb-line-strong); }
.kb-up-ico { flex: 0 0 auto; font-size: 18px; line-height: 1; }
.kb-up-txt { flex: 1 1 220px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kb-up-tit { font-size: 14px; font-weight: 800; color: #15141A; }
.kb-up-sub { font-size: 12.5px; color: #5C5A60; overflow-wrap: anywhere; }
/* 44px: el objetivo táctil de la casa (la misma regla que ya aplicó CONV-3A a
   los botones de la tarjeta de pedido). */
.kb-btn.kb-up-cta {
  flex: 0 0 auto; height: 44px; padding: 0 16px; font-size: 13px; font-weight: 700;
  background: #A32C29; color: #fff; border: 1px solid #A32C29; border-radius: 10px;
}
/* [LINEA-OUTLET · BLOQUE 13] El hover saltaba a #A3164E, un magenta: el botón
   cambiaba de FAMILIA al pasar el ratón. Ahora usa el rojo profundo de la
   paleta, que es el hover de todos los botones rojos de la casa (.kb-btn-primary
   incluido). Blanco sobre #8E2522 → 8,61:1 ✓. */
.kb-btn.kb-up-cta:hover { background: #8E2522; border-color: #8E2522; }
@keyframes kb-up-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
/* La regla global de :2355 ya recorta cualquier animación, pero aquí va
   explícito: quien pide menos movimiento ve la banda quieta desde el cuadro 1. */
@media (prefers-reduced-motion: reduce) { .kb-up { animation: none; } }
@media (max-width: 520px) {
  /* En un teléfono el botón manda a lo ancho: partido en dos mitades no cabe
     "Solicitar alta →" sin cortarse. */
  .kb-btn.kb-up-cta { flex: 1 1 100%; }
}

/* ── Móvil: las pestañas se vuelven chips horizontales con scroll REAL ──────
   [OJO · el mismo error de la escalera de la ficha] .kb-side es item de
   .kb-dash-grid, cuya columna es "1fr" = minmax(AUTO, 1fr): ese AUTO deja subir
   el mínimo del contenido (ocho chips con white-space: nowrap ≈ 1000px) hasta
   ensanchar la columna. Y como html/body/.kb-main llevan overflow-x: clip, el
   sobrante no saca barra: se corta y queda inalcanzable. Se corta la cadena en
   los dos sitios — minmax(0, 1fr) en la rejilla y min-width: 0 en la tira — para
   que el sobrante lo absorba el scroll de la propia tira. */
@media (max-width: 900px) {
  .kb-dash-grid { grid-template-columns: minmax(0, 1fr); }
  .kb-side {
    min-width: 0; max-width: 100%; gap: 8px; align-items: stretch;
    padding: 8px; scroll-padding: 8px; -webkit-overflow-scrolling: touch;
    scrollbar-width: thin;
  }
  /* La identidad ya la pinta el encabezado 4c con avatar: aquí sobraba y además
     metía un bloque no navegable en medio de la tira de chips. */
  .kb-side-user { display: none; }
  /* [MENU-TECLADO] width: auto deshace el 100% que el ítem lleva para la
     columna de escritorio: aquí .kb-side es flex-row y con 100% cada chip
     ocuparía la tira entera. Vuelve a medir lo que mide su texto. */
  .kb-side-item {
    flex: 0 0 auto; width: auto; min-height: 44px; padding: 0 14px; border-radius: 999px;
    border: 1px solid var(--kb-line); background: #fff;
  }
  .kb-dash-pro .kb-side-item { border-radius: 999px; }
  /* El activo se distingue por RELLENO, no solo por el filete interior de la
     regla de escritorio (que en una píldora casi no se ve). */
  /* [BLOQUE 22] El fondo iba escrito literal y el de escritorio sale del token. Al
     neutralizarse --kb-pink-soft, la píldora de móvil se habría quedado rosada y la columna de
     escritorio gris: el mismo estado, dos colores. Ahora los dos siguen al mismo token. */
  .kb-dash-pro .kb-side-item.on { box-shadow: none; border-color: #A32C29; background: var(--kbp-soft); color: #A32C29; }
  .kb-dash-pro .kb-side-item:hover { transform: none; }
}

/* ═══ CONV-4D · Tarjeta de soporte por WhatsApp ════════════════════════════════
   Propuesta 4d del handoff design_handoff_kbotanas_conversion, sobre
   screens-account.jsx (MiSoporte). El handoff pide la card verde #F0FAF4 con
   borde 1.5px solid #25D366; eso se respeta TAL CUAL. Lo que NO se puede
   respetar es escribir las letras en #25D366.

   ── Contraste · criterio único 4.5:1 (texto pequeño), CALCULADO ─────────────
   El verde de marca de WhatsApp NO sirve para texto sobre esta card:
     · #25D366 sobre #F0FAF4  →  1.86:1  ✗  (ni AA de texto grande, que es 3:1)
     · #FFFFFF sobre #25D366  →  1.98:1  ✗  (un botón verde de marca con letras
       blancas es ILEGIBLE; es el error clásico de este color)
   Por eso el #25D366 se queda SOLO en el borde —donde es decoración de marca y
   no lleva información: todo lo que la card dice está en el texto— y las letras
   usan el verde oscuro de la casa, el mismo #0E6B5E que ya pasa AA en la
   escalera de volumen de la ficha:
     · .kb-wa-tit      #0E6B5E sobre #F0FAF4  →  6,00:1  ✓
     · .kb-wa-horario  #15141A sobre #F0FAF4  → 17,16:1  ✓
     · .kb-wa-ref      #5C5A60 sobre #F0FAF4  →  6,38:1  ✓
     · .kb-wa-cta      #FFFFFF sobre #0E6B5E  →  6,40:1  ✓
   [REVISIÓN · BLOQUE 13] Los cuatro números de arriba estaban por debajo de la
   medida real (5.77 / 16.60 / 5.64 / 6.16) y el paréntesis que seguía decía que
   "#0E6B5E da 4.11:1 y ninguno llega a 4.5:1" justo encima de la regla que pinta
   con #0E6B5E: quedan recalculados con la fórmula WCAG calibrada (blanco/negro
   21,00 · #767676 sobre blanco 4,54). Lo que sí descarta la medición, y por eso
   se conserva el criterio del bloque, es el verde de MARCA: #25D366 no llega ni
   a 3:1 sobre esta card, así que vive solo en el borde.

   NINGÚN color de aquí es un token que theme-engine.jsx:27 repinte
   (--kb-orange, --kb-orange-deep, --kb-pink-deep, --kb-theme-*): todos son
   literales. En "Buen Fin" el acento es amarillo #D9B26A y esta card —que es la
   forma de pedir ayuda con un pedido en la semana de más venta del año— se
   quedaría sin folio legible.

   ── Táctil y desborde · MEDIDO en Chrome real ──────────────────────────────
   El botón no baja de 44px (regla de la casa) y en móvil ocupa el ancho
   completo: la etiqueta lleva el folio dentro ("Abrir WhatsApp sobre mi pedido
   KB-2026-A1B2C3 →") y partida en dos columnas no cabría sin cortarse.
   Medido a 720 / 360 / 320px, con folio normal, con el folio MÁS LARGO que se
   admite (32 caracteres) y sin folio: desborde horizontal 0 en los cinco casos
   (scrollWidth === clientWidth) y altura del botón 44px en una línea, 54px
   cuando el folio lo parte en dos. */
.kb-wa-card {
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px 12px;
  padding: 12px 14px; min-width: 0;
  background: #F0FAF4; border: 1.5px solid #25D366; border-radius: 12px;
}
.kb-wa-txt { flex: 1 1 220px; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kb-wa-tit { font-size: 14px; font-weight: 800; color: #0E6B5E; }
.kb-wa-horario { font-size: 12.5px; font-weight: 600; color: #15141A; }
/* La línea de referencia dice de QUÉ pedido trata (o por qué no hay ninguno):
   puede traer un folio largo, así que parte antes que desbordar. */
.kb-wa-ref { font-size: 12.5px; color: #5C5A60; overflow-wrap: anywhere; }
/* [OJO] .kb-btn base lleva `white-space: nowrap` y `height: 38px`. Aquí NO
   sirven: la etiqueta lleva el folio dentro y un folio de 32 caracteres en una
   sola línea se sale de la card (y en móvil, de la pantalla). Se deshacen los
   dos: el texto parte y el botón CRECE hacia abajo — nunca recorta el folio,
   que es justo el dato que el cliente va a dictar. min-height mantiene los
   44px táctiles aunque el texto quepa en una línea. */
.kb-btn.kb-wa-cta {
  flex: 0 0 auto; height: auto; min-height: 44px; padding: 10px 16px;
  font-size: 13px; font-weight: 700; line-height: 1.25;
  background: #0E6B5E; color: #fff; border: 1px solid #0E6B5E; border-radius: 10px;
  text-decoration: none; max-width: 100%;
  white-space: normal; overflow-wrap: anywhere; text-align: center;
}
/* [REVISIÓN · BLOQUE 13] El hover era #10592E: un SÉPTIMO verde, el único color
   de este bloque que no está justificado arriba y que el barrido de paleta dejó
   pasar. La paleta 1 tiene un solo verde (#0E6B5E) y no le da un "profundo" como
   el que sí tiene el rojo (--kb-orange → --kb-orange-deep), así que el hover
   oscurece hacia el carbón de la casa, igual que .kb-btn-dark:hover. Sigue
   siendo literal, como todo lo demás de esta card (el motor de temas no puede
   tocarlo), y el botón no pierde legibilidad al pasar el ratón:
     · blanco sobre #0E6B5E → 6,40:1 ✓  (reposo)
     · blanco sobre #15141A → 18,31:1 ✓ (hover; el #10592E daba 8,43:1) */
.kb-btn.kb-wa-cta:hover { background: #15141A; border-color: #15141A; }
@media (max-width: 560px) {
  /* En un teléfono el botón manda a lo ancho: partido en dos mitades no cabe
     "Abrir WhatsApp sobre mi pedido KB-2026-A1B2C3 →" sin quedar en cuatro
     líneas de dos palabras (misma decisión que .kb-up-cta en CONV-4C). */
  .kb-btn.kb-wa-cta { flex: 1 1 100%; }
}

/* ─── POS · Punto de Venta (rediseño visual) ────────────────────────────────
   Solo PRESENTACIÓN de pos.jsx: la lógica (handlers, price-preview, guard)
   no cambia. Tokens y convenciones del sistema kb-* existente. Todos los
   objetivos táctiles respetan el mínimo de 44px (regla de la casa, ver
   [A11Y] en pos.jsx). */

/* Layout raíz: catálogo + panel de ticket. En pantallas angostas apila. */
.kb-pos-wrap { display: grid; grid-template-columns: minmax(0, 1fr) 380px; gap: 14px; align-items: start; }
@media (max-width: 1024px) { .kb-pos-wrap { grid-template-columns: 1fr; } }
.kb-pos-panel { position: sticky; top: 14px; }
@media (max-width: 1024px) { .kb-pos-panel { position: static; } }

/* Tarjetas de producto del catálogo */
.kb-pos-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(122px, 1fr)); gap: 10px; margin-top: 12px; }
.kb-pos-prod {
  background: #fff; border: 1px solid var(--kb-line); border-radius: 10px;
  padding: 8px; cursor: pointer; text-align: left; font-family: inherit; color: var(--kb-ink);
  display: block; width: 100%;
  transition: transform .12s, box-shadow .12s, border-color .12s;
}
.kb-pos-prod:hover { border-color: var(--kb-orange); box-shadow: var(--kb-shadow); transform: translateY(-2px); }
.kb-pos-prod:active { transform: translateY(0) scale(.97); }
.kb-pos-prod-img { width: 100%; aspect-ratio: 1 / 1; border-radius: 8px; margin-bottom: 8px; }
.kb-pos-prod-name { font-size: 12px; font-weight: 600; line-height: 1.25; min-height: 30px; overflow: hidden; }
.kb-pos-prod-price { font-size: 14.5px; font-weight: 800; margin-top: 4px; font-family: var(--kb-font-display); display: flex; align-items: baseline; gap: 5px; flex-wrap: wrap; }
.kb-pos-prod-reg { color: var(--kb-mute2); font-weight: 500; font-size: 11px; }
/* [LINEA-OUTLET · BLOQUE 13] Este sello marca el DESCUENTO de un producto, o sea
   un precio de outlet — y el verde de esta marca significa "nivel alcanzado",
   no "barato". Pasa al rojo de la casa sobre su rosa suave: #A32C29 sobre
   #FBEDEC → 6,25:1 ✓ (el par verde anterior, #0E6B5E sobre #DCEFE2, se leía
   igual de bien pero decía otra cosa). */
.kb-pos-prod-off {
  font-family: var(--kb-font-mono); font-size: 9.5px; font-weight: 700;
  color: var(--kb-orange-text); background: var(--kb-pink-soft); border-radius: 3px; padding: 1px 4px;
}

/* Tarjeta de cliente activo */
.kb-pos-client-box { padding: 12px 13px; border-bottom: 1px solid var(--kb-line); }
.kb-pos-client { display: flex; align-items: center; gap: 10px; }
.kb-pos-client-avatar {
  width: 42px; height: 42px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; font-size: 19px;
  background: var(--kb-cream); border: 1px solid var(--kb-line-strong);
}
/* [LINEA-OUTLET · BLOQUE 13] Los tres avatares son la MISMA escalera de niveles
   que los chips de Mi Cuenta (.kb-acc-nivel), y ahora se pintan igual: detalle
   neutro (hueso), mayoreo dorado, distribuidor verde de nivel. Antes mayoreo era
   azul —color que no existe aquí— y distribuidor llevaba borde lila; de paso, el
   nivel base compartía el dorado con mayoreo, así que dos escalones distintos se
   veían idénticos. Son rellenos y bordes: el nombre del cliente va aparte, en
   tinta sobre el blanco de la tarjeta. */
.kb-pos-client-avatar.mayoreo { background: var(--kb-yellow-soft); border-color: var(--kb-yellow); }
.kb-pos-client-avatar.distribuidor { background: var(--kb-b2b-soft); border-color: var(--kb-b2b); }
.kb-pos-client-name { font-size: 13.5px; font-weight: 700; line-height: 1.2; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kb-pos-client-meta { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-top: 4px; }
.kb-pos-client-result {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  min-height: 44px; padding: 6px 10px; border-radius: 8px; cursor: pointer; font-size: 12.5px;
}
.kb-pos-client-result:hover { background: var(--kb-pink-soft); }
.kb-pos-divider {
  display: flex; align-items: center; gap: 8px; margin: 10px 0 8px;
  font-family: var(--kb-font-mono); font-size: 10px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--kb-mute2);
}
.kb-pos-divider::before, .kb-pos-divider::after { content: ""; flex: 1; height: 1px; background: var(--kb-line); }

/* Encabezado del ticket con contador de piezas */
.kb-pos-ticket-head {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 9px 13px; border-bottom: 1px solid var(--kb-line); background: var(--kb-cream);
}

/* Líneas del ticket */
.kb-pos-lines { padding: 4px 13px; max-height: 300px; overflow-y: auto; }
.kb-pos-line { padding: 10px 0; border-bottom: 1px dashed var(--kb-line); }
.kb-pos-line:last-child { border-bottom: 0; }
.kb-pos-line-top { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.kb-pos-line-name { font-size: 12.5px; font-weight: 600; line-height: 1.3; }
.kb-pos-line-total { font-family: var(--kb-font-mono); font-size: 13px; font-weight: 800; white-space: nowrap; }
.kb-pos-line-bottom { display: flex; align-items: center; gap: 10px; margin-top: 6px; }
.kb-pos-line-unit { font-size: 11px; color: var(--kb-muted); }
.kb-pos-line-rm {
  margin-left: auto; width: 44px; height: 44px; flex-shrink: 0;
  border: 0; background: transparent; border-radius: 8px; cursor: pointer;
  font-size: 16px; color: var(--kb-danger); line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s;
}
/* [LINEA-OUTLET · BLOQUE 13] El fondo del hover de "quitar" era un rosa suelto;
   el de ERROR de la paleta dice lo mismo y es token. */
.kb-pos-line-rm:hover { background: var(--kb-cola-rojo-bg); }
.kb-pos-empty { padding: 26px 12px; text-align: center; color: var(--kb-muted); font-size: 12.5px; }
.kb-pos-empty .kb-pos-empty-ico { font-size: 26px; display: block; margin-bottom: 6px; filter: grayscale(1); opacity: .7; }

/* Stepper de cantidad — controles reales, táctil ≥44px */
.kb-pos-step { display: inline-flex; align-items: stretch; border: 1px solid var(--kb-line-strong); border-radius: 9px; overflow: hidden; background: #fff; flex-shrink: 0; }
.kb-pos-step button {
  width: 44px; height: 44px; border: 0; background: var(--kb-cream);
  font-size: 18px; font-weight: 700; color: var(--kb-charcoal); cursor: pointer; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background .12s, color .12s;
}
.kb-pos-step button:hover { background: var(--kb-yellow-soft); color: var(--kb-orange-deep); }
.kb-pos-step-qty {
  min-width: 34px; padding: 0 4px; display: inline-flex; align-items: center; justify-content: center;
  font-family: var(--kb-font-mono); font-size: 14px; font-weight: 800;
}

/* Bloque de totales */
.kb-pos-totals { padding: 12px 13px; border-top: 1px solid var(--kb-line); background: var(--kb-cream); }
.kb-pos-totals-row { display: flex; justify-content: space-between; align-items: center; font-size: 12.5px; padding: 4px 0; }
.kb-pos-adjust { display: grid; grid-template-columns: 96px 1fr; gap: 6px; align-items: center; margin: 6px 0; }
.kb-pos-adjust .kb-input { height: 36px; font-size: 12px; padding: 0 8px; }
.kb-pos-total {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  margin-top: 10px; padding: 10px 14px; border-radius: 10px;
  background: var(--kb-ink); color: #fff;
}
.kb-pos-total-label {
  font-family: var(--kb-font-mono); font-size: 11px; font-weight: 700;
  letter-spacing: .1em; text-transform: uppercase; color: rgba(255,255,255,.75);
}
.kb-pos-total-amount { font-family: var(--kb-font-display); font-size: 24px; font-weight: 800; letter-spacing: -0.01em; color: var(--kb-yellow); }

/* Aviso de price-preview fallido (el botón Reintentar conserva su height:44
   inline — contrato de tests/quotes-pantalla-honesta.test.js [R3 RONDA-3]) */
.kb-pos-preview-warn {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  font-size: 11.5px; line-height: 1.35; padding: 8px 10px; margin-bottom: 8px;
  border-radius: 8px; border: 1px solid var(--kb-yellow);
  /* [LINEA-OUTLET · BLOQUE 13] #6B5300 era el sexto dorado oscuro de la hoja;
     el bronce de la paleta da 5,53:1 sobre #F6EFE0 ✓. */
  background: var(--kb-yellow-soft); color: #8A5300; font-weight: 600;
}

/* Botonera de salidas */
.kb-pos-actions { padding: 12px 13px; display: grid; gap: 8px; border-top: 1px solid var(--kb-line); }
.kb-pos-actions .kb-btn { height: auto; min-height: 46px; white-space: normal; line-height: 1.25; }
.kb-pos-actions .kb-btn-primary { min-height: 50px; font-size: 14px; }
.kb-pos-credit {
  /* [LINEA-OUTLET · BLOQUE 13] Borde y texto en dos ámbares del clon (#C9A227,
     #8C6F12) sobre un fondo que ya era el dorado suave de la casa. Ahora los
     tres son de la paleta: #8A5300 sobre #F6EFE0 → 5,53:1 ✓. */
  border-color: var(--kb-yellow) !important; color: #8A5300 !important;
  background: var(--kb-yellow-soft) !important;
}
.kb-pos-credit:hover { filter: brightness(0.96); }

/* Modal de cobro */
.kb-pos-cobro-total { font-family: var(--kb-font-display); font-size: 30px; font-weight: 800; letter-spacing: -0.01em; margin: 2px 0 0; }
.kb-pos-cobro-metodo.kb-btn { height: auto; min-height: 46px; }
.kb-pos-cobro-cambio {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: 10px; padding: 9px 12px; border-radius: 8px;
  /* [LINEA-OUTLET · BLOQUE 13] El cambio a devolver no es un nivel alcanzado ni
     un precio: es un DATO que el cajero tiene que ver de golpe. Deja el verde
     (que en esta marca significa mayoreo/distribución) y usa el dorado suave de
     lo destacado, con la cifra en carbón: 15,99:1 ✓. La variante .cero de abajo
     sigue apagándolo en hueso. */
  background: var(--kb-yellow-soft); color: var(--kb-ink); font-weight: 800; font-size: 15px;
}
.kb-pos-cobro-cambio.cero { background: var(--kb-cream); color: var(--kb-muted); }

/* ═══ [LINEA-OUTLET · BLOQUE 7] Vitrina: portada tipo perfil con grid de perfumes ═══
   Escritorio: tira de perfil + barra del pedido + filtros + grid de 5 columnas con la primera
   tarjeta 2×2 y acciones al pasar el mouse. Móvil (≤900px): mosaico de 3 columnas con el
   precio encima de la foto, sin hover, y la barra del pedido fija sobre .kb-bottomnav. */
.lo-home { padding-bottom: 8px; }
.lo-strip { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; padding: 10px 0 12px; border-bottom: 1px solid var(--kb-line); }
.lo-avatar { width: 72px; height: 72px; border-radius: 50%; border: 1px solid var(--kb-line); background: #fff; object-fit: cover; }
.lo-nm { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.lo-nm h1 { font-family: var(--kb-font-display); font-size: 24px; margin: 0; letter-spacing: -.01em; line-height: 1.1; }
.lo-chk { width: 18px; height: 18px; border-radius: 50%; background: var(--kb-b2b); color: #fff; font-size: 11px; display: inline-grid; place-items: center; font-weight: 800; }
.lo-dom { color: var(--kb-muted); font-size: 13px; }
.lo-cnts { font-size: 13px; color: var(--kb-muted); margin-top: 2px; }
.lo-cnts b { color: var(--kb-ink); }
.lo-bio { margin: 6px 0 0; font-size: 13px; color: var(--kb-muted); max-width: 64ch; }
.lo-id { flex: 1 1 240px; min-width: 0; }
.lo-acts { display: flex; flex-direction: row; align-items: center; gap: 10px; margin-left: auto; }
/* [LINEA-OUTLET · BLOQUE 13] Por defecto NEUTRA (el cliente público): gris piedra sobre
   hueso, #5C5A60 sobre #F4F1EB → 6,37:1 ✓. El verde de nivel se reserva para quien de
   verdad tiene uno: .b2b, #0E6B5E sobre #E3F1EE → 5,51:1 ✓. */
.lo-rolepill { white-space: nowrap; font-family: var(--kb-font-mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; padding: 4px 9px; border-radius: 999px; background: var(--kb-cream); color: var(--kb-muted); font-weight: 700; }
.lo-rolepill.b2b { background: var(--kb-b2b-soft); color: var(--kb-b2b); }
.lo-modes { display: inline-flex; border: 1px solid var(--kb-line-strong); border-radius: 999px; padding: 3px; background: #fff; }
.lo-modes button { border: 0; background: transparent; font: inherit; font-size: 12.5px; font-weight: 700; padding: 6px 14px; border-radius: 999px; cursor: pointer; color: var(--kb-muted); }
.lo-modes button.on { background: var(--kb-ink); color: #fff; }
.lo-cartbar { display: none; margin: 12px 0 0; padding: 10px 14px; border: 1px solid var(--kb-line); border-radius: 12px; background: #fff; gap: 6px; }
.lo-cartbar .r1, .lo-cartbar .r2 { display: flex; justify-content: space-between; align-items: center; gap: 12px; font-size: 12.5px; }
.lo-cartbar .r1 b { font-variant-numeric: tabular-nums; }
.lo-prog { height: 6px; border-radius: 999px; background: var(--kb-cream); overflow: hidden; }
.lo-prog i { display: block; height: 100%; background: var(--kb-b2b); transition: width .3s; }
.lo-nivel { color: var(--kb-ink); font-weight: 600; }
.lo-ver { border: 0; background: transparent; color: var(--kb-orange-text); font-weight: 700; cursor: pointer; font: inherit; font-size: 12.5px; white-space: nowrap; }
.lo-filters { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin: 0; flex-basis: 100%; }
/* En pantallas anchas los filtros comparten fila con la identidad, como el mockup. */
@media (min-width: 1200px) { .lo-strip { flex-wrap: nowrap; } .lo-filters { flex-basis: auto; margin-left: 8px; } .lo-bio { display: none; } }
.lo-filters button { border: 1px solid var(--kb-line-strong); background: #fff; color: var(--kb-ink); border-radius: 999px; padding: 7px 14px; font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; }
.lo-filters button.on { background: var(--kb-ink); border-color: var(--kb-ink); color: #fff; }
.lo-filters .lo-filter-all { margin-left: auto; border-color: transparent; color: var(--kb-orange-text); }
.lo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--lo-tile-min), 1fr)); grid-auto-flow: dense; gap: 3px; }
/* [LINEA-OUTLET · BLOQUE 28] LA FOTO SE VE ENTERA.
   Medido con los perfumes reales del dueño: el bloque de texto vivía ENCIMA de la foto, en
   posición absoluta, y tapaba 128 de los 302 px del azulejo — el 42 % de abajo. La foto
   esquivaba el estorbo con un 34 % de relleno inferior, así que de 302 px de alto sólo usaba
   172, y los frascos salían cortados bajo un degradado puesto para esconder el recorte.
   El cuadrado pasa del azulejo a la FOTO, y el texto baja debajo. El degradado desaparece
   porque ya no hay nada que tapar. */
.lo-tile { position: relative; overflow: hidden; background: var(--lo-tb, var(--kb-cream)); border: 0; display: flex; flex-direction: column; min-width: 0; }
.lo-tile.big { grid-column: span 2; grid-row: span 2; }
/* [LINEA-OUTLET · BLOQUE 17] Banner intercalado en el mosaico. El tamaño lo elige el
   dueño por banner (chico / mediano / grande / ancho) y viaja como estilo en línea
   —gridColumn/gridRow— porque es un DATO suyo, no una decisión de la hoja: una clase
   por tamaño obligaría a tocar el CSS cada vez que quiera uno nuevo.
   Sin aspect-ratio fijo: el banner respeta el alto de las casillas que ocupa y la imagen
   lo cubre; así nunca rompe la retícula, valga lo que valga la foto que suba.
   El foco se ve (el mosaico es navegable con teclado) y el hover apenas levanta la
   imagen, como las tarjetas. */
/* [LINEA-OUTLET · BLOQUE 17] min-height: la imagen es absoluta y no mide, así que un banner
   que quede SOLO en su fila colapsaría a 0. El de fila entera siempre está solo: además del
   suelo, toma la proporción de su imagen (--lo-ban-ar, que pone LoBanner con las medidas
   reales) y, sin ella, la de reserva 4:1 de un banner ancho. */
/* width: 100% no es decorativo: con aspect-ratio, el navegador deja de ESTIRAR la casilla
   (la trata como caja con proporción propia y la alinea al inicio), así que sin un ancho
   definido se dibujaba del ancho que pedía su proporción —384 px dentro de una fila de
   341— y se salía de la rejilla. */
.lo-banner { position: relative; overflow: hidden; display: block; background: var(--kb-cream); min-height: 96px; width: 100%; }
/* [LINEA-OUTLET · BLOQUE 19] El de fila entera ya no es casilla de la rejilla (el
   empaquetado denso lo empujaba al final): es un bloque ENTRE dos tramos del mosaico,
   con el mismo hueco de 2 px que separa las casillas. */
.lo-banner.es-ancho { aspect-ratio: var(--lo-ban-ar, 1200 / 300); margin: 2px 0; border-radius: inherit; }
.lo-banner img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .35s cubic-bezier(.2,.8,.2,1); }
.lo-banner:hover img { transform: scale(1.03); }
.lo-banner:focus-visible { outline: 2px solid var(--kb-ink); outline-offset: -2px; }
.lo-banner.sin-accion { cursor: default; }
.lo-tile-link { position: absolute; inset: 0; z-index: 1; }
.lo-tile-img { position: relative; aspect-ratio: 1; display: grid; place-items: center; padding: 9% 11%; }
.lo-tile-img > * { width: 100%; height: 100%; }
.kb-prod-frasco { display: grid; place-items: center; }
.kb-prod-frasco .kb-frasco { width: 58%; height: 78%; filter: drop-shadow(0 8px 12px rgba(21,20,26,.24)); }
.lo-tile-img .kb-prod-img { position: static; background: transparent !important; border: 0; }
.lo-tile-img .kb-frasco { width: 74px; height: 118px; filter: drop-shadow(0 10px 14px rgba(21,20,26,.28)); }
.lo-tile.big .lo-tile-img .kb-frasco { width: 156px; height: 250px; }
.lo-tile-badge { position: absolute; right: 10px; top: 10px; z-index: 2; font-style: normal; font-size: 9px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; background: var(--kb-b2b); color: #fff; padding: 3px 7px; border-radius: 5px; pointer-events: none; }
/* [LINEA-OUTLET · BLOQUE 29] El texto va en una BANDA BLANCA propia, debajo de la foto.
   Y aquí hay una medida que decide el filete: desde el bloque 22 el tinte del azulejo es un
   susurro — está a Δ 13/255 del papel — así que la banda blanca contra la foto teñida da entre
   1,10 y 1,14:1 en los 360 matices posibles. Las dos superficies NO se distinguen solas.
   Lo único que hace de banda es el filete: aquí separa dos zonas que SE TOCAN, o sea que es un
   borde funcional y pide 3:1, no el decorativo de 1,28:1. --kb-line-strong da 3,03:1 contra el
   tinte más claro y 3,44:1 contra el papel. */
.lo-tile-info { position: relative; z-index: 2; padding: 11px 12px 13px; display: flex; flex-direction: column; gap: 2px; color: var(--kb-ink); background: var(--kb-paper); border-top: 1px solid var(--kb-line-strong); pointer-events: none; }
/* La marca, sobre el nombre: en una vitrina de perfumes es la mitad de la decisión de compra y
   estaba mezclada con la familia olfativa en la misma línea pequeña. */
.lo-tile-marca { font-family: var(--kb-font-mono); font-size: 10px; letter-spacing: .13em; text-transform: uppercase; color: var(--kb-muted); }
/* Tarjeta oscura (la destacada): el texto se invierte, como en el mockup.
   [LINEA-OUTLET · BLOQUE 13] Los cuatro colores de aquí eran inventados
   (#F1E9DE, #BDB2A4 y el menta #9FC9BF). Sobre oscuro los papeles de la paleta
   se INVIERTEN y con eso basta: el hueso hace de tinta, la línea fuerte de gris
   secundario y el verde suave de nivel sigue siendo verde de nivel. Medido
   contra el fondo real de la cinta (negro al 72% sobre la foto, que en el peor
   caso se comporta como el carbón #15141A):
     · --kb-cream      #F4F1EB → 16,24:1 ✓
     · --kb-line-strong #D3CEC4 → 11,68:1 ✓
     · --kb-b2b-soft   #E3F1EE → 15,77:1 ✓ (el menta anterior daba 10,10:1) */
/* [BLOQUE 29] El azulejo destacado tiene la FOTO oscura, pero su banda de texto es blanca como
   todas: el crema de antes daba 1,10:1 encima y desaparecía. El texto va en tinta siempre. */
.lo-tile.dark .lo-tile-info { color: var(--kb-ink); }
.lo-tile.dark .lo-tile-marca { color: var(--kb-muted); }
.lo-tile.dark .lo-tile-fam, .lo-tile.dark .lo-tile-rate { color: var(--kb-muted); }
.lo-tile.dark .lo-tile-pl.may s { color: var(--kb-muted); }
.lo-tile.dark .lo-tile-pl.may b { color: var(--kb-orange-text); }
.lo-tile.dark .lo-tile-gain { color: var(--kb-b2b-soft); }
.lo-tile-nm { font-family: var(--kb-font-serif); font-size: 19px; font-weight: 600; line-height: 1.1; color: inherit; letter-spacing: .01em; }
/* [LINEA-OUTLET · BLOQUE 13] Dos arreglos en la tarjeta clara:
   · la familia y la calificación iban en #6B6B6B, un gris neutro heredado que
     sobre el hueso de la tarjeta se queda en 4,73:1 a 11,5px — pasa raspando y
     no es de la paleta. --kb-muted da 6,04:1 ✓.
   · las estrellas iban en #D4A017: dorado SIN oscurecer sobre fondo claro,
     2,11:1 — ilegible, que es exactamente lo que la marca advierte del dorado.
     El bronce de texto de la paleta (#8A5300) sigue leyéndose como dorado y da
     5,61:1 sobre hueso ✓. Sobre la tarjeta oscura se invierte al dorado de
     verdad (regla de abajo): 9,18:1 ✓. */
.lo-tile-fam { font-size: 11.5px; color: var(--kb-muted); line-height: 1.3; }
.lo-tile-rate { font-size: 11.5px; color: var(--kb-muted); display: flex; gap: 4px; align-items: center; margin-top: 2px; }
.lo-tile-rate b { color: #8A5300; font-weight: 400; letter-spacing: .06em; font-size: 11px; }
.lo-tile.dark .lo-tile-rate b { color: var(--kb-yellow); }
.lo-tile-rate u { text-decoration: none; }
.lo-tile-pl { font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; margin-top: 3px; color: inherit; }
.lo-tile-pl.may b { color: var(--kb-orange-text); font-size: 14px; }
.lo-tile-pl s { color: var(--kb-muted); font-weight: 500; margin-left: 6px; }
.lo-tile-pl i { font-style: normal; margin-left: 6px; background: var(--kb-b2b-soft); color: var(--kb-b2b); padding: 1px 6px; border-radius: 5px; font-size: 11px; }
.lo-tile-gain { font-size: 10.5px; color: var(--kb-b2b); font-weight: 600; }
.lo-tile-tier { font-family: var(--kb-font-mono); font-size: 9.5px; letter-spacing: .06em; color: var(--kb-b2b); font-weight: 700; }
.lo-tile.dark .lo-tile-tier { color: var(--kb-b2b-soft); }
.lo-tile.big .lo-tile-info { padding: 60px 16px 14px; gap: 3px; }
.lo-tile.big .lo-tile-nm { font-size: 30px; }
.lo-tile.big .lo-tile-fam, .lo-tile.big .lo-tile-rate { font-size: 13px; }
.lo-tile.big .lo-tile-pl { font-size: 16px; }
.lo-tile.big .lo-tile-pl.may b { font-size: 18px; }
.lo-tile-hov { position: absolute; inset: 0; z-index: 3; display: flex; align-items: center; justify-content: center; background: rgba(21,20,26,.38); opacity: 0; transition: opacity .15s; pointer-events: none; }
.lo-tile:hover .lo-tile-hov, .lo-tile:focus-within .lo-tile-hov { opacity: 1; }
.lo-tile-hb { display: flex; flex-direction: column; gap: 8px; align-items: center; pointer-events: auto; transform: translateY(-16%); }
.lo-stp { display: inline-flex; align-items: center; gap: 8px; background: #fff; border-radius: 999px; padding: 3px; }
.lo-stp button { width: 30px; height: 30px; border-radius: 50%; border: 0; background: var(--kb-cream); font-size: 16px; cursor: pointer; color: var(--kb-ink); }
.lo-stp b { min-width: 18px; text-align: center; font-variant-numeric: tabular-nums; }
.lo-add { border: 0; background: #fff; color: var(--kb-ink); font: inherit; font-weight: 800; padding: 9px 16px; border-radius: 8px; cursor: pointer; }
.lo-smp { border: 1px solid #fff; color: #fff; padding: 8px 14px; border-radius: 8px; font-size: 12px; font-weight: 600; text-decoration: none; }
.lo-tile-mprice { display: none; }
.lo-empty { padding: 40px 0; text-align: center; color: var(--kb-muted); }
.lo-more { display: flex; justify-content: space-between; align-items: center; margin: 18px 0 6px; color: var(--kb-muted); font-size: 13px; }
.lo-more button { border: 1px solid var(--kb-line-strong); background: #fff; font: inherit; font-weight: 700; padding: 9px 18px; border-radius: 999px; cursor: pointer; }
.lo-foot-note { font-size: 12.5px; color: var(--kb-muted); margin: 8px 0 0; }
/* Aviso editable del dueño (Admin → Anuncio) en la vitrina: una línea fina, no una franja
   negra. Sin esto el aviso se quedaba sin lugar visible al ocultar la barra superior. */
/* [LINEA-OUTLET · BLOQUE 18] El aviso puede traer VARIOS mensajes ("||" en Admin). Se
   reparten con un punto medio entre ellos y con salto de línea en pantalla estrecha, en vez
   de salirse de la franja. */
/* [LINEA-OUTLET · BLOQUE 22] LA FRANJA SE INVIERTE A TINTA.
   Era el tercer casi-blanco del encabezado: la vitrina arrancaba apilando página #FFFFFF,
   franja #F6EFE0 (1,14:1 contra la página) y buscador #F4F1EB (1,02:1 contra la franja). Tres
   capas distintas y cero diferencia perceptible — eso es lo que se veía como "sin vida": no
   faltaba color, faltaba jerarquía. Invertirla le da peso al encabezado sin ensuciar nada:
   cuesta cero saturación y devuelve 18,31:1. Se queda en una sola línea de 12 px: si la franja
   y el encabezado pasan de ~100 px, el cliente abre la tienda viendo avisos, no perfumes. */
.lo-anuncio { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; align-items: center; padding: 7px 12px; margin: 0 0 2px; background: var(--kb-ink); color: var(--kb-ink-fg); font-size: 12px; font-weight: 600; text-align: center; border-radius: 8px; }
.lo-anuncio span + span::before { content: "\00b7"; margin-right: 8px; color: var(--kb-ink-fg2); }
.lo-anuncio .kb-announce, .lo-anuncio .kb-announce-bar { all: unset; font: inherit; color: inherit; }
/* [LINEA-OUTLET · BLOQUE 8] Encabezado en modo vitrina: lo que el mockup no tiene.
   Sólo aplica mientras la portada está montada (home-outlet.jsx pone la clase). */
body.lo-vitrina .kb-topbar { display: none; }
body.lo-vitrina .kb-nav-meta { display: none; }
/* El buscador manda en el encabezado de la vitrina: ocupa el ancho que le deja el logo. */
/* [LINEA-OUTLET · BLOQUE 13] El buscador de la vitrina iba sobre #F2F2F2, un
   gris frío; el hueso de la casa es el fondo suave de esta marca. El texto que
   se escribe dentro es --kb-ink: 16,24:1 ✓. */
/* [BLOQUE 22] Esta regla es la que dejaba el buscador de la vitrina sin borde ninguno. Ahora
   sólo se ocupa de la anchura; el fondo y el canto los pone .kb-search y son los mismos en
   todo el sitio. */
body.lo-vitrina .kb-search { max-width: none; width: 100%; margin: 0 18px; }
body.lo-vitrina .kb-search-cat, body.lo-vitrina .kb-search-go { display: none; }
body.lo-vitrina .kb-push-card { display: none; }
body.lo-vitrina .kb-main { max-width: 1560px; padding-left: 18px; padding-right: 18px; }
/* Resumen del pedido dentro de la tira (escritorio). */
.lo-cartpill { display: inline-flex; flex-direction: column; align-items: flex-start; gap: 0; border: 1px solid var(--kb-line-strong); background: #fff; border-radius: 999px; padding: 5px 14px; font: inherit; font-size: 12.5px; line-height: 1.25; cursor: pointer; color: var(--kb-ink); white-space: nowrap; }
.lo-cartpill b { font-variant-numeric: tabular-nums; }
.lo-cartpill-l1 { white-space: nowrap; }
.lo-cartpill small { color: var(--kb-muted); font-size: 11px; }
/* Ficha: tabla de tamaños y escalera del cliente público */
.lo-ficha { margin: 14px 0 12px; display: grid; gap: 10px; }
.lo-ptab { width: 100%; border-collapse: collapse; font-size: 12.5px; background: #fff; border: 1px solid var(--kb-line); border-radius: 10px; overflow: hidden; }
.lo-ptab th, .lo-ptab td { padding: 8px 10px; text-align: left; border-bottom: 1px solid var(--kb-line); vertical-align: middle; }
.lo-ptab th { font-family: var(--kb-font-mono); font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--kb-muted); background: var(--kb-cream); }
.lo-ptab th small { display: block; font-weight: 400; text-transform: none; letter-spacing: 0; }
.lo-ptab tr.cur td { background: var(--kb-yellow-soft); }
.lo-ptab td.pub s { color: var(--kb-muted); }
.lo-ptab td.may { color: var(--kb-orange-text); font-weight: 800; }
.lo-ptab td.dist { color: var(--kb-b2b); font-weight: 800; }
.lo-ptab td.low { color: var(--kb-orange-text); font-weight: 700; }
.lo-add-sm { border: 0; background: var(--kb-ink); color: #fff; font: inherit; font-size: 12px; font-weight: 700; padding: 6px 12px; border-radius: 7px; cursor: pointer; }
.lo-ladlbl { font-size: 12px; color: var(--kb-muted); }
.lo-ladder { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 6px; }
.lo-ladder span { border: 1px solid var(--kb-line-strong); border-radius: 10px; padding: 8px 10px; font-size: 12px; font-weight: 700; color: var(--kb-ink); background: #fff; }
.lo-ladder span b { display: block; font-size: 11px; font-weight: 600; color: var(--kb-muted); margin-top: 2px; }
.lo-ladder span.hit { border-color: var(--kb-b2b); background: var(--kb-b2b-soft); color: var(--kb-b2b); }
.lo-ladder span.hit b { color: var(--kb-b2b); }
.lo-nivel-now { font-size: 12.5px; font-weight: 700; color: var(--kb-ink); }
.lo-gainbox { font-size: 12.5px; padding: 10px 12px; border-radius: 10px; background: var(--kb-yellow-soft); color: var(--kb-ink); }
/* [BLOQUE 27] El punto de ruptura de 1100px sobra: el ancho mínimo ya reparte solo. */
@media (max-width: 900px) {
  .lo-home { padding-bottom: 118px; }
  .lo-strip { grid-template-columns: auto 1fr; gap: 10px 12px; }
  .lo-avatar { width: 60px; height: 60px; }
  .lo-nm h1 { font-size: 20px; }
  .lo-acts { flex-basis: 100%; margin-left: 0; justify-content: space-between; }
  .lo-filters .lo-filter-all { margin-left: 0; }
  /* [BLOQUE 27] En el teléfono la vitrina se diseñó de tres azulejos por fila, y un mínimo de
     escritorio dejaría uno. Se redefine el NÚMERO, no la regla de la rejilla. Dentro de :root:
     una variable suelta en un @media es CSS inválido y se descarta en silencio. */
  :root { --lo-tile-min: 104px; }
  .lo-grid { gap: 2px; }
  .lo-tile.big { grid-column: span 1; grid-row: span 1; }
  .lo-tile-info, .lo-tile-hov { display: none; }
  .lo-tile-img { padding: 12% 14%; place-items: center; }
  .lo-tile-img .kb-frasco, .lo-tile.big .lo-tile-img .kb-frasco { width: 52px; height: 84px; filter: drop-shadow(0 6px 8px rgba(21,20,26,.22)); }
  .lo-tile-badge { right: 5px; top: 5px; font-size: 8.5px; padding: 2px 5px; }
  .lo-tile-mprice { display: block; position: absolute; left: 6px; bottom: 6px; z-index: 2; font-size: 11px; font-weight: 800; background: rgba(255,255,255,.93); color: var(--kb-ink); padding: 3px 6px; border-radius: 6px; font-variant-numeric: tabular-nums; pointer-events: none; }
  .lo-tile-mprice.may { color: var(--kb-orange-text); }
  .lo-tile-mprice s { color: var(--kb-muted); font-weight: 500; margin-left: 4px; }
  .lo-cartbar { display: grid; position: fixed; left: 8px; right: 8px; bottom: calc(64px + env(safe-area-inset-bottom)); z-index: 79; margin: 0; box-shadow: 0 8px 24px rgba(21,20,26,.18); }
  .lo-cartpill { display: none; }
  /* El aviso de notificaciones (improvements-pack.jsx) se sienta ENCIMA de la barra del
     pedido, no sobre ella: en el teléfono tapaba el nivel alcanzado y lo que falta. */
  .kb-push-card { bottom: calc(150px + env(safe-area-inset-bottom)) !important; left: 8px; right: 8px; width: auto !important; }
  .lo-ladder { grid-auto-flow: row; grid-template-columns: 1fr 1fr; }
  .lo-ptab { font-size: 12px; }
  .lo-ptab th, .lo-ptab td { padding: 7px 6px; }
}

/* ══════════════════════════════════════════════════════════════════════════
   COLA DE TRABAJO · Admin → Inicio
   El Inicio es una lista de asuntos que el dueño puede resolver hoy, ordenados
   por lo que cuesta no hacerlos. Las gráficas viven debajo del pliegue.

   [REGLA DE LA CASA] El motor de temas (theme-engine.jsx) REASIGNA en caliente
   --kb-orange, --kb-orange-deep y --kb-pink-deep con el acento de la temporada
   (el de Buen Fin es #D9B26A, amarillo). Por eso NINGUN color de estado de esta
   pantalla cuelga de esos tokens: si lo hiciera, "17 pedidos pagados sin enviar"
   se pintaria amarillo limon sobre blanco (1,7:1) durante el Buen Fin.
   Los de abajo son literales, medidos contra su fondo REAL:
     #B3261E sobre #FDECEA -> 5,71:1   ·  sobre blanco -> 6,53:1
     #8A5300 sobre #FFF7E6 -> 5,94:1   ·  sobre blanco -> 6,33:1
     #0E6B5E sobre blanco  -> 6,40:1
     #5C5A60 sobre #F4F4F5 -> 6,19:1   [BLOQUE 22] el fondo apagado dejó de ser hueso
     blanco sobre #B3261E 6,54:1 · sobre #8A5300 6,33:1 · sobre #5C5A60 6,80:1
   ══════════════════════════════════════════════════════════════════════════ */
:root {
  --kb-cola-rojo: #B3261E;
  --kb-cola-rojo-bg: #FDECEA;
  /* [BLOQUE 13] Los dos "-borde" son el tinte claro de SU PROPIO estado (rojo
     #B3261E y ámbar #8A5300), no hues nuevos, y sólo se usan como filete de
     1px: se quedan. */
  --kb-cola-rojo-borde: #F3C2BD;
  --kb-cola-ambar: #8A5300;
  --kb-cola-ambar-bg: #FFF7E6;
  --kb-cola-ambar-borde: #F2D9A6;
  --kb-cola-verde: #0E6B5E;
  --kb-cola-gris: #5C5A60;
  /* [BLOQUE 13] El fondo gris era #F4F5F7, azulado; el hueso de la paleta hace
     de fondo apagado; desde el bloque 22 es la superficie neutra de toda la casa
     (--kb-cola-gris #5C5A60 encima: 6,19:1 ✓). */
  --kb-cola-gris-bg: #F4F4F5;
  /* [BLOQUE 22] Neutro, como sus dos hermanos, y con el mismo papel: filete de 1 px. */
  --kb-cola-gris-borde: #D8D8DC;
  /* [BLOQUE 13] La tinta era #2A2F3A, un carbón AZULADO; el carbón de la marca
     mejora además el número: 16,01:1 sobre el rosado #FDECEA (antes 11,72:1) y
     17,18:1 sobre el ámbar #FFF7E6 ✓. */
  --kb-cola-tinta: #15141A;
  /* [BLOQUE 13] El "enlace" era el cuarto azul de la hoja. Un enlace es ACCIÓN y
     la acción de esta marca es el rojo outlet: 6,48:1 sobre el fondo apagado
     #F4F4F5 y 7,12:1 sobre blanco ✓ (el azul daba 6,17:1). */
  --kb-cola-enlace: #A32C29;
  /* Tramos de la barra de inventario (objetos gráficos: ≥3:1 entre sí y con el
     riel). "Sobrevendido" además lleva rayas, para no depender solo del color. */
  /* [BLOQUE 13] Los tramos de la barra son objetos gráficos: lo que hay que
     medir es cada uno contra el RIEL en el que se apoyan (#D3CEC4) y contra el
     blanco de la tarjeta, que es donde vive su cuadrito de leyenda. "Bajo" era
     #B45309 (naranja quemado) y "sobrevendido" #6B1410 (vino), dos colores de
     nadie; pasan al ámbar y al rojo profundo de la paleta:
       · sano #0E6B5E → riel 4,08:1 ✓ · blanco 6,40:1 ✓
       · bajo #8A5300 → riel 4,04:1 ✓ · blanco 6,33:1 ✓
       · cero #B3261E → riel 4,17:1 ✓ · blanco 6,54:1 ✓
       · neg  #8E2522 → riel 5,49:1 ✓ · blanco 8,61:1 ✓
     Entre tramos vecinos la diferencia sigue siendo de TONO y no de luminancia
     (el peor par, sano/bajo, da 1,01:1) — igual que antes del cambio. Por eso el
     tramo peor lleva rayas y la leyenda repite cada tramo con su cifra al lado:
     el color nunca es el único que lo dice. */
  --kb-inv-sano: #0E6B5E;
  --kb-inv-bajo: #8A5300;
  --kb-inv-cero: #B3261E;
  --kb-inv-neg:  #8E2522;
}

/* La cabecera del Inicio trae interruptor + botón: en 375px tienen que bajar de
   renglón en vez de empujar el título fuera de la pantalla. */
.kb-cola .kb-spread { flex-wrap: wrap; }
.kb-cola .kb-spread > div:last-child { flex-wrap: wrap; }
.kb-cola-hist {
  display: inline-flex; align-items: center; gap: 8px; min-height: 44px;
  font-size: 12.5px; color: var(--kb-cola-gris); cursor: pointer;
}
.kb-cola-hist input { width: 18px; height: 18px; }
/* El botón "Actualizar" hereda el .kb-btn global, que fija height: 38px. Medido
   en Chrome a 768 y 1280: 103×38 px, por debajo del piso táctil de 44. Se sube
   sólo dentro de la cola (el .kb-btn global lo comparten otras pantallas). */
.kb-cola .kb-spread .kb-btn { height: auto; min-height: 44px; }

.kb-cola-resumen { margin: 2px 0 14px; font-size: 15px; font-weight: 700; color: var(--kb-charcoal); }
.kb-cola-nota { margin: 6px 0 0; font-size: 13px; line-height: 1.5; color: var(--kb-cola-gris); }

/* ── Una tarjeta de asunto ── */
.kb-asunto {
  border: 1px solid var(--kb-line);
  border-left: 5px solid var(--kb-cola-gris);
  border-radius: 12px; padding: 16px; margin-bottom: 12px;
  background: var(--kb-paper);
}
.kb-asunto--rojo  { border-color: var(--kb-cola-rojo-borde);  border-left-color: var(--kb-cola-rojo);  background: var(--kb-cola-rojo-bg); }
.kb-asunto--ambar { border-color: var(--kb-cola-ambar-borde); border-left-color: var(--kb-cola-ambar); background: var(--kb-cola-ambar-bg); }
.kb-asunto--caido { border-color: var(--kb-cola-gris-borde);  border-left-color: var(--kb-cola-gris);  background: var(--kb-cola-gris-bg); }

.kb-asunto-cabeza { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.kb-asunto-cifra {
  font-family: var(--kb-font-display); font-size: 44px; font-weight: 800;
  line-height: 1; letter-spacing: -0.02em; flex: 0 0 auto;
}
.kb-asunto--rojo  .kb-asunto-cifra { color: var(--kb-cola-rojo); }
.kb-asunto--ambar .kb-asunto-cifra { color: var(--kb-cola-ambar); }
.kb-asunto-texto { flex: 1 1 220px; min-width: 0; }
.kb-asunto-titulo { margin: 0; font-size: 16px; font-weight: 700; line-height: 1.25; color: var(--kb-charcoal); }
.kb-asunto-costo { margin: 5px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--kb-cola-tinta); }

/* Ejemplos concretos: hasta tres, cada uno un renglón tocable de 44px. */
.kb-asunto-ejemplos { list-style: none; margin: 12px 0 0; padding: 0; }
.kb-asunto-ejemplos li { margin: 0 0 6px; }
.kb-asunto-ej {
  display: flex; align-items: center; flex-wrap: wrap; gap: 4px 10px;
  width: 100%; min-height: 44px; padding: 8px 12px; text-align: left;
  background: var(--kb-paper); border: 1px solid var(--kb-line-strong);
  border-radius: 8px; font: inherit; color: var(--kb-cola-tinta);
}
button.kb-asunto-ej { cursor: pointer; }
button.kb-asunto-ej:hover { border-color: var(--kb-cola-gris); }
.kb-asunto-ej-folio {
  font-family: var(--kb-font-mono); font-size: 12px; font-weight: 700;
  letter-spacing: 0.02em; flex: 0 0 auto; overflow-wrap: anywhere;
}
.kb-asunto--rojo  .kb-asunto-ej-folio { color: var(--kb-cola-rojo); }
.kb-asunto--ambar .kb-asunto-ej-folio { color: var(--kb-cola-ambar); }
.kb-asunto--caido .kb-asunto-ej-folio { color: var(--kb-cola-gris); }
.kb-asunto-ej-dato { font-size: 13px; line-height: 1.4; min-width: 0; overflow-wrap: anywhere; }

/* [OJO] .kb-btn base fija height:38px y white-space:nowrap. Aquí no sirven: la
   etiqueta lleva la cifra dentro ("Ver los 543 en Inventario") y debe poder
   partirse antes que desbordar. min-height mantiene los 44px táctiles. */
.kb-btn.kb-asunto-cta {
  height: auto; min-height: 44px; margin-top: 12px; padding: 10px 18px;
  font-size: 14px; font-weight: 700; line-height: 1.25;
  white-space: normal; overflow-wrap: anywhere; text-align: center;
  color: #fff; background: var(--kb-cola-gris); border-color: var(--kb-cola-gris);
  border-radius: 10px;
}
.kb-asunto--rojo  .kb-btn.kb-asunto-cta { background: var(--kb-cola-rojo);  border-color: var(--kb-cola-rojo); }
.kb-asunto--ambar .kb-btn.kb-asunto-cta { background: var(--kb-cola-ambar); border-color: var(--kb-cola-ambar); }
.kb-btn.kb-asunto-cta:hover { filter: brightness(0.92); }

/* ── Barra apilada del inventario activo ── */
.kb-inv { margin-top: 14px; }
.kb-inv-barra {
  display: flex; height: 16px; border-radius: 999px; overflow: hidden;
  background: var(--kb-cola-gris-borde); border: 1px solid var(--kb-line-strong);
}
.kb-inv-tramo { flex: 0 0 auto; height: 100%; min-width: 0; }
.kb-inv-tramo--sanos     { background: var(--kb-inv-sano); }
.kb-inv-tramo--bajos     { background: var(--kb-inv-bajo); }
.kb-inv-tramo--cero      { background: var(--kb-inv-cero); }
/* El tramo peor no se distingue solo por color: lleva rayas. */
.kb-inv-tramo--negativos,
.kb-inv-swatch--negativos {
  background: repeating-linear-gradient(135deg, var(--kb-inv-neg) 0 4px, rgba(255,255,255,0.45) 4px 7px);
}
.kb-inv-leyenda { display: flex; flex-wrap: wrap; gap: 6px 16px; list-style: none; margin: 10px 0 0; padding: 0; }
.kb-inv-chip { display: flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--kb-cola-tinta); }
.kb-inv-chip b { font-family: var(--kb-font-mono); font-size: 13px; font-weight: 700; }
.kb-inv-swatch { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 auto; }
.kb-inv-swatch--sanos { background: var(--kb-inv-sano); }
.kb-inv-swatch--bajos { background: var(--kb-inv-bajo); }
.kb-inv-swatch--cero  { background: var(--kb-inv-cero); }
.kb-inv-pie { margin: 8px 0 0; font-size: 12.5px; line-height: 1.45; color: var(--kb-cola-gris); }

/* ── Estado vacío y bloques caídos ── */
.kb-cola-vacio {
  border: 1px dashed var(--kb-cola-gris-borde); border-radius: 12px;
  padding: 26px 18px; text-align: center; background: var(--kb-cola-gris-bg);
}
.kb-cola-vacio-tit { margin: 0; font-family: var(--kb-font-display); font-size: 19px; font-weight: 700; color: var(--kb-charcoal); }
.kb-cola-celda { padding: 24px 16px; text-align: center; font-size: 13px; color: var(--kb-cola-gris); }
.kb-cola-celda-caida { padding: 24px 16px; text-align: center; font-size: 13px; color: var(--kb-cola-gris); background: var(--kb-cola-gris-bg); }
.kb-cola-relink {
  min-height: 44px; padding: 0 6px; border: 0; background: none;
  font: inherit; font-size: 13px; color: var(--kb-cola-enlace);
  text-decoration: underline; cursor: pointer;
}

/* ── Separador entre el bloque de cifras y la cola ── */
.kb-cola-pliegue { border: 0; border-top: 1px solid var(--kb-line); margin: 30px 0 18px; }
.kb-neg { margin-top: 20px; }
/* [PETICIÓN DEL DUEÑO] "El negocio en números" pasó a ser el primer bloque de la
   pantalla, arriba de las tarjetas de asuntos: pega al encabezado, sin el aire
   que necesitaba cuando venía después del pliegue. */
.kb-neg--primero { margin-top: 6px; }
/* Cuadratura de la gráfica: las barras suman (o no) el titular de arriba. */
.kb-sem-cuadre { margin-top: 8px; }
.kb-neg-titulo { margin: 0 0 12px; font-family: var(--kb-font-display); font-size: 18px; font-weight: 800; color: var(--kb-charcoal); }
.kb-neg-desde { font-size: 13px; font-weight: 600; color: var(--kb-cola-gris); }
.kb-neg-sub { margin: 20px 0 8px; font-size: 14px; font-weight: 700; color: var(--kb-charcoal); }

.kb-num-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; }
.kb-num { border: 1px solid var(--kb-line); border-radius: 10px; padding: 12px 14px; background: var(--kb-paper); }
.kb-num-etq { display: block; font-family: var(--kb-font-mono); font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--kb-cola-gris); }
.kb-num-val { display: block; margin-top: 3px; font-family: var(--kb-font-display); font-size: 26px; font-weight: 700; letter-spacing: -0.01em; color: var(--kb-charcoal); overflow-wrap: anywhere; }
.kb-num-nota { display: block; margin-top: 4px; font-size: 12px; line-height: 1.4; color: var(--kb-cola-gris); }

/* Barras SEMANALES desde la apertura (antes: 12 meses con diez en cero). Si no
   caben, la gráfica se desliza DENTRO de su caja — la página nunca se desborda. */
.kb-sem {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(54px, 1fr);
  gap: 8px; align-items: end; overflow-x: auto; padding-bottom: 6px;
  -webkit-overflow-scrolling: touch;
}
.kb-sem-col { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.kb-sem-riel { display: flex; align-items: flex-end; height: 120px; background: var(--kb-cola-gris-bg); border-radius: 6px 6px 0 0; }
.kb-sem-barra { width: 100%; background: var(--kb-cola-verde); border-radius: 4px 4px 0 0; }
.kb-sem-val { font-family: var(--kb-font-mono); font-size: 11.5px; font-weight: 700; color: var(--kb-cola-tinta); text-align: center; white-space: nowrap; }
.kb-sem-etq { font-size: 11.5px; line-height: 1.3; color: var(--kb-cola-gris); text-align: center; }

/* La tabla "Últimos pedidos" pinta el ESTADO DEL PEDIDO con los .kb-tag
   globales, y `.kb-tag.orange` cuelga su color de --kb-orange-deep — que
   theme-engine.jsx:27 repinta con el acento de temporada. Medido en Chrome con
   Buen Fin activo (#D9B26A): "Por revisar" quedaba amarillo sobre amarillo
   suave a 1,27:1 y 10,5px, o sea ilegible justo en el estado que pide acción.
   Dentro de la cola ese chip usa el ámbar estable del bloque (#8A5300 sobre
   #FFF7E6 → 5,94:1) y sube al piso de 11px de la casa. Sólo aquí dentro: el
   .kb-tag global lo comparten otras pantallas y no es de esta tarjeta tocarlo. */
.kb-cola .kb-tag { font-size: 11px; }
.kb-cola .kb-tag.orange {
  background: var(--kb-cola-ambar-bg); color: var(--kb-cola-ambar); border-color: var(--kb-cola-ambar-borde);
}
.kb-cola .kb-table th { font-size: 11px; }

/* Pie de frescura: a qué hora se midió cada bloque. */
.kb-cola-pie {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: 24px; padding-top: 12px; border-top: 1px solid var(--kb-line);
  font-size: 12px; line-height: 1.5; color: var(--kb-cola-gris);
}

/* Aviso de lista recortada en Inventario (el destino de "Ver los N"). */
.kb-inv-filtro-aviso { margin: 0; padding: 0 14px 10px; font-size: 12.5px; line-height: 1.45; color: var(--kb-cola-gris); }

@media (max-width: 560px) {
  .kb-asunto { padding: 14px; }
  .kb-asunto-cifra { font-size: 38px; }
  .kb-btn.kb-asunto-cta { width: 100%; }
}

/* ── [LINEA-OUTLET · BLOQUE 3] Catálogo de perfumes: tarjeta por perfume con tamaños ── */
.kb-pcard .kb-pcard-fam { font-size: 11.5px; color: var(--kb-muted); line-height: 1.3; margin-top: -2px; }
.kb-pcard .kb-pcard-sizes { display: flex; gap: 4px; flex-wrap: wrap; position: relative; z-index: 2; }
.kb-pcard .kb-pcard-sizes button { border: 1px solid var(--kb-line); background: #fff; border-radius: 6px; padding: 3px 8px; font-size: 11px; font-weight: 700; cursor: pointer; color: var(--kb-ink); }
.kb-pcard .kb-pcard-sizes button.on { background: var(--kb-ink); border-color: var(--kb-ink); color: #fff; }
/* [LINEA-OUTLET · BLOQUE 13] Esta pastilla dice lo que GANA el mayorista con la
   pieza, o sea habla de nivel/mayoreo — el mismo dato que .lo-tile-gain pinta de
   verde en la vitrina, y aquí salía en un rojo suelto (#7A1F1C) que además lo
   confundía con el precio. Pasa al verde de nivel sobre su suave: #0E6B5E sobre
   #E3F1EE → 5,51:1 ✓. */
.kb-pcard .kb-pcard-gain { font-size: 11px; font-weight: 600; color: var(--kb-b2b); background: var(--kb-b2b-soft); border-radius: 6px; padding: 3px 8px; align-self: flex-start; }
.kb-pcard .kb-pcard-desde { font-size: 11px; color: var(--kb-muted); }
.kb-catalog-quick { display: none; gap: 6px; overflow-x: auto; padding: 2px 0 10px; scrollbar-width: none; }
.kb-catalog-quick .kb-chip { white-space: nowrap; }
@media (max-width: 1100px) { .kb-catalog-quick { display: flex; } }
@media (min-width: 1280px) { .kb-catalog .kb-product-grid { grid-template-columns: repeat(5, 1fr); gap: 10px; } }

/* ═══ DISTRIBUIDORES · el lenguaje de la vitrina ════════════════════════════
   [LINEA-OUTLET · BLOQUE 14] La página venía del clon con pinta de landing de
   "programa de distribución": titulares de 48px en la tipografía de display,
   degradados, cajas de colores llenas y emoji multicolor como iconos (215
   líneas los usaban). Eso metía azules y amarillos que no son de la marca por
   una puerta que ninguna auditoría de CSS ve, porque son caracteres, no color.

   Aquí la página pasa a hablar como la portada: fondo hueso, titulares en la
   serif de la casa, rótulos en mono a versalitas, tarjetas blancas
   con un filete de 1px y el color reservado para lo que significa algo —rojo
   outlet = marca y acción, verde de nivel = mayoreo/distribución alcanzados,
   dorado = distinción—.

   Contrastes de los pares que pinta este bloque, calculados (calibrado: blanco
   sobre negro 21,00:1 · #767676 sobre blanco 4,54:1):
     · --kb-ink #15141A sobre --kb-cream #F4F1EB  → 16,26:1 ✓  (titulares y cuerpo)
     · --kb-muted #5C5A60 sobre #F4F1EB           →  6,37:1 ✓  (texto secundario)
     · --kb-muted sobre #FFFFFF                   →  7,00:1 ✓  (tarjetas)
     · --kb-orange-text #A32C29 sobre #FFFFFF     →  7,12:1 ✓  (cifra de mayoreo)
     · --kb-orange-text sobre #F4F1EB             →  6,80:1 ✓
     · --kb-b2b #0E6B5E sobre #FFFFFF             →  6,40:1 ✓  (cifra de distribución)
     · --kb-b2b sobre --kb-b2b-soft #E3F1EE       →  5,51:1 ✓
     · #FFFFFF sobre --kb-ink                     → 18,31:1 ✓  (botón oscuro)
     · #FFFFFF sobre --kb-orange #B8322F          →  5,94:1 ✓  (botón de acción)
     · #8A5300 sobre --kb-yellow-soft #F6EFE0     →  5,53:1 ✓  (la nota dorada)

   Y el piso de la casa: NINGUNA regla de aquí baja de 11px. Los rótulos en mono
   a versalitas piden bajar más —se ven "de etiqueta" a 9,5px— pero 11px es el
   mínimo que este repo se fijó y lo vigila tests/admin-inicio-honestidad.jsx
   para su propia pantalla; no hay motivo para que esta página sea la excepción. */
.lod { background: var(--kb-cream); }
.lod-wrap { max-width: 1120px; margin: 0 auto; padding: 0 20px; }
.lod-sec { padding: 54px 0; border-top: 1px solid var(--kb-line); }
.lod-sec:first-of-type { border-top: 0; }
.lod-head { max-width: 62ch; }
.lod-head.c { margin: 0 auto; text-align: center; }
.lod-eyebrow { display: block; font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--kb-muted); margin-bottom: 10px; }
.lod-h2 { font-family: var(--kb-font-serif); font-size: clamp(28px, 4.2vw, 42px); font-weight: 600; line-height: 1.08; letter-spacing: -.01em; color: var(--kb-ink); margin: 0; }
.lod-h2 em { font-style: normal; color: var(--kb-orange-text); }
.lod-lead { font-size: 14.5px; line-height: 1.65; color: var(--kb-muted); margin: 14px 0 0; max-width: 62ch; }
.lod-head.c .lod-lead { margin-left: auto; margin-right: auto; }

/* ── El encabezado: sin degradado y sin cifras inventadas ─────────────────── */
.lod-hero { display: grid; grid-template-columns: 1.25fr .85fr; gap: 40px; align-items: center; padding: 56px 0 50px; }
.lod-hero h1 { font-family: var(--kb-font-serif); font-size: clamp(34px, 6vw, 62px); font-weight: 600; line-height: 1.03; letter-spacing: -.015em; color: var(--kb-ink); margin: 0 0 6px; }
.lod-hero h1 em { font-style: normal; color: var(--kb-orange-text); }
.lod-hero .lod-sub { font-family: var(--kb-font-mono); font-size: 12.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--kb-muted); }
.lod-hero p { font-size: 15px; line-height: 1.65; color: var(--kb-muted); max-width: 52ch; margin: 18px 0 0; }
.lod-hero-cta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 26px; }
.lod-datos { display: flex; flex-wrap: wrap; gap: 0 34px; margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--kb-line); }
.lod-dato b { display: block; font-family: var(--kb-font-serif); font-size: 30px; font-weight: 600; line-height: 1; color: var(--kb-ink); }
.lod-dato span { display: block; font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--kb-muted); margin-top: 6px; }

/* La ficha de precio del encabezado: una tarjeta blanca, no un panel de color. */
.lod-ficha { background: #fff; border: 1px solid var(--kb-line); border-radius: 14px; padding: 22px; }
.lod-ficha-top { display: flex; justify-content: space-between; align-items: center; gap: 10px; padding-bottom: 14px; border-bottom: 1px solid var(--kb-line); }
.lod-ficha-top span { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--kb-muted); }
.lod-ficha-pct { display: flex; align-items: baseline; gap: 8px; margin: 18px 0 2px; }
.lod-ficha-pct b { font-family: var(--kb-font-serif); font-size: 58px; font-weight: 600; line-height: .9; color: var(--kb-b2b); letter-spacing: -.02em; }
.lod-ficha-pct i { font-style: normal; font-size: 13px; color: var(--kb-muted); }
.lod-ficha-cond { font-size: 12.5px; color: var(--kb-muted); font-variant-numeric: tabular-nums; }
.lod-ficha ul { list-style: none; margin: 18px 0 0; padding: 16px 0 0; border-top: 1px solid var(--kb-line); display: grid; gap: 9px; }
.lod-ficha li { display: flex; gap: 9px; align-items: flex-start; font-size: 13px; line-height: 1.45; color: var(--kb-ink); }
.lod-ficha li .lod-ic { flex: 0 0 auto; margin-top: 1px; }
.lod-min { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 18px; padding: 10px 12px; background: var(--kb-cream); border-radius: 10px; font-size: 12.5px; }
.lod-min span { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--kb-muted); }
.lod-min b { color: var(--kb-ink); }

/* ── Iconos: el juego de la marca, monocromo, nunca emoji de colores ──────── */
.lod-ic { width: 20px; height: 20px; color: var(--kb-ink); flex: 0 0 auto; }
.lod-ic svg { width: 100%; height: 100%; display: block; }
.lod-ic.red { color: var(--kb-orange-text); }
.lod-ic.b2b { color: var(--kb-b2b); }
.lod-ic.oro { color: #8A5300; }
.lod-ic.g { width: 26px; height: 26px; }

/* ── Rejillas y tarjetas ──────────────────────────────────────────────────── */
.lod-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1px; margin-top: 30px; background: var(--kb-line); border: 1px solid var(--kb-line); border-radius: 14px; overflow: hidden; }
.lod-card { background: #fff; padding: 22px 20px; display: flex; flex-direction: column; gap: 9px; }
.lod-card h3 { font-size: 15px; font-weight: 700; margin: 0; color: var(--kb-ink); letter-spacing: -.01em; }
.lod-card p { font-size: 13.5px; line-height: 1.55; color: var(--kb-muted); margin: 0; }
.lod-num { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 26px 30px; margin-top: 30px; }
.lod-num article { display: grid; grid-template-columns: auto 1fr; gap: 14px; align-items: start; }
.lod-num b { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .08em; color: var(--kb-orange-text); padding-top: 3px; }
.lod-num h3 { font-size: 14.5px; font-weight: 700; margin: 0 0 5px; color: var(--kb-ink); }
.lod-num p { font-size: 13.5px; line-height: 1.55; color: var(--kb-muted); margin: 0; }

/* ── Los dos descuentos: la pieza central ─────────────────────────────────── */
.lod-precios { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 18px; margin-top: 30px; }
.lod-precio { background: #fff; border: 1px solid var(--kb-line); border-radius: 14px; padding: 26px 24px; display: flex; flex-direction: column; }
.lod-precio.may { border-top: 3px solid var(--kb-orange); }
.lod-precio.dist { border-top: 3px solid var(--kb-b2b); }
.lod-precio h3 { font-family: var(--kb-font-serif); font-size: 30px; font-weight: 600; margin: 0; letter-spacing: -.01em; color: var(--kb-ink); }
.lod-precio .lod-para { font-size: 13px; color: var(--kb-muted); margin: 4px 0 18px; }
.lod-precio .lod-pct { display: flex; align-items: baseline; gap: 8px; padding-top: 16px; border-top: 1px solid var(--kb-line); }
.lod-precio .lod-pct b { font-family: var(--kb-font-serif); font-size: 46px; font-weight: 600; line-height: .95; letter-spacing: -.02em; }
.lod-precio.may .lod-pct b { color: var(--kb-orange-text); }
.lod-precio.dist .lod-pct b { color: var(--kb-b2b); }
.lod-precio .lod-pct i { font-style: normal; font-size: 13px; color: var(--kb-muted); }
.lod-precio .lod-cond { font-family: var(--kb-font-mono); font-size: 11.5px; letter-spacing: .04em; color: var(--kb-ink); margin-top: 8px; font-variant-numeric: tabular-nums; }
.lod-nota { background: var(--kb-cream); border-radius: 10px; padding: 12px 14px; font-size: 12.5px; line-height: 1.55; color: var(--kb-muted); margin-top: 16px; }
.lod-nota b { color: var(--kb-ink); }
.lod-precio ul { list-style: none; margin: 18px 0 0; padding: 0; display: grid; gap: 8px; flex: 1; }
.lod-precio li { display: flex; gap: 9px; align-items: flex-start; font-size: 13px; line-height: 1.45; color: var(--kb-ink); }
.lod-precio .kb-btn { margin-top: 20px; }
.lod-nohay { margin-top: 20px; padding: 16px 18px; background: var(--kb-yellow-soft); border-radius: 12px; font-size: 13px; line-height: 1.6; color: #6B4A00; }
.lod-nohay b { color: #5A3E00; }

/* ── Pasos, listas y perfiles ─────────────────────────────────────────────── */
.lod-pasos { display: grid; grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); gap: 1px; margin-top: 30px; background: var(--kb-line); border: 1px solid var(--kb-line); border-radius: 14px; overflow: hidden; }
.lod-paso { background: #fff; padding: 20px; display: grid; grid-template-columns: auto 1fr; gap: 14px; align-items: start; }
.lod-paso > b { width: 28px; height: 28px; border-radius: 50%; background: var(--kb-ink); color: #fff; display: grid; place-items: center; font-size: 12.5px; font-weight: 700; font-variant-numeric: tabular-nums; }
.lod-paso h3 { font-size: 14.5px; font-weight: 700; margin: 0 0 4px; color: var(--kb-ink); }
.lod-paso p { font-size: 13px; line-height: 1.5; color: var(--kb-muted); margin: 0; }
.lod-paso time { display: block; font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--kb-muted); margin-top: 8px; }
.lod-dos { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; margin-top: 30px; }
.lod-lista { background: #fff; border: 1px solid var(--kb-line); border-radius: 14px; padding: 22px; }
.lod-lista h3 { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--kb-muted); margin: 0 0 14px; }
.lod-lista ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; }
.lod-lista li { display: flex; gap: 10px; align-items: flex-start; font-size: 13.5px; line-height: 1.5; color: var(--kb-ink); }
.lod-lista li u { text-decoration: none; flex: 0 0 auto; font-family: var(--kb-font-mono); font-size: 12px; line-height: 1.5; }
.lod-lista.si li u { color: var(--kb-b2b); }
.lod-lista.no li { color: var(--kb-muted); }
.lod-lista.no li u { color: var(--kb-orange-text); }

/* ── Plan a 12 meses ──────────────────────────────────────────────────────── */
.lod-plan { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; margin-top: 30px; }
.lod-etapa { background: #fff; border: 1px solid var(--kb-line); border-radius: 14px; padding: 22px; }
.lod-etapa .lod-eyebrow { margin-bottom: 6px; }
.lod-etapa h3 { font-family: var(--kb-font-serif); font-size: 22px; font-weight: 600; margin: 0 0 16px; color: var(--kb-ink); line-height: 1.15; }
.lod-cifras { display: flex; gap: 26px; padding: 14px 0; border-top: 1px solid var(--kb-line); border-bottom: 1px solid var(--kb-line); margin-bottom: 14px; }
.lod-cifras div span { display: block; font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .09em; text-transform: uppercase; color: var(--kb-muted); }
.lod-cifras div b { display: block; font-family: var(--kb-font-serif); font-size: 24px; font-weight: 600; color: var(--kb-ink); margin-top: 4px; font-variant-numeric: tabular-nums; }
.lod-etapa ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 8px; }
.lod-etapa li { font-size: 13px; line-height: 1.5; color: var(--kb-muted); padding-left: 14px; position: relative; }
.lod-etapa li::before { content: ""; position: absolute; left: 0; top: 8px; width: 5px; height: 5px; border-radius: 50%; background: var(--kb-line-strong); }

/* ── Preguntas y cierre ───────────────────────────────────────────────────── */
.lod-faq { margin-top: 26px; display: grid; gap: 1px; background: var(--kb-line); border: 1px solid var(--kb-line); border-radius: 14px; overflow: hidden; }
.lod-faq details { background: #fff; padding: 16px 20px; }
.lod-faq summary { cursor: pointer; font-size: 14px; font-weight: 700; color: var(--kb-ink); list-style: none; display: flex; justify-content: space-between; gap: 14px; align-items: center; }
.lod-faq summary::-webkit-details-marker { display: none; }
.lod-faq summary::after { content: "+"; font-family: var(--kb-font-mono); color: var(--kb-muted); font-weight: 400; }
.lod-faq details[open] summary::after { content: "−"; }
.lod-faq p { font-size: 13.5px; line-height: 1.6; color: var(--kb-muted); margin: 12px 0 0; }
.lod-cierre { background: var(--kb-ink); border-radius: 16px; padding: 46px 32px; text-align: center; margin-top: 10px; }
.lod-cierre h2 { font-family: var(--kb-font-serif); font-size: clamp(28px, 4.5vw, 44px); font-weight: 600; color: var(--kb-cream); margin: 0; line-height: 1.1; }
.lod-cierre h2 em { font-style: normal; color: var(--kb-yellow); }
.lod-cierre p { font-size: 14px; color: var(--kb-line-strong); max-width: 54ch; margin: 14px auto 0; line-height: 1.6; }
.lod-cierre .lod-hero-cta { justify-content: center; }

/* ── Formulario ───────────────────────────────────────────────────────────── */
.lod-form { background: #fff; border: 1px solid var(--kb-line); border-radius: 14px; padding: 28px; max-width: 720px; margin: 26px auto 0; }
.lod-form .kb-input { margin-top: 5px; }
.lod-form label { font-size: 12px; font-weight: 700; color: var(--kb-ink); }
.lod-form-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
.lod-form-ancho { grid-column: 1 / -1; }
.lod-form-pie { font-size: 12px; color: var(--kb-muted); text-align: center; margin-top: 14px; line-height: 1.55; }
.lod-ok { text-align: center; padding: 30px 10px; }
.lod-ok h3 { font-family: var(--kb-font-serif); font-size: 26px; font-weight: 600; color: var(--kb-orange-text); margin: 10px 0; }
.lod-ok p { font-size: 14px; color: var(--kb-muted); margin: 0 0 16px; }

/* ── Calculadora ──────────────────────────────────────────────────────────
   Los controles usan el rojo de marca como color de acento del navegador
   (accent-color): es la acción del usuario, no un nivel alcanzado. */
.lod-campo-lbl { display: block; font-size: 12.5px; font-weight: 700; color: var(--kb-ink); margin: 16px 0 5px; }
.lod-lista > .lod-campo-lbl:first-of-type { margin-top: 0; }
.lod-rango { width: 100%; accent-color: var(--kb-orange); }
.lod-rango-pie { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; margin-top: 4px; font-size: 11px; color: var(--kb-muted); }
.lod-rango-pie b { font-size: 13px; color: var(--kb-ink); font-variant-numeric: tabular-nums; }
.lod-res { display: grid; gap: 0; }
.lod-res-row { display: flex; justify-content: space-between; align-items: baseline; gap: 14px; padding: 9px 0; border-bottom: 1px solid var(--kb-line); font-size: 13.5px; color: var(--kb-ink); }
.lod-res-row b { font-variant-numeric: tabular-nums; font-weight: 700; }
.lod-res-row.m { color: var(--kb-muted); }
.lod-res-row.m b { color: var(--kb-muted); }
.lod-res-row.big { font-size: 15px; }
.lod-res-row.big b { font-family: var(--kb-font-serif); font-size: 24px; font-weight: 600; }
/* El total no va en una caja de color: va en hueso, con la cifra en serif, que es
   como la vitrina destaca un número. El anual sí se levanta en carbón —el único
   oscuro de la paleta— con el dorado encima (9,18:1 ✓), que es el par que la casa
   ya usa para "lo más importante de esta pantalla". */
.lod-tot { background: var(--kb-cream); border-radius: 12px; padding: 16px 18px; margin-top: 16px; }
.lod-tot span { display: block; font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--kb-muted); }
.lod-tot b { display: block; font-family: var(--kb-font-serif); font-size: 34px; font-weight: 600; line-height: 1.05; color: var(--kb-ink); margin-top: 4px; font-variant-numeric: tabular-nums; }
.lod-tot small { display: block; font-size: 11.5px; color: var(--kb-muted); margin-top: 6px; }
.lod-tot.oscuro { background: var(--kb-ink); margin-top: 10px; }
.lod-tot.oscuro span { color: var(--kb-line-strong); }
.lod-tot.oscuro b { color: var(--kb-yellow); }

@media (max-width: 900px) {
  .lod-hero { grid-template-columns: 1fr; gap: 28px; padding: 34px 0 30px; }
  .lod-sec { padding: 40px 0; }
  .lod-datos { gap: 0 24px; }
  .lod-cierre { padding: 34px 20px; border-radius: 12px; }
}

/* ═══ PERFIL OLFATIVO · las notas del perfume ══════════════════════════════
   [LINEA-OUTLET · BLOQUE 15] Este bloque venía del "perfil sensorial" de la
   tienda de botanas y se veía como tal: cabecera con degradado, tres barras con
   ◍ ◷ ◌ delante, y las notas como PASTILLAS que aparecían con un pop de escala,
   una por una. Una pastilla es un control —invita a tocarla— y aquí no se toca
   nada: son el nombre de lo que huele el perfume. Con doce notas, la pirámide
   quedaba en un revoltijo de globitos.

   Lo que pide una casa de perfume es lo contrario del adorno: aire, una tinta,
   filetes de 1px y el nombre de cada nota escrito en serif, que es la letra con
   la que esta marca dice lo importante. La pirámide se lee como una frase
   —"Azafrán · Bergamota · Pimienta rosa"— y no como una botonera.

   Los ACORDES son la pieza nueva. Se deducen de las notas que el perfume ya
   tiene cargadas (product-sensory.jsx → acordesDe), nunca se inventan, y se
   dibujan con UNA sola tinta: la de la familia. La competencia los pinta con un
   color por acorde —diez colores distintos en una tarjeta— y eso aquí sería
   meter una segunda paleta por la puerta de atrás. La información está en el
   LARGO de la barra, no en el color.

   Contrastes (calibrado: blanco/negro 21,00:1 · #767676 sobre blanco 4,54:1):
     · rótulos --kb-muted #5C5A60 sobre papel  → 7,00:1 ✓
     · notas en serif --kb-ink #15141A         → 18,31:1 ✓
     · la tinta más floja de las cinco familias (#8A5300) sobre papel → 6,33:1 ✓
     · barra de acorde sobre su riel --kb-cream → 5,61:1 ✓ (objeto gráfico, 3:1)
   Ninguna regla baja de 11px. */
.kb-olf { border: 1px solid var(--kb-line); border-radius: 14px; background: var(--kb-paper); overflow: hidden; margin-top: 8px; }
.kb-olf-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; padding: 18px 20px 14px; border-bottom: 1px solid var(--kb-line); }
.kb-olf-eyebrow { display: block; font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .12em; text-transform: uppercase; color: var(--kb-muted); }
.kb-olf-tit { font-family: var(--kb-font-serif); font-size: 26px; font-weight: 600; line-height: 1.1; color: var(--kb-ink); margin: 4px 0 0; }
.kb-olf-tit em { font-style: normal; }
.kb-olf-meta { font-size: 12.5px; color: var(--kb-muted); margin: 6px 0 0; }
.kb-olf-body { display: grid; grid-template-columns: 1.15fr .85fr; gap: 0; }
.kb-olf-col { padding: 20px; }
.kb-olf-col + .kb-olf-col { border-left: 1px solid var(--kb-line); }

/* ── La pirámide ─────────────────────────────────────────────────────────── */
.kb-olf-tier { display: grid; grid-template-columns: 14px 1fr; gap: 14px; padding-bottom: 18px; }
.kb-olf-tier:last-child { padding-bottom: 0; }
/* El carril de la izquierda: un filete con un punto por piso. Dice "pirámide"
   sin dibujar una pirámide, que a 360px sería ilegible. */
.kb-olf-rail { position: relative; display: flex; justify-content: center; }
.kb-olf-rail::before { content: ""; position: absolute; top: 4px; bottom: -18px; width: 1px; background: var(--kb-line); }
.kb-olf-tier:last-child .kb-olf-rail::before { display: none; }
.kb-olf-dot { position: relative; width: 7px; height: 7px; border-radius: 50%; margin-top: 4px; background: var(--kb-paper); border: 1px solid var(--kb-line-strong); }
.kb-olf-dot.on { background: currentColor; border-color: currentColor; }
.kb-olf-lbl { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .1em; text-transform: uppercase; color: var(--kb-muted); }
.kb-olf-ayuda { font-size: 11.5px; color: var(--kb-mute2); margin-left: 8px; letter-spacing: 0; text-transform: none; font-family: var(--kb-font-ui); }
/* Las notas: una frase en serif, no una botonera. El separador es un punto
   medio con aire, como en una carta. */
.kb-olf-notas { font-family: var(--kb-font-serif); font-size: 19px; line-height: 1.45; color: var(--kb-ink); margin: 5px 0 0; }
/* [BLOQUE 22] Este punto separador es TEXTO (serif 19px sobre papel), no un filete, y estaba
   pintado con el color de los bordes: daba 1,57:1 con el borde viejo y 3,44:1 con el nuevo.
   19px sin negrita no es "texto grande" para la WCAG (el umbral son 24px), así que el mínimo
   sigue siendo 4,5:1. Pasa al gris de texto terciario: 5,38:1. */
.kb-olf-notas i { font-style: normal; color: var(--kb-mute2); padding: 0 7px; }

/* ── Los acordes ─────────────────────────────────────────────────────────── */
.kb-olf-acordes { display: grid; gap: 9px; margin-top: 4px; }
.kb-olf-ac { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: baseline; }
.kb-olf-ac-n { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--kb-ink); }
.kb-olf-ac-riel { grid-column: 1 / -1; height: 4px; border-radius: 99px; background: var(--kb-cream); overflow: hidden; }
.kb-olf-ac-riel i { display: block; height: 100%; border-radius: 99px; background: var(--ac); transition: width .7s cubic-bezier(.2,.8,.2,1); }
/* [BLOQUE 25] Cada fila lleva su color en --ac; la barra y el cuadrito lo toman de ahí. El
   nombre NO: se queda en tinta, que es lo que hace del color un apoyo y no un requisito. */
.kb-olf-ac { --ac: var(--kb-ink); }
.kb-olf-ac[data-acorde="citrico"] { --ac: var(--kb-ac-citrico); }
.kb-olf-ac[data-acorde="frutal"] { --ac: var(--kb-ac-frutal); }
.kb-olf-ac[data-acorde="floral"] { --ac: var(--kb-ac-floral); }
.kb-olf-ac[data-acorde="amaderado"] { --ac: var(--kb-ac-amaderado); }
.kb-olf-ac[data-acorde="especiado"] { --ac: var(--kb-ac-especiado); }
.kb-olf-ac[data-acorde="dulce"] { --ac: var(--kb-ac-dulce); }
.kb-olf-ac[data-acorde="resinoso"] { --ac: var(--kb-ac-resinoso); }
.kb-olf-ac[data-acorde="almizclado"] { --ac: var(--kb-ac-almizclado); }
.kb-olf-ac[data-acorde="cuero"] { --ac: var(--kb-ac-cuero); }
.kb-olf-ac[data-acorde="verde"] { --ac: var(--kb-ac-verde); }
.kb-olf-ac[data-acorde="acuatico"] { --ac: var(--kb-ac-acuatico); }
.kb-olf-ac[data-acorde="polvoso"] { --ac: var(--kb-ac-polvoso); }
.kb-olf-ac-n::before { content: ""; display: inline-block; width: 9px; height: 9px; border-radius: 2px; background: var(--ac); margin-right: 7px; vertical-align: -1px; }


/* ── Comportamiento en piel ──────────────────────────────────────────────── */
.kb-olf-piel { display: grid; gap: 12px; margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--kb-line); }
.kb-olf-piel-row { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px; align-items: baseline; }
.kb-olf-piel-row > span { font-family: var(--kb-font-mono); font-size: 11px; letter-spacing: .08em; text-transform: uppercase; color: var(--kb-muted); }
.kb-olf-piel-row > b { font-size: 12.5px; font-weight: 700; }
.kb-olf-piel-riel { grid-column: 1 / -1; height: 4px; border-radius: 99px; background: var(--kb-cream); overflow: hidden; }
.kb-olf-piel-riel i { display: block; height: 100%; border-radius: 99px; background: currentColor; transition: width .8s cubic-bezier(.2,.8,.2,1); }
.kb-olf-nota-pie { padding: 0 20px 18px; font-size: 11.5px; color: var(--kb-mute2); line-height: 1.5; }

@media (max-width: 900px) {
  .kb-olf-body { grid-template-columns: 1fr; }
  .kb-olf-col + .kb-olf-col { border-left: 0; border-top: 1px solid var(--kb-line); }
  .kb-olf-tit { font-size: 22px; }
  .kb-olf-notas { font-size: 17px; }
}
