/* Bulgi Kéfir — components.css
   Cero hex. Todo por var(). Los estados forzados con [data-estado] existen
   para las láminas de documentación y para pruebas visuales. */

/* ============ B1 · Botón ============ */
.b-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--e-sm);
  min-height: var(--toque-min);
  padding: var(--e-md) var(--e-2xl);
  font-family: var(--fuente-cuerpo); font-weight: 600;
  font-size: var(--t-body-size); line-height: 1.1;
  text-decoration: none; cursor: pointer;
  /* El canto va del color del RELLENO, no de `--borde` (2026-08-25). No pierde
     el borde —lo necesita para no cambiar de tamaño y para que la sombra no se
     mueva—, solo deja de dibujarlo: se lee como una pastilla maciza.
     Puede permitírselo porque el relleno ya delimita de sobra: cobalto sobre
     papel 14.53:1, dorado sobre cobalto 6.53:1, contra los 3:1 de WCAG 1.4.11.
     ⚠️ Lo hereda TODO lo que sea `.b-btn`, y por eso `.b-btn--outline` se lo
     tiene que devolver: ahí el borde ES el botón. */
  border: var(--borde-ancho) solid var(--accion-fondo);
  border-radius: var(--r-pill);
  background: var(--accion-fondo); color: var(--accion-tinta);
  box-shadow: var(--sombra);
  transition: transform var(--dur-rapida) var(--curva),
              box-shadow var(--dur-rapida) var(--curva);
}
.b-btn:hover, .b-btn[data-estado="hover"] {
  transform: translate(-2px, -2px); box-shadow: var(--sombra-alta);
}
.b-btn:active, .b-btn[data-estado="activo"] {
  transform: translate(3px, 3px); box-shadow: var(--sombra-nula);
}
.b-btn[disabled], .b-btn[data-estado="deshabilitado"] {
  opacity: .45; box-shadow: var(--sombra-nula);
  transform: none; cursor: not-allowed;
}
/* El de contorno recupera `--borde`: no tiene relleno que lo dibuje.

   ⚠️ Y su sombra NO es `box-shadow`, es `drop-shadow`. No es un capricho: es lo
   único que quita la raya clara que se veía entre el canto y la sombra.

   Por qué salía solo aquí. El fondo de un elemento se pinta por DEBAJO de su
   borde, así que en el botón lleno el azul llega hasta el canto y el suavizado
   del borde mezcla azul con café: dos oscuros, no se nota. En el de contorno el
   fondo era `transparent`, así que debajo del borde no había nada y el suavizado
   mezclaba café con el papel de la página. Encima, `box-shadow` se recorta
   contra el contorno de la caja, así que borde y sombra suavizaban el MISMO
   píxel por separado y ninguno lo cubría entero.

   `drop-shadow` no recorta: dibuja la silueta del elemento ya compuesto y la
   desplaza. Por eso hace falta el fondo OPACO (`--superficie`, que siempre vale
   lo que la sección) — con fondo transparente la silueta sería el aro y el
   texto, y saldría una sombra con forma de letras.

   Medido a 3x sobre el botón real del hero, siendo 54 el café pleno: el píxel de
   la juntura iba a 102, con un anillo de `box-shadow` bajó a 77, y así va a 53.
   Descartados por medición: anillos de 1, 1.5 y 2px (los tres se quedan en 77
   porque el píxel está DENTRO del borde y un anillo exterior no lo alcanza),
   `background-clip` a dos capas (empeora a 130) y un pseudoelemento detrás (sale
   igual de bien, pero depende de `z-index: -1` y ahí una sección con fondo se lo
   come sin avisar).

   ⚠️ El desplazamiento se repite en hover y en activo. Aquí no valen
   `--sombra-alta` ni `--sombra-nula`: son tokens de `box-shadow` y no se pueden
   meter en un `drop-shadow()`. Si cambian los 4/6px de las sombras del sitio,
   estos números hay que cambiarlos a mano. */
.b-btn--outline {
  background: var(--superficie); color: var(--tinta);
  border-color: var(--borde);
  box-shadow: none;
  filter: drop-shadow(4px 4px 0 var(--sombra-color));
  transition: transform var(--dur-rapida) var(--curva),
              filter var(--dur-rapida) var(--curva);
}
.b-btn--outline:hover, .b-btn--outline[data-estado="hover"] {
  filter: drop-shadow(6px 6px 0 var(--sombra-color));
}
.b-btn--outline:active, .b-btn--outline[data-estado="activo"],
.b-btn--outline[disabled], .b-btn--outline[data-estado="deshabilitado"] {
  filter: none;
}
/* El secundario también tiene que responder al puntero. Hasta el 2026-08-25
   solo se movía 2px: el lleno cambiaba de sombra Y de bulto, y el de contorno
   se quedaba idéntico salvo el desplazamiento. Se rellena con la tinta al 8%
   —no con el acento— justo porque NO es la acción principal: se enciende sin
   competir con el botón lleno del mismo bloque (regla 9).
   Sale de `--tinta`, así que funciona igual sobre los cuatro fondos de sección
   sin declarar nada por tema. */
/* ⚠️ Se mezcla con `--superficie`, NO con `transparent`. El fondo de este botón
   tiene que quedarse OPACO: `drop-shadow` dibuja la silueta según la
   transparencia del elemento, así que un fondo a medias deja la sombra pálida
   justo al pasar el puntero. Sobre la superficie de la sección se ve igual que
   antes, porque `--superficie` vale exactamente lo que hay detrás. */
.b-btn--outline:hover, .b-btn--outline[data-estado="hover"] {
  background: color-mix(in srgb, var(--tinta) 8%, var(--superficie));
}
/* ⚠️ Aquí vivió `.b-btn--claro` unas horas del 2026-08-26 y NO se repone.
   Era un contorno para bloques oscuros —papel con sombra dorada— hecho a
   petición de Mario, porque el de contorno se rellena con `--superficie` y en
   cobalto se lee como un recuadro del mismo azul del fondo. Se probó en un solo
   botón, se vio en pantalla y él lo descartó: «se ve horrible así blanco con
   sombra amarilla».

   👉 Y el asunto está CERRADO, no pendiente: que el botón secundario sea del
   color del fondo sobre cobalto no es un defecto. El contorno y la sombra ya lo
   separan de sobra, y así se queda (Mario, 2026-08-26). No hace falta arreglarlo. */
.b-btn--texto {
  background: transparent; color: var(--tinta);
  border-color: transparent; box-shadow: none;
  padding-inline: var(--e-sm);
  text-decoration: underline; text-decoration-thickness: 2px;
  text-underline-offset: 4px;
}
.b-btn--texto:hover, .b-btn--texto[data-estado="hover"] {
  transform: none; box-shadow: none;
  text-decoration-thickness: 3px;
}
.b-btn--texto:active, .b-btn--texto[data-estado="activo"] {
  transform: translateY(1px); box-shadow: none;
}
.b-btn--grande { padding: var(--e-lg) var(--e-3xl); font-size: var(--t-body-l-size); }

/* ============ B2 · Header + nav ============ */

/* El cromo pegado arriba. Envuelve al header Y al menú móvil: ver el porqué en
   `js/partials.js`, en `montarHeader`.
   `sticky` y no `fixed` a propósito — `fixed` lo saca del flujo y obliga a
   compensar el hueco con un padding en cada página, que es justo el tipo de
   número que se queda viejo cuando el header cambia de alto.
   z-index 50: por debajo del flotante de WhatsApp (60), que tiene que seguir
   encima de todo. */
.b-cromo {
  position: sticky; top: 0; z-index: 50;
  transition: transform var(--dur-media) var(--curva);
  /* La sombra dura del canto de abajo. Su COLOR sale del tema del cromo, y el
     tema cambió el 2026-08-28: la barra es CAFÉ (Mario), no `papel` con sombra
     café. En café `--sombra-color` es BLANCO, y esa línea sigue haciendo falta
     aunque el café ya se despegue solo del lienzo: el cromo es PEGADO y pasa por
     encima de las secciones cobalto, donde café sobre cobalto da 1.25:1. Sobre
     `papel` no se ve —vale lo mismo— y ahí no separa nada, porque ya no hay nada
     que separar.

     ⚠️ Va SIN desplazamiento horizontal, al revés que `--sombra` (4px 4px 0).
     La barra ocupa todo el ancho: una sombra corrida a la derecha se sale de la
     pantalla por ese lado y deja el izquierdo sin nada, o sea torcida. Aquí solo
     cae hacia abajo. */
  box-shadow: 0 4px 0 var(--sombra-color);
}
.b-cromo[data-oculto] { transform: translateY(-100%); }
@media (prefers-reduced-motion: reduce) {
  .b-cromo { transition: none; }
}

