/* ═══════════════════════════════════════════════════════════════════════════
   kcore-vitrina.css — EL ASPECTO DE LA VITRINA, COMPARTIDO

   Lo cargan DOS superficies: la Mesa (`#kcore-root`) y el portal del cliente
   (`#dbch-root`). Es el mismo archivo, no una copia, y esa es la decisión:
   David pidió que el cliente vea la vitrina exactamente como la ve el equipo,
   y con dos copias «idéntica» dura hasta el primer retoque.

   Ver la cabecera de `kcore-vitrina.js` para el razonamiento completo, que es
   el mismo del §4 de CLAUDE.md: el frontend no se forkea.

   ── ⚠️ POR QUÉ CADA SELECTOR LLEVA `:is(#kcore-root, #dbch-root)` ──

   Porque las dos superficies tienen raíces distintas y estas reglas tienen que
   ganarle a los estilos base del tema (Astra) en las dos. El prefijo de id no
   es decorativo: el historial del portal tiene varios defectos que costaron una
   sesión de diagnóstico cada uno y que eran exactamente esto, un estilo base
   pisando al del componente.

   `:is()` toma la especificidad de su argumento más alto, o sea la de un id
   (1,0,0), que es la MISMA que tenía `#kcore-root .x` antes de mudarse. Escribir
   `.kc-vitrina .x` habría bajado a (0,1,0) y habría destapado justo esa clase de
   defecto en el sitio donde nadie está mirando.

   Y por eso también hay que sostener la forma: **una regla nueva aquí lleva el
   `:is(...)` completo**. Una que se escriba solo con `#kcore-root` funciona en
   la Mesa, se ve perfecta al revisarla, y no existe en el portal.

   ── Lo que NO está aquí ──

   El modal de curación, el visor de pdf.js y la portada de la tarjeta del
   Tablero se quedaron en `kcore-mesa.css`: son de la Mesa. El cliente mira, no
   cura, y pdf.js no viaja al portal (para eso las páginas se convierten a
   imagen al publicar).

   ── ⚠️ LAS VARIABLES DE DISEÑO LAS PONE LA RAÍZ, Y NO SON LAS MISMAS ──

   Este archivo usa los tokens del sistema (`--surface-3`, `--accent`, `--r-lg`,
   `--border`…), y cada raíz declara los suyos: la Mesa en `kcore-mesa.css`, el
   portal en `dbch-dashboard.css`. Coinciden porque el §3.b de CLAUDE.md obliga
   a que la Mesa COPIE los valores del portal, y esa es justamente la razón por
   la que compartir esta hoja funciona.

   Pero no coinciden todos. Al mudar el archivo se comprobaron los 17 que usa y
   **`--tint-2` solo existía en la Mesa**: en el portal esos dos elementos
   habrían salido sin fondo, sin ningún error, solo un poco mal. Por eso lleva
   valor de respaldo, `var(--tint-2, #F1F7D6)`.

   La regla para lo que se escriba aquí de ahora en adelante: **un token que no
   declaren LAS DOS raíces va con respaldo**. Declararlo aquí no sirve, porque
   entonces el orden de carga de las hojas decide quién gana, y ese orden no es
   el mismo en las dos superficies.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════ LA VITRINA (BD v18, 9 ago 2026) ══════════════════
 *
 * Pedido de David: un espacio en la pieza donde se previsualice el entregable
 * "de una forma wao", con un apartado propio donde se elige QUÉ se muestra,
 * separado de todo lo que el equipo sube a la mesa interna.
 *
 * Vive como primer bloque de `.kc-dr-main`, la columna ANCHA de la ficha
 * (~777px útiles en 1440), y no en la de la conversación (~319px). Ver el
 * comentario largo en `kcore-mesa.js`: en aquella columna un post cuadrado sale
 * del tamaño de una miniatura, que es justo lo contrario de lo que se busca.
 */

/* El estado vacío TIENE QUE QUEDARSE CHICO. Las 96 piezas de producción nacen
 * sin vitrina, así que un placeholder alto arriba de cada ficha empujaría el
 * brief y el plan fuera de pantalla en todas ellas.
 *
 * El 11 ago 2026 pasó de una línea a una tarjeta de ~76px (David). La razón de
 * arriba no se rompe —sigue siendo una fila, no un lienzo— pero el margen se
 * acabó: si esto vuelve a crecer, hay que volver a medir qué queda por encima
 * del pliegue en una ficha SIN vitrina, que son todas menos las que se curen. */
:is(#kcore-root, #dbch-root) .kc-vit-add { display: flex; width: 100%; align-items: center; gap: 13px;
  padding: 15px 17px; border-radius: var(--r-lg); border: 1px solid var(--border);
  background: var(--surface); }