.b-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e-2xl);
  padding-block: var(--e-lg);
  border-bottom: var(--borde-ancho) solid var(--borde);
  background: var(--superficie); color: var(--tinta);

  /* La barra va ORILLADA, no alineada con el contenido, y es una decisión
     tomada mirándola (Mario, 2026-08-25).

     El 2026-08-25 se probó alinearla con `.b-interior` —
     `max(var(--e-2xl), calc((100% - var(--ancho-max)) / 2))`, que a 1684px la
     metía 252px— y quedaba mal: **la barra de menú no tiene que respetar los
     márgenes de las secciones de abajo.** Es cromo, vive en otro plano que el
     contenido, y pegarla a la misma rejilla la volvía pesada.
     Lo que sí sobraba era lo contrario: con los 24px de origen el logo se
     comía el canto de la ventana. Así que margen del doble, y nada más.

     El `clamp()` dice las tres cosas de una vez, y el valor de en medio —dónde
     empieza el contenido— es el que evita los dos fallos:
       · suelo `--e-2xl` (24px): en celular no se toca. A 320px, 48 por lado se
         llevan 96 de 320 y el CTA se queda sin sitio.
       · techo 96px (`--e-4xl` × 2): se subió de 48 a 96 el 2026-08-25, a ojo y a
         petición de Mario. Sigue sin ser una alineación — a 1684px el contenido
         arranca en 252 y la barra en 96.
       · en medio, el arranque del contenido. Sin él, entre 640 y 900px la barra
         se metía 48px mientras el contenido seguía en 24: el logo quedaba MÁS
         adentro que el texto del hero, el problema al revés. Ahí el reparto es
         negativo, gana el suelo, y los dos se quedan en 24.
     Resultado medido: 24px hasta 768, 48px de 1280 en adelante. */
  padding-inline: clamp(
    var(--e-2xl),
    calc((100% - var(--ancho-max)) / 2),
    calc(var(--e-4xl) * 2)
  );
}
.b-nav { display: flex; align-items: center; gap: var(--e-2xl); }
.b-nav a {
  font-weight: 600; font-size: var(--t-body-size);
  text-decoration: none; padding: var(--e-sm) 0;
  border-bottom: var(--borde-ancho) solid transparent;
}
.b-nav a:hover { border-bottom-color: var(--tinta); }
/* La página actual se subraya en ACENTO, no en `--accion-fondo`. Valen lo mismo
   sobre el header (que es claro), pero son cosas distintas: el subrayado marca
   dónde estás, no algo que se pueda accionar. Ver la misma corrección en
   `.b-paso-num` y en `.b-prosa blockquote`. */
.b-nav a[aria-current="page"] { border-bottom-color: var(--acento); }
.b-nav a[data-pendiente] { color: var(--tinta-suave); }
.b-hamburguesa {
  display: inline-flex; flex-direction: column; justify-content: center;
  gap: 5px; width: var(--toque-min); height: var(--toque-min);
  padding: 0 var(--e-md);
  background: transparent; border: var(--borde-ancho) solid var(--borde);
  border-radius: var(--r-badge); cursor: pointer;
}
.b-hamburguesa span { display: block; height: 2px; background: var(--tinta); }
.b-menu-movil {
  display: grid; gap: var(--e-lg);
  padding: var(--e-2xl);
  border-bottom: var(--borde-ancho) solid var(--borde);
  background: var(--superficie); color: var(--tinta);
}
.b-menu-movil a {
  font-family: var(--fuente-titulo); font-weight: 600;
  font-size: var(--t-title-s-size); text-decoration: none;
  min-height: var(--toque-min); display: flex; align-items: center; gap: var(--e-sm);
  border-bottom: var(--borde-ancho) solid color-mix(in srgb, var(--borde) 35%, transparent);
}

/* ============ B3 · FAB WhatsApp ============ */
.b-fab {
  display: inline-flex; align-items: center; gap: var(--e-sm);
  min-height: 56px; padding: var(--e-md) var(--e-xl);
  /* Mismo criterio que `.b-btn`: es un relleno de acción, así que su canto va
     del color del relleno y no de `--borde`. Con el canto café se veía como un
     botón distinto al "Comprar" de al lado, siendo los dos pastillas azules. */
  border: var(--borde-ancho-fuerte) solid var(--accion-fondo);
  border-radius: var(--r-pill);
  background: var(--accion-fondo); color: var(--accion-tinta);
  box-shadow: var(--sombra-alta);
  font-weight: 600; text-decoration: none; cursor: pointer;
  transition: transform var(--dur-rapida) var(--curva);
}
.b-fab:hover, .b-fab[data-estado="hover"] { transform: translate(-2px, -2px); }

/* ============ B4 · Footer ============ */
.b-footer { padding: var(--bloque-aire) var(--e-2xl) var(--e-3xl); }
.b-footer-cols {
  display: grid; gap: var(--e-4xl);
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  max-width: var(--ancho-max); margin-inline: auto;
}
.b-footer-cols h4 {
  font-family: var(--fuente-cuerpo); font-weight: 600;
  font-size: var(--t-eyebrow-size); text-transform: uppercase;
  /* Antetítulo → acento. Antes era --tinta-suave, o sea gris: las tres
     cabeceras del pie salían apagadas.
     ⚠️ Desde el 2026-08-25 el acento del café es DORADO (5.23:1), no celeste
     (7.53:1). Lo pide la regla 5 de v3: superficie oscura → acento dorado, sin
     importar el logo. Los dos pasan AA; se escogió el que iguala el pie con el
     otro fondo oscuro del sitio, el cobalto. */
  letter-spacing: .12em; color: var(--acento);
  margin-bottom: var(--e-lg);
}
.b-footer-cols ul { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--e-md); }
.b-footer-cols a { min-height: 28px; display: inline-flex; align-items: center; gap: var(--e-sm); }

/* ============ C1 · Bloque de sección ============ */
.b-bloque { padding: var(--bloque-aire) var(--e-2xl); position: relative; }
.b-bloque > .b-interior { max-width: var(--ancho-max); margin-inline: auto; }
.b-bloque--recorte {
  clip-path: polygon(0 0, 100% 3vw, 100% 100%, 0 100%);
  margin-top: -3vw; padding-top: calc(var(--bloque-aire) + 3vw);
}
/* El primer bloque de la página respira por arriba menos que los de en medio.
   `--bloque-aire` (hasta 112px) existe para SEPARAR dos bloques de color; arriba
   del todo no separa nada y solo deja una franja vacía entre el menú y el
   contenido. Aquí abajo el aire se conserva entero: sigue habiendo bloque que
   separar. */
.b-bloque--cima { padding-block-start: clamp(40px, 5vw, 64px); }

/* ---- El filete entre dos secciones del lienzo (sistema v3) ----
   En v3 la mayoría de las secciones comparten fondo (`papel`) y se distinguen
   por aire, jerarquía y acento. Eso quita el confeti de v2 —doce secciones,
   doce fondos— pero abre el riesgo contrario: que dos secciones seguidas se
   lean como UNA sola muy larga.

   Este filete lo resuelve sin gastar un color de fondo. Es la pieza que hace
   que la regla 1 sea segura, así que no es adorno: si se quita, hay que
   devolverle a alguna de las dos un fondo propio.

   Tres decisiones que parecen detalles y no lo son:
   · Va en `::before` y no en `border-top` porque un borde va a sangre, de canto
     a canto de la ventana, y aquí la línea tiene que morir donde muere el
     contenido: alineada con `.b-interior`. De ahí el `min()` con `--ancho-max`.
   · Es 1px y al 14% de la tinta. El sitio usa bordes de 2px negros en botones y
     tarjetas; a ese grosor esto no sería una separación sutil, sería otra caja.
   · El selector pide `.b-bloque` a los dos lados a propósito. `[data-bloque]`
     suelto también casa con el header y el menú móvil, que son hermanos y los
     dos `papel`: saldría una raya encima del menú desplegado, justo debajo del
     borde del header. */
.b-bloque[data-bloque="papel"] + .b-bloque[data-bloque="papel"]::before {
  content: "";
  position: absolute;
  top: 0; left: 50%;
  transform: translateX(-50%);
  width: min(var(--ancho-max), calc(100% - var(--e-2xl) * 2));
  height: 1px;
  background: color-mix(in srgb, var(--tinta) 14%, transparent);
}

/* ============ Tarjetas genéricas ============ */
.b-card {
  border: var(--borde-ancho) solid var(--borde);
  border-radius: var(--r-card);
  background: var(--superficie); color: var(--tinta);
  box-shadow: var(--sombra);
  padding: var(--e-2xl);
}
.b-card--destacada { border-width: var(--borde-ancho-fuerte); box-shadow: var(--sombra-alta); }

/* ============ C4 · Paso numerado ============
   ⚠️ Lo pintaba `--accion-fondo` hasta el 2026-08-25, o sea que los números de
   "Cómo tomarlo" eran del color y la forma exactos de un botón —pastilla,
   relleno de acción, borde de 2px— sin serlo. Es el mismo error que ya se había
   corregido en `.b-prosa blockquote` el 2026-08-21, en otro sitio.
   Ahora van en `--realce`, que es el token que existe para esto: color de
   marca, decorativo, sin prometer que se puede tocar. De paso los saca del azul
   y los mete en celeste (sobre claro) o dorado (sobre oscuro), que es donde el
   sistema v3 quiere que aparezca ese color. */
.b-paso-num {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  font-family: var(--fuente-titulo); font-weight: 700; font-size: var(--t-title-s-size);
  border: var(--borde-ancho) solid var(--realce-borde); border-radius: var(--r-pill);
  background: var(--realce); color: var(--realce-tinta);
}

/* Los tres pasos de «Cómo tomarlo» van en DORADO sobre claro, no en celeste
   (petición de Mario, 2026-08-26). Es un modificador y no un cambio de
   `.b-paso-num` a secas porque `gracias/` usa el mismo componente con iconos, y
   ahí no se pidió nada.

   ⚠️ Solo cambia el RELLENO. `--realce-tinta` y `--realce-borde` son negros en
   los siete temas, así que el círculo queda dorado con dígito y canto negros:
   7.55:1 el texto y 7.55:1 el borde, los mismos números que el sticker dorado
   que ya vive en `#comprar`. Tocar aquí la tinta o el borde rompería la regla 7
   —el realce es un relleno y su canto es negro— y `tests/canto.test.js` no lo
   vería, porque esa prueba mide `tokens.css` y esto es una anulación local. */
.b-paso-num--dorado { --realce: var(--dorado); }

/* ============ C5 · Acordeón FAQ ============ */
.b-faq {
  border-bottom: var(--borde-ancho) solid var(--borde);
}
.b-faq summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e-lg); cursor: pointer; list-style: none;
  padding: var(--e-xl) var(--e-sm);
  min-height: var(--toque-min);
  font-family: var(--fuente-titulo); font-weight: 600;
  font-size: var(--t-title-s-size); line-height: var(--t-title-s-lh);
}
.b-faq summary::-webkit-details-marker { display: none; }
.b-faq summary:hover { background: color-mix(in srgb, var(--tinta) 6%, transparent); }
.b-faq .b-faq-signo {
  flex: 0 0 auto; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border: var(--borde-ancho) solid var(--borde); border-radius: var(--r-pill);
  font-family: var(--fuente-cuerpo); font-weight: 600;
}
/* Abierta → la pastilla se llena de realce (celeste sobre claro, dorado sobre
   oscuro). Era `--tinta` sobre `--superficie`: una mancha negra. Cambia el
   COLOR además de la forma, así que el estado se lee de un vistazo en una lista
   larga, y mete color de marca en un sitio que antes era neutro. */
.b-faq[open] .b-faq-signo {
  background: var(--realce); color: var(--realce-tinta);
  border-color: var(--realce-borde);
}
/* Rejilla, no bloque: `base.css` pone `p { margin: 0 }` en todo el sitio, así que
   una respuesta de varios párrafos —las dos de "supermercado vs. búlgaros"— salía
   como un solo muro de texto. El aire lo pone `gap`, igual que en `.b-prosa`. Con
   una sola respuesta de un párrafo se ve idéntico que antes. */
.b-faq-cuerpo {
  padding: 0 var(--e-sm) var(--e-2xl); max-width: 62ch; color: var(--tinta-suave);
  display: grid; gap: var(--e-lg);
}

/* ⚠️ NO hay animación de apertura en el acordeón, y es una decisión, no un
   olvido. Se intentó el 2026-08-26 con `::details-content` (`block-size: 0 →
   auto` + `interpolate-size`) y con `grid-template-rows: 0fr → 1fr`, que son
   las dos formas conocidas de animar un <details> sin JavaScript. Las dos
   dejaron la respuesta SIN ABRIRSE al alternar `open` desde código: 74px antes
   y 74px después.
   Lo que no se pudo determinar es si eso es real o un artefacto de medir sin
   cabeza — `requestAnimationFrame` no corre dentro de un iframe headless en
   este Mac (ya documentado por lo del escondite de la barra), y las
   transiciones de `content-visibility` dependen de él. Ante la duda no se
   publica: que el FAQ no abra es MUCHO peor que que abra de golpe.
   👉 Si se retoma: hay que probarlo en un navegador de verdad, con las manos, y
   la referencia bonita es la versión anterior del sitio, en
   `_archive/bulgi-web-sitio-anterior-2026-08-26/styles.css:693` — aunque
   aquella necesitaba JavaScript, que es lo que aquí no se quiere. */

/* El `+` gira 45° y se vuelve una ×. Es el mismo gesto que hacía la flecha de
   la versión vieja (`.faq-item.is-open summary::after { rotate(180deg) }`),
   traducido al signo que usa este sistema. */
/* El signo se DIBUJA con dos barras, no se escribe con el carácter "+".
   ⚠️ El "+" del HTML sigue ahí (los `<span aria-hidden>` no cambian), pero se
   esconde con `font-size: 0`. El motivo es que un glifo no se puede centrar:
   la caja de texto SÍ estaba centrada —medido, desvío 0.0px— y aun así se veía
   alto, porque la tinta del "+" se apoya en el eje matemático de la fuente y no
   en el centro de su renglón. Moverlo a ojo lo ata a la fuente del sistema, que
   cambia en cada aparato.
   Es el mismo motivo por el que este repo ya sacó `←` y `☐` del contenido
   (DEC-38): los símbolos se dibujan, no se escriben. */
.b-faq-signo, .b-tienda-signo {
  position: relative;
  font-size: 0;
  transition:
    transform var(--dur-media) var(--curva),
    background-color var(--dur-media) var(--curva),
    border-color var(--dur-media) var(--curva),
    color var(--dur-media) var(--curva);
}
.b-faq-signo::before, .b-faq-signo::after,
.b-tienda-signo::before, .b-tienda-signo::after {
  content: "";
  position: absolute; top: 50%; left: 50%;
  width: 12px; height: var(--borde-ancho);
  border-radius: var(--borde-ancho);
  background: currentColor;
  transform: translate(-50%, -50%);
}
/* La segunda barra en vertical: juntas son un "+". Al abrirse, el giro de 45°
   del círculo entero las convierte en "×" sin tocar nada más. */
.b-faq-signo::after, .b-tienda-signo::after {
  transform: translate(-50%, -50%) rotate(90deg);
}
.b-faq[open] .b-faq-signo,
.b-tienda[open] .b-tienda-signo { transform: rotate(45deg); }

@media (prefers-reduced-motion: reduce) {
  .b-faq-signo, .b-tienda-signo { transition: none; }
}

/* ============ C6 · Tienda aliada (acordeón) + visor de mapa ============

   Una lista de acordeones a la izquierda y UN mapa a la derecha. Antes eran 5
   tarjetas con 5 iframes: cinco descargas a Google y cinco cosas que abrir y
   cerrar para entender dónde caen las tiendas.

   Es <details> nativo y no un widget a propósito: abre sin JavaScript, no hay
   que atrapar el foco ni escuchar Escape, y ya es el patrón que usa el FAQ. Se
   le copia el lenguaje visual (el `+` en su círculo, matar el marcador nativo)
   pero con clases propias: FAQ y tiendas son cosas distintas y compartir clase
   las dejaría acopladas para siempre. */

/* .b-card trae padding: aquí lo pone cada parte, para que el hover del summary
   llegue hasta el borde de la tarjeta en vez de flotar dentro. */
.b-tienda { padding: 0; overflow: hidden; }
.b-tienda summary {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--e-lg); cursor: pointer; list-style: none;
  padding: var(--e-lg) var(--e-xl);
  min-height: var(--toque-min);
}
.b-tienda summary::-webkit-details-marker { display: none; }
.b-tienda summary:hover { background: color-mix(in srgb, var(--tinta) 6%, transparent); }
.b-tienda-nombre { display: grid; gap: var(--e-2xs); }
.b-tienda-signo {
  flex: 0 0 auto; width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  border: var(--borde-ancho) solid var(--borde); border-radius: var(--r-pill);
  font-family: var(--fuente-cuerpo); font-weight: 600;
}
.b-tienda[open] .b-tienda-signo {
  background: var(--realce); color: var(--realce-tinta);
  border-color: var(--realce-borde);
}
.b-tienda-cuerpo {
  display: grid; gap: var(--e-md); justify-items: start;
  padding: var(--e-lg) var(--e-xl);
  border-top: var(--borde-ancho) solid var(--borde);
}

/* [open] dice "está desplegada" y lo pone el navegador; [data-activa] dice "es
   la que se está viendo en el mapa de al lado" y lo pone ui.js. Coinciden
   mientras haya JavaScript, y por eso la marca vive en su propio atributo: sin
   JavaScript no hay mapa que seguir, así que no se marca nada y no miente. */
.b-tienda[data-activa] {
  border-width: var(--borde-ancho-fuerte);
  box-shadow: var(--sombra-alta);
}

.b-visor { display: grid; gap: var(--e-md); align-content: start; }
.b-visor-marco {
  width: 100%; aspect-ratio: 4 / 3; display: block;
  border: var(--borde-ancho) solid var(--borde); border-radius: var(--r-card);
}

/* ============ D1 · Tarjeta de producto ============ */
.b-producto { display: grid; gap: var(--e-lg); }

/* La foto de producto es CUADRADA, y no es una preferencia: las de la tienda
   son 895×895 y 1200×1200 (comprobado en products.json el 2026-08-26).
   `.l-figura` las metía en una caja 4/5 con `object-fit: contain`, así que una
   imagen cuadrada quedaba con una banda vacía arriba y otra abajo del 12.5%
   del ancho cada una — a 578px de tarjeta, 72px de aire por lado que no
   contiene nada. Eso es lo que se veía como "la foto flota en la tarjeta".
   ⚠️ Va acotado a `.b-producto` a propósito: `.l-figura` la comparten los dos
   huecos de foto pendiente de la portada (Mario en la cocina, el vaso servido),
   que SÍ son verticales y tienen que seguir en 4/5.

   Y en escritorio se le pone tope. Sin él la foto crece con la tarjeta —a
   1280px daba 578px de alto— y empujaba el precio y el botón fuera de la
   pantalla: había que hacer scroll para elegir y otro para ver el carrito.
   El tope va por ANCHO, que con `aspect-ratio` fija el alto sin recortar: un
   `max-height` con `contain` solo cambiaría las bandas de sitio, y con `cover`
   le cortaría el corcho a la botella. */