:is(#kcore-root, #dbch-root) .kc-vit-add-x { width: 34px; height: 34px; border-radius: 10px; flex: none;
  display: grid; place-items: center; background: var(--tint-2, #F1F7D6); color: #46530A; }
:is(#kcore-root, #dbch-root) .kc-vit-add-x svg { width: 18px; height: 18px; display: block; }
:is(#kcore-root, #dbch-root) .kc-vit-add-b { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
:is(#kcore-root, #dbch-root) .kc-vit-add-b b { font-size: 13.5px; font-weight: 600; color: var(--text-1); }
:is(#kcore-root, #dbch-root) .kc-vit-add-b span { font-size: 12px; color: var(--text-3); }
:is(#kcore-root, #dbch-root) .kc-vit-add .kc-b { flex: none; }
:is(#kcore-root, #dbch-root) .kc-vit-add .kc-b svg { width: 15px; height: 15px; display: block; }
/* En angosto el botón no cabe al lado del texto y se parte en dos renglones
   ilegibles: el bloque pasa a columna y el botón a ancho completo. */
@media (max-width: 1180px) {
  :is(#kcore-root, #dbch-root) .kc-vit-add { flex-wrap: wrap; }
  :is(#kcore-root, #dbch-root) .kc-vit-add .kc-b { width: 100%; justify-content: center; }
}

:is(#kcore-root, #dbch-root) .kc-vit-h { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; }
:is(#kcore-root, #dbch-root) .kc-vit-h h3 { margin: 0; font-size: 13px; font-weight: 700; color: var(--text-1);
  letter-spacing: .02em; }
:is(#kcore-root, #dbch-root) .kc-vit-n { font-size: 11.5px; color: var(--text-3); }
/* Empuja "Editar vitrina" al borde derecho. Es la misma regla que ya usa
 * `.kc-head-row > .kc-b` en los encabezados de sección. */
:is(#kcore-root, #dbch-root) .kc-vit-h .kc-b { margin-left: auto; }

/* ══ EL CHECK DE PUBLICADO/SIN PUBLICAR (24 ago 2026) ══
   Puramente visual: ubica de un vistazo qué publicaciones de la vitrina ya
   salieron a redes esta semana. En la Mesa es un `<button>` clicable
   (`editable`); al cliente se le pinta como etiqueta de solo lectura
   (`.kc-vit-pub-ro`, un `<span>`), sin ninguna afordancia de que se pueda
   tocar — la otra mitad de "el cliente lo ve, solo la Mesa lo activa". Mismo
   reset de `.kc-vpub`: `all: unset` y se repone lo que hace falta EN LA MISMA
   regla, nunca en una declarada después (§5.c.5). */
:is(#kcore-root, #dbch-root) .kc-vit-pub { all: unset; box-sizing: border-box;
  display: inline-flex; align-items: center; gap: 6px; cursor: pointer;
  font-family: inherit; font-size: 11.5px; font-weight: 600; white-space: nowrap;
  padding: 5px 11px; border-radius: 20px; background: var(--surface-3, #F4F4F2);
  color: var(--text-3, #8A8A85); transition: background .15s ease, color .15s ease; }
:is(#kcore-root, #dbch-root) .kc-vit-pub-ro { cursor: default; }
:is(#kcore-root, #dbch-root) button.kc-vit-pub:hover { background: var(--surface-2, #EAEAE6); }
:is(#kcore-root, #dbch-root) .kc-vit-pub:focus-visible { outline: 2px solid var(--accent, #CBF904);
  outline-offset: -2px; }
:is(#kcore-root, #dbch-root) .kc-vit-pub.on { background: var(--tint-2, #F1F7D6); color: #46530A; }
:is(#kcore-root, #dbch-root) .kc-vit-pub-dot { width: 7px; height: 7px; border-radius: 50%; flex: none;
  background: currentColor; opacity: .5; }
:is(#kcore-root, #dbch-root) .kc-vit-pub.on .kc-vit-pub-dot { opacity: 1; }

/* ══ EL EJE EXTERIOR: pasar de PUBLICACIÓN (12 ago 2026, BD v25) ══

   Con las medidas del estándar de la plataforma (`.kc-tabs` / `.kc-tab`, §5.b):
   riel de radio 22 con padding 4, y la activa en pastilla BLANCA con sombra,
   nunca en lima. La pastilla ya comunica la selección, y meterle color de acento
   le pelea a esa señal.

   ⚠️ Los tokens llevan RESPALDO. `--tint-2` es el caso ya documentado en la
   cabecera de este archivo —solo existía en `#kcore-root`— y aquí vuelve a
   aplicar la regla: un token que no declaren LAS DOS raíces se escribe con su
   valor de reserva, o en el portal el elemento sale sin fondo, sin error, y
   solo un poco mal. */
/* ⚠️ VIVE DENTRO DE `.kc-vit-h` desde el 2 sep 2026: era una banda propia entre
   la cabecera y el escenario, y eso hacía que una vitrina con dos publicaciones
   midiera una fila MÁS que una con una sola, empujando la pieza fuera de la
   ventana en cuanto se añadía la segunda. Ver la nota de `vitCabecera()`.

   De ahí `flex: 1` (se queda con el hueco libre entre el contador y el botón),
   `min-width: 0` (sin esto un eje largo no se deja encoger y empuja el botón
   fuera de la fila, que es el mismo modo de fallo que ya arregló el `flex-wrap`
   de la cabecera en móvil) y el relleno más apretado, para que la fila no crezca
   de alto más de lo imprescindible al aparecer el eje. */
/* ── El RIEL del eje y sus flechas (David, 4 sep 2026) ──
 * *"en la tarjeta… tampoco hay cómo navegar entre publicaciones de la vitrina,
 * para el cliente en el portal también debe ser así"*.
 *
 * Mismo problema y misma cura que la barra del modal: la tira siempre pudo
 * desplazarse, pero con el scrollbar oculto nada lo decía. Medido: cuatro
 * publicaciones de nombre largo dejan 559px escondidos tras un corte limpio que
 * se lee como el final de la lista.
 *
 * ⚠️ EL RIEL HEREDA `flex: 1 1 auto; min-width: 0` DE LA TIRA, y esos dos valores
 * no son cosméticos: los explica la nota de abajo (el eje vive dentro de
 * `.kc-vit-h`, entre el contador y el botón, y sin ellos empuja el botón fuera de
 * la fila). Al envolver hay que mirar de qué se encargaba lo envuelto — es la
 * misma trampa que costó el `margin-left: auto` de `.kc-dr-acts`. La tira, ya
 * dentro, pasa a quedarse con todo el riel.
 *
 * Las flechas van SUPERPUESTAS: en la fila le robarían ancho al eje siempre,
 * incluso con dos publicaciones, en un bloque que ya va justo de ancho. */
:is(#kcore-root, #dbch-root) .kc-vpubs-w { position: relative; display: flex;
  flex: 1 1 auto; min-width: 0; max-width: 100%; }
:is(#kcore-root, #dbch-root) .kc-vpubs-nav { all: unset; box-sizing: border-box;
  cursor: pointer; position: absolute; top: 50%; transform: translateY(-50%);
  z-index: 2; display: none; place-items: center; width: 24px; height: 24px;
  border-radius: 50%; background: #FFFFFF; color: var(--text-2, #5C5C57);
  font-family: inherit; font-size: 16px; line-height: 1;
  box-shadow: 0 1px 6px rgba(0,0,0,.16); transition: color .15s, background .15s; }
:is(#kcore-root, #dbch-root) .kc-vpubs-nav:hover { color: var(--text-1, #0D1117); }
:is(#kcore-root, #dbch-root) .kc-vpubs-prev { left: 2px; }
:is(#kcore-root, #dbch-root) .kc-vpubs-next { right: 2px; }
:is(#kcore-root, #dbch-root) .kc-vpubs-izq .kc-vpubs-prev,
:is(#kcore-root, #dbch-root) .kc-vpubs-der .kc-vpubs-next { display: grid; }
/* El desvanecido bajo la flecha: sin él la pestaña que pasa por debajo se ve
   cortada contra el círculo blanco y parece un defecto de pintado. El color es
   el mismo `--surface-3` del riel, con su respaldo, porque el degradado tiene
   que morir en el fondo exacto de la tira. */
:is(#kcore-root, #dbch-root) .kc-vpubs-w::before,
:is(#kcore-root, #dbch-root) .kc-vpubs-w::after { content: ''; position: absolute;
  top: 0; bottom: 0; width: 40px; z-index: 1; pointer-events: none; display: none;
  border-radius: 22px; }
:is(#kcore-root, #dbch-root) .kc-vpubs-w::before { left: 0;
  background: linear-gradient(to right, var(--surface-3, #F4F4F2), rgba(244,244,242,0)); }
:is(#kcore-root, #dbch-root) .kc-vpubs-w::after { right: 0;
  background: linear-gradient(to left, var(--surface-3, #F4F4F2), rgba(244,244,242,0)); }
:is(#kcore-root, #dbch-root) .kc-vpubs-izq::before,
:is(#kcore-root, #dbch-root) .kc-vpubs-der::after { display: block; }

:is(#kcore-root, #dbch-root) .kc-vpubs { display: flex; gap: 4px;
  background: var(--surface-3, #F4F4F2); border-radius: 22px; padding: 3px;
  flex: 1 1 auto; min-width: 0; max-width: 100%;
  /* Doce publicaciones no caben en el ancho de la ficha, y sin esto empujarían
     el bloque y arrastrarían la página entera a scroll horizontal. La barra
     scrollea lo suyo, que es la regla de layout de siempre. */
  overflow-x: auto; overflow-y: hidden; scrollbar-width: none; }
:is(#kcore-root, #dbch-root) .kc-vpubs::-webkit-scrollbar { display: none; }
:is(#kcore-root, #dbch-root) .kc-vpub { all: unset; box-sizing: border-box;
  cursor: pointer; flex: none; font-family: inherit; color: #8A8A85;
  background: transparent; padding: 4px 12px; border-radius: 18px;
  transition: all .15s ease; display: inline-flex; align-items: center; gap: 7px;
  white-space: nowrap; }
:is(#kcore-root, #dbch-root) .kc-vpub:hover { color: #0D1117; }
:is(#kcore-root, #dbch-root) .kc-vpub:focus-visible { outline: 2px solid var(--accent, #CBF904);
  outline-offset: -2px; }
:is(#kcore-root, #dbch-root) .kc-vpub.on { background: #FFFFFF; color: #0D1117;
  box-shadow: 0 2px 8px rgba(0,0,0,.08); }
/* El número va NEUTRO en los dos estados, igual que el contador de `.kc-tab`. */
:is(#kcore-root, #dbch-root) .kc-vpub-n { flex: none; width: 17px; height: 17px;
  border-radius: 50%; display: grid; place-items: center; background: #E9E9E3;
  color: #6B7280; font-size: 10px; font-weight: 700; }
:is(#kcore-root, #dbch-root) .kc-vpub.on .kc-vpub-n { background: #EFF1F5; color: var(--text-1, #0D1117); }
:is(#kcore-root, #dbch-root) .kc-vpub-l { font-size: 12px; font-weight: 500; }
:is(#kcore-root, #dbch-root) .kc-vpub.on .kc-vpub-l { font-weight: 600; }

/* Una publicación de la parrilla que se quedó sin láminas. NO es el estado
   vacío de la vitrina (`.kc-vit-add`): ahí no hay nada publicado, aquí hay una
   parrilla con contenido y esta publicación concreta está a medias. Va discreta
   y con borde punteado, el mismo lenguaje que las columnas vacías del Tablero:
   es un hueco por llenar, no un error. */
:is(#kcore-root, #dbch-root) .kc-vit-nada { display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 5px; text-align: center;
  padding: 34px 20px; border: 1px dashed var(--border, #E5E7EB);
  border-radius: var(--r-lg, 14px); background: var(--surface-3, #F4F4F2); }
:is(#kcore-root, #dbch-root) .kc-vit-nada b { font-size: 13px; font-weight: 600;
  color: var(--text-2, #6B7280); }
:is(#kcore-root, #dbch-root) .kc-vit-nada span { font-size: 12px; color: var(--text-3, #9AA0A6); }

/* `position: relative` crea el contexto de apilamiento de las flechas, así que
 * su z-index es LOCAL y no entra en la escala de este archivo (100000+). Es lo
 * que evita tener que competir con la ficha, el modal y el visor. */
:is(#kcore-root, #dbch-root) .kc-vit-stage { position: relative; background: var(--surface-3);
  border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden;
  min-height: 240px; display: flex; align-items: center; justify-content: center; }

:is(#kcore-root, #dbch-root) .kc-vit-slide { all: unset; box-sizing: border-box; display: block; width: 100%;
  cursor: zoom-in; font-family: inherit; }
/* ⚠️ ESTA LÍNEA NO ES OPCIONAL, y su falta fue el defecto que David vio: con
 * cinco láminas se pintaban LAS CINCO en fila, no una.
 *
 * El JS oculta las no activas con el atributo `hidden`, que el navegador aplica
 * con `display:none` desde su hoja de USUARIO. Cualquier `display` declarado en
 * una hoja de autor le gana, así que el `display:block` de arriba lo anula por
 * completo y en silencio. Es la misma familia que `all:unset` reseteando
 * `box-sizing` (§5.c.5): una propiedad que se da por sentada y una regla propia
 * que la pisa sin que nada avise.
 *
 * Va DESPUÉS de la regla base a propósito: a igual especificidad gana la última. */
:is(#kcore-root, #dbch-root) .kc-vit-slide[hidden] { display: none; }
:is(#kcore-root, #dbch-root) .kc-vit-slide:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ══════════ LA PISTA: pasar de lámina DESLIZANDO (David, 2 sep 2026) ══════════
 *
 * *"la animación cuando se selecciona la imagen siguiente debería ser un efecto
 * de deslizarse como se ve en Instagram, no simplemente que aparezca la otra"*.
 *
 * Las láminas van todas en fila y lo que se mueve es la pista, con un
 * `translateX` de -100% por lámina. Solo la montan el post y el carrusel: ver
 * `desliza` en `vitrinaHtml()`.
 *
 * ⚠️ Dentro de una pista las láminas NO llevan `hidden`. La regla de arriba
 * —`[hidden] { display: none }`, la que arregló que se pintaran las cinco en
 * fila— las sacaría de la fila y el `translateX` apuntaría a huecos. Por eso
 * `vitVer()` mueve la pista O reparte los `hidden`, nunca las dos cosas.
 *
 * El recorte lo pone `.kc-vit-stage`, que ya venía con `overflow: hidden`.
 */
:is(#kcore-root, #dbch-root) .kc-vit-track { display: flex; width: 100%; flex: none;
  transition: transform .34s cubic-bezier(.32, .72, 0, 1); will-change: transform; }
:is(#kcore-root, #dbch-root) .kc-vit-track .kc-vit-slide { flex: 0 0 100%; width: 100%; }
/* Quien haya pedido menos movimiento cambia de lámina igual, sin el barrido. */
@media (prefers-reduced-motion: reduce) {
  :is(#kcore-root, #dbch-root) .kc-vit-track { transition: none; }
}

/* ── Los puntos, ENCIMA de la lámina y abajo, como Instagram ──
 * Pequeños y juntos a propósito: son una posición, no un control que reclame
 * atención. La sombra es lo que los mantiene legibles sobre un arte claro. */
:is(#kcore-root, #dbch-root) .kc-vit-pd { position: absolute; z-index: 3; left: 0; right: 0; bottom: 10px;
  display: flex; align-items: center; justify-content: center; gap: 3.5px;
  pointer-events: none; }
:is(#kcore-root, #dbch-root) .kc-vit-pdi { all: unset; box-sizing: content-box; cursor: pointer;
  pointer-events: auto; width: 5px; height: 5px; padding: 4px; border-radius: 50%;
  background: rgba(255,255,255,.55); background-clip: content-box;
  filter: drop-shadow(0 1px 2px rgba(0,0,0,.45)); transition: background-color .18s; }
:is(#kcore-root, #dbch-root) .kc-vit-pdi.on { background: #fff; background-clip: content-box; }
:is(#kcore-root, #dbch-root) .kc-vit-pdi:focus-visible { outline: 2px solid var(--accent); outline-offset: -1px; }

/* `width:auto` + `max-*` y NUNCA `width:100%` con `max-height`: eso segundo
 * deforma una story vertical, que es justo el formato donde más se nota. Así
 * una pieza de cualquier proporción se centra sobre el mate sin distorsión, y
 * el `min-height` del escenario acota el salto mientras carga. */
:is(#kcore-root, #dbch-root) .kc-vit-slide img { display: block; margin: 0 auto;
  max-width: 100%; max-height: 460px; width: auto; height: auto; }

:is(#kcore-root, #dbch-root) .kc-vit-doc { display: flex; flex-direction: column; align-items: center; gap: 9px;
  padding: 40px 20px; text-align: center; }
:is(#kcore-root, #dbch-root) .kc-vit-doc b { font-size: 13px; font-weight: 600; color: var(--text-1);
  max-width: 320px; overflow-wrap: anywhere; }
:is(#kcore-root, #dbch-root) .kc-vit-ext { display: inline-flex; align-items: center; justify-content: center;
  width: 46px; height: 46px; border-radius: 10px; background: var(--surface);
  border: 1px solid var(--border); font-size: 11px; font-weight: 700;
  text-transform: uppercase; color: var(--text-2); }

:is(#kcore-root, #dbch-root) .kc-vit-nav { all: unset; box-sizing: border-box; position: absolute; top: 50%;
  transform: translateY(-50%); z-index: 2; cursor: pointer; font-family: inherit;
  width: 32px; height: 32px; border-radius: 50%; background: rgba(255,255,255,.92);
  box-shadow: var(--sh-sm); color: var(--text-1); font-size: 19px; line-height: 1;
  display: flex; align-items: center; justify-content: center; transition: background .15s; }
:is(#kcore-root, #dbch-root) .kc-vit-nav:hover { background: #fff; }
:is(#kcore-root, #dbch-root) .kc-vit-nav:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:is(#kcore-root, #dbch-root) .kc-vit-prev { left: 10px; }
:is(#kcore-root, #dbch-root) .kc-vit-next { right: 10px; }

:is(#kcore-root, #dbch-root) .kc-vit-dots { display: flex; justify-content: center; gap: 6px; margin-top: 10px; }
:is(#kcore-root, #dbch-root) .kc-vit-dot { all: unset; box-sizing: border-box; cursor: pointer;
  width: 7px; height: 7px; border-radius: 50%; background: var(--border); transition: background .15s; }
:is(#kcore-root, #dbch-root) .kc-vit-dot.on { background: var(--accent); }
:is(#kcore-root, #dbch-root) .kc-vit-dot:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* ══════════ LOS MARCOS DE CONTEXTO (BD v19, fase 2) ══════════
 *
 * Envuelven el escenario de la vitrina para que la pieza se vea donde va a
 * vivir. Todo es CSS sobre la MISMA imagen: ni una librería, ni un componente
 * por red social, ni un `@import` nuevo.
 *
 * ⚠️ La llave es la FORMA (post, story, web, doc), NO la red social. Un post de
 * Instagram y uno de LinkedIn se leen igual; lo que cambiaría son iconos
 * decorativos, y guardar la red obligaría a migrar datos cada vez que nazca una
 * red nueva. Ver `KCORE_Showcase::FRAMES`.
 *
 * ⚠️ Ningún marco pinta métricas. Los iconos del post van vacíos, sin contadores
 * de "me gusta" ni de comentarios: un número inventado en la pantalla donde el
 * cliente aprueba se lee como maqueta y, en el peor caso, alguien lo toma por
 * dato. Lo que sí es real es el logo y el nombre del cliente, que salen de su
 * departamento (la misma marca que esa persona ve en su portal).
 */

:is(#kcore-root, #dbch-root) .kc-vf { display: block; }

/* La identidad, compartida por el post y la story. */
:is(#kcore-root, #dbch-root) .kc-vhead { display: flex; align-items: center; gap: 9px; padding: 11px 13px; }
:is(#kcore-root, #dbch-root) .kc-vhead-av { width: 30px; height: 30px; border-radius: 50%; overflow: hidden;
  flex: none; display: flex; align-items: center; justify-content: center;
  background: var(--ink); color: var(--lime); font-size: 12px; font-weight: 700; }
:is(#kcore-root, #dbch-root) .kc-vhead-av img { width: 100%; height: 100%; object-fit: cover; display: block; }
:is(#kcore-root, #dbch-root) .kc-vhead-n { font-size: 13px; font-weight: 600; color: var(--text-1);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* El «⋯» de la tarjeta del post. Decorativo, como los iconos de abajo. */
:is(#kcore-root, #dbch-root) .kc-vhead-m { margin-left: auto; flex: none; display: flex;
  color: var(--text-3); }
:is(#kcore-root, #dbch-root) .kc-vhead-m svg { width: 18px; height: 18px; display: block; }

/* ── Post social ──
 * `contain` y NO `cover`, al revés que la story: aquí se está APROBANDO una
 * pieza, y recortarla escondería justo lo que hay que revisar. La story sí va a
 * sangre porque ese formato es a sangre de verdad. */
:is(#kcore-root, #dbch-root) .kc-vf-post { background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); overflow: hidden; max-width: 470px; box-shadow: var(--sh-sm); }
:is(#kcore-root, #dbch-root) .kc-vf-post .kc-vit-stage { border: 0; border-radius: 0; border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border); min-height: 180px; }
:is(#kcore-root, #dbch-root) .kc-vacts { display: flex; align-items: center; gap: 15px; padding: 11px 13px; color: var(--text-2); }
:is(#kcore-root, #dbch-root) .kc-vacts svg { width: 21px; height: 21px; display: block; }

/* ── Carrusel ──
 * La misma tarjeta del post con la caja BLOQUEADA a 4/5, que es la proporción
 * con la que los canales publican un carrusel. Sin bloquearla, la tarjeta cambia
 * de alto en cada lámina y todo lo de abajo brinca: eso era lo que se veía mal.
 * `position: relative` es lo que ancla el contador. */
:is(#kcore-root, #dbch-root) .kc-vf-car { position: relative; }
:is(#kcore-root, #dbch-root) .kc-vf-car .kc-vit-stage { aspect-ratio: 4 / 5; min-height: 0; }
:is(#kcore-root, #dbch-root) .kc-vf-car .kc-vit-track { height: 100%; }
:is(#kcore-root, #dbch-root) .kc-vf-car .kc-vit-slide { height: 100%; display: flex;
  align-items: center; justify-content: center; }
:is(#kcore-root, #dbch-root) .kc-vf-car .kc-vit-slide[hidden] { display: none; }
/* `contain` dentro de la caja fija: se está APROBANDO, y recortar escondería lo
 * que hay que revisar. Las bandas cuando la lámina no encaja son información. */
:is(#kcore-root, #dbch-root) .kc-vf-car .kc-vit-slide img { max-height: 100%; max-width: 100%;
  width: auto; height: auto; }

/* ── Story / Reel ──
 * `aspect-ratio` fija la caja ANTES de que cargue la imagen, así que el marco no
 * salta. 9/16 es la proporción real del formato. */
:is(#kcore-root, #dbch-root) .kc-vf-story { display: flex; justify-content: center; }

/* ══════════ EL TELÉFONO: BISEL + PANTALLA (David, 2 sep 2026) ══════════
 *
 * David pasó una referencia de cómo quiere que se lea una story: teléfono de
 * bisel CLARO, no el bloque oscuro de antes, con su «×» arriba y su barra de
 * «Enviar mensaje» abajo.
 *
 * Son dos cajas y no una a propósito: `.kc-vph` es el bisel y `.kc-vph-s` la
 * pantalla. La cabecera, el progreso y la barra de mensaje se colocan con
 * `position: absolute` contra la PANTALLA; con una sola caja habría que restarle
 * el grosor del bisel a cada `inset` a mano y el primer olvido dejaría un
 * control montado sobre el marco. Ver la nota gemela en `marcoHtml()`.
 *
 * El `aspect-ratio` se mudó al mismo sitio que el contenido —la pantalla— para
 * que el 9/16 siga siendo el de la PIEZA y no el del teléfono con su marco.
 * Puesto en el bisel, la pieza salía achatada por el grosor del borde.
 */
:is(#kcore-root, #dbch-root) .kc-vph { position: relative; width: 100%; max-width: var(--kc-vph-w, 300px);
  /* 15px, la mitad de los 30 que tuvo primero (David, 2 sep 2026): a 233px de
     ancho, 30 era el radio de un icono, no el de un teléfono. */
  background: #FFFFFF; border-radius: 15px; padding: 3px;
  box-shadow: 0 14px 40px rgba(15,20,30,.20), 0 2px 8px rgba(15,20,30,.08);
  /* El filo interior del bisel. Sin él el blanco del marco y el blanco de una
     pieza clara se funden y el teléfono deja de leerse como un objeto. */
  border: 1px solid rgba(15,20,30,.09); }
/* ⚠️ TODO LO DE DENTRO DE LA PANTALLA VA EN `em`, NO EN PÍXELES.
 *
 * *"el nombre de usuario está muy grande, debe mantenerse en la misma
 * proporción del mockup… al igual que el «enviar mensaje» y el icono"*
 * (David, 2 sep 2026).
 *
 * Y era literal: el nombre iba a 13px fijos dentro de un teléfono de ~220px de
 * ancho. En un 1080×1920 de verdad eso es un nombre de 64px — el doble de lo que
 * pinta Instagram. La cabecera se leía como una etiqueta pegada encima de la
 * pieza, no como parte de la captura.
 *
 * La pantalla declara aquí su propia unidad —su ancho partido por 24— y todo lo
 * que va encima (nombre, avatar, «×», barra de mensaje, avión, sus rellenos y
 * los velos) se mide en `em` contra ella. Así el mockup se ve igual de
 * proporcionado a 220px que a 330, sin una sola media query.
 *
 * El 1/24 no es la proporción exacta de Instagram (sería ~1/36): a ese tamaño el
 * nombre del cliente quedaría en 6px, ilegible. Es el compromiso entre parecerse
 * y poder leerse, y es el único número de aquí que se ajustó a ojo. */
:is(#kcore-root, #dbch-root) .kc-vph-s { position: relative; aspect-ratio: 9 / 16;
  border-radius: 12px; overflow: hidden; background: #111;
  font-size: calc(var(--kc-vph-w, 300px) / 24); }
:is(#kcore-root, #dbch-root) .kc-vph .kc-vit-stage { position: absolute; inset: 0; border: 0; border-radius: 0;
  min-height: 0; background: #111; }
/* El velo de arriba. La cabecera va en blanco sobre la pieza y no se puede saber
   de antemano cómo es el arte: con una story clara —que es la mitad de ellas— el
   nombre y la «×» desaparecían. La sombra del texto sola no bastaba. */
:is(#kcore-root, #dbch-root) .kc-vph-s::before { content: ''; position: absolute; z-index: 1;
  top: 0; left: 0; right: 0; height: 9em; pointer-events: none;
  background: linear-gradient(rgba(0,0,0,.38), rgba(0,0,0,0)); }
:is(#kcore-root, #dbch-root) .kc-vph .kc-vit-slide { height: 100%; }
/* A sangre: es lo que un story hace de verdad, y verlo recortado ES el dato. */
:is(#kcore-root, #dbch-root) .kc-vph .kc-vit-slide img { width: 100%; height: 100%; max-height: none;
  object-fit: cover; }
/* La cabecera y la barra van ENCIMA del escenario, que es absoluto. */
:is(#kcore-root, #dbch-root) .kc-vph .kc-vprog, :is(#kcore-root, #dbch-root) .kc-vph .kc-vhead { position: absolute; z-index: 2; left: 0; right: 0; }
:is(#kcore-root, #dbch-root) .kc-vprog { top: 1.1em; display: flex; gap: .32em; padding: 0 1.25em; }
:is(#kcore-root, #dbch-root) .kc-vprog i { flex: 1; height: .27em; border-radius: 2px; background: rgba(255,255,255,.35); }
:is(#kcore-root, #dbch-root) .kc-vprog i.on { background: #fff; }
:is(#kcore-root, #dbch-root) .kc-vhead-c { top: 1.8em; padding: .55em 1.15em; gap: .8em; }
/* La «×» de cerrar del teléfono. Decorativa, como el «⋯» del post. */
:is(#kcore-root, #dbch-root) .kc-vhead-x { margin-left: auto; flex: none; display: flex; color: #fff; }
:is(#kcore-root, #dbch-root) .kc-vhead-x svg { width: 1.85em; height: 1.85em; display: block;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.5)); }

/* ── La barra de «Enviar mensaje» del pie de la story ──
   Decorativa y sin campo real: ver `storyBarraHtml()`. */
:is(#kcore-root, #dbch-root) .kc-vsb { position: absolute; z-index: 2; left: 1.15em; right: 1.15em; bottom: 1.15em;
  display: flex; align-items: center; gap: 1em; color: #fff; }
:is(#kcore-root, #dbch-root) .kc-vsb-i { flex: 1; min-width: 0; border: .105em solid rgba(255,255,255,.65);
  border-radius: 2em; padding: .72em 1.3em; font-size: 1em; color: rgba(255,255,255,.92);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
:is(#kcore-root, #dbch-root) .kc-vsb svg { width: 1.85em; height: 1.85em; flex: none; display: block;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,.5)); }
/* El velo de abajo, por lo mismo que el de arriba: la barra es blanca sobre la
   pieza y una story clara la deja invisible. */
:is(#kcore-root, #dbch-root) .kc-vph-s::after { content: ''; position: absolute; z-index: 1;
  bottom: 0; left: 0; right: 0; height: 8em; pointer-events: none;
  background: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,.34)); }
/* El reel no lleva barra de mensaje, así que tampoco necesita su velo. */
:is(#kcore-root, #dbch-root) .kc-vf-reel .kc-vph-s::after { display: none; }
:is(#kcore-root, #dbch-root) .kc-vhead-c .kc-vhead-av { width: 2.5em; height: 2.5em; font-size: 1em;
  box-shadow: 0 0 0 .16em rgba(255,255,255,.85); }
/* Texto blanco sobre la pieza, con sombra: una imagen clara lo dejaría ilegible
 * y no se puede saber de antemano cómo es el arte. */
:is(#kcore-root, #dbch-root) .kc-vhead-c .kc-vhead-n { color: #fff; font-size: 1.05em; font-weight: 600;
  text-shadow: 0 1px 4px rgba(0,0,0,.55); }
:is(#kcore-root, #dbch-root) .kc-vf-story .kc-vit-nav { background: rgba(255,255,255,.85); }

/* ── Navegador ──
 * El toggle a móvil solo estrecha el marco; NO cambia la imagen, porque la
 * pieza es la que es. Lo que muestra es cuánto respira el diseño a ese ancho. */
/* ⚠️ `column`, no la fila de antes. El marco web era un flex en FILA para
   centrar el navegador, y al meterle encima las pestañas de vista (mockup D)
   estas se pusieron a su LADO en vez de arriba: media tarjeta vacía a la
   izquierda y el navegador aplastado a la derecha. Con la columna, las pestañas
   quedan encima y `align-items: center` sigue centrando el navegador. */
:is(#kcore-root, #dbch-root) .kc-vf-web { display: flex; flex-direction: column; align-items: center; }
:is(#kcore-root, #dbch-root) .kc-vf-web > .kc-vwtabs { align-self: stretch; }

/* ══════════ EL APARATO: monitor, tablet o teléfono (David, 2 sep 2026) ══════════
 *
 * *"quiero que se vea como en la pantalla de un monitor al ser desktop, en un
 * celular cuando sea mobile y en una tablet cuando sea tablet"*.
 *
 * `.kc-vdev` es el CUERPO del aparato y `.kc-vbr` la PANTALLA —la barra de
 * dirección más la captura—, que es lo que ya existía. Cuál se pinta lo decide
 * la vista guardada en la lámina, no un conmutador.
 *
 * ⚠️ La captura NO se encoge para caber: sigue yendo a ancho completo con el
 * escenario haciendo de viewport y su propio scroll. Esa decisión es de un
 * defecto real —una captura de página entera mide 1400×6000, y al escalarla
 * quedaba una tira de 110px dentro de un navegador enorme— y el aparato no la
 * toca. Lo que cambia con el alto de la ventana es cuánto viewport se ve, que es
 * exactamente lo que cambia en un monitor de verdad.
 */
:is(#kcore-root, #dbch-root) .kc-vdev { position: relative; width: 100%;
  display: flex; flex-direction: column; }

/* ⚠️ EL BISEL ES GRIS, NO NEGRO. Empezó en `#17190F` —casi el negro del mate— y
   el monitor desaparecía: sobre un fondo negro, un aparato negro no es un
   aparato, es un agujero. El filo claro de arriba remata lo mismo que en la
   tarjeta del post. */

/* ══ LA PANTALLA DEL MONITOR ES 16:9 (David, 2 sep 2026) ══
 *
 * Antes tomaba el ANCHO de la columna y el ALTO del área, así que su proporción
 * salía de dos números que no tienen nada que ver entre sí: en una ficha ancha
 * quedaba una franja apaisadísima y la captura se veía cortada. Un monitor tiene
 * una proporción, y es esa.
 *
 * Se fija el ALTO —lo que deja el área tras las pestañas, el bisel y el pie— y
 * el ancho lo deriva `aspect-ratio`. Por eso el aparato pasa a medir su
 * contenido (`fit-content`) en vez del 100%: el bisel tiene que abrazar la
 * pantalla, no la columna.
 *
 * `max-width: 100%` es la salida por si la ficha es muy estrecha para ese ancho:
 * ahí manda el ancho y la pantalla se queda más baja de lo calculado, que es
 * preferible a desbordar. */
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vdev { width: fit-content; max-width: 100%; }
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vbr {
  aspect-ratio: 16 / 9; width: auto; max-width: 100%;
  height: calc(var(--kc-vesc-c, 500px) - 55px - 32px - 34px); }
/* La captura se queda con lo que sobra tras la barra de dirección, y sigue
   scrolleando dentro: la página no se encoge para caber, que es la decisión de
   siempre. */
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vbr .kc-vit-stage {
  height: calc(100% - 44px); max-height: none; }

/* ── MONITOR ── Bisel más grueso abajo, y pie. */
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vdev {
  background: #2A2E22; border: 1px solid rgba(255,255,255,.10);
  border-radius: 12px; padding: 12px 12px 20px;
  box-shadow: 0 20px 50px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.06);
  margin-bottom: 34px; }
/* El cuello y la peana. Dos pseudoelementos y ningún marcado extra: el aparato
   es decorativo entero, así que no debe existir para nadie más que para el ojo. */
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vdev::before,
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vdev::after {
  content: ''; position: absolute; left: 50%; transform: translateX(-50%);
  background: #2A2E22; pointer-events: none; }
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vdev::before {
  bottom: -20px; width: 76px; height: 22px;
  background: linear-gradient(#232619, #2E3225); }
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vdev::after {
  bottom: -28px; width: 190px; height: 9px; border-radius: 5px;
  box-shadow: 0 8px 20px rgba(0,0,0,.5); }

/* ── TABLET ── Bisel parejo y esquinas más redondas; sin pie. */
:is(#kcore-root, #dbch-root) .kc-vf-tablet .kc-vdev {
  background: #2A2E22; border: 1px solid rgba(255,255,255,.10);
  border-radius: 20px; padding: 14px;
  box-shadow: 0 20px 50px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.06); }

/* ── TELÉFONO ── El mismo lenguaje que el marco de la story: bisel fino y radio
   corto. No se reusa `.kc-vph` porque aquel fuerza 9/16 en su pantalla, y aquí
   la captura manda su propio alto. */
:is(#kcore-root, #dbch-root) .kc-vf-mobile .kc-vdev {
  background: #2A2E22; border: 1px solid rgba(255,255,255,.10);
  border-radius: 18px; padding: 6px;
  box-shadow: 0 18px 44px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.06); }
/* La muesca. Decorativa, como todo el aparato. */
:is(#kcore-root, #dbch-root) .kc-vf-mobile .kc-vdev::after {
  content: ''; position: absolute; z-index: 2; top: 6px; left: 50%; transform: translateX(-50%);
  width: 74px; height: 15px; border-radius: 0 0 9px 9px; background: #2A2E22;
  pointer-events: none; }

/* ══ EL ANCHO DE CADA APARATO SALE DE SU PROPORCIÓN, no de un número fijo ══
 *
 * El monitor se queda con el ancho de la columna: es apaisado y la pantalla se
 * lo lleva entero, como en un escritorio.
 *
 * La tablet y el teléfono son VERTICALES, y ahí un ancho fijo no vale: el alto
 * lo manda el área, así que un tope de 340px dejaba un «teléfono» de 340 × 366
 * —tan ancho como alto—, que no se lee como un teléfono sino como una tablet
 * pequeña. El ancho se despeja del alto disponible con la proporción del
 * aparato: 3/4 la tablet, 9/19.5 el teléfono, que es la de un móvil de hoy.
 *
 * El sumando final es el bisel, igual que en el teléfono de la story: es lo que
 * no es pantalla y por tanto no entra en la proporción. */
:is(#kcore-root, #dbch-root) .kc-vf-tablet .kc-vdev {
  max-width: calc((var(--kc-vesc-c, 500px) - 55px - 28px) * .75 + 28px); }
:is(#kcore-root, #dbch-root) .kc-vf-mobile .kc-vdev {
  max-width: calc((var(--kc-vesc-c, 500px) - 55px - 12px) * .4615 + 12px); }

/* Las pestañas Desktop / Tablet / Mobile van sobre el mate, así que se repintan
   en claro. Nacieron sobre blanco y en tinta; sobre negro, la activa se leía
   igual de apagada que las otras dos. Mismo caso que la cabecera de la tira. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vwtabs { border-bottom-color: rgba(244,245,238,.16); }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vwtab { color: rgba(244,245,238,.55); }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vwtab:hover { color: rgba(244,245,238,.85); }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vwtab.on { color: #0D1117; background: var(--lime, #CBF904); }
/* La PANTALLA. Dentro del bisel ya no necesita borde ni sombra propios —los
   pone el aparato— y el radio se acorta para que quede concéntrico con él. */
:is(#kcore-root, #dbch-root) .kc-vbr { width: 100%; background: var(--surface); border: 0;
  border-radius: 5px; overflow: hidden; box-shadow: none; transition: max-width .22s ease; }
:is(#kcore-root, #dbch-root) .kc-vf-tablet .kc-vbr { border-radius: 9px; }
:is(#kcore-root, #dbch-root) .kc-vf-mobile .kc-vbr { border-radius: 13px; }
/* `.kc-vf-mob` era del conmutador viejo. Lo sustituyen `.kc-vf-mobile` y
   `.kc-vf-tablet`, que salen de la vista GUARDADA y no de un booleano de vista. */
:is(#kcore-root, #dbch-root) .kc-vurl { display: flex; align-items: center; gap: 6px; padding: 9px 12px;
  background: var(--surface-3); border-bottom: 1px solid var(--border); }
/* ⚠️ El candado LLEVA TAMAÑO, y no es un detalle: un `<svg>` sin `width` toma
   el tamaño de su caja, y dentro de un flex sin restricciones eso fue un
   candado de 700px que empujó la captura fuera del marco. Cualquier icono nuevo
   aquí necesita lo mismo — los otros bloques lo declaran junto al icono, este se
   quedó sin regla al sustituir los tres puntitos del navegador falso. */
:is(#kcore-root, #dbch-root) .kc-vurl svg { width: 13px; height: 13px; display: block; flex: none;
  color: var(--text-3); }
:is(#kcore-root, #dbch-root) .kc-vurl-b { flex: 1; background: var(--surface); border-radius: 20px;
  padding: 4px 12px; font-size: 11.5px; color: var(--text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* ⚠️ La página va a ANCHO COMPLETO y el marco la RECORTA, no la encoge.
 *
 * Era el defecto que David reportó: una captura de página completa mide 1400 de
 * ancho por 6000 de alto, y con el `max-height: 460px` general se escalaba a 460
 * de alto, o sea a ~110px de ancho, quedando una tira diminuta en medio de un
 * marco de navegador enorme. Un navegador de verdad no encoge la página: la
 * muestra a su ancho y deja el resto fuera de la ventana.
 *
 * Por eso aquí la imagen ocupa el 100% del ancho, se ancla ARRIBA (que es donde
 * empieza una página) y el escenario se convierte en el viewport, con su propio
 * scroll. Así el toggle escritorio/móvil además dice la verdad: al estrechar, se
 * ve cuánto respira el diseño en esa medida.
 *
 * `overflow-x: hidden` explícito: declarar `overflow-y` enciende el otro eje en
 * `auto` por especificación y aparece una barra horizontal parásita (§5.c.2). */
/* ⚠️ EL ALTO DEL VIEWPORT SALE DEL ÁREA, no de un 460 fijo. Es la misma regla
   que el resto de marcos: al bloque le toca medir lo mismo en los siete. Se le
   descuenta lo que no es pantalla —las pestañas y su hueco, el bisel, la barra
   de dirección y, en el monitor, el pie—, y lo que queda es cuánta página se ve.
   Que ese número cambie con la ventana es correcto: en un monitor de verdad
   pasa igual.

   ⚠️ Los descuentos están MEDIDOS en el navegador, no estimados. La primera
   versión los puso a ojo (46 de pestañas y 35 de barra) y se quedó 18px corta:
   el bloque salía en 491 contra los 473 de los demás. Son 55 y 44. Si se toca
   el relleno de las pestañas o de la barra de dirección, hay que volver a
   medirlos aquí. */
:is(#kcore-root, #dbch-root) .kc-vbr .kc-vit-stage { border: 0; border-radius: 0; min-height: 0;
  display: block; overflow-y: auto; overflow-x: hidden;
  /* Los 2px de más son medidos: el aparato redondea su bisel y el bloque del web
     salía 2px por debajo de los otros seis. Se le restan aquí para que los siete
     terminen a la misma altura. */
  max-height: calc(var(--kc-vesc-c, 500px) - 55px - 28px - 44px - 2px); }
:is(#kcore-root, #dbch-root) .kc-vf-desktop .kc-vbr .kc-vit-stage {
  max-height: calc(var(--kc-vesc-c, 500px) - 55px - 32px - 44px - 34px); }
:is(#kcore-root, #dbch-root) .kc-vf-mobile .kc-vbr .kc-vit-stage {
  max-height: calc(var(--kc-vesc-c, 500px) - 55px - 12px - 44px); }
:is(#kcore-root, #dbch-root) .kc-vbr .kc-vit-slide img { width: 100%; height: auto;
  max-width: none; max-height: none; margin: 0; }
/* Las flechas del carrusel son `position:absolute` contra el escenario; con el
 * escenario scrolleando se irían con el contenido. Se quedan fijas al marco. */
:is(#kcore-root, #dbch-root) .kc-vf-web { position: relative; }
:is(#kcore-root, #dbch-root) .kc-vf-web .kc-vit-nav { position: absolute; }

/* ── El PDF, con el visor NATIVO del navegador ──
 * Páginas de verdad, zoom y navegación, sin una sola librería. Sustituye a la
 * tarjeta gris que solo repetía el nombre del archivo. La animación de pasar la
 * hoja es otra cosa y necesitaría pdf.js + una librería de flip (npm, contra
 * §6): esto da las páginas, no el gesto. */
/* ⚠️ El alto fijo es SOLO para el visor de pdf.js (un iframe necesita alto
 * explícito o colapsa). El LIBRO no lo quiere: su alto sale de la proporción
 * real del documento, y forzarlo aquí lo recortaría o le dejaría banda. Por eso
 * la regla se acota con `:has()` en vez de aplicarse a todo `.kc-vpdf-s`. */
:is(#kcore-root, #dbch-root) .kc-vpdf-s { display: block; width: 100%; height: 620px; }
:is(#kcore-root, #dbch-root) .kc-vpdf-s:has(.kc-flip) { height: auto; }
:is(#kcore-root, #dbch-root) .kc-vpdf-s[hidden] { display: none; }
:is(#kcore-root, #dbch-root) .kc-vpdf { width: 100%; height: 100%; border: 0; display: block; background: #fff; }
/* En el marco Documento el PDF ocupa la hoja entera, y ahí sí se le da el alto
 * de una carta para que se lea como un documento y no como una ventanita. */
:is(#kcore-root, #dbch-root) .kc-vdoc .kc-vpdf-s { height: 780px; }
:is(#kcore-root, #dbch-root) .kc-vdoc .kc-vpdf-s:has(.kc-flip) { height: auto; }

/* ⚠️ El marco Documento se APAGA cuando dentro hay un libro.
 *
 * `.kc-vdoc` es una hoja: 620px de ancho, borde y sombra. Eso servía cuando el
 * PDF se enseñaba como una página suelta. Un libro de DOBLE página dentro de una
 * hoja de 620px queda apretado a la mitad de su tamaño (medido: 490px de ancho
 * en una columna que da 777), y encima con una hoja dibujada alrededor de otra.
 *
 * El libro YA es el documento: trae su lomo, sus sombras y sus proporciones. */
:is(#kcore-root, #dbch-root) .kc-vf-doc:has(.kc-flip) .kc-vdoc { max-width: none; border: 0;
  border-radius: 0; box-shadow: none; background: transparent; }
:is(#kcore-root, #dbch-root) .kc-vf-doc:has(.kc-flip) { padding: 0; }
:is(#kcore-root, #dbch-root) .kc-vf-doc .kc-vit-stage { min-height: 0; }

/* ── Documento ── */
:is(#kcore-root, #dbch-root) .kc-vf-doc { display: flex; justify-content: center; padding: 6px 0; }
:is(#kcore-root, #dbch-root) .kc-vdoc { width: 100%; max-width: 620px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 4px; overflow: hidden;
  box-shadow: 0 8px 26px rgba(0,0,0,.13); }
:is(#kcore-root, #dbch-root) .kc-vdoc .kc-vit-stage { border: 0; border-radius: 0; background: var(--surface); min-height: 260px; }

/* ══ Las VISTAS de los marcos (11 ago 2026, mockups A–D) ══
   Aquí vivía `.kc-vtog`, el conmutador Escritorio/Móvil de la fase 2, que
   encogía la MISMA imagen porque hasta la v22 no había una captura por
   dispositivo. Lo reemplazan `.kc-vwtabs`, que enseñan capturas de verdad. */

/* El reparto de dos columnas: la tira o el copy al lado del escenario. */
:is(#kcore-root, #dbch-root) .kc-vlay { display: flex; align-items: flex-start; gap: 16px; }
:is(#kcore-root, #dbch-root) .kc-vlay > .kc-vf { flex: 1; min-width: 0; }
/* Con el panel de copy el marco NO crece: un post cuadrado estirado a 700px
   deja de parecerse a lo que se va a publicar, que es el punto del marco. */
:is(#kcore-root, #dbch-root) .kc-vlay-copy > .kc-vf { flex: 0 1 380px; }
/* ══════════ QUE LA VITRINA QUEPA DE UN VISTAZO (David, 2 sep 2026) ══════════
 *
 * *"quisiera que al primer momento de verlo se viera completa la vitrina, que no
 * se viera cortada o que toque hacer scroll down (…) entiendo que tendríamos que
 * sacrificar un poco el tamaño"*.
 *
 * ── Por qué se toca el ANCHO para arreglar un problema de ALTO ──
 *
 * Porque en el carrusel la caja está bloqueada a 4/5 (ver `.kc-vf-car`), y esa
 * proporción es lo que impide que la tarjeta cambie de alto en cada lámina. Si
 * el alto se recortara con un `max-height`, la proporción cedería y la lámina
 * saldría con bandas a los lados: un marco dentro de otro marco. Con el ancho,
 * la tarjeta se hace más pequeña ENTERA y sigue pareciéndose a lo que se va a
 * publicar, que es el único motivo por el que el marco existe.
 *
 * ── El número ──
 *
 * `--kc-vit-sobra` es todo lo que NO es la lámina y aun así ocupa alto en la
 * ventana: el hueco del cajón (14) + su cabecera (~148) + el relleno de la
 * columna (24) + la franja de propiedades (~97) + el espacio entre bloques (26)
 * + la cabecera de la vitrina (~40) + el relleno del escenario (26 arriba y 26
 * abajo) + el final de la columna (34) + la cabecera y los iconos de la propia
 * tarjeta (~95). Y el `* .8` convierte el alto que queda en ancho, que es lo que
 * significa 4/5.
 *
 * ⚠️ ES UNA ESTIMACIÓN, y por eso está en UNA variable y no repartida en tres
 * cuentas. Dos de esos sumandos no son fijos —la cabecera crece si el título va
 * a dos líneas, y la franja de propiedades crece si se parte en dos filas—, así
 * que si en alguna ficha sigue quedando cortada, lo que se sube es este número y
 * nada más. Cuánto: los píxeles que se vean de más, multiplicados por 1.
 *
 * El `min` es el tope de siempre —una pantalla alta no la agranda más de 420—, y
 * el `max` es el suelo: por debajo de 240 la pieza deja de poder juzgarse, y
 * entonces es mejor que vuelva a haber scroll que enseñar un sello.
 *
 * El par `vh` / `dvh` es a propósito y en ese orden: donde no se entienda `dvh`
 * la declaración entera se descarta y queda la de `vh`, que es la de siempre.
 */
/* ⚠️ LOS TOKENS DE TAMAÑO VIVEN EN `.kc-vit`, EL BLOQUE ENTERO, no en el mate.
   Nacieron en `.kc-vesc` porque solo los usaba el post. Desde el 2 sep 2026 los
   necesita también el flipbook del PDF, que NO lleva mate de color —su fondo es
   uno de los prediseñados, decisión de David— y por tanto no es descendiente de
   `.kc-vesc`. Declarados aquí los heredan los dos, y la regla del tamaño sigue
   siendo UNA. */
:is(#kcore-root, #dbch-root) .kc-vit {
  /* ⚠️ ES EL ÚNICO NÚMERO QUE SE TOCA si una vitrina sigue quedando cortada.
     `--kc-vit-hueco` es lo que ocupa LA FICHA por encima y por debajo del mate:
     el hueco del cajón (14) + su cabecera (~148) + el relleno de la columna (24)
     + la franja de propiedades (~97) + la separación entre bloques (26) + la
     cabecera de la vitrina (~40) + el relleno del escenario (26 y 26) + el final
     de la columna (34). Medido contra la Mesa real: da 455.

     Dos de esos sumandos NO son fijos —la cabecera crece si el título va a dos
     líneas, la franja si se parte en dos filas—, así que si se pasa, se suben
     aquí los píxeles que se vean de más y nada más.

     Cada marco le suma lo SUYO, que es lo que no es la pieza dentro de su
     propio mockup, y convierte el alto que sobra en ancho con la proporción del
     formato. Son las dos únicas cuentas de todo esto:

       tarjeta   + 95 de cabecera e iconos del post   ×  .8     (4/5)
       teléfono  + 16 de bisel + 17 de los puntos     ×  .5625  (9/16)  */
  --kc-vit-hueco: 455px;
  /* ⚠️ EN LA MESA la franja de propiedades se fue a la cabecera cuando la pieza
     tiene vitrina (2 sep 2026), así que ya no ocupa alto en la columna: 97 de la
     franja y 26 de su separación. El portal NO ha hecho ese movimiento —su
     detalle es otro— y por eso el número se declara por RAÍZ y no aquí: es la
     misma regla del §3.b, un token que las dos superficies no comparten se
     escribe en cada una. */
  /* El ancho de la TARJETA (post y carrusel), UNA sola vez: lo usan la propia
     tarjeta y la columna del copy, y si se repitiera la cuenta se
     desincronizarían al primer retoque. */
  /* El techo subió de 420 a 520 el 2 sep 2026: con la franja fuera de la columna
     hay alto de sobra en un monitor alto, y 420 se había convertido en el que
     mandaba en vez de en un tope. El suelo de 240 se queda: por debajo la pieza
     deja de poder juzgarse. */
  --kc-vit-w:  max(240px, min(520px, calc((100vh - var(--kc-vit-hueco) - 95px) * .8)));
}
/* 455 menos los 123 que dejó de ocupar la franja (97 ella y 26 su separación),
   MÁS 35 de holgura. Los 35 no son un redondeo: descontar los 123 enteros dejaba
   el bloque cabiendo por 5px, y con 5px cualquier título a dos líneas lo vuelve
   a cortar. El 455 original también los llevaba —de ahí venían los ~40px de
   sobra que se medían antes— y perderlos era cambiar un problema por otro. */
/* 392 y no 367: al devolverle el borde y el relleno a la franja (mockup de
   David) la cabecera creció 25px medidos, y con 367 la holgura se quedaba en 15.
   Estos 25 son el precio de que la franja se lea como un grupo, y salen del alto
   de la pieza, no de la holgura. */
/* 356 = 392 menos los 36 que dejó de medir la cabecera al subir la píldora de
   estado a la línea del cliente: se fue una fila entera (~44px con su margen) y
   la línea del breadcrumb creció 8 al compartir renglón con la píldora.
   ⚠️ Esos 36 NO salen del banco: la cabecera reproducida allí nunca tuvo la fila
   del estado, así que allí este cambio no mueve nada. Salen de medir las piezas
   que sí existían en la Mesa. Si en alguna ficha se queda corto, es este número. */
/* 334: los 356 de antes menos 22 de holgura que ya no hacen falta.
 *
 * El margen de seguridad existía porque dos sumandos de la cabecera podían
 * crecer sin avisar: el título si se partía en dos líneas, y la franja de
 * propiedades si se envolvía. Ninguno de los dos pasa ya —el título va clavado a
 * una línea con recorte y la franja vive arriba con altura estable—, así que
 * reservar 38px «por si acaso» era regalarle a la ficha alto que le toca a la
 * pieza. Medido, quedan ~16px, que es lo que separa el mate del borde de la
 * columna.
 *
 * ⚠️ Y 298 desde el 2 sep 2026, no 334: los 36 que faltaban salían de restar el
 * `padding-bottom: 34px` de la columna como si fuera un TOPE, y no lo es. Ese
 * relleno se ve al final de todo el scroll, no entre el mate y el borde del
 * cajón; debajo de la vitrina lo que viene es la sección siguiente, que se corta
 * porque hay scroll. El límite de verdad es el borde inferior del cajón.
 *
 * ⚠️ Es el número más apretado de toda la vitrina. A 298 el mate tocaba el borde
 * exacto —medido, 0px— y se quedó en 308 para dejarle ~10 de respiro: pegado al
 * canto se lee como un corte, no como un final. Esos 10 también absorben que los
 * 38px que se descuentan por arriba son una medida de la ficha real, no una
 * constante garantizada.
 *
 * Si alguna ficha se sale por abajo, sube AQUÍ, y de 6 en 6: cada 6px de hueco
 * son ~5 de pieza. */
/* ⚠️ UN SOLO NÚMERO PARA TODO EL ESCRITORIO, y ya no hay media query que lo
   cambie por anchura. Es la consecuencia directa de que la cabecera dejara de
   cambiar de forma: David la rediseñó en dos filas fijas el 4 sep 2026 (ver la
   nota larga en `kcore-mesa.css`), así que mide **191px en cualquier ventana de
   portátil**, de 1181 para arriba. 62 más que los 129 de la cabecera de una
   fila, y por eso 308 + 62 = 370.

   Las dos media queries que vivían aquí —1400 primero, luego 1599 y 1279— salían
   de intentos anteriores en los que la cabecera se reacomodaba al estrecharse.
   Ninguna se sostiene ya, y dejarlas puestas sería reservar alto por un cambio
   de forma que no ocurre. */
#kcore-root .kc-vit { --kc-vit-hueco: 370px; }
/* Por debajo de 1180 el cajón sí cambia de forma por su cuenta —la conversación
   se apila— y la franja se parte en dos filas: 258px medidos, 67 más. */
@media (max-width: 1180px) { #kcore-root .kc-vit { --kc-vit-hueco: 437px; } }
@supports (height: 100dvh) {
  :is(#kcore-root, #dbch-root) .kc-vit {
    --kc-vit-w: max(240px, min(520px, calc((100dvh - var(--kc-vit-hueco) - 95px) * .8)));
  }
}

/* ══════ EL MATE MIDE SIEMPRE LO MISMO (David, 2 sep 2026) ══════
 *
 * *"el área de la vitrina debe mantenerse siempre fija, del mismo tamaño que
 * tenemos ya aprobado en post y carrusel"*.
 *
 * Y tiene razón de sobra: el mate se ajustaba a su contenido, así que cambiar de
 * publicación en la parrilla —de un carrusel a una story— hacía que el bloque
 * entero pegara un brinco de alto. La vitrina es UN sitio, no un sitio distinto
 * por formato.
 *
 * La medida de referencia es la que ya se aprobó: la tarjeta del post. Todo lo
 * demás sale de aquí, y por eso hay UNA sola cuenta y no una por marco:
 *
 *   --kc-vit-w    ancho de la tarjeta                 ← lo manda la ventana
 *   --kc-vesc-c   alto del CONTENIDO del mate          = tarjeta entera
 *   --kc-vph-w    ancho del teléfono                   ← derivado de lo anterior
 *
 * El teléfono ya no tiene su propia cuenta contra la ventana: se le da el alto
 * que queda —el del contenido menos su fila de puntos— y de ahí sale su ancho a
 * 9/16, que es la proporción de un 1080×1920 real. Así el teléfono es lo más
 * grande que cabe en el área aprobada, que es lo otro que pedía David: *"siento
 * que se ve más pequeño"*. Antes se calculaba aparte y salía más chico que el
 * hueco que tenía.
 */
:is(#kcore-root, #dbch-root) .kc-vit {
  /* La tarjeta del post: su caja 4/5 más la cabecera y la fila de iconos (95). */
  --kc-vesc-c: calc(var(--kc-vit-w) * 1.25 + 95px);
  /* Lo que ocupan los puntos debajo del teléfono (12 de margen + 7 de punto). */
  --kc-vph-p: 19px;
  /* El ancho de la tira de miniaturas. La mitad de los 108px que tuvo siempre
     (David, 2 sep 2026). Vive aquí y no en `.kc-vlat` porque la rejilla de la
     story lo necesita DOS veces: para la columna de la tira y para la columna
     vacía que la equilibra al otro lado. */
  --kc-vlat-w: 54px;
  /* Y el bisel, que no es pantalla: 3 de relleno y 1 de borde a cada lado.
     ⚠️ Si se cambia el relleno de `.kc-vph`, ESTE número cambia con él: es lo
     que se le descuenta al alto para despejar el ancho del teléfono. Con los dos
     desincronizados el teléfono deja de llenar su área y nada avisa. */
  --kc-vph-b: 8px;
  /* Despejar el ancho del alto: alto_total = (ancho - bisel) * 16/9 + bisel, o
     sea ancho = (alto_total - bisel) * 9/16 + bisel. El `+ bisel` del final se
     me olvidó en la primera versión y el teléfono salía 16px estrecho: dejaba
     28px del mate sin usar, que es justo el *"se ve más pequeño"* de David. */
  --kc-vph-w: calc((var(--kc-vesc-c) - var(--kc-vph-p) - var(--kc-vph-b)) * .5625
              + var(--kc-vph-b));
  /* El alto del ÁREA, mate incluido: contenido + su relleno de arriba y abajo.
     Es lo que tienen que medir todos los marcos, lleven mate o no. */
  --kc-vesc-h: calc(var(--kc-vesc-c) + 52px);
}
:is(#kcore-root, #dbch-root) .kc-vesc {
  min-height: var(--kc-vesc-h);
  display: flex; flex-direction: column; justify-content: center;
}
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay-copy > .kc-vf {
  flex-grow: 0; flex-shrink: 1; flex-basis: var(--kc-vit-w);
}
/* El REEL también va con copy al lado, pero su marco es el TELÉFONO y no la
   tarjeta: sin esto se quedaba con el ancho de la tarjeta (265) y el teléfono
   (218) flotaba centrado dentro, con 23px muertos a cada lado. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay-copy > .kc-vf-story { flex-basis: var(--kc-vph-w); }

/* ══════════ MITAD Y MITAD (David, 2 sep 2026) ══════════
 *
 * *"siento que no está bien distribuido el espacio de la vitrina (…) hay un
 * espacio después del copy al lado derecho, me imagino que esté mejor
 * distribuido, mitad para el mockup, mitad para el texto"*.
 *
 * El hueco de la derecha salía de que el escenario se estiraba a TODA la columna
 * de la ficha mientras la tarjeta y el texto ocupaban lo suyo y ya. La solución
 * no es ensanchar la tarjeta —no puede, su alto está atado a la ventana— sino
 * que el mate mida exactamente lo que mide la pareja: dos columnas iguales.
 *
 * De paso arregla el post SIN copy, donde ese vacío era el 70 % del mate.
 *
 * El copy no se hace más alto por ser más estrecho: `.kc-vcopy` va estirado a la
 * altura de la tarjeta y su cuerpo scrollea por dentro, así que un copy largo
 * NO empuja el escenario por debajo de la ventana. Ese es el otro motivo por el
 * que se puede repartir así sin deshacer lo de que quepa de un vistazo.
 *
 * ── Se estrecha la PAREJA, no el mate ──
 *
 * Lo primero que probé fue ponerle un `max-width` al escenario entero. Reparte
 * igual de bien, pero el mate dejaba de llegar al borde de la columna y se veía
 * desalineado de su propia cabecera —«Vitrina … Editar vitrina»—, que sí llega.
 * Puestos uno al lado del otro no había duda. Así que el mate sigue a todo el
 * ancho y lo que se acota y se centra es la fila de dentro.
 *
 * ── Y el copy es MÁS ANCHO que la tarjeta, no igual ──
 *
 * La primera versión repartía dos columnas exactamente iguales. Con un copy real
 * —el de una publicación de verdad, no el de prueba— eso lo dejaba en ~38
 * caracteres por línea, se hacía más alto que la tarjeta y **el escenario lo
 * recortaba en silencio**: el `overflow: clip` que evita el arrastre del mate se
 * comía las últimas líneas y los hashtags. David lo vio en la Mesa antes que yo.
 *
 * Así que la pareja se equilibra por LEGIBILIDAD, no por simetría: la tarjeta
 * mide lo que le deja el alto de la ventana y el copy se queda con una medida de
 * lectura cómoda, con los márgenes laterales del mate más estrechos para dejarle
 * sitio. Sigue centrada, que era el punto.
 *
 * ⚠️ Y el recorte silencioso se tapó en su origen, no solo con más ancho: ver
 * `min-height: 0` en `.kc-vcopy-b`. Un copy lo bastante largo siempre puede
 * pasarse, y entonces tiene que scrollear por dentro — nunca desaparecer.
 */
/* ⚠️ ALTO DEFINIDO, no `min-height`, y esta es la línea que hace que el área sea
 * fija DE VERDAD.
 *
 * Con solo `min-height` en el mate el área era fija mientras la tarjeta fuera lo
 * más alto. En una ventana baja la tarjeta se encoge, y entonces un copy largo
 * pasaba a ser lo más alto de la fila y estiraba el mate con él: medido, 815px
 * contra los 447 de un post corto. O sea el mismo brinco de antes, con otra
 * causa.
 *
 * Y no bastaba el `min-height: 0` del panel de copy: para que su `overflow`
 * decida algo, su contenedor necesita un alto DEFINIDO contra el que decidir.
 * `align-self: stretch` dentro de una fila de alto automático no lo es — la fila
 * mide lo que mida el más alto, que era el propio copy. Circular.
 *
 * Con la fila clavada al alto del contenido del mate, el copy scrollea y el área
 * mide lo mismo lleve tres líneas o dos mil doscientos caracteres. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay { justify-content: center;
  height: var(--kc-vesc-c); }

/* ── La story y el reel dentro del mate (David, 2 sep 2026) ──
 * El teléfono no se estira: la fila es «tira + teléfono» y esa PAREJA es la que
 * se centra en el mate, igual que «tarjeta + copy» en el post. Sin el
 * `flex: 0 0 auto` el marco se queda con todo el ancho sobrante y el
 * `justify-content` del mate no tiene nada que centrar — el teléfono acaba
 * centrado respecto al hueco a la derecha de la tira, no respecto al mate.
 *
 * ⚠️ Y va en REJILLA, no en flex (David, 2 sep 2026): *"las miniaturas
 * alineadas a la izquierda de la vitrina y el mockup de la historia que se está
 * viendo al centro de la vitrina"*.
 *
 * Son dos alineaciones distintas a la vez —una pegada al borde y otra al centro
 * del mate— y con una fila flex no salen: centrar la pareja deja la tira flotando
 * dentro, y pegar la tira a la izquierda deja el teléfono centrado respecto al
 * hueco que sobra, no respecto al mate. Aquí lo resuelve una tercera columna
 * VACÍA del mismo ancho que la tira: la del medio queda centrada de verdad y la
 * tira se queda en el borde. Sin posicionamiento absoluto y sin números mágicos.
 */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay-ph {
  display: grid; align-items: start; gap: 14px;
  grid-template-columns: var(--kc-vlat-w) 1fr var(--kc-vlat-w); }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay-ph > .kc-vf {
  grid-column: 2; justify-self: center; width: var(--kc-vph-w, 300px); }
/* Los puntos de la story van dentro del mate, DEBAJO del teléfono.
   `.kc-vf-story` es un flex en fila —así centraba el teléfono—, así que al
   meterle los puntos dentro se colocaron como tercer elemento A SU LADO y
   además le robaron 20px de ancho al teléfono. En columna caen donde tienen que
   caer y el teléfono recupera su ancho entero. */
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vf-story { flex-direction: column; align-items: center; }
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vit-dots { margin-top: 12px; }

/* ── «SIN MARCO» (`libre`): la story a la que se le quitó el teléfono ──
 * David, 4 sep 2026: *"que se vea como los stories pero lógicamente sin el marco
 * y sin agregarle algún mockup, solo la imagen con una sombra debajo del tamaño
 * proporcional en el que viene la imagen, si son varias se ven las miniaturas al
 * lado izquierdo como stories, con el mismo fondo"*.
 *
 * Reusa entero el andamio de la story —`.kc-vesc-ph` y `.kc-vlay-ph`, o sea el
 * mate, la rejilla de tres columnas y la tira encogida— y lo único que hace este
 * bloque es DESHACER lo que el teléfono imponía. Por eso son overrides y no un
 * marco nuevo: si mañana cambia el ancho de la tira o el alto del mate, `libre`
 * se mueve con la story sin que nadie se acuerde de él.
 *
 * Son tres cosas, y cada una anula algo concreto:
 *
 *  1. EL ANCHO. `.kc-vlay-ph > .kc-vf` clava `--kc-vph-w`, que es el ancho del
 *     teléfono, y ese número solo tiene sentido con un 9/16 dentro. Aquí la
 *     columna del medio se ocupa entera (`justify-self: stretch`) y quien decide
 *     el ancho es la imagen. Con `justify-self: center` + `width: auto` el ítem
 *     encoge a su contenido, y una pieza apaisada se quedaba estrecha teniendo
 *     sitio de sobra al lado.
 *
 *  2. EL ALTO. La caja se estira al alto del mate y el `max-height: 100%` de la
 *     imagen se mide contra él. ⚠️ Esa cadena necesita alto DEFINIDO en cada
 *     eslabón: sin `height: 100%` en la lámina, el porcentaje de la imagen se
 *     resuelve contra una caja de alto automático, o sea que no se resuelve, y
 *     la imagen se queda en el `max-height: 460px` de la regla base y desborda
 *     el mate. Es el mismo fallo de siempre en este archivo con otra ropa.
 *
 *  3. LA CAJA. El escenario se queda sin fondo, sin borde y sin `min-height`:
 *     lo que se enseña es la pieza sobre el mate, no una pieza dentro de una
 *     tarjeta sobre el mate.
 *
 * ⚠️ LA SOMBRA VA EN LA `<img>`, NO EN SU CAJA, y es literal lo que pidió David:
 * *"solo la imagen con una sombra debajo"*. La caja mide el hueco disponible, no
 * la pieza — una foto apaisada deja aire a los lados —, así que una sombra ahí
 * dibujaría el rectángulo del hueco flotando alrededor de la imagen. En la `img`
 * abraza la pieza, que es lo que la despega del mate.
 *
 * ⚠️ `:not([hidden])` y no la lámina a secas. Aquí NO hay pista deslizante
 * —`desliza` es solo post y carrusel—, así que las láminas que no tocan se
 * apagan con `hidden`, y `hidden` lo aplica la hoja de USUARIO: cualquier
 * `display` de autor le gana y se pintarían todas en fila. Ya pasó una vez con
 * `.kc-vit-slide` y está anotado arriba; el `:not()` es lo que evita repetirlo.
 */
/* ⚠️ EL SELECTOR REPITE `.kc-vesc .kc-vlay-ph >` Y NO ES REDUNDANCIA: es lo
   único que hace que estas tres líneas ganen.
   La regla que impone el ancho del teléfono es
   `.kc-vesc .kc-vlay-ph > .kc-vf` — un id y TRES clases. Escrito como
   `.kc-vesc-ph .kc-vf-libre` (un id y dos) esto pierde en silencio, y lo que se
   ve es una pieza apaisada encajonada en 382px con medio mate vacío a los
   lados: exactamente el ancho del teléfono que este marco no tiene. Medido en
   el banco antes de enseñarlo. Igualada la cuenta, gana por ir después. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay-ph > .kc-vf-libre {
  width: auto; justify-self: stretch; align-self: stretch; min-height: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; }
:is(#kcore-root, #dbch-root) .kc-vf-libre .kc-vit-stage {
  flex: 1 1 auto; min-height: 0; width: 100%;
  background: none; border: 0; border-radius: 0; overflow: visible; }
:is(#kcore-root, #dbch-root) .kc-vf-libre .kc-vit-slide:not([hidden]) {
  display: flex; align-items: center; justify-content: center;
  width: 100%; height: 100%; }
/* ⚠️ UNA SOMBRA NEGRA SOBRE UN MATE NEGRO NO ES NADA, y así estuvo el primer
   intento: `rgba(0,0,0,.55)` sobre un fondo que en el centro es `#000000`
   literal. Visto en el banco: la pieza quedaba PEGADA al fondo, que es
   exactamente lo contrario de lo que pidió David al pedir la sombra.

   Sobre oscuro lo que despega una pieza es luz, no falta de ella, así que van
   las tres capas juntas y cada una hace algo distinto:
     · la sombra oscura, que sigue existiendo y sí se ve cuando por detrás pasa
       uno de los brillos de la aurora — el mate se mueve, así que no es negro
       en todas partes ni todo el rato;
     · el filo blanco a un píxel, que es lo que dibuja el borde de la pieza
       contra el negro plano del centro;
     · un derrame lima MUY tenue por debajo, en el color de la propia aurora, que
       es lo que da el «está apoyada encima» sin inventarle un color al mate.
   Los tres al 100 % de opacidad serían un marco, y este marco existe para no
   tener ninguno: por eso ninguna pasa de .22 salvo la sombra. */
:is(#kcore-root, #dbch-root) .kc-vf-libre .kc-vit-slide img {
  max-width: 100%; max-height: 100%; width: auto; height: auto;
  border-radius: 10px;
  box-shadow:
    0 26px 58px rgba(0, 0, 0, .78),
    0 0 0 1px rgba(255, 255, 255, .11),
    0 20px 44px -14px rgba(214, 255, 64, .20); }
/* Las flechas, claras sobre el mate: el marco no tiene tarjeta blanca detrás
   donde apoyarse, que es de donde sacaban su contraste en el post. */
:is(#kcore-root, #dbch-root) .kc-vf-libre .kc-vit-nav { background: rgba(255, 255, 255, .85); }

/* ⚠️ LA TIRA NO PUEDE DECIDIR EL ALTO DEL MATE.
 *
 * Su lista tiene `max-height: 420px` desde que existe, y eso bastaba cuando la
 * story no estaba dentro de un mate atado a la ventana. Ahora no: con dos
 * historias la tira ya mide 422px contra los 376 del teléfono, y con cinco se
 * va a 450 — o sea que la vitrina volvería a salirse por abajo, y por culpa de
 * las MINIATURAS, que es lo último que uno miraría.
 *
 * Así que la lista se acota al alto del teléfono y scrollea lo suyo. La cuenta
 * es la del propio teléfono al revés: el ancho menos su bisel, por 16/9, menos
 * la cabecera de la tira. El bisel sale de `--kc-vph-b` y no de un 16 escrito
 * aquí, para que no haya dos sitios que sincronizar a mano. */
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat-s {
  max-height: calc((var(--kc-vph-w, 300px) - var(--kc-vph-b, 8px)) * 1.7778 - 22px); }
/* ⚠️ El mate del teléfono lleva EL MISMO relleno vertical que el del post (26).
   Tuvo 22 un rato y era justo lo que impedía que las dos áreas midieran igual:
   el alto fijo se calcula como contenido + 52, y con 22 salían 8px de menos. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay-copy > .kc-vf { flex-shrink: 0; }
/* 480px ≈ 62 caracteres a 13,5px: la medida de lectura de un párrafo. Más ancho
   no se lee mejor, solo llena. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy { flex: 0 1 480px; min-width: 0; }

/* ── La tira lateral de miniaturas (story y flipbook) ── */
:is(#kcore-root, #dbch-root) .kc-vlat { flex: none; width: 108px; display: flex; flex-direction: column; gap: 8px; }
/* Dentro del mate, la mitad. Y con la tira a la mitad, su cabecera tiene que
   encogerse con ella: a 12px, «Instagram Stories» necesitaba 95px de ancho y se
   partía en tres líneas. */
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat { width: var(--kc-vlat-w); gap: 6px; }
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat-s { gap: 6px; padding: 2px 2px 2px 0; }
/* Sin salto: «3 historias» partido en dos líneas se lee como dos cosas. Cabe
   justo en los 54px de la tira, y si algún día no cupiera prefiero que asome
   sobre el hueco de al lado antes que volver a partirlo. */
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat-h { width: max-content; }
/* ⚠️ Claro sobre el mate. `.kc-vlat-h b` va en `--text-1` —tinta— porque nació
   fuera del escenario, y al volverse negro el mate se quedó invisible: quedaba
   como un renglón fantasma en la esquina. Mismo motivo que los rótulos del copy.
   Cualquier texto que entre en el mate hay que repintarlo aquí. */
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat-h b { font-size: 10px; line-height: 1.25;
  white-space: nowrap; color: rgba(244,245,238,.88); }
/* ⚠️ El subtítulo («Instagram Stories») NO CABE en media tira y se esconde.
   A 9px necesita ~65px de ancho contra los 54 que hay, así que se partía en dos
   líneas y, con el título en otras dos, la cabecera crecía a cuatro y empujaba
   las miniaturas hacia abajo. No se pierde información: el eje de publicaciones
   ya rotula la publicación como «Story» y el marco es un teléfono. */
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat-h span { display: none; }
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat-i { border-width: 1.5px; border-radius: 7px; }
:is(#kcore-root, #dbch-root) .kc-vesc-ph .kc-vlat-n { left: 3px; bottom: 3px; min-width: 13px;
  padding: 0 3px; font-size: 8.5px; }
:is(#kcore-root, #dbch-root) .kc-vlat-h { display: flex; flex-direction: column; gap: 1px; padding: 0 2px; }
:is(#kcore-root, #dbch-root) .kc-vlat-h b { font-size: 12px; font-weight: 700; color: var(--text-1); }
:is(#kcore-root, #dbch-root) .kc-vlat-h span { font-size: 10.5px; color: var(--text-3); }
:is(#kcore-root, #dbch-root) .kc-vlat-s { display: flex; flex-direction: column; gap: 8px;
  max-height: 420px; overflow-y: auto; overflow-x: hidden; padding: 2px; }
/* ⚠️ `flex: none`, y no es cosmético: es lo que sostiene la proporción.
 *
 * La tira es un flex en columna con el alto acotado (ver `.kc-vlat-s` dentro del
 * mate). En un flex, un hijo se ENCOGE antes de que el contenedor decida
 * scrollear, así que las miniaturas se aplastaban contra el tope: medidas,
 * 104×176 en vez de 104×185, o sea 1.69 en vez del 1.78 que es un 1080×1920.
 * David lo describió exacto —*"se ven cuadradas"*— y era eso.
 *
 * Con `flex: none` mantienen su caja y lo que scrollea es la tira, que es la
 * regla de layout de siempre. Es la misma familia que el `flex: none` de los
 * bloques de la ficha, documentado en `kcore-mesa.css`. */
:is(#kcore-root, #dbch-root) .kc-vlat-i { all: unset; box-sizing: border-box; cursor: pointer; position: relative;
  border-radius: 9px; overflow: hidden; border: 2px solid transparent; background: var(--surface-3);
  aspect-ratio: 9 / 16; flex: none; transition: border-color .15s; }
:is(#kcore-root, #dbch-root) .kc-vlat-i img { width: 100%; height: 100%; object-fit: cover; display: block; }
:is(#kcore-root, #dbch-root) .kc-vlat-i.on { border-color: var(--accent); }
:is(#kcore-root, #dbch-root) .kc-vlat-i:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
:is(#kcore-root, #dbch-root) .kc-vlat-doc { display: grid; place-items: center; width: 100%; height: 100%;
  font-size: 11px; font-weight: 700; text-transform: uppercase; color: var(--text-3); }
:is(#kcore-root, #dbch-root) .kc-vlat-n { position: absolute; left: 5px; bottom: 5px; min-width: 17px;
  padding: 1px 4px; border-radius: 20px; background: rgba(0,0,0,.55); color: #fff;
  font-size: 10px; font-weight: 700; text-align: center; }
/* ⚠️ Se esconde en ≤768px y sigue siendo DEUDA anotada: ahí no caben la tira y
   el escenario a la vez. Si el móvil pasa a importar, hay que darle otra forma
   (una tira horizontal debajo), no simplemente devolverla. */
@media (max-width: 900px) { :is(#kcore-root, #dbch-root) .kc-vlat { display: none; } }

/* ═══════════ EL ESCENARIO del post y el carrusel (David, 2 sep 2026) ═══════════
 *
 * *"el fondo se toma de los colores que tiene la imagen como tal, pero es más
 * clara para que se vea el texto del copy"*.
 *
 * ── Por qué es la IMAGEN y no un color ──
 *
 * El razonamiento completo está en `vitConCopy()`, que es quien pone la
 * variable. En corto: leer el color dominante obliga a un `<canvas>`, y un
 * canvas con una imagen de otro origen se cae con `SecurityError` — en silencio
 * y en producción. Esto son dos pseudoelementos y ni una línea de JavaScript.
 *
 * ── Las tres capas, y por qué cada una ──
 *
 *   ::before  la lámina, desenfocada y sobresaturada. El desenfoque no es
 *             estético: sin él se lee como una segunda copia del arte detrás
 *             del arte. Va a `inset: 0` y CRECIDA con `scale`, no con un
 *             `inset` negativo: ver el aviso de abajo.
 *   ::after   el velo blanco. Es lo que lo hace «más claro», o sea lo que deja
 *             que el copy se lea encima. Si algún día alguien lo baja, mirar
 *             primero el contraste del texto sobre una lámina oscura.
 *   el fondo  `--surface-3` debajo de todo: es lo que se ve cuando no hay
 *             lámina utilizable, y es un estado bueno, no un hueco.
 *
 * ⚠️ `isolation: isolate` es lo que hace que los `z-index` negativos se queden
 * DENTRO del escenario. Sin él los pseudoelementos se van por detrás del panel
 * blanco de la ficha y el mate desaparece sin dar ningún error.
 *
 * ⚠️ Y `overflow: clip`, no `hidden`, con `hidden` delante solo de respaldo.
 *
 * La primera versión sacaba el desenfoque con `inset: -14%`, que es la forma
 * habitual de esconder los bordes transparentes que deja un `blur` grande. Eso
 * le da al escenario CONTENIDO QUE SE SALE, y un elemento con `overflow: hidden`
 * y contenido que se sale sigue teniendo un puerto de scroll: no se puede
 * arrastrar, pero el navegador SÍ lo desplaza solo cuando algo de dentro recibe
 * el foco. O sea que al llegar con el tabulador a una flecha del carrusel —o al
 * pincharla— el mate entero se corría 22px y aparecía una franja de la lámina
 * sin velo pegada al borde. Encontrado pinchando de verdad; con `dispatchEvent`
 * no habría salido, que es la misma lección del arrastre y del observador.
 *
 * `overflow: clip` no crea puerto de scroll, así que no hay nada que desplazar.
 * Y aun así el desenfoque ya no se sale: crece con `scale`, dentro de su caja.
 */
:is(#kcore-root, #dbch-root) .kc-vesc { position: relative; isolation: isolate;
  overflow: hidden; overflow: clip;
  /* Menos a los lados que arriba y abajo (David, 2 sep 2026): ese margen es lo
     que le faltaba de ancho al copy. Arriba y abajo se mantiene, que es lo que
     hace que la pieza «respire» dentro del mate. */
  border-radius: var(--r-lg); padding: 26px 18px; background: var(--surface-3); }
:is(#kcore-root, #dbch-root) .kc-vesc::before { content: ''; position: absolute; inset: 0; z-index: -3;
  background-image: var(--kc-vesc-img, none); background-size: cover; background-position: center;
  filter: blur(44px) saturate(1.9); transform: scale(1.4); }


/* ══════ LA LUZ QUE SE MUEVE (David, 2 sep 2026) ══════
 *
 * *"agrega un efecto de movimiento sutil al fondo, algún elemento en degradado
 * verde lima moviéndose sutilmente en el fondo"*.
 *
 * Tres capas por debajo del contenido, y el orden importa: la lámina
 * desenfocada (-3), el velo que la aclara u oscurece (-2) y esta luz (-1), que
 * va ENCIMA del velo. Debajo se la comería: el velo del reel es tinta al 90 %.
 *
 * Se mueve con `transform`, que el navegador compone sin repintar. La primera
 * idea era animar la posición del degradado dentro del `::after`, y eso repinta
 * un degradado del tamaño del bloque en cada fotograma.
 *
 * 26 segundos y `alternate`: tiene que notarse solo si te quedas mirando. Una
 * animación que se ve es una animación que distrae de la pieza, y esta pantalla
 * existe para mirar la pieza.
 *
 * Hoy la enseña solo el mate OSCURO del reel. En los claros una mancha lima
 * teñiría de verde un fondo que sale de los colores de la pieza, que es justo lo
 * que ese mate promete. Encenderla ahí es quitar el `display: none`. */
:is(#kcore-root, #dbch-root) .kc-vesc-luz { display: none; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz {
  display: block; position: absolute; z-index: -1; pointer-events: none;
  left: 6%; top: -20%; width: 58%; aspect-ratio: 1;
  /* El degradado ya es su propio desenfoque, así que el `blur` es poco y solo
     rompe el borde del círculo. La primera versión iba a .22 con 26px de blur y
     no se veía: contra una tinta del 90 % hay que subir más de lo que parece. */
  background: radial-gradient(circle,
    rgba(203,249,4,.34), rgba(203,249,4,.13) 45%, rgba(203,249,4,0) 70%);
  filter: blur(12px);
  animation: kc-vesc-luz 26s ease-in-out infinite alternate; }
@keyframes kc-vesc-luz {
  from { transform: translate3d(-8%, -6%, 0) scale(.9); }
  to   { transform: translate3d(42%, 16%, 0) scale(1.15); }
}
/* Quien haya pedido menos movimiento se queda con la luz quieta, no sin ella:
   el mate perdería su acento por una preferencia de accesibilidad. */
@media (prefers-reduced-motion: reduce) {
  :is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz { animation: none; }
}
/* El desenfoque es caro de animar y no aporta nada en movimiento; en impresión
   además se pierde. Se deja quieto y ya. */
@media print { :is(#kcore-root, #dbch-root) .kc-vesc::before { display: none; } }

/* ══════ EL MATE OSCURO DEL REEL (David, 2 sep 2026) ══════
 *
 * *"un fondo oscuro con una pequeña iluminación del marco pero en color verde
 * lima de Katalyst, y el copy en color blanco para que se vea"*.
 *
 * Es un MODIFICADOR del escenario, no un escenario aparte: el `::before` sigue
 * siendo la portada desenfocada, y lo único que cambia es el velo de encima —de
 * blanco a tinta—. Así un reel de una pieza cálida da un oscuro cálido, en vez
 * de un gris muerto igual para todos, y el día que se retoque el mate se retoca
 * para los cinco marcos a la vez.
 *
 * ⚠️ Y por eso el velo va MÁS opaco que el claro (.90/.955 contra .74/.88): en
 * claro, el texto es tinta sobre un fondo que se aclara y el contraste sobra;
 * en oscuro es blanco sobre un fondo que puede venir de una portada CLARA, y
 * ahí un velo flojo deja el copy ilegible. El velo es lo que garantiza el
 * contraste, no el color de la pieza.
 */
:is(#kcore-root, #dbch-root) .kc-vesc { background: #000000; }

/* ⚠️ EN EL MATE OSCURO LA LÁMINA NO SE PINTA, y es un cambio de promesa.
 *
 * Hasta el 2 sep 2026 el fondo del reel era la portada desenfocada, como en los
 * claros. David pasó luego una referencia —una aurora de manchas suaves con
 * grano— y pidió *"que el estilo del fondo fuera algo más como la referencia
 * pero con los colores de Katalyst"*. Una aurora ENCIMA de una portada
 * desenfocada da barro: dos fuentes de color peleándose. Así que aquí la lámina
 * se apaga y el fondo es de marca, no de la pieza.
 *
 * Es la única diferencia de fondo entre los cinco marcos, y está decidida: el
 * post, el carrusel y la story siguen sacando su color de la pieza. */
:is(#kcore-root, #dbch-root) .kc-vesc::before {
  background-image: none;
  /* Capa A de la aurora: las dos manchas grandes, las que dan el cuerpo. */
  background:
    radial-gradient(46% 50% at 14% 22%, rgba(214,255,64,.52), rgba(214,255,64,0) 70%),
    radial-gradient(42% 44% at 80% 72%, rgba(122,152,0,.62), rgba(122,152,0,0) 72%);
  filter: blur(34px);
  animation: kc-aur-a 10s ease-in-out infinite alternate; }

/* Las otras dos capas cuelgan de `.kc-vesc-luz`, que deja de ser una mancha
   suelta para ser el contenedor de la aurora. Tres capas y no una porque una
   sola, por mucho que se mueva, se lee como «un círculo desplazándose»; tres a
   ritmos distintos (34, 43 y 52 s) no repiten nunca el mismo dibujo, que es lo
   que hace que parezca luz y no una animación. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz {
  display: block; position: absolute; inset: 0; z-index: -2; pointer-events: none;
  background: none; filter: none; animation: none; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz::before,
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz::after {
  content: ''; position: absolute; inset: -10%; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz::before {
  background:
    radial-gradient(32% 36% at 60% 20%, rgba(203,249,4,.40), rgba(203,249,4,0) 68%),
    radial-gradient(28% 32% at 32% 80%, rgba(154,196,10,.46), rgba(154,196,10,0) 70%);
  filter: blur(40px);
  animation: kc-aur-b 13s ease-in-out infinite alternate; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz::after {
  background:
    radial-gradient(24% 28% at 88% 32%, rgba(238,255,168,.38), rgba(238,255,168,0) 66%),
    radial-gradient(30% 26% at 6% 56%, rgba(122,152,0,.40), rgba(122,152,0,0) 70%);
  filter: blur(46px);
  animation: kc-aur-c 17s ease-in-out infinite alternate; }

/* ══ POR QUÉ ESTO PULSA Y NO SOLO SE DESPLAZA ══
 *
 * Dos intentos no se vieron —David: *"sigue sin notarse el movimiento"*— y
 * subir el recorrido no lo arregló. La causa no era la velocidad:
 *
 *   una mancha MUY grande y MUY difusa que se traslada casi no cambia el brillo
 *   de ningún punto concreto. El ojo no detecta «el degradado se movió 40px»;
 *   detecta que una zona se aclara o se oscurece.
 *
 * Y encima las tres capas se movían RÍGIDAS: cada una lleva dos manchas dentro
 * del mismo degradado, así que su geometría relativa nunca cambiaba. Un bloque
 * entero desplazándose no es un morph, es una diapositiva moviéndose.
 *
 * Lo que sí se siente son dos cosas, y ambas se componen en GPU igual que el
 * `transform`:
 *
 *   1. `opacity` pulsando: es un cambio de brillo, justo lo que el ojo registra;
 *   2. periodos DISTINTOS y no múltiplos (10, 13 y 17 s): las tres capas nunca
 *      vuelven a coincidir, así que el dibujo se recompone todo el rato. Eso es
 *      el morph — no lo hace ninguna capa por su cuenta, lo hace el desfase.
 *
 * Si algún día sobra movimiento, lo primero que se baja es el RANGO de opacidad,
 * no la velocidad: es de donde sale casi toda la sensación. */
@keyframes kc-aur-a {
  from { transform: translate3d(-13%, -9%, 0) scale(.96) rotate(-7deg);  opacity: .26; }
  to   { transform: translate3d(19%, 15%, 0) scale(1.40) rotate(21deg);  opacity: 1; }
}
@keyframes kc-aur-b {
  from { transform: translate3d(17%, 13%, 0) scale(1.34) rotate(13deg);   opacity: 1; }
  to   { transform: translate3d(-19%, -15%, 0) scale(.82) rotate(-19deg); opacity: .22; }
}
@keyframes kc-aur-c {
  from { transform: translate3d(-15%, 16%, 0) scale(.98) rotate(-15deg); opacity: .34; }
  to   { transform: translate3d(21%, -13%, 0) scale(1.46) rotate(18deg); opacity: 1; }
}
/* Quieta, no ausente: la aurora es el fondo, y quitarla dejaría el bloque negro. */
@media (prefers-reduced-motion: reduce) {
  :is(#kcore-root, #dbch-root) .kc-vesc::before,
  :is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz::before,
  :is(#kcore-root, #dbch-root) .kc-vesc .kc-vesc-luz::after { animation: none; }
}

/* ── El GRANO y la viñeta, encima de todo ──
 * Es lo que separa la referencia de un degradado de plantilla: sin grano, tres
 * manchas desenfocadas se ven como plástico y además se bandean —los degradados
 * suaves en 8 bits hacen escalones visibles sobre fondo oscuro—. El ruido los
 * rompe.
 *
 * El ruido es un SVG en `data:`, no un archivo: un PNG de textura serían otra
 * petición y otro archivo que versionar en el plugin, y `feTurbulence` lo genera
 * el navegador. La viñeta va en la misma capa para no gastar una tercera. */
/* ⚠️ Declara `content` e `inset` POR SU CUENTA. Los heredaba de una regla base
   —el velo blanco de la versión clara del mate— que desapareció al hacer la
   aurora el fondo de todos; sin ellos este pseudoelemento deja de existir y el
   grano se va sin dar ningún error. Pasó, y se vio como «el fondo se ve más
   plano», no como «falta una capa». */
:is(#kcore-root, #dbch-root) .kc-vesc::after {
  content: ''; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.22'/%3E%3C/svg%3E"),
    radial-gradient(138% 116% at 40% 32%, rgba(0,0,0,0) 66%, rgba(0,0,0,.34) 100%);
  background-size: 140px 140px, cover;
  mix-blend-mode: normal; }

:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-b p { color: #F4F5EE; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-s label { color: rgba(244,245,238,.62); }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-t { border-top-color: rgba(244,245,238,.18); }
/* ⚠️ Los hashtags NO llevan color propio aquí, y es a propósito. Escribí una
   regla poniéndolos en lima y no hacía nada: `.kc-vcopy-tags` es el mismo `<p>`
   que la regla de arriba, que gana por especificidad (0,2,1 contra 0,2,0). Se
   quita en vez de dejarla —una regla que no aplica es peor que ninguna, porque
   el siguiente que lea el archivo creerá que ese color está decidido—. Y en
   blanco es lo correcto: David pidió el copy en blanco, y los hashtags son
   copy. */

/* ── El chip «Publicado / Sin publicar», en la esquina del mate ──
 * Se mudó aquí desde la cabecera (ver `vitCabecera()`): allí desaparecía al
 * pasar a un formato no publicable y corría de sitio todo lo demás.
 *
 * Va en versión OSCURA porque el mate lo es. El chip claro de la cabecera sigue
 * existiendo en la hoja para quien lo use fuera del mate; aquí se repinta. */
/* ── EL RÓTULO: el nombre de la publicación, en la esquina (BD v45) ──
 * David, 4 sep 2026: *"que este mismo nombre aparezca sutilmente en la
 * publicación dentro de la vitrina, en una esquina superior"*.
 *
 * Abajo a la DERECHA (David, 4 sep 2026, tras verlo arriba a la izquierda).
 * Comparte el cristal con el check de publicado —fondo translúcido, filo claro y
 * desenfoque detrás— porque los dos flotan sobre arte que no se controla: sobre
 * una foto clara un texto blanco a secas desaparece, y una caja opaca taparía la
 * pieza. Lo que no comparte es la esquina, y así siguen sin estorbarse: el check
 * se queda arriba a la derecha, donde nació.
 *
 * `SUTILMENTE` es literal y son tres decisiones: no lleva negrita, va en 11px y
 * el color no llega al blanco (.86). Es una etiqueta para ubicarse, no un título.
 *
 * ⚠️ Se acota a 40% del ancho y corta con puntos suspensivos. El tope de 60
 * caracteres de `MAX_NAME` cabe en la pestaña del eje, que es ancha; aquí, sobre
 * un teléfono de 382px, cruzaría la pieza de lado a lado. Cortar es peor que no
 * enseñarlo entero, y taparle media cara al arte es peor que cortar. */
:is(#kcore-root, #dbch-root) .kc-vit-rot {
  position: absolute; z-index: 3; bottom: 14px; right: 16px;
  max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 5px 11px; border-radius: 20px;
  font-size: 11px; font-weight: 500; letter-spacing: .01em;
  background: rgba(255,255,255,.10); color: rgba(244,245,238,.86);
  border: 1px solid rgba(255,255,255,.14); backdrop-filter: blur(6px);
  pointer-events: none; }
/* En el PDF el fondo es uno de los prediseñados —puede ser claro— y encima el
   envoltorio no es el mate, así que necesita su propio ancla y su propia tinta:
   oscura sobre cristal claro, que es lo contrario del caso anterior. */
:is(#kcore-root, #dbch-root) .kc-vdocw { position: relative; }
:is(#kcore-root, #dbch-root) .kc-vdocw > .kc-vit-rot {
  bottom: 10px; right: 12px;
  background: rgba(255,255,255,.72); color: var(--text-2, #5C5C57);
  border-color: rgba(0,0,0,.08); }

:is(#kcore-root, #dbch-root) .kc-vesc > .kc-vit-pub {
  position: absolute; z-index: 3; top: 14px; right: 16px;
  background: rgba(255,255,255,.10); color: rgba(244,245,238,.82);
  border: 1px solid rgba(255,255,255,.14); backdrop-filter: blur(6px); }
:is(#kcore-root, #dbch-root) button.kc-vesc > .kc-vit-pub:hover,
:is(#kcore-root, #dbch-root) .kc-vesc > button.kc-vit-pub:hover {
  background: rgba(255,255,255,.18); color: #fff; }
:is(#kcore-root, #dbch-root) .kc-vesc > .kc-vit-pub.on {
  background: rgba(203,249,4,.16); color: var(--lime, #CBF904);
  border-color: rgba(203,249,4,.38); }

/* ══════ LO DE DENTRO DE LA TARJETA ESCALA CON ELLA (David, 2 sep 2026) ══════
 *
 * *"sus elementos en el interior de cada sección deberían ser un poco más
 * grandes PROPORCIONALMENTE sin que se descuadren"*.
 *
 * La cabecera y la fila de iconos iban a píxeles fijos, así que la tarjeta
 * crecía y ellos no: a 265px de ancho se veían bien y a 331 se quedaban
 * pequeños, como una publicación con la interfaz de otro tamaño. Es el mismo
 * problema que el nombre del teléfono, y la misma solución: la tarjeta declara
 * su unidad —su ancho partido por 26— y todo lo que va encima se mide en `em`.
 *
 * ⚠️ El COPY no escala, y es a propósito: su columna no depende del ancho de la
 * tarjeta (mide 480px de medida de lectura), así que atarlo a ella cambiaría el
 * cuerpo del texto sin que cambie el ancho de línea. Es texto para leer, no
 * parte del mockup.
 *
 * ⚠️ Y los 95px que `--kc-vesc-c` descuenta por la cabecera y los iconos dejan
 * de ser exactos al escalar: a 331 la tarjeta pide ~99. La diferencia se la come
 * la holgura de `--kc-vit-hueco`, y por eso ese margen no se toca. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post { font-size: calc(var(--kc-vit-w, 380px) / 26); }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post .kc-vhead { padding: .87em 1.02em; gap: .7em; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post .kc-vhead-av { width: 2.36em; height: 2.36em;
  font-size: .95em; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post .kc-vhead-n { font-size: 1.02em; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post .kc-vhead-m svg { width: 1.42em; height: 1.42em; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post .kc-vacts { padding: .87em 1.02em; gap: 1.18em; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post .kc-vacts svg { width: 1.65em; height: 1.65em; }
/* Los puntos de la lámina también: son parte del mockup, no del chrome. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vit-pd { gap: .28em; bottom: .8em; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vit-pdi { width: .4em; height: .4em; padding: .32em; }

/* Dentro del escenario la tarjeta tiene que despegarse del mate. Sobre negro la
   sombra no basta para separarla, así que lleva además un filo claro. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vf-post { box-shadow: 0 20px 50px rgba(0,0,0,.55),
  0 3px 12px rgba(0,0,0,.35); border-color: rgba(255,255,255,.14); }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay { gap: 26px; }

/* ── El panel de copy, al lado del post (mockup A) ── */
:is(#kcore-root, #dbch-root) .kc-vcopy { flex: 1; min-width: 0; align-self: stretch;
  border: 1px solid var(--border); border-radius: var(--r-lg); background: var(--surface);
  display: flex; flex-direction: column; overflow: hidden; }
/* Dentro del escenario el copy NO va en tarjeta: el mate ya es el contenedor, y
   una caja blanca encima de él lo partiría en dos. Es lo que pinta el mockup. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy { border: 0; background: transparent; }
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-b { padding: 8px 6px 6px; gap: 12px; }
/* ⚠️ `min-height: 0` NO ES DECORATIVO, y su falta fue un defecto real.
 *
 * Esto es un hijo de un flex column (`.kc-vcopy`) y lleva `overflow-y: auto`
 * desde siempre. Pero en un contenedor flex el mínimo de un hijo es `auto`, o
 * sea el ALTO DE SU CONTENIDO: sin bajarlo a 0 el bloque nunca se encoge, el
 * `overflow` no llega a activarse y lo que hace es CRECER por debajo del panel.
 * Mientras el copy cabía no se notaba; en cuanto la columna se estrechó (2 sep
 * 2026) un copy normal se salió del escenario y el `overflow: clip` del mate se
 * comió las últimas líneas. Sin error y sin barra: el texto simplemente no
 * estaba. Es la misma familia que el `[hidden]` del escenario y que el
 * `box-sizing` de `all: unset`.
 *
 * Con esto, un copy que no quepa scrollea dentro de su columna. Que scrollee es
 * feo; que se pierda, no se puede. */
:is(#kcore-root, #dbch-root) .kc-vcopy-b { padding: 16px 18px; display: flex; flex-direction: column; gap: 8px;
  min-height: 0; overflow-y: auto; }

/* ══ UN COPY LARGO SE DESVANECE, NO SE CORTA (9 sep 2026) ═══════════════════
 *
 * Reportado por David desde producción, con un post real de METAZA: *"no me
 * molesta que haya scroll down pero el diseño rompe con la armonía de la
 * vitrina, debe ser mucho más sutil"*.
 *
 * Eran DOS cosas a la vez, y las dos se ven en su captura:
 *
 *   1. la BARRA por defecto del navegador —gruesa, con su carril claro— pegada
 *      al borde de un panel que va sobre el mate oscuro;
 *   2. el texto CORTADO a media línea abajo, sin nada que dijera que sigue.
 *      Un tajo limpio no se lee como «hay más», se lee como un defecto.
 *
 * ── El desvanecido ──
 *
 * Va como MÁSCARA sobre el propio bloque que scrollea, así que se queda pegado
 * a su borde inferior mientras el texto pasa por debajo.
 *
 * ⚠️ Y el `padding-bottom` NO es aire decorativo: es lo que hace que al llegar
 * al FINAL el último renglón se vea nítido. Sin él, el desvanecido —que es
 * fijo— se comería la última línea justo cuando ya no hay nada más que ver, o
 * sea que el copy nunca terminaría de leerse. Con el relleno, abajo del todo lo
 * que queda bajo el degradado es hueco.
 *
 * Por eso los dos valores van atados y hay que moverlos juntos.
 */
:is(#kcore-root, #dbch-root) .kc-vcopy-b {
  --kc-fade: 26px;
  padding-bottom: var(--kc-fade);
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--kc-fade)), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - var(--kc-fade)), transparent);
}

/* ── La barra, del ancho de un hilo ──
 *
 * Sin carril y con el pulgar traslúcido, para que tome el tono de lo que tenga
 * detrás en vez de imponer un gris del sistema. El `border` transparente con
 * `background-clip: content-box` es lo que lo deja de 4px reales dentro de un
 * canal de 8: un pulgar pegado al borde se lee como parte del marco.
 *
 * Firefox no tiene pseudo-elementos de barra y se atiende con las dos
 * propiedades estándar, que hacen lo mismo con menos control. */
:is(#kcore-root, #dbch-root) .kc-vcopy-b {
  scrollbar-width: thin;
  scrollbar-color: rgba(13,17,23,.22) transparent;
}
:is(#kcore-root, #dbch-root) .kc-vcopy-b::-webkit-scrollbar { width: 8px; }
:is(#kcore-root, #dbch-root) .kc-vcopy-b::-webkit-scrollbar-track { background: transparent; }
:is(#kcore-root, #dbch-root) .kc-vcopy-b::-webkit-scrollbar-thumb {
  background: rgba(13,17,23,.22);
  border-radius: 999px;
  border: 2px solid transparent;
  background-clip: content-box;
}
:is(#kcore-root, #dbch-root) .kc-vcopy-b:hover::-webkit-scrollbar-thumb { background: rgba(13,17,23,.34); background-clip: content-box; }

/* SOBRE EL MATE el pulgar se invierte: ahí el fondo es oscuro y una barra
   oscura desaparece del todo, que es el defecto contrario. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-b {
  scrollbar-color: rgba(244,245,238,.26) transparent;
  /* ⚠️ El relleno se REPITE aquí, y no es redundancia. Sobre el mate el panel
     lleva su propio `padding: 8px 6px 6px` con más especificidad —un id y DOS
     clases— así que le gana al de arriba y dejaría el desvanecido comiéndose el
     último renglón justo en el escenario, que es donde David lo vio. */
  padding-bottom: var(--kc-fade);
}
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-b::-webkit-scrollbar-thumb {
  background: rgba(244,245,238,.26); background-clip: content-box;
}
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-b:hover::-webkit-scrollbar-thumb {
  background: rgba(244,245,238,.40); background-clip: content-box;
}
/* Los rótulos son VERSALITA, no titulares: nombran la sección y se apartan. Con
   el peso de antes (13px, 700, tinta) competían con el copy, que es el texto que
   de verdad hay que leer. */
:is(#kcore-root, #dbch-root) .kc-vcopy-s { display: flex; align-items: baseline; gap: 10px; }
:is(#kcore-root, #dbch-root) .kc-vcopy-s label { font-size: var(--fs-micro, 10.5px); font-weight: 600;
  letter-spacing: .13em; text-transform: uppercase; color: var(--text-2); }
:is(#kcore-root, #dbch-root) .kc-vcopy-t { margin-top: 4px; padding-top: 16px; border-top: 1px solid var(--border); }
/* Sobre el mate el borde de `--border` casi no se ve; aquí se pinta con tinta a
   muy baja opacidad para que la línea exista en cualquier color de fondo. */

:is(#kcore-root, #dbch-root) .kc-vcopy-b p { margin: 0; font-size: 13px; line-height: 1.65; color: var(--text-1);
  white-space: pre-wrap; }
/* Dentro del escenario el copy se lee como el texto de la publicación: un pelo
   más de cuerpo y más aire entre líneas.
   ⚠️ SIN `max-width` de medida de lectura. Lo llevaba (46ch) y era la mitad del
   hueco que David vio a la derecha: la columna ya es estrecha —mide lo mismo que
   la tarjeta— así que el tope no protegía de nada y solo dejaba aire muerto. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-b p { font-size: 13.5px; line-height: 1.75; }
:is(#kcore-root, #dbch-root) .kc-vcopy-tags { color: #4C7FD1; word-break: break-word; }
/* Los hashtags del mockup van en tinta, no en el azul del enlace: aquí no son
   enlaces, son parte del texto que se aprueba. */
:is(#kcore-root, #dbch-root) .kc-vesc .kc-vcopy-tags { color: var(--text-1); font-weight: 500; }
@media (max-width: 1180px) {
  :is(#kcore-root, #dbch-root) .kc-vlay { flex-direction: column; }
  :is(#kcore-root, #dbch-root) .kc-vlay-copy > .kc-vf { flex: none; width: 100%; }
  :is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay-copy > .kc-vf { flex: none; width: 100%; }
  :is(#kcore-root, #dbch-root) .kc-vcopy { width: 100%; }
  /* Apilado, el mate se estrecha para no comerse el ancho que ya escasea. */
  :is(#kcore-root, #dbch-root) .kc-vesc { padding: 16px; }
}

/* ── Las pestañas Desktop / Tablet / Mobile del marco web (mockup D) ── */
:is(#kcore-root, #dbch-root) .kc-vwtabs { display: flex; align-items: center; gap: 4px;
  padding: 0 4px 10px; border-bottom: 1px solid var(--border); margin-bottom: 12px; }
:is(#kcore-root, #dbch-root) .kc-vwtab { all: unset; box-sizing: border-box; cursor: pointer; display: inline-flex;
  align-items: center; gap: 6px; padding: 7px 12px; border-radius: 9px; font-family: inherit;
  font-size: 12px; font-weight: 500; color: var(--text-3); transition: all .14s; }
:is(#kcore-root, #dbch-root) .kc-vwtab svg { width: 15px; height: 15px; display: block; }
:is(#kcore-root, #dbch-root) .kc-vwtab:hover { color: var(--text-1); }
/* Subrayado y no píldora: es una pestaña de vista, no un filtro. */
:is(#kcore-root, #dbch-root) .kc-vwtab.on { color: var(--text-1); font-weight: 600;
  box-shadow: inset 0 -2px 0 var(--accent); border-radius: 9px 9px 0 0; }
:is(#kcore-root, #dbch-root) .kc-vwtab:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* El ancho del navegador cambia con la vista: es lo que hace que una captura de
   móvil se lea como móvil y no como una imagen estrecha suelta. */


/* ── «Preparando el documento» ──
   Ocupa el hueco de la vitrina mientras las páginas del PDF se rasterizan en el
   navegador de quien curó. Antes ahí se veía el visor crudo de pdf.js durante
   esos segundos y luego saltaba al libro: un estado intermedio que no es ni el
   resultado ni el progreso. */
:is(#kcore-root, #dbch-root) .kc-vconv { display: flex; flex-direction: column; gap: 12px;
  padding: 20px 22px; border-radius: var(--r-lg); border: 1px solid var(--border);
  background: var(--surface); }
:is(#kcore-root, #dbch-root) .kc-vconv-t { display: flex; flex-direction: column; gap: 3px; }
/* Una sola línea (David): el aviso se lee de reojo mientras se espera, y un
   párrafo explicando el porqué no se lee en tres segundos. */
:is(#kcore-root, #dbch-root) .kc-vconv-t b { font-size: 13.5px; font-weight: 600; color: var(--text-1); }
:is(#kcore-root, #dbch-root) .kc-vconv-bar { height: 6px; border-radius: 4px; background: #EAEAE4; overflow: hidden; }
:is(#kcore-root, #dbch-root) .kc-vconv-bar i { display: block; height: 100%; border-radius: 4px;
  background: var(--accent); transition: width .3s cubic-bezier(.16,1,.3,1); }
/* Con un solo documento la barra se queda en 0 hasta que termina, así que se
   mueve sola: una barra quieta durante diez segundos se lee como colgada. */
:is(#kcore-root, #dbch-root) .kc-vconv-bar i { animation: kc-vconv-lat 1.4s ease-in-out infinite; }
@keyframes kc-vconv-lat { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
:is(#kcore-root, #dbch-root) .kc-vconv-bar i[style*="width:0%"] { min-width: 22%; }
@media (prefers-reduced-motion: reduce) {
  :is(#kcore-root, #dbch-root) .kc-vconv-bar i { animation: none; }
}

/* ── «Esta vitrina no está lista para el cliente» (v23, 11 ago 2026) ──
   El cartel que avisa de un documento que no terminó de convertirse. Es
   PERSISTENTE, a diferencia del toast que había antes: un aviso de cinco
   segundos se lo pierde quien estaba mirando otra cosa, y el documento se
   quedaba a medias sin que nadie volviera a enterarse.

   Ámbar y no rojo a propósito: no hay nada roto ni nada perdido, hay algo que
   todavía no está listo y tiene arreglo en un clic. El rojo se reserva para lo
   que ya salió mal. */
:is(#kcore-root, #dbch-root) .kc-vit-warn { display: flex; align-items: center; gap: 12px;
  padding: 12px 14px; margin-bottom: 12px; border-radius: var(--r-lg);
  border: 1px solid #F0DFA8; background: #FDF8EA; }
:is(#kcore-root, #dbch-root) .kc-vit-warn-x { flex: none; width: 30px; height: 30px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center;
  background: #F6ECCB; color: #8A6D1F; }
:is(#kcore-root, #dbch-root) .kc-vit-warn-x svg { width: 17px; height: 17px; display: block; }
:is(#kcore-root, #dbch-root) .kc-vit-warn-b { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
:is(#kcore-root, #dbch-root) .kc-vit-warn-b b { font-size: 13px; font-weight: 600; color: #6B5410; }
:is(#kcore-root, #dbch-root) .kc-vit-warn-b span { font-size: 12px; color: #8A6D1F; line-height: 1.45; }
:is(#kcore-root, #dbch-root) .kc-vit-warn .kc-b { flex: none; }
/* Apilado en móvil, con el botón a lo ancho: el mismo patrón que `.kc-vit-add`,
   que ya resolvió que tres cosas en fila no caben en 375px. */
@media (max-width: 900px) {
  :is(#kcore-root, #dbch-root) .kc-vit-warn { flex-wrap: wrap; }
  :is(#kcore-root, #dbch-root) .kc-vit-warn .kc-b { width: 100%; justify-content: center; }
}

/* ── La cabecera del documento (mockup C) ── */
/* ══════ EL PDF MIDE LO MISMO QUE LOS DEMÁS (David, 2 sep 2026) ══════
 *
 * *"la misma regla en cuanto al tamaño de la vitrina y que no se desborde"*.
 *
 * ⚠️ Y SIN mate de color: *"el fondo de PDF se mantiene eligiendo los que
 * tenemos prediseñados"*. Por eso el flipbook NO se envuelve en `.kc-vesc`: su
 * fondo ya lo pinta `.kc-flip[data-bg]` con el que el equipo eligió
 * (`KCORE_Backgrounds`), y meterle el mate encima sería pisar esa decisión con
 * un color sacado de la primera página. El flipbook ES su propio mate.
 *
 * El bloque entero —cabecera del archivo + libro— se clava al alto del área, y
 * el libro se queda con lo que sobre después de la cabecera. Así el PDF ya no
 * es 26px más alto que un post, que era lo que lo desbordaba.
 */
:is(#kcore-root, #dbch-root) .kc-vdocw { display: flex; flex-direction: column; gap: 12px; }
:is(#kcore-root, #dbch-root) .kc-vdocw:has(.kc-flip) { height: var(--kc-vesc-h); }
:is(#kcore-root, #dbch-root) .kc-vdocw:has(.kc-flip) > .kc-vf-doc { flex: 1; min-height: 0; }
/* `aspect-ratio: 16/9` decidía el alto del libro desde su ancho, o sea desde el
   ancho de la ficha. Ahora lo decide el hueco que queda, que es lo que hace que
   el área sea la misma en los siete marcos. */
/* ⚠️ EL ALTO DEL FLIPBOOK SALE DEL TOKEN, NO DE UN `height: 100%`.
 *
 * Lo intenté con porcentajes y no llegaba: entre el marco y el flipbook hay tres
 * cajas, y basta con que una regla anterior de la hoja le fije el alto a alguna
 * para que la cadena se corte. Y el fallo es traicionero porque durante un rato
 * NO se ve: mientras el libro pedía justo el alto disponible, el flipbook medía
 * lo mismo por contenido que por porcentaje. Solo al darle aire al libro
 * apareció el hueco —el fondo se quedó en 328px de los 473— y entonces el PDF
 * dejó de medir lo que los demás marcos.
 *
 * Con el token es una sola cuenta, la misma que usa el mate, y no depende de
 * ningún antepasado. */
:is(#kcore-root, #dbch-root) .kc-vdocw:has(.kc-flip) > .kc-vf-doc { align-items: stretch; padding: 0; }
:is(#kcore-root, #dbch-root) .kc-vdocw:has(.kc-flip) .kc-flip {
  aspect-ratio: auto; height: var(--kc-vesc-h); }
:is(#kcore-root, #dbch-root) .kc-vdoc-h { display: flex; align-items: center; gap: 12px; }
:is(#kcore-root, #dbch-root) .kc-vdoc-b { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
:is(#kcore-root, #dbch-root) .kc-vdoc-t { font-size: 11px; color: var(--text-3); }
:is(#kcore-root, #dbch-root) .kc-vdoc-n { font-size: 13px; font-weight: 600; color: var(--text-1);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
:is(#kcore-root, #dbch-root) .kc-vdoc-m { font-size: 11.5px; color: var(--text-3); }

/* ── El contador y el botón de pantalla completa, sobre el escenario ── */
:is(#kcore-root, #dbch-root) .kc-vit-cnt { position: absolute; top: 12px; right: 52px; z-index: 3;
  padding: 4px 11px; border-radius: 20px; background: rgba(255,255,255,.92);
  font-size: 11.5px; font-weight: 600; color: var(--text-1);
  box-shadow: 0 2px 8px rgba(0,0,0,.12); font-variant-numeric: tabular-nums; }
:is(#kcore-root, #dbch-root) .kc-vit-full { all: unset; box-sizing: border-box; cursor: pointer; position: absolute;
  top: 10px; right: 10px; z-index: 3; width: 32px; height: 32px; border-radius: 9px;
  display: grid; place-items: center; background: rgba(255,255,255,.92); color: var(--text-2);
  box-shadow: 0 2px 8px rgba(0,0,0,.12); transition: color .14s; }
:is(#kcore-root, #dbch-root) .kc-vit-full svg { width: 16px; height: 16px; display: block; }
:is(#kcore-root, #dbch-root) .kc-vit-full:hover { color: var(--text-1); }
:is(#kcore-root, #dbch-root) .kc-vit-full:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Dentro de un marco los dos se meten en la caja de la lámina, no en la tarjeta:
   flotando sobre la cabecera del post taparían el nombre del cliente. */
:is(#kcore-root, #dbch-root) .kc-vf .kc-vit-cnt, :is(#kcore-root, #dbch-root) .kc-vf .kc-vit-full { top: 8px; }

/* ── Los puntos y el marcador de la fila de acciones (mockup A) ── */
:is(#kcore-root, #dbch-root) .kc-vacts-d { display: flex; align-items: center; gap: 4px; margin: 0 auto; }
/* Pasaron de `<i>` mudos a botones que llevan a su lámina (2 sep 2026), así que
   necesitan el `all: unset` de siempre y un área de toque que no sea de 5px:
   el punto sigue midiendo 5, el botón mide 14 con el relleno. */
:is(#kcore-root, #dbch-root) .kc-vacts-d button { all: unset; box-sizing: content-box; cursor: pointer;
  width: 5px; height: 5px; padding: 5px; border-radius: 50%;
  background: #D8D8D2; background-clip: content-box; transition: background-color .15s; }
:is(#kcore-root, #dbch-root) .kc-vacts-d button.on { background: #4C7FD1; background-clip: content-box; }
:is(#kcore-root, #dbch-root) .kc-vacts-d button:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; }
:is(#kcore-root, #dbch-root) .kc-vacts-s { margin-left: auto; display: flex; }
/* Con puntos, el margen automático ya lo pone el contenedor de los puntos: dos
   `margin-left:auto` seguidos empujarían el marcador fuera de la fila. */
:is(#kcore-root, #dbch-root) .kc-vacts-d + .kc-vacts-s { margin-left: 0; }

/* En angosto los marcos pierden su ancho máximo y la story se encoge: a 375px
 * un teléfono de 288px dentro de la pantalla deja el arte del tamaño de un
 * sello. Se estrecha el teléfono, no la pieza. */
@media (max-width: 900px) {
  :is(#kcore-root, #dbch-root) .kc-vf-post { max-width: none; }
  :is(#kcore-root, #dbch-root) .kc-vph { max-width: min(242px, var(--kc-vph-w, 242px)); }
  :is(#kcore-root, #dbch-root) .kc-vf-tablet .kc-vdev, :is(#kcore-root, #dbch-root) .kc-vf-mobile .kc-vdev { max-width: none; }
  /* ⚠️ La cabecera ENVUELVE, y esto no es cosmético: lo encontró medir el
   * preview a 375px. Con el marco «web» esa fila lleva cuatro cosas (título,
   * contador, el toggle escritorio/móvil y «Editar vitrina») y en un teléfono
   * no caben: el botón se salía 22px y arrastraba la página a scroll
   * horizontal. Los otros marcos no lo destapaban porque no tienen toggle.
   * `row-gap` va aparte del `gap` de la fila para que al partirse no se abra
   * un hueco tan grande como el que separa los elementos en horizontal. */
  :is(#kcore-root, #dbch-root) .kc-vit-h { flex-wrap: wrap; row-gap: 8px; }
  :is(#kcore-root, #dbch-root) .kc-vwtabs { flex-wrap: wrap; }
}

/* ══════ EL LIBRO: DOBLE PÁGINA CON LOMO (BD v20, 10 ago 2026) ══════
 *
 * Las páginas se rasterizaron una vez al publicar, así que esto es CSS sobre
 * imágenes: cuando la vitrina viaje al portal, el cliente no descarga pdf.js.
 *
 * ⚠️ REEMPLAZA a la primera versión (una hoja suelta girando sobre su borde
 * izquierdo), que David rechazó: *"se ven como detrás de la página, no se
 * entiende la animación"*. Tenía razón, y por dos motivos estructurales:
 *
 *   1. Una hoja sola NO TIENE LOMO. En un libro la hoja gira sobre el centro, y
 *      sin ese eje visible el movimiento no se lee como pasar página.
 *   2. La hoja NO TENÍA DORSO. Con `backface-visibility: hidden` y nada detrás,
 *      al pasar de 90° desaparecía: literalmente el "se va detrás".
 *
 * Ahora cada hoja tiene DOS CARAS y gira sobre el lomo, dejando ver la de abajo.
 * Es lo que hace el papel, y por eso se entiende sin explicación.
 *
 * Se descartó turn.js (ver la nota del 10 ago): exige jQuery, su versión buena
 * es comercial, y NO lee PDF. Esto son cuarenta líneas.
 */
/* ⚠️ `padding-left` deja el hueco de las miniaturas, que van ABSOLUTAS.
 *
 * Estaban como columna flex y eso tenía un efecto que solo se ve midiendo: con
 * ocho páginas la tira quiere 800px de alto, así que ARRASTRABA la caja entera a
 * 620 (su tope) mientras el libro apaisado ocupaba 175. El resultado era un
 * marco enorme con el documento pequeño en medio, que fue lo que David reportó
 * como *"no se ve completo en el espacio"*.
 *
 * Sacándolas del flujo, el alto lo decide EL LIBRO y la tira scrollea dentro. */
/* ── El ESCENARIO es 16:9 fijo (BD v41, fondos del flipbook) ──
 *
 * Pedido de David: los fondos se suben a 1920×1080 y el escenario tiene que
 * calzar exacto, o el fondo no se ve completo. El LIBRO no cambia — sigue
 * derivándose de la página real del PDF, ver `.kc-lib` más abajo — solo
 * cambia el contenedor donde se centra: de alto flexible a una caja fija.
 * `.kc-lib-main { overflow: auto }` (más abajo, la regla del zoom) es la red
 * de seguridad si el libro llegara a pedir más alto del que el 16:9 da.
 *
 * `background-size: cover` es lo que garantiza que NINGÚN fondo se deforme,
 * sea cual sea su resolución exacta: 1920×1080 es una recomendación de
 * calidad, no un requisito que valide el servidor.
 *
 * ⚠️ `width: 100%` es OBLIGATORIO y no cosmético. `.kc-flip` es un ITEM del
 * flex `.kc-vit-stage` (que centra con `align-items/justify-content`, no
 * con `stretch`), así que sin ancho explícito su caja se ajustaría al
 * CONTENIDO en vez de llenar el escenario — y un `aspect-ratio` sobre una
 * caja que no sabe cuánto ancho tiene no calza con un fondo de 1920×1080.
 * Con `width: 100%` el ancho lo pone SIEMPRE el escenario, y el alto sale
 * de ahí. */
:is(#kcore-root, #dbch-root) .kc-flip { position: relative; display: flex; gap: 14px;
  width: 100%; padding: 14px 14px 14px 114px; background-color: var(--surface-3);
  background-size: cover; background-position: center;
  /* Las mismas esquinas que el mate de los otros marcos (David, 2 sep 2026): el
     flipbook ES el mate del PDF, así que tiene que redondear igual. `clip` y no
     `hidden` por lo mismo que en `.kc-vesc`: el fondo se sale de la caja y con
     `hidden` eso crea puerto de scroll, que el foco de un botón desplaza. */
  border-radius: var(--r-lg); overflow: hidden; overflow: clip;
  aspect-ratio: 16 / 9; }
/* En pantalla completa no hay caja que redondear. */
:is(#kcore-root, #dbch-root) .kc-flip:fullscreen { border-radius: 0; }
/* En pantalla completa el 16:9 se suelta: forzarlo ahí letterboxearía en
 * cualquier monitor que no sea exactamente 16:9, y `cover` ya deja el fondo
 * bien puesto sobre cualquier proporción de caja.
 *
 * ⚠️ `align-items: center` SE QUITÓ (David, 24 ago 2026): con ella, `.kc-lib-
 * main` deja de ESTIRARSE al alto completo de `.kc-flip` (el `stretch` de
 * siempre) y pasa a medir solo lo que su contenido pide, centrado como
 * bloque. `.kc-lib-bar` está anclada con `bottom: 0` A `.kc-lib-main`, así
 * que si esa caja ya no llega al borde real de la pantalla, la barra tampoco
 * — quedaba flotando a mitad de camino, «como antes», que fue justo lo que
 * David vio. El libro se sigue centrando igual sin esto: lo centra
 * `justify-content: center` DENTRO de `.kc-lib-main`, no `align-items` de su
 * padre. */
:is(#kcore-root, #dbch-root) .kc-flip:fullscreen { background-color: #1A1A1A;
  aspect-ratio: auto; height: 100%; padding: 26px 26px 26px 126px; }

/* ── EL TAMAÑO DEL LIBRO EN PANTALLA COMPLETA ──
 *
 * Defecto que reportó David el 11 sep 2026: en pantalla completa el libro seguía
 * midiendo lo mismo que dentro de la ficha, y el zoom crecía desde ese tamaño
 * pequeño en vez de desde la pantalla.
 *
 * La causa: `--kc-hbase` (el presupuesto de alto del libro) se deriva de
 * `--kc-vesc-h`, que es el alto del ÁREA de la ficha — un número pensado para
 * que los siete marcos midan igual dentro del cajón. En pantalla completa ese
 * número deja de tener sentido: la caja pasa a medir la pantalla entera y el
 * presupuesto se queda en los ~460px de la ficha, así que el libro ocupaba poco
 * más de un tercio de un monitor.
 *
 * Aquí el presupuesto sale de la PANTALLA, con la misma resta que el token: 52
 * del relleno de `.kc-flip` (26 arriba y 26 abajo) y 52 del hueco que
 * `.kc-lib-main` reserva para la barra. El `.88` es el aire, y es más generoso
 * que el `.65` de la ficha a propósito: en pantalla completa no compite con
 * nada, que es justo para lo que se abre.
 */
:is(#kcore-root, #dbch-root) .kc-flip:fullscreen .kc-lib {
  --kc-hbase: calc((100dvh - 104px) * .88); }

/* ⚠️ Y el centrado pasa a hacerlo el MARGEN, no `justify-content`.
 *
 * Con el libro ya a tamaño de pantalla, acercar lo desborda — que es lo que se
 * espera de un zoom. Pero un ítem más alto que su contenedor flex CENTRADO
 * desborda por los dos lados, y la mitad de arriba queda fuera del área de
 * scroll: no hay forma de llegar a ella. Es el defecto clásico del flex
 * centrado con overflow.
 *
 * `margin: auto 0` centra igual mientras SOBRA sitio (los márgenes automáticos
 * se reparten el hueco) y se rinde a 0 cuando falta, y entonces manda el
 * `flex-start`: el libro arranca arriba y se recorre entero. Solo en pantalla
 * completa: dentro de la ficha el libro nace pequeño y la caja está afinada a
 * mano (ver `--kc-hbase`), así que ahí no se toca. */
:is(#kcore-root, #dbch-root) .kc-flip:fullscreen .kc-lib-main { justify-content: flex-start; }
:is(#kcore-root, #dbch-root) .kc-flip:fullscreen .kc-lib { margin: auto 0; }

/* ── El FONDO elegido, predefinido o subido (BD v41) ──
 * Los dos se resuelven igual en el servidor (`KCORE_Backgrounds::resolve()`)
 * a una URL de imagen — un predefinido es un archivo del plugin, uno subido
 * una fila de la biblioteca, y para el CSS son la misma cosa: una URL en
 * `--kc-flip-img`, puesta inline por el JS (mismo mecanismo que `--kc-ar` en
 * `.kc-lib`). Por eso UNA regla basta para cualquier `data-bg` no vacío, en
 * vez de una por llave.
 *
 * Con fallback a `none`: si el fondo se borró de la biblioteca entretanto,
 * `background-color: var(--surface-3)` de la regla base sigue ahí debajo. */
:is(#kcore-root, #dbch-root) .kc-flip[data-bg] {
  background-image: var(--kc-flip-img, none); }

/* ── Las miniaturas ── */
:is(#kcore-root, #dbch-root) .kc-lib-side { position: absolute; left: 14px; top: 14px; bottom: 14px;
  width: 86px; display: flex; flex-direction: column;
  gap: 8px; overflow-y: auto; overflow-x: hidden; padding-right: 4px; }
:is(#kcore-root, #dbch-root) .kc-lib-mini { all: unset; box-sizing: border-box; cursor: pointer;
  display: block; border-radius: 6px; padding: 3px; border: 2px solid transparent;
  background: var(--surface); text-align: center; }
:is(#kcore-root, #dbch-root) .kc-lib-mini img { width: 100%; height: auto; display: block; border-radius: 3px; }
:is(#kcore-root, #dbch-root) .kc-lib-mini span { font-size: 10.5px; color: var(--text-3); line-height: 1.6; }
:is(#kcore-root, #dbch-root) .kc-lib-mini.on { border-color: var(--accent); }
:is(#kcore-root, #dbch-root) .kc-lib-mini:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ⚠️ `padding-bottom` reserva el sitio de la barra, y `position: relative`
 * la hace el ancla de `.kc-lib-bar` (ver su nota más abajo). Sin la reserva,
 * el libro centrado usaría el alto ENTERO de esta caja y se acercaría a
 * pisar la barra, que ahora vive fuera del flujo. */
:is(#kcore-root, #dbch-root) .kc-lib-main { flex: 1; min-width: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 12px; position: relative;
  padding-bottom: 52px; }

/* La caja mide DOS páginas. El tope de ancho sale del propio ratio y no se fija
 * ningún lado: fijar uno y dejar que el otro se recorte rompe la proporción, y
 * una página cuadrada ya lo destapó midiendo. */
/* ⚠️ Sin fondo ni sombra PROPIOS, y no es un olvido: la caja mide DOS páginas
 * siempre, así que un fondo blanco aquí pintaría media hoja fantasma al lado de
 * la portada cuando el libro está cerrado. La sombra la lleva cada cara, que es
 * lo que de verdad se ve. */
/* ── El TAMAÑO del libro, y por qué depende de la forma de la página ──
 *
 * El libro se limita por DOS lados y gana el que apriete primero: el ancho
 * disponible (`100%`) y un tope de alto (`--kc-hmax`). Con eso, la caja «cabe»
 * como lo haría un `object-fit: contain`, sea la página vertical u horizontal.
 *
 * ⚠️ El tope bajó de 600px a 440, y la razón la encontró David con dos casos
 * suyos: un PDF de láminas HORIZONTALES se veía bien y unas páginas VERTICALES
 * se veían enormes. No era un fallo de las imágenes — es que con páginas
 * anchas el límite que aprieta es el ANCHO (la caja mide dos páginas, así que
 * un 16:9 doble es casi 3,6:1 y el alto sobra), mientras que con páginas
 * verticales el que aprieta es el ALTO, y 600px de libro dentro de una ficha
 * que además lleva brief, plan y adjuntos es demasiado.
 *
 * El `min(…, 46vh)` es la otra mitad: en una pantalla baja, 440px fijos se
 * comen la ventana. Con las dos, el libro se ve igual de «cabido» en cualquier
 * proporción de página y en cualquier pantalla, que es lo que se pidió.
 *
 * El zoom multiplica este tope (ver su nota más abajo): así escala por LAYOUT y
 * no toca `transform`, que es de quien depende el centrado del libro cerrado. */
/* ⚠️ Bajó de 440 a 350 con el escenario 16:9 (BD v41), y la primera baja (a
 * 400) se quedó CORTA: David lo reportó viendo la barra del libro recortada
 * contra el borde del escenario.
 *
 * El error, medido y no supuesto: `--kc-hbase` solo presupuestaba el LIBRO,
 * y `.kc-lib-bar` (la barra de flechas/zoom/pantalla completa) vive DEBAJO
 * del libro dentro del mismo `.kc-lib-main`, con un `gap` de 12px. A los
 * ~777px de columna medidos en este archivo, la caja 16:9 da ~409px útiles
 * en `.kc-lib-main` tras el padding de `.kc-flip`; con `--kc-hbase: 400px`
 * el libro solo, sin la barra, YA se comía casi todo ese espacio, y sumarle
 * la barra (~40px) más el gap (12px) empujaba 11px fuera del contenedor.
 * `.kc-lib-main { overflow: auto }` evita que se vea RECORTADO a la fuerza,
 * pero sin barra de scroll visible se leía igual de cortado.
 *
 * 350px deja esos ~52px (barra + gap) de margen real, medido en el
 * navegador con una página apaisada (el caso que de verdad aprieta el
 * alto) y confirmado sin overflow ni barra recortada. */
/* ⚠️ `--kc-hbase` SALE DEL ÁREA desde el 2 sep 2026, no de la ventana.
 *
 * Era `min(350px, 36vh)`: un presupuesto de alto suelto que no sabía nada del
 * hueco real que tenía debajo. Con el área ya clavada eso deja de valer —o
 * sobra sitio, o se desborda— así que se descuenta del alto del área lo que no
 * es libro y se queda escrito de dónde sale cada número:
 *
 *     relleno de `.kc-flip`             28   (14 arriba y 14 abajo)
 *     barra de páginas y zoom           52   (el `padding-bottom` reservado)
 *     el hueco entre libro y barra      12
 *
 * La cabecera del archivo se llevaba otros 52 y se retiró el 2 sep 2026 (ver la
 * nota en el JS), así que el libro los recuperó: la página pasó de 243 a ~272px.
 *
 * ⚠️ Y SIN el `min(…, 36vh)` que llevaba de tope de seguridad. Era redundante
 * —el área ya sale del alto de la ventana— y había pasado de red a CORSÉ: a
 * 876px de ventana, 36vh son 315px contra los 381 que da el área, así que era el
 * 36vh el que decidía y el libro se quedaba pequeño con sitio de sobra al lado.
 * Un tope que se activa siempre no es un tope, es la regla. */
:is(#kcore-root, #dbch-root) .kc-lib { position: relative; perspective: 2600px;
  /* ⚠️ Y con AIRE. Descontar solo lo que no es libro daba un libro que llenaba el
     hueco hasta el último píxel: la portada quedaba a 15px del borde de arriba y
     se leía *"muy encima, como si el zoom inicial fuera mucho"* (David, 2 sep
     2026). Una hoja pegada a su marco no se lee como una hoja.

     El .65 es el número que pidió él, y es de ARRANQUE, no un tope: los botones
     de zoom siguen creciendo desde aquí (ver `--kc-lib-z`). */
  --kc-hbase: calc((var(--kc-vesc-h, 480px) - 92px) * .65);
  --kc-hmax: calc(var(--kc-hbase) * var(--kc-lib-z, 1));
  width: 100%; height: auto;
  max-width: min(100%, calc(var(--kc-hmax) * var(--kc-ar, 1.545)));
  transition: transform .72s cubic-bezier(.35,.05,.2,1), max-width .2s ease; }

/* ── CERRADO en los extremos ──
 * Pedido de David: el libro tiene que ABRIR cerrado, con la portada sola, y
 * terminar en la contraportada. La caja no puede encogerse (el giro necesita las
 * dos mitades), así que se DESPLAZA media página para centrar la única visible.
 * Misma curva que el giro: abrir y cerrar se siente parte del mismo movimiento. */
/* ⚠️ El SENTIDO no es intuitivo y me lo comí a la primera: la caja mide dos
 * páginas y la PORTADA vive en la mitad DERECHA, así que para centrarla hay que
 * correr el libro hacia la IZQUIERDA media página (-25% de un ancho doble). La
 * contraportada vive en la mitad izquierda, y va al revés. Puesto al contrario,
 * la portada se sale del marco y se recorta. */
:is(#kcore-root, #dbch-root) .kc-lib-ini { transform: translateX(-25%); }
:is(#kcore-root, #dbch-root) .kc-lib-fin { transform: translateX(25%); }
/* Cerrado no hay lomo que enseñar: es una sola hoja. */
:is(#kcore-root, #dbch-root) .kc-lib-lomo { transition: opacity .4s ease; }
:is(#kcore-root, #dbch-root) .kc-lib-ini .kc-lib-lomo,
:is(#kcore-root, #dbch-root) .kc-lib-fin .kc-lib-lomo { opacity: 0; }

/* ── La hoja: dos caras que giran sobre el LOMO ── */
:is(#kcore-root, #dbch-root) .kc-lib-hoja { position: absolute; top: 0; left: 50%; width: 50%; height: 100%;
  transform-origin: left center; transform-style: preserve-3d;
  transition: transform .72s cubic-bezier(.35,.05,.2,1); }
:is(#kcore-root, #dbch-root) .kc-lib-pasada { transform: rotateY(-180deg); }
/* La sombra vive AQUÍ y no en el libro: así la portada sola proyecta la suya y
 * no arrastra la de una segunda página que no está. Abierto, las dos caras
 * juntas se leen como un solo volumen porque el lomo cose la unión. */
:is(#kcore-root, #dbch-root) .kc-lib-cara { position: absolute; inset: 0; overflow: hidden;
  background: #fff; backface-visibility: hidden;
  box-shadow: 0 12px 38px rgba(0,0,0,.18); }
/* El DORSO es lo que faltaba: es la cara que se ve al levantar la hoja. Sin
 * ella el papel desaparecía a mitad del giro. */
:is(#kcore-root, #dbch-root) .kc-lib-dorso { transform: rotateY(180deg); }
/* ⚠️ `max-width` y `max-height` a `none`, y NO es defensivo de más: los quita a
 * PROPÓSITO. La página del libro vive dentro de una `.kc-vit-slide`, así que le
 * llega el `max-height: 460px` de la regla general de las láminas (busca ese
 * número más arriba). Empatan en especificidad y esta regla va después, pero eso
 * solo decide `width`/`height`: el tope de la otra sigue en pie porque aquí
 * nadie lo declaraba.
 *
 * No se veía en la ficha porque el libro nunca llega a 460px de alto ahí. Se
 * destapó en PANTALLA COMPLETA con zoom (David, 11 sep 2026): la cara blanca
 * crecía y la imagen se quedaba clavada en 460, o sea *"se hace grande la página
 * pero no el diseño"*, con la página pegada arriba y una franja blanca debajo. */
:is(#kcore-root, #dbch-root) .kc-lib-cara img { width: 100%; height: 100%; object-fit: contain; display: block;
  max-width: none; max-height: none; }

/* La sombra del lomo. Es lo que hace que dos imágenes lado a lado se lean como
 * UN documento abierto y no como dos fotos pegadas. Va por encima del papel y
 * sin capturar clics. */
:is(#kcore-root, #dbch-root) .kc-lib-lomo { position: absolute; inset: 0; z-index: 50; pointer-events: none;
  background: linear-gradient(to right, transparent 46%, rgba(0,0,0,.13) 49.6%,
    rgba(0,0,0,.16) 50%, rgba(0,0,0,.13) 50.4%, transparent 54%); }

/* ── La barra ──
 * David (24 ago 2026), con capturas: se veía «pegada» al libro, con un hueco
 * grande y variable entre ella y el borde inferior del marco. La causa era
 * que vivía DENTRO del flex centrado de `.kc-lib-main` junto al libro: el
 * `gap` entre los dos es fijo (12px), pero el espacio libre que sobra por el
 * centrado se reparte arriba del libro Y debajo de la barra, así que un
 * libro más chico dejaba un hueco enorme debajo de la barra en vez de
 * repartirse.
 *
 * Ahora la barra se saca del flujo y se ancla al borde inferior de
 * `.kc-lib-main` (`bottom: 0`), que YA queda a la distancia del padding de
 * `.kc-flip` (14px normal, 8px en móvil, 26px en pantalla completa) —
 * automático en los tres casos, sin repetir el número. Es la MISMA
 * distancia al marco sea cual sea el alto del libro. */
:is(#kcore-root, #dbch-root) .kc-lib-bar { position: absolute; left: 50%; bottom: 0;
  transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 22px;
  padding: 4px 6px; box-shadow: var(--sh-sm); }
:is(#kcore-root, #dbch-root) .kc-lib-b { all: unset; box-sizing: border-box; cursor: pointer; font-family: inherit;
  width: 30px; height: 30px; border-radius: 50%; display: flex; align-items: center;
  justify-content: center; font-size: 17px; color: var(--text-1); transition: background .14s; }
:is(#kcore-root, #dbch-root) .kc-lib-b:hover { background: var(--surface-3); }
:is(#kcore-root, #dbch-root) .kc-lib-b[disabled] { opacity: .3; cursor: default; background: none; }
:is(#kcore-root, #dbch-root) .kc-lib-b:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
:is(#kcore-root, #dbch-root) .kc-lib-full { font-size: 13px; }
:is(#kcore-root, #dbch-root) .kc-lib-n { font-size: 12px; font-weight: 600; color: var(--text-2);
  padding: 0 8px; min-width: 62px; text-align: center; }
:is(#kcore-root, #dbch-root) .kc-lib-b svg { width: 15px; height: 15px; display: block; }
:is(#kcore-root, #dbch-root) .kc-lib-b.on { background: var(--tint-2, #F1F7D6); color: #46530A; }
:is(#kcore-root, #dbch-root) .kc-lib-sep { width: 1px; height: 18px; background: var(--border); margin: 0 2px; flex: none; }
:is(#kcore-root, #dbch-root) .kc-lib-z { font-size: 12px; font-weight: 600; color: var(--text-2);
  min-width: 44px; text-align: center; font-variant-numeric: tabular-nums; }

/* ── El zoom (mockup C) ──
   Es una escala sobre las páginas YA rasterizadas, no un re-render de pdf.js:
   cambiar la escala de la librería obligaría a rasterizar otra vez en cada
   paso. Las páginas vienen a 1400px de ancho, así que hay resolución de sobra.

   ⚠️ El zoom crece el TOPE DE ALTO, no aplica un `transform: scale`.

   La primera versión sí escalaba con `transform`, y eso PISÓ el
   `translateX(-25%)` con el que `.kc-lib-ini` centra la portada del libro
   cerrado: misma especificidad y esta regla iba después, así que ganaba. El
   resultado —que David vio en dos capturas seguidas— era la portada pegada a la
   derecha con media caja vacía a la izquierda, en la PRIMERA vista del
   documento. Un fallo de cascada, no de diseño.

   Creciendo `--kc-hmax` el libro escala por LAYOUT: la caja sigue derivando su
   ancho del ratio, el translate de cierre se conserva intacto, y no hay dos
   reglas peleándose por la misma propiedad. */
:is(#kcore-root, #dbch-root) .kc-flip { --kc-lib-z: 1; }
/* Con zoom la caja se sale de su hueco, así que el contenedor recorta y deja
   recorrerla. Sin esto, acercar tapaba el brief que hay debajo. */
:is(#kcore-root, #dbch-root) .kc-lib-main { overflow: auto; }

/* ── La rejilla de páginas (mockup C) ──
   La MISMA lista que la tira lateral, en grande y sobre el libro. Se enciende
   con su botón y nace oculta. */
:is(#kcore-root, #dbch-root) .kc-lib-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(108px, 1fr));
  gap: 10px; margin-top: 12px; padding: 14px; border-radius: var(--r);
  background: var(--surface-3); max-height: 340px; overflow-y: auto; }
:is(#kcore-root, #dbch-root) .kc-lib-gi { all: unset; box-sizing: border-box; cursor: pointer; position: relative;
  border-radius: 8px; overflow: hidden; border: 2px solid transparent; background: var(--surface);
  transition: border-color .15s; }
:is(#kcore-root, #dbch-root) .kc-lib-gi img { width: 100%; display: block; }
:is(#kcore-root, #dbch-root) .kc-lib-gi:hover { border-color: var(--accent); }
:is(#kcore-root, #dbch-root) .kc-lib-gi:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
:is(#kcore-root, #dbch-root) .kc-lib-gi span { position: absolute; left: 5px; bottom: 5px; min-width: 17px;
  padding: 1px 5px; border-radius: 20px; background: rgba(0,0,0,.55); color: #fff;
  font-size: 10px; font-weight: 700; text-align: center; }

/* Quien pide menos movimiento no ve el giro: la hoja cambia y ya. Es la única
 * animación de verdad del producto, y es decoración. */
@media (prefers-reduced-motion: reduce) {
  :is(#kcore-root, #dbch-root) .kc-lib-hoja { transition: none; }
}

/* En angosto no caben dos páginas ni la tira de miniaturas: se esconden y el
 * libro se queda con su proporción, que sigue siendo la del documento. */
@media (max-width: 900px) {
  /* El `min-height` de siempre lo reemplaza el `aspect-ratio: 16/9` de la
   * regla base, que ya sostiene el alto solo (BD v41). */
  :is(#kcore-root, #dbch-root) .kc-flip { padding: 8px; }
  :is(#kcore-root, #dbch-root) .kc-lib-side { display: none; }
  /* El zoom sigue multiplicando también aquí: si esta línea fijara los px a
     secas, acercar en un teléfono no haría nada.
     ⚠️ Bajó de 320 a 170 y LUEGO a 115, mismo motivo que la nota de arriba:
     170 no dejaba sitio para la barra (~52px de barra + gap) dentro de los
     ~181px útiles. 115 deja ese margen, medido igual que en desktop. */
  :is(#kcore-root, #dbch-root) .kc-lib { --kc-hbase: min(115px, 26vh); }
}

/* En angosto la ficha colapsa a una columna y el cuerpo scrollea entero, así
 * que la vitrina queda arriba y la conversación debajo, que es el orden
 * correcto. Solo se bajan las alturas, que en móvil se comen la pantalla.
 *
 * ⚠️ Esto pesa MÁS en el portal que en la Mesa: el cliente mira su portal
 * desde el teléfono mucho más que el equipo la Mesa. */
@media (max-width: 900px) {
  :is(#kcore-root, #dbch-root) .kc-vit-stage { min-height: 180px; }
  :is(#kcore-root, #dbch-root) .kc-vit-slide img { max-height: 300px; }
}

/* ══ VA AL FINAL A PROPÓSITO ══
   A igual especificidad gana la última regla del archivo, y varias de arriba
   declaran display. Es la misma disciplina que `.kc-hidden` al final de
   `kcore-mesa.css` y `.dbch-hidden` al final de `dbch-dashboard.css`
   (CLAUDE.md §5.c.5).

   ⚠️ Y aquí hace falta de verdad, no por simetría: la vista de cuadrícula del
   flipbook nace con `kc-hidden` y el visor la pinta en las DOS superficies. En
   la Mesa la regla llega igual desde `kcore-mesa.css`; en el portal ese archivo
   no existe, así que sin esta línea el cliente vería la cuadrícula desplegada
   debajo del libro desde el primer momento. Es exactamente el tipo de defecto
   que solo aparece del lado donde nadie está mirando. */
:is(#kcore-root, #dbch-root) .kc-hidden { display: none !important; }

/* ══════════ EL REEL: PORTADA + PLAY (BD v24, 11 ago 2026) ══════════
 *
 * Decisión de David: dentro del marco de teléfono se ve la PORTADA a 9:16 y el
 * play encima; el video se abre al tocarlo. Un reproductor de Drive incrustado
 * de entrada pinta su barra, su menú y sus bandas, y el resultado se lee como
 * «un reproductor metido en un teléfono», no como un reel.
 *
 * El marco reusa entero `.kc-vf-story` —el teléfono ya estaba resuelto ahí— y
 * esto solo añade lo del video. Un marco propio habría duplicado el teléfono y
 * los dos se habrían separado al primer ajuste.
 */
:is(#kcore-root, #dbch-root) .kc-vreel { position: relative; display: block;
  width: 100%; height: 100%; }
:is(#kcore-root, #dbch-root) .kc-vreel img { display: block; width: 100%; height: 100%;
  object-fit: cover; }

/* El play: círculo claro translúcido, no el rojo de YouTube ni el azul de
   Drive. Lo que se está mirando es la pieza del cliente, no la marca de dónde
   está alojado el archivo. */
:is(#kcore-root, #dbch-root) .kc-vplay { all: unset; box-sizing: border-box;
  cursor: pointer; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 62px; height: 62px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.92); color: #0D1117;
  box-shadow: 0 6px 22px rgba(0,0,0,.28);
  transition: transform .15s cubic-bezier(.34,1.4,.5,1), background .15s ease; }
:is(#kcore-root, #dbch-root) .kc-vplay svg { width: 26px; height: 26px; display: block;
  /* El triángulo se ve centrado cuando está corrido un pelo a la derecha: su
     centro óptico no es su centro geométrico. */
  margin-left: 3px; }
:is(#kcore-root, #dbch-root) .kc-vplay:hover { background: #fff; transform: translate(-50%, -50%) scale(1.06); }
:is(#kcore-root, #dbch-root) .kc-vplay:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* ⚠️ EL REPRODUCTOR YA NO VIVE DENTRO DEL TELÉFONO.
   David lo probó con un Drive real: «se ve corrido, no centrado en el marco».
   No era nuestro CSS. El reproductor es un iframe de OTRO dominio, escala el
   video a su manera y le añade su barra y su botón de pantalla completa; desde
   fuera no se puede tocar nada de eso. Así que dentro del marco va solo la
   portada, que sí controlamos, y el video se abre en una capa. De paso se ve
   mejor: un reel dentro de 288px de ancho es una miniatura. */
:is(#kcore-root, #dbch-root) .kc-vplayer { display: block; width: 100%; height: 100%;
  background: #000; }
:is(#kcore-root, #dbch-root) .kc-vplayer iframe { display: block; width: 100%; height: 100%;
  border: 0; }

/* Dentro del teléfono la lámina tiene que llenar el hueco, no dejar el mate
   alrededor: un reel ocupa la pantalla entera del dispositivo. */
:is(#kcore-root, #dbch-root) .kc-vf-reel .kc-vit-slide { height: 100%; }
:is(#kcore-root, #dbch-root) .kc-vf-reel .kc-vit-stage { background: #000; }

@media (prefers-reduced-motion: reduce) {
  :is(#kcore-root, #dbch-root) .kc-vplay { transition: none; }
  :is(#kcore-root, #dbch-root) .kc-vplay:hover { transform: translate(-50%, -50%); }
}

/* ── La capa del video ──
   Cuelga del `body`, no de la vitrina, y por eso no necesita competir con el
   z-index de los modales que la contienen: `position: fixed` dentro de un
   elemento con `transform` deja de ser relativo al viewport, y la ficha de la
   Mesa y el modal del portal tienen ambos transformaciones. Ver la nota en
   `abrirVideo()`. */
.kc-vlbox { position: fixed; inset: 0; z-index: 2147483000;
  background: rgba(8,10,12,.92); display: flex; align-items: center;
  justify-content: center; padding: 28px; }
.kc-vlbox-c { width: 100%; max-width: 420px; aspect-ratio: 9 / 16;
  max-height: calc(100vh - 56px); border-radius: 14px; overflow: hidden;
  background: #000; box-shadow: 0 18px 60px rgba(0,0,0,.5); }
.kc-vlbox .kc-vplayer, .kc-vlbox .kc-vplayer iframe { width: 100%; height: 100%; display: block; border: 0; }
.kc-vlbox-x { all: unset; box-sizing: border-box; cursor: pointer;
  position: absolute; top: 16px; right: 18px; width: 38px; height: 38px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.14); color: #fff; font-size: 16px; line-height: 1; }
.kc-vlbox-x:hover { background: rgba(255,255,255,.24); }
.kc-vlbox-x:focus-visible { outline: 2px solid #D7F227; outline-offset: 2px; }

/* Sin portada: un lienzo neutro con el nombre del proveedor y el mismo play,
   para que el comportamiento sea UNO solo y no dos. */
/* El lienzo neutro es también el RESPALDO de la portada del proveedor: la
   miniatura se pinta como `background-image` encima de este color, así que si
   no carga (un Drive sin compartir, un YouTube sin `maxresdefault`) queda esto
   y no un icono de imagen rota. Degradación automática, sin `onerror`. */
:is(#kcore-root, #dbch-root) .kc-vreel-vacia { display: flex; align-items: center;
  justify-content: center; background-color: #15181C;
  background-size: cover; background-position: center; background-repeat: no-repeat; }
:is(#kcore-root, #dbch-root) .kc-vreel-n { font-size: 12px; color: rgba(255,255,255,.62);
  position: absolute; bottom: 16px; left: 0; right: 0; text-align: center; }

/* ══ LA VITRINA EN EL CELULAR (≤768px, 16 sep 2026) ═════════════════════════
   Salió de las pruebas de David en un iPhone, sobre la vitrina real de un
   cliente. Tres cosas se montaban ENCIMA de la pieza, que es justo lo que la
   vitrina existe para enseñar:

   1. Las flechas ‹ › del carrusel, una a cada lado del arte. Se esconden: el
      módulo ahora escucha el dedo (ver `tocaIni`/`tocaFin` en kcore-vitrina.js)
      y los puntos siguen siendo botones para saltar a una lámina.
   2. El chip «Publicado / Sin publicar», flotando arriba a la derecha.
   3. El rótulo con el nombre de la publicación, flotando abajo a la derecha,
      pegado a los puntos.

   Los dos chips pasan a ir EN FLUJO, encima del marco. Flotan sobre el arte
   porque en escritorio hay mate de sobra alrededor; en 375px ese mate mide
   18px y no hay dónde flotar sin tapar algo. */
@media (max-width: 900px) {
  :is(#kcore-root, #dbch-root) .kc-vit-nav { display: none; }
  /* ⚠️ Siguen ABSOLUTOS, pero sobre una banda reservada. Ponerlos en flujo
     parecía más limpio y no lo es: el mate centra su contenido con
     `justify-content: center` y, en cuanto el grupo pasa del alto de la caja,
     el primer hijo se sale por arriba y acaba pisando el marco (medido: chip
     469-498, marco desde 486). Con 52px de relleno arriba hay sitio para los
     dos chips sin tocar la pieza. */
  /* Y el mate deja de tener alto fijo: con `min-height` + `justify-content:
     center`, un marco más alto que la caja se desborda por ARRIBA y se mete en
     la banda de los chips (medido: caja 463px, marco 414 + banda 52 + relleno
     26 = 492). En el celular el alto lo pone la pieza. */
  :is(#kcore-root, #dbch-root) .kc-vesc {
    padding-top: 52px; min-height: 0; justify-content: flex-start; }
  /* ⚠️ Y la rejilla interna también suelta su alto fijo (`--kc-vesc-c`). Era la
     de verdad: con el alto clavado y el marco más alto, el marco se centra y se
     DESBORDA hacia arriba, metiéndose en la banda de los chips. Medido: marco
     en 482 cuando su contenedor empieza en 505. */
  :is(#kcore-root, #dbch-root) .kc-vesc .kc-vlay { height: auto; align-items: flex-start; }
  :is(#kcore-root, #dbch-root) .kc-vesc > .kc-vit-pub {
    top: 14px; left: 16px; right: auto; }
  :is(#kcore-root, #dbch-root) .kc-vit-rot {
    top: 16px; right: 16px; bottom: auto; max-width: 46%; }
  /* Los puntos crecen: 7px es un blanco imposible para un dedo. */
  :is(#kcore-root, #dbch-root) .kc-vit-dots { gap: 9px; margin-top: 14px; }
  :is(#kcore-root, #dbch-root) .kc-vit-dot { width: 9px; height: 9px; }
}

/* ══ EN EL IPAD LA PIEZA NO CRECE SIN LÍMITE (601–900px, 16 sep 2026) ═══════
   Al subir el corte del layout de app a 900, el iPad vertical entró en las
   reglas del celular, donde el mate suelta su alto fijo y la pieza manda. En un
   teléfono eso es lo correcto; en 834px de ancho un carrusel 4:5 pide 925px de
   alto y se come la pantalla entera, dejando el resto de la ficha debajo del
   pliegue. La pieza se queda en un ancho de lectura y centrada. */
/* ⚠️ Hasta 1180 y no hasta 900: la ficha colapsa a UNA columna en 1180, así que
   en un iPad horizontal el mate mide ~880px de ancho y la pieza crecía con él.
   El tope la deja en un tamaño de lectura y centrada en el mate. */
@media (min-width: 601px) and (max-width: 1180px) {
  :is(#kcore-root, #dbch-root) .kc-vesc .kc-vf { max-width: 430px; margin-inline: auto; }
}