.b-producto .l-figura { aspect-ratio: 1 / 1; }

/* ⚠️ El tope fue 260px durante unas horas del 2026-08-26 y era DEMASIADO
   pequeño: la botella quedaba de estampilla dentro de una tarjeta de 578px y el
   producto dejaba de verse. 380 es el punto medio entre eso y la foto original
   sin tope, que a 1280 daba 578px de alto y empujaba el precio y el botón fuera
   de la pantalla. Si se vuelve a tocar, es un número de OJO: se mira, no se
   calcula. */
@media (min-width: 56em) {
  .b-producto .l-figura { max-width: 380px; margin-inline: auto; }
}
.b-producto-precio {
  font-family: var(--fuente-titulo); font-weight: 700;
  font-size: var(--t-display-m-size); line-height: 1;
  display: flex; align-items: baseline; gap: var(--e-sm); flex-wrap: wrap;
}
/* El precio de antes, tachado, al elegir suscripción. Va MÁS PEQUEÑO y en tinta
   suave a propósito: el número que el cliente tiene que leer es el de ahora, y
   dos cifras del mismo tamaño compiten. `flex-wrap` arriba es para 320px, donde
   los dos precios no siempre caben en una línea.

   ⚠️ Es un <s>, no un <span> con `text-decoration`. Un lector de pantalla anuncia
   el <s> como contenido tachado; con CSS a secas leería «135 pesos, 121 pesos con
   50» seguidos y sonaría a que cuesta la suma de los dos. */
.b-precio-antes {
  font-size: var(--t-title-s-size); font-weight: 400;
  color: var(--tinta-suave); text-decoration-thickness: 2px;
}
/* ---- Compra única o suscripción, dentro de la tarjeta ----
   Solo se pinta si la tienda ofrece planes para ESA variante (los lee
   `obtenerPlanes` de /products/{handle}.js). Si no hay, la tarjeta queda
   exactamente como estaba.

   Es un <fieldset> de verdad, con <legend>, y no un div con role: los radios
   necesitan un grupo con nombre accesible o el lector de pantalla anuncia
   "Una sola vez" sin decir de qué producto. El reset de `base.css` le quita el
   marco, así que aquí solo va la retícula. */
.b-modalidad { display: grid; gap: var(--e-sm); border: 0; padding: 0; margin: 0; }
.b-modalidad legend { padding: 0; margin-bottom: var(--e-xs); }

/* El radio y su texto en un renglón, alineados por el centro de la primera
   línea —no del bloque— para que un texto que se parta en dos no deje el
   círculo flotando a media altura. */
.b-modalidad-opcion {
  display: grid;
  grid-template-columns: auto 1fr;
  align-items: center;
  gap: var(--e-sm);
  cursor: pointer;
}
/* 44px de alto de toque sin agrandar el círculo: el área la da el <label>
   entero, que es lo que el dedo toca. */
.b-modalidad-opcion { min-height: 44px; }
.b-modalidad-opcion input { width: 1.25rem; height: 1.25rem; accent-color: var(--acento, var(--tinta)); }

/* El campo de frecuencia va indentado bajo su radio: se lee como consecuencia
   de haber elegido «Recíbelo seguido», no como una tercera opción suelta. */
.b-modalidad .b-campo { margin-left: calc(1.25rem + var(--e-sm)); }

.b-skeleton {
  border-radius: var(--r-badge);
  background: color-mix(in srgb, var(--tinta) 14%, transparent);
  animation: b-pulso 1.4s var(--curva) infinite alternate;
}
@keyframes b-pulso { from { opacity: .45; } to { opacity: .9; } }

/* ============ D2 · Resumen del carrito ============
   El panel de /comprar/ donde se juntan las líneas antes de ir a pagar.

   ⚠️ Es la pieza que arregla el bug de fondo: hasta el 2026-08-26 cada tarjeta
   mandaba su propio cart permalink de UNA línea, y ese enlace REEMPLAZA el
   carrito de Shopify. Sin este panel no hay dónde juntar dos productos.

   No inventa colores ni cantos: hereda los de `.b-card`, que es quien lo pinta.
   Aquí solo vive la retícula. */
/* ⚠️ El `margin-top` NO es decorativo y no se quita "porque `.b-interior` ya
   separa": no separa nada. `.b-interior` es `display: block` y la rejilla de
   productos no lleva margen abajo, así que el panel nacía pegado al fondo de
   las tarjetas —medido: 0.0px exactos— y los dos cantos de 2px con sus sombras
   duras de 4px se encamaban. Parecía un error de maquetación y lo era.
   Vale `--e-2xl` porque es EXACTAMENTE el `gap` de `.l-rejilla`: así el panel
   se lee como una celda más del mismo ritmo y no como algo pegado debajo. Si
   algún día cambia el gap de la rejilla, este número cambia con él. */
.b-carrito { display: grid; gap: var(--e-lg); margin-top: var(--e-2xl); }

/* 🔴 SIN ESTA LÍNEA EL PANEL NO SE OCULTA NUNCA, y el fallo es de los que no
   dan ningún error. El script pone `panel.hidden = true` con el carrito vacío,
   pero `hidden` esconde las cosas a través de `[hidden] { display: none }` de
   la HOJA DEL NAVEGADOR, y cualquier `display` de autor —el `display: grid` de
   la línea de arriba— le gana. Resultado: el cliente entraba a /comprar/ y veía
   un "Tu pedido" con el total en blanco y un "Ir a pagar" que no llevaba a
   ningún lado, ocupando media pantalla.
   ⚠️ Lo cazó Mario con una captura. Mi arnés preguntaba por `panel.hidden` —la
   PROPIEDAD, que valía true— en vez del `display` calculado, así que cantaba
   "panel oculto: true" sobre un panel que estaba a la vista. Otra vez lo mismo:
   un guardián que mide otra cosa es peor que no tenerlo.
   ✅ 2026-08-26: al caer en la MISMA trampa una tercera vez (el campo de
   frecuencia de las tarjetas), se puso `[hidden] { display: none !important }`
   global en `base.css`. Esta línea ya no es la que salva el panel —lo salva
   aquélla—, pero se queda porque documenta el fallo y no estorba. */
.b-carrito[hidden] { display: none; }

/* El panel aparecía de golpe: pasaba de no existir a ocupar media pantalla y
   empujaba todo hacia abajo de un tirón. Entra subiendo y apareciendo, con la
   misma curva que el resto del sitio.
   Va como `animation` y no como `transition` porque no hay dos estados que
   interpolar: el panel no existe en el layout hasta que deja de estar
   `hidden`, y una transición desde `display: none` no arranca. La animación se
   dispara sola cada vez que el elemento vuelve a pintarse, que es justo cuando
   se quiere. */
/* ⚠️ Los DOS fotogramas van escritos, y SIN `fill-mode`. Se intentó primero con
   solo `from` y `both`, que es la forma corta de siempre, y el panel se quedaba
   en **opacidad 0**: invisible. Con un único fotograma no hay estado final que
   retener, y `forwards` congelaba el inicial. Lo cazó la medición, no el ojo —
   en pantalla solo se veía que el carrito "no aparecía". */
@keyframes b-carrito-entra {
  from { opacity: 0; transform: translateY(-8px); }
  to   { opacity: 1; transform: none; }
}
.b-carrito { animation: b-carrito-entra var(--dur-media) var(--curva); }

@media (prefers-reduced-motion: reduce) {
  .b-carrito { animation: none; }
}

.b-carrito-lineas { display: grid; gap: var(--e-lg); }

/* Nombre · cantidad · subtotal · quitar, en UN renglón mientras quepan.
   Solo el nombre lleva fracción: el <select> y el botón ya traen su ancho
   mínimo de toque (44px) de `.b-btn`, y darles `1fr` deja un campo enorme con
   un dígito dentro. El nombre es el que se come el sobrante. */
.b-carrito-linea {
  display: grid;
  grid-template-columns: 1fr auto auto auto;
  align-items: center;
  gap: var(--e-md) var(--e-lg);
}
.b-carrito-subtotal { justify-self: end; font-weight: 600; }

/* El nombre lleva debajo la frecuencia cuando la línea es una suscripción
   ("cada 2 semanas"). Va DENTRO del nombre y no en una columna propia para no
   tocar la retícula de 4 columnas ni su corte de 34em. Sin suscripción el
   <span> no se pinta y esto no cambia nada. */
.b-carrito-nombre { display: grid; gap: 2px; }

/* Por debajo de 34em el nombre ya no cabe junto a los otros tres: se sube a su
   propio renglón y abajo quedan cantidad, subtotal y quitar. Va en `em` y no en
   `px` porque lo que decide el corte es cuánto texto cabe, no el aparato —con
   el tipo más grande el nombre necesita su renglón antes. */
@media (max-width: 34em) {
  .b-carrito-linea { grid-template-columns: auto 1fr auto; }
  .b-carrito-nombre { grid-column: 1 / -1; }
}

.b-carrito-total {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--e-lg);
  padding-top: var(--e-lg);
  border-top: var(--borde-ancho) solid var(--borde);
  font-family: var(--fuente-titulo); font-weight: 700;
  font-size: var(--t-title-s-size);
}

/* ============ E2 · Burbujas de fermentación ============ */
.b-burbujas { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.b-burbuja {
  position: absolute; bottom: -14%;
  border: var(--borde-ancho) solid var(--borde);
  border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--tinta) 8%, transparent);
  animation: b-subir var(--b-dur, 11s) linear var(--b-delay, 0s) infinite;
}
@keyframes b-subir {
  from { transform: translateY(0) translateX(0); opacity: 0; }
  12%  { opacity: .85; }
  to   { transform: translateY(-115vh) translateX(var(--b-drift, 14px)); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  /* estáticas, no ocultas: son parte de la composición */
  .b-burbuja { animation: none; bottom: auto; opacity: .7; }
}

/* ============ E1 · Bulgito ============ */

/* El sprite vive inline al inicio del <body>. No se esconde con display:none:
   hay navegadores que dejan de pintar un <use> cuyo <symbol> cuelga de un
   display:none. Fuera del flujo y con tamaño cero hace lo mismo sin ese riesgo. */
.b-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.b-bulgito { display: inline-grid; gap: var(--e-sm); justify-items: center; color: var(--tinta); }

/* Aquí vivía `.l-bulgito-centrado`, que centraba a Bulgito en su columna. Su
   único uso era el del hero, y el 2026-08-27 se fue al costado del titular:
   quedó sin usar y se borró. Si vuelve a hacer falta:
   `git log -S "l-bulgito-centrado" -- css/components.css`. */

.b-bulgito-svg {
  /* --bulgito-size es el ANCHO: los dibujos son ~4:3, no cuadrados como el
     maniquí que había antes.
     🔴 El `aspect-ratio` NO es decoración y aquí decía que el alto salía del
     viewBox de cada symbol: no salía. El viewBox vive en el <symbol>, y el
     <svg> que lo usa NO tiene ninguno, así que no tiene proporción intrínseca
     y `height: auto` caía al alto por defecto de un SVG sin dimensiones —
     **150px**, fijo, para cualquier --bulgito-size. La caja medía 150 y el
     dibujo 48: los 102px sobrantes no se veían mientras Bulgito colgaba solo
     al final de una columna, y salieron a la luz al ponerlo al lado de un
     titular, donde lo dejaban flotando a media frase.
     Y al revés en `l-bulgito-xl` (260px de ancho): el dibujo se encogía para
     caber en los 150 de alto y se pintaba a 200px, nunca a los 260 pedidos.
     Los dos symbols son 236×177 y 258×193 — los dos 4:3 con tres decimales. */
  width: var(--bulgito-size, 88px);
  aspect-ratio: 4 / 3;
  height: auto;
  /* El cuerpo va con fill="currentColor" (el color: var(--tinta) del padre) y
     los ojos con var(--bulgito-ojos). Ambos entran al <use> por herencia, que
     es justo lo que un <img src=".svg"> no permite. */
  --bulgito-ojos: var(--bulgito-cara);
  transition: transform var(--dur-media) var(--curva);
}
.b-bulgito:hover .b-bulgito-svg { transform: rotate(3deg); }

/* Se escriben las dos variantes en cada sitio y el tema del bloque decide cuál
   se ve (tokens.css: --bulgito-mostrar-*). Así mover una sección de bloque
   cambia la variante sola, sin tocar el HTML ni una línea de JS. */
.b-bulgito-svg--solido  { display: var(--bulgito-mostrar-solido, block); }
.b-bulgito-svg--outline { display: var(--bulgito-mostrar-outline, none); }

/* Pide el contorno aunque el bloque mande relleno. Excepción deliberada a la
   regla de arriba, para cuando la variante es una decisión de composición y no
   del tema: hoy la usa la 404, donde el dibujo es lo único que hay en la página
   y el contorno pesa menos que una mancha sólida (decisión de Mario).
   ⚠️ NO basta con conmutar los dos interruptores. En un bloque claro
   --bulgito-cara es la superficie, porque en la variante sólida los ojos son
   HUECOS recortados en la mancha. En contorno los ojos son trazo: con ese mismo
   valor se pintarían del color del fondo y el dibujo saldría SIN OJOS, sin
   error y sin aviso. Por eso la cara pasa a tinta, igual que hace el tema café.
   Los otros dos van por coherencia con tokens.css, que pide que --bulgito-var
   concuerde siempre con los interruptores. */
.b-bulgito--contorno {
  --bulgito-var: outline;
  --bulgito-mostrar-solido: none;
  --bulgito-mostrar-outline: block;
  --bulgito-relleno: transparent;
  --bulgito-cara: var(--tinta);
}

/* Stub, igual que antes: el parpadeo queda fuera de alcance en esta fase. Solo
   se reapunta el selector al <svg>, porque la cara del maniquí ya no existe. */
.b-bulgito[data-parpadeo="si"] .b-bulgito-svg { animation: none; }

/* ============ E3 · Sticker / badge ============ */
.b-sticker {
  display: inline-flex; align-items: center; gap: var(--e-xs);
  padding: var(--e-sm) var(--e-md);
  font-family: var(--fuente-cuerpo); font-weight: 600;
  font-size: var(--t-eyebrow-size); text-transform: uppercase; letter-spacing: .1em;
  border: var(--borde-ancho) solid var(--borde); border-radius: var(--r-badge);
  background: var(--superficie); color: var(--tinta);
  box-shadow: var(--sombra);
  transform: rotate(var(--rot-a));
}
.b-sticker--b { transform: rotate(var(--rot-b)); }
/* ⚠️ Aquí vivió `.b-sticker--accion` (relleno del color de acción). Su único uso
   era «La más rápida» en la portada, y el 2026-08-25 pasó a
   `data-superficie="dorado"`: la sección es cobalto, la tarjeta celeste y el
   botón cobalto, así que en azul eran tres azules apilados.
   No se repone «por simetría» con `--realce`: un sticker de color se pinta con
   `data-superficie`, que le da relleno, tinta, canto y sombra de una vez. */
/* El sticker de color de marca, sin prometer que se puede tocar. `--accion` es
   para el que acompaña a un CTA; este es para etiquetar («Hecho en Guadalajara»,
   «Producto vivo»). Sobre fondo claro sale celeste, sobre oscuro dorado. */
.b-sticker--realce {
  background: var(--realce); color: var(--realce-tinta);
  border-color: var(--realce-borde);
}

/* ============ E4 · Campo de formulario ============ */
.b-campo { display: grid; gap: var(--e-sm); }
.b-campo label { font-weight: 600; font-size: var(--t-body-s-size); }
/* El <select> entra en la misma regla que input y textarea: el selector de
   cantidad de /comprar/ es un campo más y no merece un componente aparte.
   Sin `color`/`background` explícitos, Safari lo pinta con los colores del
   sistema y el campo se sale de la paleta. */
.b-campo input, .b-campo textarea, .b-campo select {
  font-family: var(--fuente-cuerpo); font-size: var(--t-body-size);
  min-height: var(--toque-min);
  padding: var(--e-md) var(--e-lg);
  color: var(--tinta); background: var(--superficie);
  border: var(--borde-ancho) solid var(--borde); border-radius: var(--r-badge);
}
/* El selector de cantidad hereda `--fuente-cuerpo` como cualquier campo. No
   necesita fuente propia: los dígitos de Garbata ya están redirigidos a Recoleta
   en `base.css` para todo el sitio (H-26), no solo aquí. */
.b-campo select { cursor: pointer; }
.b-campo input::placeholder, .b-campo textarea::placeholder { color: var(--tinta-suave); }
.b-campo[data-error] input, .b-campo[data-error] textarea {
  border-width: var(--borde-ancho-fuerte);
}
.b-campo-error {
  display: flex; align-items: center; gap: var(--e-sm);
  font-size: var(--t-body-s-size); font-weight: 600;
}

/* ============ E6 · Prosa legal ============
   Claude Design entregó estas tres reglas y nunca se usaron: hasta el
   2026-08-06 el sitio no tenía ninguna página de texto largo. Al maquetar las
   tres legales salió que cubrían los títulos y nada más — listas, tablas y
   separadores caían con los valores por defecto del navegador, que no vienen
   de ningún token y se veían de otro sitio web. Lo de abajo cierra ese hueco.
   Todo sale de tokens.css; ni un color escrito a mano. */
/* ⚠️ Sin `font-size`: la prosa hereda el tamaño de cuerpo del sitio (17px).
   Claude Design la entregó a `--t-legal-size` (14px) y así se publicó primero,
   pero se leía chica en una página que es solo texto — Mario lo pidió al tamaño
   del resto del sitio. El `line-height` sí queda holgado: un texto largo se
   sigue mejor con más aire, y la altura de línea no afecta a lo de abajo.
   👉 No le pongas `font-size` aquí sin leer el aviso de `.l-lectura`. */
.b-prosa { max-width: 68ch; display: grid; gap: var(--e-xl); line-height: var(--t-legal-lh); }
.b-prosa h2 { font-size: var(--t-display-m-size); line-height: var(--t-display-m-lh); margin-top: var(--e-2xl); }
.b-prosa h3 { font-family: var(--fuente-titulo); font-weight: 600; font-size: var(--t-title-s-size); }

/* `.b-prosa` es una rejilla, así que el aire entre bloques lo pone `gap`. Los
   márgenes propios de <ul>/<ol>/<table> se le sumarían encima y el ritmo
   quedaría al doble en unos lugares y no en otros. Se anulan y el espaciado
   queda en un solo lugar. El sangrado sí se conserva: sin él la viñeta se sale
   del contenedor. */
.b-prosa ul, .b-prosa ol { margin: 0; padding-inline-start: var(--e-2xl); display: grid; gap: var(--e-sm); }
.b-prosa li { padding-inline-start: var(--e-2xs); }
.b-prosa li::marker { color: var(--tinta-suave); }

/* Tablas (encargados y transferencias del aviso). `display: block` + scroll
   propio para que una tabla ancha se desplace DENTRO de su caja y no empuje la
   página entera: en un teléfono de 320px, tres columnas de texto no caben de
   ninguna manera y sin esto el <body> desborda en horizontal. */
.b-prosa .b-tabla-envoltorio { overflow-x: auto; }
.b-prosa table { border-collapse: collapse; width: 100%; min-width: 30rem; text-align: start; }
.b-prosa th, .b-prosa td {
  border: var(--borde-ancho) solid var(--borde);
  padding: var(--e-md); vertical-align: top; text-align: start;
}
.b-prosa th { font-family: var(--fuente-titulo); font-weight: 600; }

/* Separador entre el bloque de resumen y el articulado. */
.b-prosa hr { border: 0; border-top: var(--borde-ancho) solid var(--borde); width: 100%; margin: 0; }

/* Columna de lectura centrada. Envuelve al encabezado y a la prosa juntos, y
   por eso existe en vez de un `margin-inline: auto` suelto en cada uno: con
   márgenes automáticos por separado, el título (56ch) y el cuerpo (68ch) se
   centrarían sobre anchos distintos y sus bordes izquierdos no coincidirían.
   Con un solo contenedor, el título queda alineado con el texto y el conjunto
   va al medio.

   ⚠️ NI ESTE NI `.b-prosa` DECLARAN `font-size`, Y ESO ES LO QUE HACE QUE EL
   CENTRADO FUNCIONE. `ch` se resuelve contra el tamaño de letra DEL PROPIO
   elemento, así que dos cajas con `68ch` miden distinto si tienen tamaños
   distintos. Pasó: `.l-lectura` heredaba los 17px del cuerpo (68ch = 760px) y
   `.b-prosa` bajaba a 14px (68ch = 626px). El contenedor quedaba perfectamente
   centrado y el texto colgando de su borde izquierdo, corrido 67px. Medido en
   el navegador: interior y lectura con centro en 640 —el de la ventana— y la
   prosa en 573.
   Ahora las dos heredan el mismo tamaño y no pueden desalinearse. Si alguien
   necesita cambiar el tamaño del texto legal, va en las DOS o en ninguna.

   Solo para páginas de texto largo. El resto del sitio alinea a la izquierda
   dentro de `.b-interior` a propósito: ahí el contenido llena el ancho y no
   deja media pantalla vacía. */
.l-lectura { max-width: 68ch; margin-inline: auto; }

/* Cita de resumen: el "en corto" que abre la política de devoluciones. Hereda
   el color del bloque, así que no necesita superficie propia.
   Tenía `font-size` propio para destacar cuando la prosa era más chica; ahora
   que el cuerpo subió al tamaño del sitio, lo que la distingue es la línea de
   acento y nada más.
   ⚠️ La pintaba `--accion-fondo` hasta el 2026-08-21. Eso era usar el color de
   los BOTONES como adorno, y una raya decorativa pintada de "color de botón"
   empieza a leerse como algo en lo que se puede hacer clic. Ahora la pinta
   `--acento`, que es el token que existe exactamente para esto.
   ⚠️ En v3 los dos tokens VUELVEN A VALER LO MISMO en todos los temas (claro →
   cobalto y cobalto; oscuro → dorado y dorado), así que hoy este cambio no se
   nota en pantalla. No lo deshagas por eso. Que coincidan es una consecuencia
   de la regla 5, no una decisión de este componente: siguen siendo conceptos
   distintos, y el día que alguien separe la acción del acento —o mueva este
   bloque a una superficie donde diverjan— el token correcto ya está puesto. */
.b-prosa blockquote {
  margin: 0; padding-inline-start: var(--e-xl);
  border-inline-start: var(--borde-ancho-fuerte) solid var(--acento);
}

/* ============ Utilidades de maquetación ============
   Claude Design entregó los componentes pero ninguna rejilla, y sin rejilla
   cada sección es una columna sola. Solo aire y disposición, con la escala de
   tokens.css — el color siempre lo pone el tema del bloque. */
.l-pila      { display: grid; gap: var(--e-lg); }
.l-pila--aire{ gap: var(--e-2xl); }
.l-prosa     { display: grid; gap: var(--e-lg); max-width: 62ch; }
.l-encabezado{ display: grid; gap: var(--e-md); max-width: 56ch; margin-bottom: var(--e-4xl); }
/* Ese margen existe para SEPARAR el encabezado de lo que viene abajo. Cuando el
   encabezado es lo único que hay en la sección —las cabeceras de /comprar/,
   /contacto/ y /donde-comprar/— no separa nada: se suma al relleno inferior del
   bloque y deja una franja vacía que solo alarga la página (queja de Mario,
   2026-08-26). Va con `:last-child` y no quitándolo de cada página para que la
   siguiente cabecera que se escriba nazca bien sin que nadie se acuerde. */
.l-encabezado:last-child { margin-bottom: 0; }
/* El `min(Xpx, 100%)` no es adorno: un minmax con un mínimo fijo obliga a la
   columna a medir ese mínimo aunque el contenedor sea más angosto, y la página
   se desborda. Pasaba a 320px (iPhone SE viejo), por 4px. Con min() la columna
   nunca puede ser más ancha que su contenedor. */
.l-rejilla   { display: grid; gap: var(--e-2xl); grid-template-columns: repeat(auto-fit, minmax(min(240px, 100%), 1fr)); }
/* Puntos de venta: en móvil una columna en el orden del DOM (lista y luego
   mapa); desde 900px, lista a la izquierda y mapa a la derecha. Las columnas
   van con `minmax(0, …)` y no con `1fr` a secas: el mínimo automático de una
   columna de rejilla es su contenido, y un iframe se planta en su ancho
   intrínseco y desborda la página. */
.l-puntos    { display: grid; gap: var(--e-2xl); align-items: start; }
@media (min-width: 900px) {
  .l-puntos  { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--e-4xl); }
  /* El mapa acompaña el recorrido de la lista en vez de quedarse arriba. Vive
     dentro del `align-items: start` de la rejilla: un item estirado a toda la
     altura no tiene margen donde pegarse y el sticky no haría nada. */
  .l-puntos > .b-visor { position: sticky; top: var(--e-2xl); }
  /* En el bloque de las tiendas el mapa va PRIMERO en el HTML —una columna, o
     sea teléfono, es donde tiene que verse antes que los diez acordeones—, así
     que con dos columnas hay que devolverlo a la derecha. `order: 2` lo manda
     detrás del <ul>, que se queda en el 0 de fábrica.
     ⚠️ Solo lo lleva ese bloque: el de recolección tiene el mapa detrás de UNA
     tarjeta de texto corta, no de una lista larga, y ahí no hay nada que
     perderse. */
  .l-puntos--mapa-arriba > .b-visor { order: 2; }
}
.l-rejilla--ancha { grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); }
.l-partida   { display: grid; gap: var(--e-4xl); align-items: center; grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr)); }
/* Intercambia las dos columnas sin tocar el orden del HTML. El 700px no es un
   número al azar: con columnas de 300px, gap de 48 y el aire del bloque (24 por
   lado), la segunda columna aparece justo a partir de 696px. Por debajo hay una
   sola columna y ahí manda el orden del HTML, que es el que queremos leer. */
@media (min-width: 700px) {
  .l-partida--invertida > :first-child  { order: 2; }
  .l-partida--invertida > :nth-child(2) { order: 1; }
}
.l-acciones  { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e-lg); }
.l-limpia    { list-style: none; margin: 0; padding: 0; }
.l-figura    { aspect-ratio: 4 / 5;  object-fit: contain; }
.l-figura--ancha { aspect-ratio: 16 / 10; object-fit: cover; }
.l-linea     { height: 1.25rem; }
.l-linea--corta { width: 45%; }
.l-bulgito-s { --bulgito-size: 64px; }
.l-bulgito-l { --bulgito-size: 104px; }
/* Tamaño para cuando Bulgito NO acompaña a un texto sino que ocupa su propia
   columna, como en la 404. A 104px se perdía en media página vacía. El tope en
   % es para móvil, donde la columna se estrecha hasta el ancho del teléfono. */
.l-bulgito-xl { --bulgito-size: min(260px, 70%); }
.l-aire-arriba { margin-block-start: var(--e-4xl); }

/* ---- Bulgito acompañando al titular (Mario, 2026-08-27) ----
   Hasta ese día la mascota colgaba del FINAL del texto o de la columna de la
   foto. Hoy acompaña al título, que es donde Mario la quiere.

   ⚠️ Envuelve al <h1>/<h2>, NO se mete dentro. `css/base.css:67` pone todo
   `svg` en `display: block`: dentro del titular la mascota se llevaría su
   propio renglón y partiría la frase — el mismo fallo que documenta
   `.b-icono-linea` con la chispa de `gracias/`.

   `align-items: flex-end` la apoya en la ÚLTIMA línea del título. Con `center`
   un titular de tres líneas la deja flotando a media frase.

   🔴 La `flex-basis` del titular NO es opcional, y es justo lo que falla en
   silencio. Con `auto`, su tamaño hipotético es el del titular en UNA línea
   —enorme a display-xl—, así que se lleva la fila entera y la mascota cae
   DEBAJO: se ve exactamente como el sitio de antes y no hay error de CSS que
   lo diga. Con una base corta el titular toma el espacio que queda y parte sus
   líneas él solo. Comprobado renderizando, que es lo único que lo enseña. */
.l-titular-bulgito { display: flex; flex-wrap: wrap; align-items: flex-end; gap: var(--e-lg); }
/* El `min-width` es el que devuelve el salto de línea en móvil: sin él los dos
   se quedan pegados aunque el titular se estruje a dos letras por renglón.
   ⚠️ En `rem`, NUNCA en `ch`: el `ch` se mide con la fuente del PROPIO elemento
   y aquí el elemento es un titular de hasta 6rem — 12ch son ~576px, más ancho
   que la columna, así que el titular volvía a llevarse la fila entera y la
   mascota a caer debajo. Mismo síntoma que sin la regla, y ninguna pista. */
.l-titular-bulgito > :first-child { flex: 1 1 8rem; min-width: min(100%, 20rem); }

/* Encabezado a la izquierda y la mascota arriba a la derecha del bloque.
   `align-items: flex-start` la sube al titular: sin él, el margen inferior de
   `.l-encabezado` (--e-4xl) estira la fila y la mascota se va con él. */
.l-encabezado-mascota { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: var(--e-2xl); }

/* 🔴 El aire de abajo lo lleva el GRUPO, no el encabezado de dentro
   (Mario, 2026-08-28: «los bulgitos quedan muy cerca del primer card, se ve
   casi traslapado»). Medido a 390px: el hueco entre la mascota y la primera
   tarjeta era de **0.0px exactos**.

   Por qué pasaba, que es lo que hay que no repetir: el envoltorio de esta
   sección es `.b-interior` a secas —sin `.l-pila`, o sea SIN `gap`—, así que lo
   único que separaba el encabezado de las tarjetas era el `margin-bottom` de
   `.l-encabezado`. En escritorio funciona: los dos van en la misma fila, y ese
   margen estira la caja flex y empuja la lista. Pero al ENVOLVER en móvil el
   encabezado pasa a ser la primera fila y su margen queda **entre el titular y
   los bulgitos** — o sea dentro del grupo—, y debajo de la mascota no queda
   nada. El fallo solo existe por debajo del punto de envoltura, que es
   justamente donde nadie mira.

   Así que el margen sube al contenedor y se apaga el de dentro. La separación
   es del grupo entero contra lo que viene después; que dependiera de uno de sus
   dos hijos era lo que la hacía desaparecer al reacomodarse.

   ⚠️ Se apaga SOLO aquí dentro, con el hijo directo: `.l-encabezado` es una
   clase compartida y su `margin-bottom` sostiene el aire de casi todos los
   bloques del sitio. */
.l-encabezado-mascota { margin-block-end: var(--e-5xl); }
.l-encabezado-mascota > .l-encabezado { margin-bottom: 0; }

/* El reparto del aire en móvil: POCO con el titular, MUCHO con la tarjeta
   (Mario, 2026-08-28 — «menos aire con el título, más separado de la card»).
   Con 24 arriba y 48 abajo la mascota todavía se leía colgando del titular.
   Hoy son 8 y 72.

   ⚠️ `row-gap`, no `gap`. El `gap` de arriba vale para los dos ejes y en
   escritorio es la separación HORIZONTAL entre el titular y la mascota, que no
   se toca. El `row-gap` solo existe cuando hay dos filas, o sea solo cuando
   envuelve — así que esto es móvil sin escribir una media query, y no puede
   desincronizarse de un punto de corte que no está escrito en ningún lado. */
.l-encabezado-mascota { row-gap: var(--e-sm); }

/* La pareja de «Cómo tomarlo»: el segundo cae medio cuerpo más abajo, en
   diagonal. Es el ÚNICO sitio con dos bulgitos juntos (Mario, 2026-08-27); en
   todos los demás va uno. */
.l-bulgitos-pareja { display: flex; align-items: flex-start; gap: var(--e-sm); }
.l-bulgitos-pareja > :last-child { margin-block-start: var(--e-3xl); }

/* Burbujas: posición y ritmo. El color lo pone el tema del bloque. */
.b-burbuja--1 { left:  8%; width: 22px; height: 22px; --b-dur: 12s; --b-delay: 0s;   --b-drift:  18px; }
.b-burbuja--2 { left: 24%; width: 12px; height: 12px; --b-dur:  9s; --b-delay: 1.6s; --b-drift: -12px; }
.b-burbuja--3 { left: 52%; width: 30px; height: 30px; --b-dur: 14s; --b-delay: 3.2s; --b-drift:  22px; }
.b-burbuja--4 { left: 71%; width: 16px; height: 16px; --b-dur: 10s; --b-delay: 0.8s; --b-drift: -16px; }
.b-burbuja--5 { left: 88%; width: 24px; height: 24px; --b-dur: 13s; --b-delay: 2.4s; --b-drift:  10px; }

/* ============ Colocación de los partials ============
   Claude Design entregó cómo se VEN header, menú y FAB, pero no dónde caen:
   sin una sola media query y sin posicionar .b-fab. Estas reglas cubren ese
   hueco. Vivieron un rato inyectadas desde partials.js y se bajaron aquí: si
   el JS tarda o falla, la página tiene que salir colocada igual. Solo posición
   y visibilidad — el color sigue saliendo del tema del bloque. */

/* El envoltorio del FAB lleva `data-bloque` por una razón concreta: cuelga de
   <body> y no de una sección, así que sin tema no resolvería --accion-fondo ni
   --borde y la pastilla saldría sin color. Pero `[data-bloque]` (base.css) hace
   DOS cosas —define el tema **y** pinta el fondo—, y aquí solo se quería lo
   primero: el fondo dejaba un rectángulo crema detrás del botón, bien visible
   sobre el pie café. Se apaga la pintura y se conservan los tokens.
   ⚠️ Va aquí y no en base.css a propósito: base.css define la regla general y
   está bien como está; esto es la excepción de un componente. Funciona porque
   components.css se carga después con la misma especificidad. */
[data-partial="fab"] {
  position: fixed; right: var(--e-lg); bottom: var(--e-lg); z-index: 60;
  background: transparent;

  /* ---- El FAB va DORADO, no cobalto (decisión de Mario, 2026-08-25) ----
     Es una excepción deliberada a la regla 5 del sistema v3 («superficie clara →
     acción cobalto»). Se sostiene porque el FAB no compite con los botones de la
     página: flota por encima de TODAS las secciones, claras y oscuras, así que
     no forma parte de la jerarquía de ningún bloque. Dorado lo separa del
     "Comprar" cobalto en vez de duplicarlo.

     ⚠️ Se redefine el PAR de acción, no `.b-fab`. Ahí el relleno, el canto y la
     tinta salen los tres de estos dos tokens, así que cambiarlos por separado
     deja el botón descuadrado —canto azul con relleno dorado— y nada avisa.

     ⚠️ LA TINTA ES NEGRA Y NO ES NEGOCIABLE: blanco sobre dorado da 2.30:1 y es
     ilegible (regla 8). Negro sobre dorado da 7.55:1, AAA. Lo fija una prueba en
     `tests/canto.test.js`, porque el verificador de contraste NO mira este
     archivo: solo lee los temas de `tokens.css`.

     El canto dorado sobre papel da 1.86:1, o sea que el contorno no dibuja la
     pastilla — la dibuja `--sombra-alta`, que es café (11.64:1 sobre papel). Por
     eso el FAB conserva su sombra y no se le puede quitar. */
  --accion-fondo: var(--dorado);
  --accion-tinta: var(--negro);
}

.b-header [data-marca] {
  display: inline-flex; align-items: center;
  min-height: var(--toque-min); text-decoration: none;
  /* Le gana al `font-size` y al `font-weight` de `.t-title-s` que trae en el
     marcado; de esa clase conserva la familia (Recoleta).

     NEGRITA (700), no el semibold 600 del resto de títulos: es el logotipo, y a
     este tamaño el 600 se leía flojo (Mario, 2026-08-25). El pie se queda en 600
     a propósito — ahí la marca va a 20px, donde el semibold tiene densidad de
     sobra, y Mario dijo que así se ve bien.

     ⚠️ `line-height: 1` es lo que permite subir el tamaño. La caja de línea es
     `font-size × line-height` y NO puede pasar de `--toque-min` (44px) o la barra
     crece en las nueve páginas. Con el 1.15 que traía `.t-title-s` el techo eran
     ~38px; con 1 sube a 44. Una palabra suelta no necesita interlínea. */
  font-size: var(--t-marca-size);
  font-weight: 700;
  line-height: 1;
}
.b-header [data-acciones] { display: flex; align-items: center; gap: var(--e-lg); }

/* El [hidden] del navegador pierde contra .b-menu-movil { display: grid } */
.b-menu-movil[hidden] { display: none; }

/* .b-nav a y .b-menu-movil a le ganan por especificidad a .b-btn y le borran el
   padding y el borde. El CTA vive dentro del nav por diseño, así que aquí
   recupera su forma con los mismos tokens.

   ⚠️ El borde se restaura con `--accion-fondo`, NO con `--borde`. Esta regla le
   gana por especificidad a `.b-btn { border-color: var(--accion-fondo) }`, así
   que con `--borde` el "Comprar" de la barra salía con canto café mientras el
   del hero lo llevaba azul: el MISMO botón con dos cantos distintos en la misma
   pantalla. Lo cazó Mario, no las pruebas. */
.b-nav a.b-btn, .b-menu-movil a.b-btn {
  padding: var(--e-md) var(--e-2xl);
  border: var(--borde-ancho) solid var(--accion-fondo);
  border-radius: var(--r-pill);
  font-family: var(--fuente-cuerpo);
}
/* Y el de contorno lo recupera: no tiene relleno que lo dibuje. Hace falta
   porque la regla de arriba también le gana a `.b-btn--outline`. Hoy el CTA del
   nav es lleno, pero `CTA_CONFIG` es un interruptor de canal: si el primario
   vuelve a ser WhatsApp (variante `outline`), esto ya está resuelto. */
.b-nav a.b-btn--outline, .b-menu-movil a.b-btn--outline {
  border-color: var(--borde);
}

@media (min-width: 900px)    { .b-hamburguesa, .b-menu-movil { display: none; } }
@media (max-width: 899.98px) { .b-header .b-nav { display: none; } }

/* En móvil el flotante se reduce al icono. 56×56 queda por encima de
   --toque-min (44), así que encoger no rompe el área táctil. */
@media (max-width: 640px) {
  .b-fab { width: 56px; padding: 0; justify-content: center; }
  .b-fab [data-fab-texto] { display: none; }
}

/* ---- Fotos reales ----
   El redondeo es el de la landing que está hoy en vivo (28px allá, `--r-cont`
   aquí): una foto a sangre con las esquinas en pico se lee como un error de
   maquetación al lado de tarjetas y botones que sí son redondos.
   `object-fit: cover` obliga a fijar también la proporción; sin ella el `cover`
   no recorta nada y la caja se queda con el alto natural del archivo. */
.b-foto {
  width: 100%;
  object-fit: cover;
  border-radius: var(--r-cont);
}
/* La del hero. El tope de ancho lo pone `.l-media-hero`, y es ÉL quien le pone
   tope al alto: cabe hacerlo al revés con un `max-height`, pero sobre un retrato
   de 864×1184 eso recorta por arriba y se come el corcho de la botella. A 520px
   de ancho el alto queda en ~650, el mismo tope que usa la landing en vivo.

   ✅ Desde el 2026-08-27 el sitio tiene sus OCHO fotos reales y no queda ni un
   `.b-ph` puesto en ninguna página. El intercambio funcionó tal como estaba
   previsto —`<div class="b-ph l-figura…">` → `<img class="b-foto l-figura…">`,
   sin recolocar nada— porque `.b-ph` y `.b-foto` comparten `--r-cont`.
   ⚠️ `.b-ph` NO es código muerto: es el hueco que se vuelve a poner el día que
   nazca una sección con foto pendiente, y por eso los dos radios siguen atados. */
.b-foto--hero { aspect-ratio: 4 / 5; }

/* La columna de la foto: solo la foto.
   ⚠️ Bulgito NO va aquí, y no es un olvido. Estuvo debajo de la imagen y luego
   asomado a su esquina, y las dos veces estorbó: debajo sumaba ~120px de alto a
   esta columna y, como `.l-partida` centra las dos columnas ENTRE SÍ, empujaba el
   texto de al lado muy por debajo del centro de la foto; encima, se encimaba con
   ella (decisión de Mario). Vive con el texto, junto al sticker. Si algún día
   vuelve a esta columna, vuelve el descuadre del texto. */
.l-media-hero { max-width: 520px; margin-inline: auto; }

/* ============ Texto que arranca al tope de la foto y se centra al bajar ======

   Idea de Mario: en las secciones partidas, el texto empieza con su primera
   línea a la altura del BORDE DE ARRIBA de la foto, y conforme se hace scroll
   baja solo hasta quedar centrado con ella (que es como está sin scroll).

   Va sin una línea de JavaScript: es una animación de scroll de CSS. Los tres
   candados de abajo son lo que la hace segura de tener puesta.

   ⚠️ CÓMO SABE CUÁNTO BAJAR. La distancia es (alto del texto − alto de la fila)/2
   y ninguna de las dos se conoce al escribir el CSS: dependen del ancho de la
   ventana y de cuánto texto haya. Se saca con dos unidades distintas dentro del
   mismo `calc`:
     · `50%`    en un `translateY` es la mitad del alto DEL PROPIO elemento;
     · `50cqh`  es la mitad del alto de su contenedor de consulta.
   El envoltorio `.l-desliza` se estira a la altura de la fila (`align-self:
   stretch`) y se declara contenedor de tamaño, así que `cqh` ES el alto de la
   fila. De ahí sale la cuenta exacta, sin medir nada.

   ⚠️ `container-type: size` implica contención de tamaño: el contenido de esta
   columna deja de contar para el alto de la fila. Aquí da igual porque la fila
   la manda la foto —por eso solo se aplica donde la foto es la columna alta—,
   pero si el texto creciera hasta pasarla, se saldría del bloque. Es la razón
   del candado de ancho: por debajo de 1100px las columnas se angostan, el texto
   crece de alto y en varias secciones adelanta a la foto.

   👉 Dónde SÍ se puede: hero, "El bulgicultor" y "¿A qué sabe?" — las de foto
   vertical (4/5), que a ese ancho miden ~700px contra ~400 del texto.
   👉 Dónde NO: las de foto apaisada (16/10) — `¿Qué es bulgi?`, la segunda fila
   de "El bulgicultor" y "Producto vivo"—. Ahí la columna alta es el TEXTO, así
   que no hay recorrido que hacer: alinear su primera línea con el tope de la
   foto sería empujarlo hacia ABAJO, fuera del bloque. Medido, no supuesto.

   ⛔ DECIDIDO el 2026-08-20 (DEC-33): esto se queda así. No es una tarea a medias
   ni un "falta ponerlo en las otras tres". La única variante que cabría en las
   apaisadas es la simétrica —mover la FOTO en vez del texto—, que es otro efecto
   distinto. No lo añadas sin que Mario lo pida.

   Se anima el hijo y no `.l-desliza` porque el envoltorio es justamente la
   referencia: moverlo movería también la medida. */
@keyframes b-texto-al-tope {
  from { transform: translateY(calc(50% - 50cqh)); }
  to   { transform: none; }
}

@supports (animation-timeline: view()) {
  @media (min-width: 1100px) and (prefers-reduced-motion: no-preference) {
    .l-desliza {
      align-self: stretch;
      container-type: size;
      display: grid; align-content: center;
    }
    .l-desliza > * {
      animation-name: b-texto-al-tope;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      /* Del momento en que la sección asoma por abajo hasta que su centro llega
         al centro de la ventana: cuando la estás mirando de frente, ya llegó. */
      animation-range: entry 0% cover 50%;
    }
    /* El hero no puede usar `view()`: está arriba del todo, así que con la página
       recién cargada su recorrido ya estaría consumido y no se movería nunca. Se
       cuelga del scroll de la página, que ahí sí empieza en cero. */
    .l-desliza--hero > * {
      animation-timeline: scroll(root block);
      animation-range: 0 300px;
    }
  }
}

/* ---- Huecos de imagen (las fotos reales llegan en T-24) ----

   Panel LISO y vacío, no un placeholder de desarrollo. Hasta el 2026-08-25 esto
   era un marco punteado con trama diagonal y el texto "FOTO PENDIENTE · <qué va
   ahí>" dentro. Eso es una nota de producción: en el sitio publicado se lee como
   que la página está a medias, y el visitante no tiene forma de saber que es a
   propósito. La sugerencia de cada hueco NO se perdió — vive en un comentario
   HTML justo encima suyo, que es donde sirve al que trae las fotos y no llega a
   ninguna pantalla.

   ⚠️ El radio es `--r-cont`, el MISMO que `.b-foto`, y esa es la razón de ser de
   esta regla. Era `--r-card` (14px) contra los 24px de la foto: al sustituir el
   hueco por la `<img>` mañana, el marco cambiaba de forma. Con la proporción
   (`.l-figura` / `.l-figura--ancha`) y el radio iguales, el intercambio es 1:1 y
   la página no se recoloca. Si tocas uno de los dos, toca el otro. */
.b-ph {
  background: var(--crema);
  border: var(--borde-ancho) solid var(--borde);
  border-radius: var(--r-cont);
}

/* ---- Icono dentro de una línea de texto ----
   `css/base.css` pone TODO `svg` en `display: block` (línea 67). Dentro de un
   contenedor flex —los botones, `.b-paso-num`— eso es justo lo que se quiere:
   el icono se convierte en un elemento flex y se centra solo. Pero en mitad de
   un párrafo, `block` le da al icono su propio renglón: el texto termina, el
   icono cae debajo y la frase queda partida.

   Verificado renderizando, no leyendo: la chispa del final de `gracias/` caía
   sola a la línea siguiente, y `white-space: nowrap` NO lo arregla —el problema
   no es dónde puede partir la línea, es que el icono ya no es de línea—.

   `1em` y `vertical-align: -.125em`: crece con el texto y se apoya en la línea
   base. Sin el vertical-align el icono se sienta ENCIMA de ella y levanta todo
   el renglón. El color lo pone `currentColor` en el propio SVG. */
.b-icono-linea {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -.125em;
}
