/**
 * Superchat · Estilos del nucleo
 *
 * Metafora de carpeta de archivador (#259): cada cara —chat, ramas, lienzo— es
 * una carpeta con su pestaña en el canto superior, escalonada y con su color.
 *
 * La pestaña se coloca respecto a SU carpeta, y como todas comparten caja, el
 * escalonado las convierte en una columna (canto izquierdo) o en una fila
 * (arriba, en movil y pantalla completa) sin cambiar nada mas.
 *
 * Todo sale de los tokens del design system. Ni un color literal: asi el mismo
 * nucleo se ve como app-skeleton aqui y como spine alli.
 *
 * LEGIBILIDAD: la carta del chat es MATE desde la #322 — el sitio donde se lee
 * no negocia con lo que haya detras. El peor fondo no es un gris amable, es la
 * Boveda con estrellas moviendose, y contra eso un 0.86 seguia siendo una
 * apuesta. Lo traslucido es el lienzo, donde no hay parrafos que leer.
 * `--sc-opacidad` se queda para las capas que si flotan (Nomai, historial).
 */

.sc-shell {
    /* Los cuatro los pisa el tirador que corresponda, y se guardan. */
    --sc-panel-ancho: 450px;      /* barra lateral */
    --sc-modal-ancho: 1100px;     /* ventana */
    --sc-modal-alto: 760px;       /* ventana */
    --sc-chat-ancho: 480px;       /* la carta del chat dentro del panel */
    --sc-opacidad: 0.86;
    --sc-radio: 14px;

    /* Lo que se come el aparato: la muesca de la camara arriba, la barra de
       gestos abajo. En el navegador de escritorio los dos valen 0, asi que esto
       no cambia nada ahi. Funciona porque `views/layout.ejs` ya declara
       `viewport-fit=cover`; sin eso, `env()` devuelve 0 siempre y el fallo se
       diagnostica en el sitio equivocado. */
    /* `max()` y no `env()` a secas: en una pestaña normal del navegador el
       inset vale 0 —la barra del navegador ya protege el borde— y la cabecera
       quedaba pegada al canto. Ese minimo es aire, no muesca. Cuando el chat
       entra en pantalla completa (ver `shell.js`), `env()` pasa a dar el valor
       de verdad y manda el. */
    /* Tres fuentes, y gana la mayor:
     *
     *   1. `env(safe-area-inset-top)`. Es el aparato diciendo que no se puede
     *      usar. En pantalla completa da el valor de verdad; en una pestaña
     *      normal da 0, y ahi es correcto porque la barra del navegador ya
     *      protege el borde.
     *   2. `--sc-veto-manual`, la calibracion del usuario. Existe porque hay
     *      moviles con camara en agujero donde el navegador reporta 0 y la
     *      camara tapa igual. Ahi no hay nada que detectar: solo se puede
     *      preguntar. Se mide una vez y se guarda.
     *   3. 6px de suelo, que es aire y no muesca.
     *
     * Nada interactivo vive dentro de esta franja, y no por acuerdo: el
     * archivador ENTERO empieza por debajo, asi que un boton nuevo no puede
     * caer ahi aunque a nadie se le ocurra pensarlo. */
    --sc-seguro-arriba: max(env(safe-area-inset-top, 0px), var(--sc-veto-manual, 0px), 6px);
    --sc-seguro-abajo: max(env(safe-area-inset-bottom, 0px), 6px);

    /* Alto real del campo de escribir. Lo mide el nucleo con un ResizeObserver
       porque el campo CRECE al escribir: un numero fijo aqui deja el ultimo
       mensaje tapado en cuanto alguien escribe tres lineas. */
    --sc-alto-entrada: 68px;

    position: fixed;
    inset: 0;
    /* ── Por que hace falta esto y no basta con `inset: 0` ────────────────
     *
     * En el navegador de un movil, un elemento `fixed` con `inset: 0` se
     * resuelve contra el viewport PEQUEÑO: el que queda con la barra de URL a la
     * vista. En cuanto la barra se esconde al hacer scroll, el area visible
     * crece y el panel se queda como estaba, y por ese hueco de abajo asoma la
     * pagina de detras — que es exactamente lo que se veia debajo del campo de
     * escribir.
     *
     * `dvh` es la altura DINAMICA: sigue al area visible mientras la barra
     * aparece y desaparece. El `vh` de arriba se queda de respaldo para
     * navegadores que no lo entiendan, y ahi no se pierde nada porque en
     * escritorio las dos miden lo mismo.
     *
     * No se ve en el emulador de un escritorio: alli no hay barra de URL que se
     * esconda, asi que las dos alturas coinciden siempre. Solo se ve en un
     * telefono de verdad.
     */
    height: 100vh;
    height: 100dvh;
    /* Y por encima de las dos, lo que el shell haya medido de verdad con
       `visualViewport` (ver `shell.js`). Solo aparece si esa API existe; si no,
       `dvh` manda. */
    height: var(--sc-alto-visible, 100dvh);
    z-index: 1200;
    pointer-events: none;
    opacity: 0;
    transition: opacity 160ms ease;
}
.sc-shell.is-abierto { pointer-events: auto; opacity: 1; }

.sc-velo {
    position: absolute; inset: 0;
    background: var(--color-scrim, #000);
    opacity: 0;
    transition: opacity 160ms ease;
    pointer-events: none;
}

/* El panel NO pinta nada: es solo la caja donde se colocan las carpetas.
 *
 * Si tuviera fondo, la franja del canto de pestañas seria una banda de color
 * muerta pegada al borde de la pantalla. Sin fondo, por ahi se ve la pagina y
 * las pestañas quedan flotando sobre ella, que es lo que se quiere. Cada carpeta
 * pinta su propio cuerpo, su borde y su sombra. */
.sc-panel {
    position: absolute;
    overflow: hidden;
    background: transparent;
    color: var(--color-text);
    transition: transform 200ms ease, opacity 160ms ease;
}
.sc-carpeta-cuerpo { box-shadow: var(--shadow-lg, -8px 0 32px rgb(0 0 0 / 0.28)); }

/* ── Modos ─────────────────────────────────────────────────────────────── */

.sc-shell--lateral .sc-panel {
    top: 0; right: 0; bottom: 0;
    width: min(var(--sc-panel-ancho), 100vw);
    border-right: none;
    transform: translateX(100%);
}
.sc-shell--lateral.is-abierto .sc-panel { transform: translateX(0); }

.sc-shell--modal .sc-panel {
    top: 50%; left: 50%;
    width: min(var(--sc-modal-ancho), 96vw);
    height: min(var(--sc-modal-alto), 92vh);
    transform: translate(-50%, -50%) scale(0.98);
    border-radius: var(--radius-lg, 16px);
    opacity: 0;
}
.sc-shell--modal.is-abierto .sc-panel { transform: translate(-50%, -50%) scale(1); opacity: 1; }
.sc-shell--modal.is-abierto .sc-velo { opacity: 0.55; pointer-events: auto; }

.sc-shell--completa .sc-panel,
.sc-shell--movil .sc-panel {
    /* `100%` del shell, que ya mide `100dvh`. Se pone tambien `dvh` directo por
       si el shell heredara una altura rara de un contenedor con transform. */
    inset: 0; width: 100%; height: 100%;
    height: 100dvh;
    transform: none; opacity: 1;
    border: none; border-radius: 0;
    /* Aqui el panel SI pinta fondo. La transparencia solo tiene sentido cuando
       el panel flota sobre una pagina que se ve —barra lateral, ventana—; a
       pantalla completa el panel es la pagina, y dejarlo transparente convierte
       la franja de las pestañas en una ventana a la cabecera de detras, que
       asoma cortada. */
    background: var(--color-bg, var(--color-surface));
}
.sc-shell--movil .sc-velo { opacity: 0; pointer-events: none; }

/* ── El aparato se queda con dos franjas ────────────────────────────────
 *
 * Arriba, la muesca de la camara con el reloj, la señal y la bateria. Abajo, la
 * barra de gestos. Ocupar esa franja no la esconde: la tapa, y lo que se pierde
 * es la hora y la bateria del usuario.
 *
 * Se resuelve empujando el panel, no cada pieza de dentro. Empujar pieza a pieza
 * es garantizar que la siguiente que se añada se olvide.
 */
/* Se empuja el ARCHIVADOR, no el panel.
 *
 * Poner `padding-top` en el panel no sirve: la caja de relleno de un elemento
 * INCLUYE su padding, asi que un hijo posicionado con `top: 0` se ancla igual
 * arriba del todo y sigue metiendose debajo de la muesca. Moviendo el
 * archivador se van con el las pestañas y los cuerpos de las tres carpetas, en
 * un solo sitio; empujar pieza a pieza es garantizar que la siguiente que se
 * añada se olvide.
 *
 * El panel se queda detras pintando esa franja, que si no la muesca recortaria
 * un trozo de pestaña a medias. */
.sc-shell--movil .sc-archivador { top: var(--sc-seguro-arriba); }

/* El rebote elastico: al llegar al final el hilo se seguia arrastrando y volvia
 * de golpe, dejando ver el fondo del panel por debajo. `contain` corta el
 * encadenado hacia la pagina, `none` quita ademas el rebote propio. Van los dos
 * porque los navegadores no coinciden en cual respetan.
 */
.sc-shell--movil .sc-panel,
.sc-shell--movil .sc-hilo { overscroll-behavior: none; }

/* ── Tirador del borde izquierdo ───────────────────────────────────────── */

/* Tres tiradores, cada uno en el borde de lo que redimensiona.
 *
 * El del panel va por DEBAJO de las pestañas (z-index 1 contra 2/3): comparte
 * franja con ellas y no debe robarles el clic.
 */
.sc-tirador {
    position: absolute;
    z-index: 1;
    background: transparent;
}
.sc-tirador[hidden] { display: none; }

/* 10px, como los otros dos. Media `--sc-tab-w` porque compartia franja con el
   canto de pestañas y su linea se pintaba al borde de esa franja; sin pestañas
   (#320) esa franja vale 0 y el tirador se quedaba sin nada que agarrar. */
.sc-tirador--panel { top: 0; left: 0; bottom: 0; width: 10px; cursor: ew-resize; }
.sc-tirador--alto  { left: 0; right: 0; bottom: 0; height: 10px; cursor: ns-resize; z-index: 7; }

/* El del chat va en SU borde izquierdo, que es la junta con el lienzo: es el
   sitio que el usuario mira cuando quiere darle mas o menos sitio al chat. */
.sc-tirador--chat {
    top: 0; left: -5px; bottom: 0;
    width: 10px;
    z-index: 7;
    cursor: ew-resize;
}

/* La linea se pinta pegada a la junta, no a media franja. */
.sc-tirador::after {
    content: '';
    position: absolute;
    background: var(--color-primary);
    opacity: 0;
    transition: opacity 120ms ease;
}
.sc-tirador--panel::after { inset: 0 auto 0 0; width: 2px; }
.sc-tirador--chat::after  { inset: 0 auto 0 4px; width: 2px; }
.sc-tirador--alto::after  { inset: auto 0 0 0; height: 2px; }

.sc-tirador:hover::after,
.sc-tirador:focus-visible::after { opacity: 1; }

/* Mientras se arrastra, el texto de la pagina no debe seleccionarse. */
body.sc-arrastrando { user-select: none; cursor: ew-resize; }

/* ── Archivador y carpetas ─────────────────────────────────────────────── */

.sc-archivador { position: absolute; inset: 0; }

/* La SECCION de una carpeta no recibe puntero; lo reciben su cuerpo y su pestaña.
 *
 * Las tres carpetas estan apiladas en la misma caja y la del chat va la ultima
 * del DOM. Sin esto, su seccion —vacia y transparente, con el cuerpo en
 * `display:none`— quedaba por encima de la carpeta activa y se tragaba todos los
 * clics: en la pestaña "Ramas" el mapa se veia pero no respondia ni al pulsar un
 * nodo ni al escribir en un campo. Un elemento sin fondo sigue siendo un
 * elemento: se pinta transparente, pero para el puntero es una pared. */
.sc-carpeta {
    position: absolute;
    display: block;
    pointer-events: none;
}
/* El tirador del chat cuelga de la seccion, no del cuerpo: sin esto se queda
   sin puntero y la junta entre carta y suelo deja de poder moverse. */
.sc-carpeta-cuerpo,
.sc-carpeta > .sc-tirador { pointer-events: auto; }

.sc-carpeta-cuerpo {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    min-height: 0;
    background: var(--sc-tono, var(--color-surface-elevated, var(--color-surface)));
    border: 1px solid var(--color-border);
    border-radius: 0 var(--sc-radio) var(--sc-radio) 0;
    overflow: hidden;
}

/**
 * El vidrio del lienzo sale de la MISMA receta que el menu de la cabecera.
 *
 * El primer intento fue `color-mix(var(--color-surface) 55%, transparent)`, y el
 * usuario lo vio antes que yo: al lado del menu de usuario se veia peor. La
 * diferencia no es el desenfoque, es **cuanto color se pinta encima**.
 *
 * `partials/header.ejs` lo tenia resuelto desde hace tiempo:
 *
 *     [data-theme="dark"]  .nav-dropdown-content { background: rgba(surface, 0.10); }
 *     [data-theme="light"] .nav-dropdown-content { background: rgba(sheen,   0.45); }
 *     [data-theme="cyber"] .nav-dropdown-content { background: rgba(surface, 0.90); }
 *
 * En oscuro son **0.10**, no 0.55. Un vidrio es casi todo desenfoque y un velo
 * finisimo de color; pintar medio gris encima no da vidrio, da niebla — que es
 * exactamente como se veia. En claro el velo va del lado del blanco
 * (`--color-sheen`) y no del gris de superficie, porque sobre una pagina clara
 * el gris ensucia. Y `cyber` casi no es vidrio a proposito.
 *
 * Se declara como variable y no se copian los numeros para que el dia que el
 * menu cambie de receta esto se pueda cambiar al lado, en un sitio, en vez de
 * descubrirlo comparando capturas.
 */
:root, [data-theme="light"] { --sc-vidrio: rgba(var(--color-sheen-rgb), 0.45); }
[data-theme="dark"]         { --sc-vidrio: rgba(var(--color-surface-rgb), 0.10); }
[data-theme="cyber"]        { --sc-vidrio: rgba(var(--color-surface-rgb), 0.90); }

/**
 * El tono de cada carpeta (#322).
 *
 * Eran tres tonos escalonados, como las lenguetas de un archivador, y el del
 * lienzo era **verde**: `color-mix(… var(--color-success) 10% …)`. Tenia sentido
 * cuando las carpetas se turnaban y el color era lo que decia cual estabas
 * viendo. Ahora se ven las dos a la vez, asi que el color ya no distingue nada
 * — solo tiñe, y de un verde que no esta en el lenguaje de esta app.
 *
 * **Lo que separa al lienzo del chat es la PROFUNDIDAD, no el color.** El
 * lienzo es el suelo: mas transparente, se le ve la pagina detras. El chat es la
 * carta de encima: mas denso y con sombra. Eso se lee sin tener que aprenderlo,
 * y funciona igual en claro y en oscuro, que es mas de lo que hacia el verde.
 *
 * Si alguna vez hace falta volver a distinguirlos por color, es una linea: se le
 * devuelve el `color-mix` a `--sc-tono` y sale sobre el vidrio como un matiz, no
 * como plastico.
 */
.sc-carpeta--lienzo { --sc-tono: var(--color-surface); }
.sc-carpeta--chat   { --sc-tono: var(--color-surface); }

/* ── La capa de Nomai ──────────────────────────────────────────────────── */
/**
 * El mapa de ramas se abre ENCIMA del panel, no al lado ni en vez de (#320).
 *
 * Fue carpeta y se turnaba con el chat, con el resultado de que entrar en el
 * mapa se llevaba por delante la cabecera del chat — y con ella el unico boton
 * que devolvia al chat. Una capa deja lo de debajo montado: la salida no puede
 * desaparecer porque nunca se fue.
 *
 * Vive dentro de `.sc-panel`, que recorta (`overflow: hidden`), asi que en
 * ventana se queda dentro de la ventana y no ocupa la pagina entera. El velo es
 * suyo y no el del shell: cerrar la capa no puede ser lo mismo que cerrar el
 * chat.
 */
.sc-capa {
    position: absolute;
    inset: 0;
    z-index: 10;
    display: flex;
    align-items: stretch;
    justify-content: center;
}
.sc-capa[hidden] { display: none; }

.sc-capa-velo {
    position: absolute;
    inset: 0;
    background: rgb(0 0 0 / 0.35);
    backdrop-filter: blur(2px);
    -webkit-backdrop-filter: blur(2px);
}

/* La caja se apoya en el vidrio del resto del panel en vez de traer el suyo:
   es una capa del mismo chat, no una ventana de otro sitio. */
.sc-capa-caja {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 0;
    width: 100%;
    max-width: 560px;
    margin: var(--space-4, 1rem);
    border: 1px solid var(--color-border);
    border-radius: var(--sc-radio);
    background: color-mix(in srgb, var(--color-surface) calc(var(--sc-opacidad) * 100%), transparent);
    backdrop-filter: blur(18px) saturate(1.15);
    -webkit-backdrop-filter: blur(18px) saturate(1.15);
    box-shadow: var(--shadow-lg, 0 16px 48px rgb(0 0 0 / 0.35));
    overflow: hidden;
}

/* En una barra lateral o un telefono, 560px con margen no dejan nada: la capa
   ocupa el panel entero y se lee como una pantalla, no como una tarjeta. */
.sc-shell--pestanas .sc-capa-caja,
.sc-shell--movil .sc-capa-caja {
    max-width: none;
    margin: 0;
    border: none;
    border-radius: 0;
}

.sc-capa-cabecera {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2, 0.5rem);
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    border-bottom: 1px solid var(--color-border);
    flex-shrink: 0;
}
.sc-capa-titulo {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2, 0.5rem);
    margin: 0;
    font-size: var(--font-size-sm, 0.9rem);
    font-weight: 600;
}
.sc-capa-titulo svg { display: block; flex-shrink: 0; }

/* Columna, no un bloque con scroll: lo que va dentro —hoy el mapa de ramas—
   se dimensiona a si mismo con `height: 100%` y lleva su propio pie. El scroll
   se queda por si lo que entra un dia es una lista. */
.sc-capa-cuerpo {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    overflow: auto;
}

/* ── Disposicion: CAPAS (PC, normal) ───────────────────────────────────── */
/* Una carpeta de suelo detras, el chat delante como carta traslucida. */

.sc-shell--capas .sc-carpeta--lienzo { inset: 0; }

/**
 * El LIENZO termina donde empieza el chat (#308).
 *
 * Que el suelo siga por debajo de la carta es la metafora de capas, y para el
 * ARBOL sigue estando bien: es un fondo, y lo que asome ya se entiende.
 *
 * El lienzo dejo de ser un fondo. Es un sitio donde se COLOCAN cosas, y una
 * franja de ~480px que no se ve nunca es una franja donde se puede soltar un
 * postit y no volver a encontrarlo.
 *
 * Y hay algo peor que lo estetico. El CONTRATO.md §6.2 dice que la oclusion se
 * descuenta, y `Shell.espacioDelSuelo()` es quien tiene que descontarla — su
 * propio comentario dice que existe "para no repetir el fallo de la Boveda:
 * medir contra el panel entero". Pero mide `zonas.lienzo` con
 * `getBoundingClientRect()`, y con `inset: 0` ESO ERA el panel entero: la
 * funcion que descuenta la oclusion devolvia el ancho sin descontar, y los
 * presentadores negociaban su tamaño creyendose ~480px mas anchos.
 *
 * Al recortar el elemento, la funcion se arregla sola porque mide el elemento.
 * Se corrige aqui y no en el JS a proposito: descontarlo solo en la funcion
 * dejaria el CSS igual, o sea que se seguiria pudiendo soltar algo donde no se
 * ve. Las dos cosas tienen que decir lo mismo, y la forma de garantizarlo es
 * que solo haya una.
 *
 * La condicion es la misma que ya usa `.sc-mapa-pie` para sus controles: con
 * el chat contraido la carta no esta y se recupera el ancho entero.
 */
/**
 * …salvo el ancho de la esquina, que SI se mete debajo (#311).
 *
 * Cortado a ras del chat, la esquina redondeada de la carta dejaba ver la
 * PAGINA por el hueco de la curva, no el lienzo. Y una carta con pagina asomando
 * por su curva no parece estar encima de nada: parece estar al lado.
 *
 * Asi que el lienzo se mete `--sc-radio` por debajo — exactamente lo que mide la
 * curva, ni un pixel mas. Con eso lo que se ve por el hueco es lienzo, que es lo
 * que hay debajo de verdad.
 *
 * **Esto no reabre el agujero de la #308**, y no de palabra: la franja tapada se
 * hace EXACTAMENTE el padding derecho de la rejilla, en la regla de abajo. Asi
 * lo que queda debajo del chat no es area util escondida, es margen — ahi no se
 * puede soltar nada porque ahi no hay celda.
 *
 * El padding normal son 12px y la curva mide 14, asi que sin la segunda regla
 * los 2px de diferencia se comerian el canto de la ultima columna. Son 2px, no
 * se perderia un postit por eso; pero la #308 existe justamente porque alguien
 * dio por bueno un "no pasa nada" de este tipo, solo que de 480px.
 */
.sc-shell--capas.sc-shell--con-suelo:not(.sc-shell--chat-contraido) .sc-carpeta--lienzo {
    right: calc(min(var(--sc-chat-ancho), 100%) - var(--sc-radio));
}
.sc-shell--capas.sc-shell--con-suelo:not(.sc-shell--chat-contraido) .sc-carpeta-cuerpo[data-sc="lienzo"] {
    padding-right: var(--sc-radio);
}

.sc-shell--capas .sc-carpeta--lienzo:not(.is-activa) .sc-carpeta-cuerpo { display: none; }

/**
 * El lienzo no tiene bordes propios (#311). Solo el chat.
 *
 * Desde la #308 el lienzo termina donde empieza la carta del chat, en vez de
 * seguir por debajo — hacia falta, porque en esa franja invisible se podia
 * soltar un postit y no volver a verlo. Pero heredaba de `.sc-carpeta-cuerpo`
 * un borde y `border-radius: 0 var(--sc-radio) var(--sc-radio) 0`, o sea las
 * dos esquinas redondeadas **justo en el canto donde se tocan**. Con el chat
 * redondeando el suyo al lado, se leian como dos tarjetas puestas de lado.
 *
 * La metafora es una carta ENCIMA de un suelo que continua. Un suelo no tiene
 * esquinas: lo que lo recorta es el panel, que ya redondea y recorta
 * (`overflow: hidden`). Asi que no se le quita solo el radio del lado derecho
 * —eso dejaria el izquierdo, que es igual de falso—: se le quitan el borde, el
 * radio y la sombra enteros, en los dos estados (con el chat desplegado y
 * contraido), porque en ninguno de los dos ese canto es un borde de verdad.
 *
 * La sombra tambien se va: una sombra en el canto que toca al chat es
 * exactamente el "aqui termino" que se intenta quitar. La unica sombra en esa
 * junta la pone el chat, hacia la izquierda, que es la que dice quien esta
 * delante.
 */
.sc-shell--capas .sc-carpeta--lienzo .sc-carpeta-cuerpo {
    border: none;
    border-radius: 0;
    box-shadow: none;

    /**
     * Y el vidrio (#322). El mismo material que el chat, distinta densidad:
     *
     *   · lienzo → 0.55 y `blur(14px)`: es el suelo, y se le tiene que ver la
     *     pagina detras. Es lo que dice que hay algo debajo.
     *   · chat   → 0.86 y `blur(18px)`: es la carta, y encima se lee.
     *
     * Si los dos llevaran el mismo numero se perderia cual esta delante, que es
     * justo lo que la metafora de capas tiene que decir sin explicarse.
     */
    background: var(--sc-vidrio);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
}

/* El chat se ancla a la DERECHA y el lienzo asoma por la IZQUIERDA: hacia la
   derecha no hay sitio que ganar, ahi esta el borde de la pantalla.
   Su ancho lo manda el tirador de su propia junta. */
.sc-shell--capas .sc-carpeta--chat {
    top: 0; right: 0; bottom: 0;
    left: auto;
    width: min(var(--sc-chat-ancho), 100%);
    z-index: 4;
}
.sc-shell--capas:not(.sc-shell--con-suelo) .sc-carpeta--chat { width: 100%; }
/**
 * La carta del chat es MATE, no vidrio.
 *
 * Lo era: `--sc-opacidad` (0.86) con `blur(18px)`. Con el lienzo hecho vidrio de
 * verdad debajo, eso ponia **dos translucidos uno encima de otro** y el
 * resultado era un vidrio de peor calidad que cualquiera de los dos por
 * separado: el desenfoque del chat desenfocaba un lienzo que ya venia
 * desenfocado, y el texto se leia sobre una mezcla imprevisible.
 *
 * El reparto que queda es mas claro de leer y mas barato de pintar:
 *
 *   · el lienzo es VIDRIO  → se le ve la pagina detras, dice que hay algo debajo
 *   · el chat es MATE      → es la superficie donde se lee, y no negocia
 *
 * La sombra hacia la izquierda es lo unico que se conserva de la carta: es lo
 * que dice quien esta delante.
 */
.sc-shell--capas .sc-carpeta--chat .sc-carpeta-cuerpo {
    inset: 0;
    border-radius: var(--sc-radio) 0 0 var(--sc-radio);
    border-right: none;
    background: var(--color-surface);
    box-shadow: var(--shadow-lg, -8px 0 32px rgb(0 0 0 / 0.28));
}

/* Sin suelo detras, la carta deja de ser carta: una tarjeta flotando sobre
   nada es un marco desperdiciado. */
.sc-shell--capas:not(.sc-shell--con-suelo) .sc-carpeta--chat .sc-carpeta-cuerpo {
    border: none;
    border-radius: 0;
    background: var(--color-surface);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    box-shadow: none;
}

.sc-shell--chat-contraido .sc-carpeta--chat {
    transform: translateX(101%);
    opacity: 0;
    pointer-events: none;
    transition: transform 220ms ease, opacity 160ms ease;
}

/* ── Disposicion: PESTAÑAS (lateral, movil) ────────────────────────────── */
/* Una sola carpeta a la vez y el chat es una pestaña mas. */

/* El panel pinta fondo en esta disposicion.
 *
 * En `capas` el panel es transparente a proposito: lo que se ve detras de la
 * carta es el SUELO, que es parte del chat. Aqui no hay suelo detras — hay
 * pagina. Con el panel transparente, la franja de 40px del canto de pestañas
 * deja ver el menu de la app, y la pestaña de arriba parece mezclarse con lo
 * que hay detras (#275).
 *
 * Las carpetas siguen pintando su tono encima; esto solo tapa el hueco. */
.sc-shell--pestanas .sc-panel { background: var(--color-bg, var(--color-surface)); }

.sc-shell--pestanas .sc-carpeta { inset: 0; }
.sc-shell--pestanas .sc-carpeta:not(.is-activa) .sc-carpeta-cuerpo { display: none; }
/* Aqui el cuerpo llega hasta el borde de la pantalla: redondearlo o ponerle
   borde por ese lado deja un pico contra el marco del navegador. */
.sc-shell--pestanas .sc-carpeta-cuerpo {
    border-radius: 0;
    border-right: none;
    border-top: none;
    border-bottom: none;
}
/* En esta disposicion ya no hay canto de pestañas (#313): con dos carpetas
   —chat y Nomai— una pestaña por carpeta es un control de mas, y se cambia con
   el boton de la cabecera. Asi que los cuerpos llegan al borde, TODOS y no solo
   el del chat: dejarle los 40px al mapa de ramas era una franja vacia con la
   pagina asomando. */
.sc-shell--pestanas .sc-carpeta-cuerpo { inset: 0; }

.sc-shell--movil .sc-tirador { display: none; }

/* ── Cabecera del chat ─────────────────────────────────────────────────── */

.sc-cabecera {
    display: flex;
    align-items: center;
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    border-bottom: 1px solid var(--color-border);
    flex-shrink: 0;
}

.sc-cabecera-fila {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2, 0.5rem);
    min-width: 0;
    width: 100%;
}
.sc-titulo {
    margin: 0;
    font-size: var(--font-size-sm, 0.9rem);
    font-weight: 600;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    min-width: 0;
}
.sc-controles { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }

/* Historial y conversacion nueva: acciones, no elecciones. Van arriba, pegadas
   a los controles de ventana, y no se encogen: son iconos. */
.sc-herramientas {
    display: flex; align-items: center; gap: 2px;
    flex-shrink: 0; margin-left: auto;
}

/* Los iconos, del mismo tamaño en todos los botones del shell. Sin fijarlo,
   cada glifo trae su propia caja y los botones quedan a distintas alturas. */
.sc-btn .material-symbols-outlined,
.sc-burbuja .material-symbols-outlined {
    font-size: 20px;
    line-height: 1;
    display: block;
}
.sc-burbuja .material-symbols-outlined { font-size: 26px; }

/* Enviar y Detener siguen siendo texto: son las dos acciones que no se
   confunden con nada y un icono ahi obligaria a adivinar. */
.sc-btn--enviar, .sc-btn--parar { font-size: var(--font-size-sm, 0.9rem); }

/* ── Historial ─────────────────────────────────────────────────────────── */

/* Cuelga de la zona del chat, no de la cabecera: en barra lateral la cabecera
   mide poco mas de 40px y una lista dentro no cabe de ninguna manera. */
.sc-historial {
    position: absolute;
    /* Por debajo de la cabecera, que ahora son DOS filas. */
    top: var(--space-2, 0.5rem); right: var(--space-3, 0.75rem);
    z-index: 3;
    width: min(20rem, calc(100% - 1.5rem));
    max-height: min(24rem, 60%);
    overflow-y: auto;
    padding: var(--space-2, 0.5rem);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 10px);
    background: var(--color-surface);
    box-shadow: 0 10px 30px rgb(0 0 0 / 0.25);
}
.sc-historial-vacio {
    margin: 0; padding: var(--space-3, 0.75rem);
    font-size: var(--font-size-xs, 0.75rem);
    color: var(--color-text-secondary);
}
.sc-historial-fila { display: flex; align-items: center; gap: var(--space-1, 0.25rem); }
.sc-historial-abrir {
    flex: 1; min-width: 0;
    text-align: left;
    background: transparent; border: 0;
    border-radius: var(--radius-sm, 6px);
    padding: var(--space-2, 0.5rem);
    color: var(--color-text);
    font: inherit; font-size: var(--font-size-sm, 0.9rem);
    cursor: pointer;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sc-historial-abrir:hover { background: var(--color-surface-hover, var(--color-border)); }
.sc-historial-fila.is-actual .sc-historial-abrir { color: var(--color-primary); font-weight: 600; }
.sc-historial-borrar {
    background: transparent; border: 0; cursor: pointer;
    color: var(--color-text-secondary);
    padding: var(--space-1, 0.25rem) var(--space-2, 0.5rem);
    border-radius: var(--radius-sm, 6px);
    font: inherit; line-height: 1;
}
.sc-historial-borrar:hover { color: var(--color-danger, #ef4444); background: var(--color-surface-hover, var(--color-border)); }
.sc-sep { width: 1px; height: 1.1em; background: var(--color-border); margin: 0 var(--space-1, 0.25rem); }

.sc-btn {
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--radius-sm, 6px);
    color: var(--color-text-secondary);
    cursor: pointer;
    padding: var(--space-1, 0.25rem) var(--space-2, 0.5rem);
    font: inherit;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.sc-btn:hover:not(:disabled) { background: var(--color-surface-hover, var(--color-border)); color: var(--color-text); }
.sc-btn.is-activo { border-color: var(--color-primary); color: var(--color-primary); }
.sc-btn:disabled { opacity: 0.35; cursor: default; }

.sc-btn:focus-visible, .sc-devolver:focus-visible,
.sc-btn-pie:focus-visible, .sc-entrada textarea:focus-visible, .sc-tirador:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}
.sc-shell--movil [data-modo] { display: none; }

/* `relative` para que el panel de historial se ancle AQUI y no al viewport:
   la carpeta del chat se mueve con los tiradores, y un panel anclado fuera se
   quedaria flotando donde el chat ya no esta. */
/* `relative` para que el panel de historial y el campo de escribir se anclen
   AQUI y no al viewport: la carpeta del chat se mueve con los tiradores, y algo
   anclado fuera se quedaria flotando donde el chat ya no esta. */
.sc-chat-zona { position: relative; display: flex; flex-direction: column; flex: 1; min-height: 0; }

.sc-devolver {
    position: absolute;
    right: var(--space-4, 1rem);
    bottom: var(--space-4, 1rem);
    z-index: 5;
    padding: var(--space-2, 0.5rem) var(--space-4, 1rem);
    border-radius: 999px;
    border: 1px solid var(--color-border);
    background: var(--color-surface);
    color: var(--color-text);
    cursor: pointer;
    font: inherit;
    box-shadow: var(--shadow-md, 0 4px 16px rgb(0 0 0 / 0.25));
}
.sc-devolver[hidden] { display: none; }

/* ── Hilo ──────────────────────────────────────────────────────────────── */

.sc-hilo {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-4, 1rem);
    /* El hueco de abajo es el alto del campo de escribir, que flota encima. Sin
       el, el ultimo mensaje se lee a medias por detras del campo — y como el
       campo crece al escribir, tiene que ser una medida y no un numero. */
    padding-bottom: calc(var(--sc-alto-entrada) + var(--space-4, 1rem));
    display: flex;
    flex-direction: column;
    gap: var(--space-4, 1rem);
    scroll-behavior: smooth;

    /* Que no arrastre la pagina de detras al llegar al final. */
    overscroll-behavior: contain;

    /* ── El texto se disuelve, no se corta ──────────────────────────────
     *
     * Arriba, al pasar por debajo de la cabecera; abajo, al pasar por detras del
     * campo de escribir. Una linea de corte dice "aqui se acaba"; un degradado
     * dice "sigue habiendo conversacion", que es lo que hay.
     *
     * Los dos degradados van en la MISMA mascara: dos `mask-image` separadas se
     * pisan, no se suman.
     */
    -webkit-mask-image: linear-gradient(to bottom,
        transparent 0,
        #000 28px,
        #000 calc(100% - var(--sc-alto-entrada) - 8px),
        transparent calc(100% - var(--sc-alto-entrada) + 24px));
    mask-image: linear-gradient(to bottom,
        transparent 0,
        #000 28px,
        #000 calc(100% - var(--sc-alto-entrada) - 8px),
        transparent calc(100% - var(--sc-alto-entrada) + 24px));
}

.sc-msg { display: flex; flex-direction: column; gap: var(--space-1, 0.25rem); max-width: min(46rem, 100%); }
.sc-msg--user { align-self: flex-end; align-items: flex-end; }
.sc-msg--assistant { align-self: flex-start; }

.sc-msg-cuerpo {
    overflow-wrap: anywhere;
}
.sc-msg--user .sc-msg-cuerpo {
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    border-radius: var(--radius-md, 10px);
    background: var(--color-primary);
    color: var(--color-on-primary, #fff);
}
.sc-msg--assistant .sc-msg-cuerpo {
    padding: var(--space-2, 0.5rem) 0;
    color: var(--color-text);
}
.sc-msg-cuerpo :where(p, ul, ol, pre, blockquote, table, hr, h1, h2, h3, h4, h5, h6):first-child {
    margin-top: 0;
}
.sc-msg-cuerpo :where(p, ul, ol, pre, blockquote, table, hr, h1, h2, h3, h4, h5, h6):last-child {
    margin-bottom: 0;
}

/* ── Tipografía del contenido de mensajes (Markdown) ───────────────────── */

/* Párrafos */
.sc-msg-cuerpo p {
    margin-top: 0;
    margin-bottom: var(--space-3, 0.75rem);
    line-height: 1.55;
}

/* Encabezados: escala descendente pegada a lo que titula */
.sc-msg-cuerpo h1 {
    font-size: var(--font-size-xl, 1.25rem);
    font-weight: 700;
    line-height: 1.3;
    margin-top: var(--space-5, 1.25rem);
    margin-bottom: var(--space-2, 0.5rem);
}

.sc-msg-cuerpo h2 {
    font-size: var(--font-size-lg, 1.125rem);
    font-weight: 600;
    line-height: 1.35;
    margin-top: var(--space-4, 1rem);
    margin-bottom: var(--space-2, 0.5rem);
}

.sc-msg-cuerpo h3 {
    font-size: var(--font-size-base, 1rem);
    font-weight: 600;
    line-height: 1.4;
    margin-top: var(--space-3, 0.75rem);
    margin-bottom: var(--space-1, 0.25rem);
}

/* De h4 para abajo NO se sigue encogiendo, a proposito.
   La escala bajaba a 14px y luego a 12px, con el parrafo en 16: un `####`
   salia MAS PEQUEÑO que el texto que introduce, y eso se lee como pie de
   foto, no como titulo. Los agentes escriben `####` a menudo, asi que se
   notaba. Del cuerpo hacia abajo la jerarquia la marcan el peso y el
   espacio, no el tamaño. */
.sc-msg-cuerpo h4 {
    font-size: var(--font-size-base, 1rem);
    font-weight: 700;
    line-height: 1.4;
    margin-top: var(--space-3, 0.75rem);
    margin-bottom: var(--space-1, 0.25rem);
}

/* h5 y h6 tampoco bajan del cuerpo. Se distinguen de h4 —y entre si— por el
   peso y por ir en secundario, no por ser mas chicos. Antes median los dos
   12px, o sea que ademas eran indistinguibles el uno del otro. */
.sc-msg-cuerpo h5 {
    font-size: var(--font-size-base, 1rem);
    font-weight: 600;
    line-height: 1.4;
    margin-top: var(--space-2, 0.5rem);
    margin-bottom: var(--space-1, 0.25rem);
}

.sc-msg-cuerpo h6 {
    font-size: var(--font-size-base, 1rem);
    font-weight: 600;
    color: var(--color-text-secondary);
    line-height: 1.4;
    margin-top: var(--space-2, 0.5rem);
    margin-bottom: var(--space-1, 0.25rem);
}

/* Listas */
.sc-msg-cuerpo ul,
.sc-msg-cuerpo ol {
    padding-left: var(--space-5, 1.25rem);
    margin-top: var(--space-2, 0.5rem);
    margin-bottom: var(--space-3, 0.75rem);
}

.sc-msg-cuerpo li {
    margin-top: var(--space-1, 0.25rem);
    margin-bottom: var(--space-1, 0.25rem);
    line-height: 1.5;
}

.sc-msg-cuerpo li > :where(ul, ol) {
    margin-top: var(--space-1, 0.25rem);
    margin-bottom: 0;
    padding-left: var(--space-4, 1rem);
}

/* Código en línea: usa currentColor para funcionar en usuario y asistente */
.sc-msg-cuerpo :not(pre) > code {
    background: color-mix(in srgb, currentColor 12%, transparent);
    border-radius: var(--radius-sm, 0.25rem);
    padding: 0.1em 0.35em;
    font-family: var(--font-family-mono, ui-monospace, monospace);
    font-size: 0.9em;
}

/* Bloques de código preformateado */
.sc-msg-cuerpo pre {
    overflow-x: auto;
    background: color-mix(in srgb, currentColor 8%, transparent);
    border: 1px solid color-mix(in srgb, currentColor 14%, transparent);
    padding: var(--space-3, 0.75rem);
    border-radius: var(--radius-sm, 0.25rem);
    font-family: var(--font-family-mono, ui-monospace, monospace);
    font-size: var(--font-size-sm, 0.875rem);
    line-height: 1.45;
    margin-top: var(--space-2, 0.5rem);
    margin-bottom: var(--space-3, 0.75rem);
}

.sc-msg-cuerpo pre code {
    background: transparent;
    padding: 0;
    border-radius: 0;
    font-family: inherit;
    font-size: inherit;
}

/* Tablas: scroll horizontal autónomo y bordes adaptables */
.sc-msg-cuerpo table {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    margin-top: var(--space-2, 0.5rem);
    margin-bottom: var(--space-3, 0.75rem);
    -webkit-overflow-scrolling: touch;
}

.sc-msg-cuerpo th,
.sc-msg-cuerpo td {
    padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    border: 1px solid color-mix(in srgb, currentColor 18%, transparent);
    text-align: left;
    font-size: var(--font-size-sm, 0.875rem);
}

.sc-msg-cuerpo th {
    background: color-mix(in srgb, currentColor 12%, transparent);
    font-weight: 600;
}

.sc-msg-cuerpo tr:nth-child(even) td {
    background: color-mix(in srgb, currentColor 4%, transparent);
}

/* Citas */
.sc-msg-cuerpo blockquote {
    margin: var(--space-2, 0.5rem) 0 var(--space-3, 0.75rem) 0;
    padding-left: var(--space-3, 0.75rem);
    border-left: 3px solid color-mix(in srgb, currentColor 30%, transparent);
    color: color-mix(in srgb, currentColor 85%, transparent);
    font-style: italic;
}

/* Enlaces */
.sc-msg-cuerpo a {
    color: inherit;
    text-decoration: underline;
    text-underline-offset: 2px;
    font-weight: 500;
    transition: opacity 120ms ease;
}

.sc-msg-cuerpo a:hover {
    opacity: 0.8;
}

/* Regla horizontal */
.sc-msg-cuerpo hr {
    border: 0;
    height: 1px;
    background: color-mix(in srgb, currentColor 18%, transparent);
    margin: var(--space-3, 0.75rem) 0;
}

/* Énfasis y formato de texto */
.sc-msg-cuerpo strong {
    font-weight: 600;
}

.sc-msg-cuerpo em {
    font-style: italic;
}

.sc-msg-cuerpo del {
    text-decoration: line-through;
    opacity: 0.75;
}

.sc-msg-pie {
    display: flex; align-items: center; gap: var(--space-2, 0.5rem);
    font-size: var(--font-size-xs, 0.75rem);
    color: var(--color-text-secondary);
    min-height: 1.2em;
}
.sc-versiones { font-variant-numeric: tabular-nums; }

.sc-btn-pie {
    background: none; border: none; padding: 0;
    color: var(--color-text-secondary);
    cursor: pointer; font: inherit;
    text-decoration: underline; text-underline-offset: 2px;
    opacity: 0; transition: opacity 120ms ease;
}
.sc-msg:hover .sc-btn-pie, .sc-btn-pie:focus-visible { opacity: 1; }

/* ── Bloques ───────────────────────────────────────────────────────────── */

.sc-bloque + .sc-bloque { margin-top: var(--space-3, 0.75rem); }

.sc-bloque--traza {
    font-size: var(--font-size-xs, 0.75rem);
    color: var(--color-text-secondary);
    display: flex; align-items: center; gap: var(--space-2, 0.5rem);
}
.sc-bloque--traza::before {
    content: ''; width: 6px; height: 6px; border-radius: 50%;
    background: var(--color-text-secondary); flex-shrink: 0;
}
.sc-bloque--traza.is-curso::before { background: var(--color-warning, #ea933e); }
.sc-bloque--traza.is-ok::before    { background: var(--color-success, #10b981); }
.sc-bloque--traza.is-error::before { background: var(--color-danger, #ef4444); }

.sc-bloque--adjunto img { max-width: 100%; height: auto; display: block; border-radius: var(--radius-sm, 6px); }

.sc-bloque--presentador.is-respaldo {
    font-size: var(--font-size-sm, 0.875rem);
    color: var(--color-text-secondary);
    border-left: 2px solid var(--color-border);
    padding-left: var(--space-3, 0.75rem);
}

/* ── Estado vacio y espera ─────────────────────────────────────────────── */

/* El saludo de una conversacion vacia. Va centrado y en secundario porque NO es
   un mensaje: es la interfaz diciendo donde estas. Pintarlo como una burbuja
   del asistente haria creer que el agente ya ha hablado. */
.sc-bienvenida {
    margin: auto 0;
    padding: var(--space-6, 1.5rem) var(--space-4, 1rem);
    text-align: center;
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm, 0.9rem);
    line-height: 1.6;
}
.sc-bienvenida p { margin: 0 0 var(--space-3, 0.75rem); }
.sc-bienvenida p:last-child { margin-bottom: 0; }

/* Los tres puntos mientras el destino piensa y no ha escrito todavia. Sin esto
   la burbuja esta vacia y parece que no pasa nada — que es lo que se ve cuando
   un CLI tarda dos minutos en arrancar. */
.sc-pensando { display: flex; gap: 5px; padding: var(--space-2, 0.5rem) 0; }
.sc-pensando span {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--color-text-secondary);
    animation: sc-latido 1.2s infinite ease-in-out;
}
.sc-pensando span:nth-child(2) { animation-delay: 0.15s; }
.sc-pensando span:nth-child(3) { animation-delay: 0.3s; }

@keyframes sc-latido {
    0%, 60%, 100% { opacity: 0.25; transform: translateY(0); }
    30%           { opacity: 1;    transform: translateY(-3px); }
}

/* ── Entrada ───────────────────────────────────────────────────────────── */

/* ── El campo de escribir flota sobre el hilo ───────────────────────────
 *
 * Antes era una barra opaca al final de una columna: el hilo terminaba contra
 * ella y la conversacion parecia acabarse ahi. Ahora los mensajes se ven pasar
 * por detras y la conversacion sigue hacia abajo.
 *
 * Sin borde superior a proposito: la linea es justo lo que devolvia la sensacion
 * de barra. Lo que separa es el desenfoque.
 */
.sc-entrada {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    padding: var(--space-3, 0.75rem) var(--space-4, 1rem);
    /* La barra de gestos del movil se come el borde de abajo si no se le deja
       su sitio. En escritorio `env()` vale 0 y esto no suma nada. */
    padding-bottom: calc(var(--space-3, 0.75rem) + var(--sc-seguro-abajo));
    flex-shrink: 0;

    /* El suelo minimo de opacidad del §6.3 del contrato. El peor fondo no es un
       gris amable: es la Boveda con estrellas moviendose, y ahi es donde se
       comprueba si el texto que uno escribe se lee. */
    background: color-mix(in srgb, var(--color-surface) 82%, transparent);
    -webkit-backdrop-filter: blur(16px) saturate(140%);
    backdrop-filter: blur(16px) saturate(140%);

    /**
     * Y su propia esquina, que es lo que redondea la del CHAT (#311).
     *
     * `backdrop-filter` obliga al navegador a componer este elemento aparte, y
     * esa capa se pinta como un RECTANGULO: el `overflow: hidden` con radio del
     * cuerpo de carpeta recorta el contenido normal, pero no el fondo
     * desenfocado de un descendiente compuesto. Como el compositor esta pegado
     * al fondo del chat, su rectangulo rellenaba la muesca de la esquina
     * inferior izquierda y la carta parecia acabar en pico.
     *
     * Por eso la de arriba se veia bien y la de abajo no: la cabecera no lleva
     * `backdrop-filter`, asi que ahi no hay ninguna capa que se salga.
     *
     * La solucion no es quitarle el desenfoque —lo que difumina no es la pagina,
     * son los MENSAJES que pasan por detras al hacer scroll, y sin eso el hilo
     * parece cortado en seco— sino darle la misma esquina que la carta. Un
     * rectangulo redondeado ya no se sale.
     *
     * `--sc-radio` solo abajo a la izquierda porque es el unico canto donde la
     * carta del chat se redondea; en barra lateral y movil llega al borde de la
     * pantalla y ahi vale 0.
     */
    border-radius: 0;
}
.sc-shell--capas .sc-carpeta--chat .sc-entrada { border-radius: 0 0 0 var(--sc-radio); }

.sc-entrada-caja {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 0.5rem);
    background: var(--color-surface);
    display: flex;
    flex-direction: column;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
    overflow: hidden;
}

.sc-entrada-caja:focus-within {
    border-color: var(--color-primary);
}

.sc-entrada.is-bifurcando .sc-entrada-caja {
    border-color: var(--color-primary);
}

.sc-entrada textarea {
    border: none;
    background: transparent;
    outline: none;
    width: 100%;
    box-sizing: border-box;
    resize: none;
    min-height: 2.5rem;
    max-height: 200px;
    padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    color: var(--color-text);
    font: inherit;
    line-height: 1.5;
    display: block;
}

.sc-entrada-barra {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-2, 0.5rem);
    margin-top: var(--space-2, 0.5rem);
    padding: 0;
    min-height: 28px;
    flex-wrap: wrap;
}

.sc-entrada-opciones {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    flex: 1 1 auto;
    flex-wrap: wrap;
}

/* Desplegables de destino, espacio y modelo dentro del compositor (#296) */
.sc-selectores {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 4px;
    min-width: 0;
}
.sc-selectores[hidden] { display: none; }

/* ── Pastilla de eleccion (#307) ─────────────────────────────────────────
   Sustituye a los `<select>` nativos. El motivo no era el disparador -eso se
   arregla con `appearance:none`-: era que **el menu lo pintaba el sistema
   operativo**, sin nuestros colores, sin nuestros grupos y sin marca de cual
   estaba elegido. Ver §6 del manual de identidad. */

.sc-pastilla-sel { position: relative; display: inline-flex; min-width: 0; flex-shrink: 1; }
.sc-pastilla-sel[hidden] { display: none; }

.sc-pastilla-sel__disparador {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    border: none;
    background: transparent;
    color: var(--color-text-secondary);
    font: inherit;
    font-size: var(--font-size-xs, 0.75rem);
    line-height: 1.4;
    padding: 2px var(--space-2, 0.5rem);
    border-radius: var(--radius-full, 9999px);
    cursor: pointer;
    max-width: 10rem;
    min-width: 0;
    transition: background-color 120ms ease, color 120ms ease;
}

.sc-pastilla-sel__texto {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* El chevron hereda el color: por eso va como SVG en el DOM y no como
   data-URI de fondo, donde el color se escribe a fuego y el linter no lo ve. */
.sc-pastilla-sel__chevron {
    width: 10px;
    height: 10px;
    flex-shrink: 0;
    transition: transform 120ms ease;
}
.sc-pastilla-sel.is-abierto .sc-pastilla-sel__chevron { transform: rotate(180deg); }

.sc-pastilla-sel__disparador:hover,
.sc-pastilla-sel.is-abierto .sc-pastilla-sel__disparador {
    background: color-mix(in srgb, currentColor 10%, transparent);
    color: var(--color-text);
}
.sc-pastilla-sel__disparador:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
    color: var(--color-text);
}

/* Con algo elegido a mano pesa mas que con el valor por defecto: se distingue
   "esto lo decidi yo" de "que decida el nodo" sin leer el texto. */
.sc-pastilla-sel.is-elegido .sc-pastilla-sel__disparador {
    color: var(--color-text);
    font-weight: 600;
}

/* ── El menu ──────────────────────────────────────────────────────────── */

.sc-pastilla-sel__menu {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    z-index: var(--z-dropdown);
    min-width: 12rem;
    max-width: min(20rem, 90vw);
    max-height: 18rem;
    overflow-y: auto;
    padding: var(--space-1, 0.25rem);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md, 0.5rem);
    box-shadow: var(--shadow-lg);
}
.sc-pastilla-sel__menu[hidden] { display: none; }

/* El compositor vive pegado al borde inferior del panel, asi que "no cabe
   abajo" es el caso NORMAL, no el raro. Lo decide el componente al abrir. */
.sc-pastilla-sel__menu.is-arriba {
    top: auto;
    bottom: calc(100% + 6px);
}

/* Rotulo de grupo: mas pequeño, mas ligero y mas apagado que lo que agrupa
   (manual de identidad §5.1). Las tres señales en la misma direccion. */
.sc-pastilla-sel__grupo {
    padding: var(--space-2, 0.5rem) var(--space-2, 0.5rem) var(--space-1, 0.25rem);
    font-size: var(--font-size-xs, 0.75rem);
    font-weight: 500;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-text-secondary);
}

.sc-pastilla-sel__opcion {
    display: grid;
    grid-template-columns: 1rem 1fr;
    align-items: baseline;
    gap: var(--space-1, 0.25rem) var(--space-2, 0.5rem);
    padding: var(--space-2, 0.5rem);
    border-radius: var(--radius-sm, 0.25rem);
    font-size: var(--font-size-sm, 0.875rem);
    color: var(--color-text);
    cursor: pointer;
}

.sc-pastilla-sel__opcion:hover,
.sc-pastilla-sel__opcion.is-activa {
    background: color-mix(in srgb, currentColor 8%, transparent);
}
.sc-pastilla-sel__opcion:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: -2px;
}

/* La marca ocupa su hueco SIEMPRE, elegida o no: si apareciera y desapareciera,
   el texto de las opciones bailaria al moverse por la lista. */
.sc-pastilla-sel__marca {
    color: var(--color-primary);
    visibility: hidden;
}
.sc-pastilla-sel__opcion.is-elegida .sc-pastilla-sel__marca { visibility: visible; }
.sc-pastilla-sel__opcion.is-elegida { font-weight: 600; }

.sc-pastilla-sel__detalle {
    grid-column: 2;
    font-size: var(--font-size-xs, 0.75rem);
    color: var(--color-text-secondary);
}

/* Un destino caido se ofrece igual -elegirlo dice POR QUE no contesta-, pero
   se ve que lo esta. */
.sc-pastilla-sel__opcion[data-estado="apagado"],
.sc-pastilla-sel__opcion[data-estado="offline"] {
    color: var(--color-text-secondary);
}

.sc-entrada-acciones {
    display: flex;
    align-items: center;
    gap: var(--space-1, 0.25rem);
    flex-shrink: 0;
    margin-left: auto;
}

.sc-btn--enviar {
    border-color: var(--color-primary);
    background: var(--color-primary);
    color: var(--color-on-primary, #fff);
    padding: 2px var(--space-3, 0.75rem);
    border-radius: var(--radius-sm, 0.25rem);
    font-size: var(--font-size-xs, 0.75rem);
    font-weight: 500;
}
.sc-btn--enviar:hover:not(:disabled) { filter: brightness(1.08); }
.sc-btn--parar {
    border-color: var(--color-border);
    color: var(--color-text);
    padding: 2px var(--space-3, 0.75rem);
    border-radius: var(--radius-sm, 0.25rem);
    font-size: var(--font-size-xs, 0.75rem);
}

.sc-btn--adjuntar {
    width: 28px;
    height: 28px;
    padding: 0;
    border-radius: var(--radius-sm, 0.25rem);
    color: var(--color-text-secondary);
    background: transparent;
    border: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    transition: background-color 120ms ease, color 120ms ease;
}
.sc-btn--adjuntar:hover {
    background: color-mix(in srgb, currentColor 10%, transparent);
    color: var(--color-text);
}
.sc-btn--adjuntar:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}
.sc-btn--adjuntar .material-symbols-outlined {
    font-size: 20px;
}

.sc-aviso-bifurcacion { margin: 0 0 var(--space-2, 0.5rem); font-size: var(--font-size-xs, 0.75rem); color: var(--color-primary); }

/* Avisos de la INTERFAZ, no del agente: "solo caben 4 imagenes" y compañia.
   Antes se decian fabricando un mensaje del asistente, que ademas acababa en el
   historial que se le manda al modelo. */
.sc-aviso-entrada {
    margin: 0 0 var(--space-2, 0.5rem);
    font-size: var(--font-size-xs, 0.75rem);
    color: var(--color-warning, #f59e0b);
}

/* ── Adjuntos ──────────────────────────────────────────────────────────── */

.sc-adjuntos {
    display: flex; flex-wrap: wrap; gap: var(--space-2, 0.5rem);
    padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem) 0;
}
.sc-adjunto { position: relative; width: 56px; height: 56px; }
.sc-adjunto img {
    width: 100%; height: 100%;
    object-fit: cover;
    border-radius: var(--radius-sm, 0.25rem);
    border: 1px solid var(--color-border);
}
.sc-adjunto-quitar {
    position: absolute; top: -6px; right: -6px;
    width: 18px; height: 18px;
    display: grid; place-items: center;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: 10px; line-height: 1; cursor: pointer; padding: 0;
}
.sc-adjunto-quitar:hover { color: var(--color-danger, #ef4444); }

/* Que se ve arrastrando un fichero por encima. Sin señal, soltarlo es una
   apuesta: no hay forma de saber si el panel lo va a coger o si el navegador
   va a navegar al fichero. */
.sc-panel.is-arrastrando { outline: 2px dashed var(--color-primary); outline-offset: -6px; }

/* Los adjuntos ya enviados, dentro de la burbuja del mensaje. */
.sc-bloque--adjunto img { max-width: 100%; border-radius: var(--radius-sm, 0.25rem); }

/* El desenfoque es caro y en equipos justos se nota al hacer scroll; sin el la
   carta sigue siendo legible porque la opacidad ya lo garantiza. */
@media (prefers-reduced-transparency: reduce) {
    .sc-shell--capas .sc-carpeta--chat .sc-carpeta-cuerpo {
        background: var(--color-surface);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }

    /* El campo de escribir tampoco es una excepcion, y menos que ninguno: es
       donde se lee lo que uno esta escribiendo. Sin desenfoque, un mensaje
       pasando por detras del texto propio lo vuelve ilegible. */
    .sc-entrada {
        background: var(--color-surface);
        border-top: 1px solid var(--color-border);
        backdrop-filter: none;
        -webkit-backdrop-filter: none;
    }
    /* Con el campo opaco, el degradado de abajo sobra: ya no se ve nada por
       detras que disolver, y dejarlo solo desvanece texto sin motivo. */
    .sc-hilo {
        -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 28px);
        mask-image: linear-gradient(to bottom, transparent 0, #000 28px);
    }
}

@media (prefers-reduced-motion: reduce) {
    /* Los puntos se quedan quietos pero se siguen VIENDO: son la unica señal de
       que algo esta pasando, y quitarlos del todo dejaria la espera muda. */
    .sc-pensando span { animation: none; opacity: 0.6; }

    .sc-shell, .sc-panel, .sc-velo, .sc-carpeta, .sc-btn-pie, .sc-hilo {
        transition: none; scroll-behavior: auto;
    }
}

/* ── Mapa de ramas (Escritura Nomai) ───────────────────────────────────── */

.sc-mapa {
    position: relative;
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}
.sc-mapa-lienzo {
    flex: 1;
    min-height: 0;
    display: block;
    /* Sin esto el gesto de arrastrar el mapa lo interpreta el navegador como
       desplazar la pagina, y en tactil el mapa no se puede mover. */
    touch-action: none;
    cursor: grab;
}
.sc-mapa-lienzo[hidden] { display: none; }

.sc-mapa-vacio {
    position: absolute;
    inset: 0;
    margin: 0;
    display: grid;
    place-items: center;
    padding: var(--space-6, 1.5rem);
    text-align: center;
    color: var(--color-text-secondary);
    font-size: var(--font-size-sm, 0.875rem);
}
.sc-mapa-vacio[hidden] { display: none; }

.sc-mapa-pie {
    flex-shrink: 0;
    height: 34px;
    display: flex;
    align-items: center;
    gap: var(--space-3, 0.75rem);
    padding: 0 var(--space-3, 0.75rem);
    border-top: 1px solid var(--color-border);
    font-size: var(--font-size-xs, 0.75rem);
    color: var(--color-text-secondary);
}
.sc-mapa-pie span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* En capas, la carta del chat tapa la franja derecha del suelo (CONTRATO.md
   §6.2). El LIENZO puede seguir por debajo —de eso trata la metafora—, pero los
   CONTROLES no: un boton medio escondido detras de una carta traslucida no se
   ve, no se pulsa y encima parece un fallo de pintado. Con el chat contraido la
   carta ya no esta y la franja se recupera. */
.sc-shell--capas.sc-shell--con-suelo:not(.sc-shell--chat-contraido) .sc-mapa-pie {
    padding-right: calc(min(var(--sc-chat-ancho), 100%) + var(--space-3, 0.75rem));
}

.sc-mapa-btn {
    flex-shrink: 0;
    background: transparent;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm, 6px);
    color: var(--color-text-secondary);
    cursor: pointer;
    font: inherit;
    padding: 2px var(--space-2, 0.5rem);
}
.sc-mapa-btn:hover { color: var(--color-text); border-color: var(--color-text-secondary); }
.sc-mapa-btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* El globo se coloca dentro del mapa y no puede robar el puntero: si lo
   capturase, pasar el raton sobre el se leeria como salir del nodo y parpadearia. */
.sc-mapa-globo {
    position: absolute;
    z-index: 2;
    pointer-events: none;
    max-width: 200px;
    padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm, 6px);
    background: var(--color-surface);
    box-shadow: var(--shadow-md, 0 4px 16px rgb(0 0 0 / 0.25));
    font-size: var(--font-size-xs, 0.75rem);
    line-height: 1.35;
    color: var(--color-text);
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.sc-mapa-globo[hidden] { display: none; }
.sc-mapa-globo strong { color: var(--color-text-secondary); }
.sc-mapa-globo.is-activo strong { color: var(--color-primary); }
.sc-mapa-globo span { overflow-wrap: anywhere; }

/* ── Selector de versiones ─────────────────────────────────────────────── */

.sc-versiones { display: inline-flex; align-items: center; gap: 2px; }
.sc-versiones-cuenta { font-variant-numeric: tabular-nums; }

.sc-versiones-btn {
    background: none;
    border: none;
    padding: 0 var(--space-1, 0.25rem);
    color: var(--color-text-secondary);
    cursor: pointer;
    font: inherit;
    line-height: 1;
}
.sc-versiones-btn:hover:not(:disabled) { color: var(--color-primary); }
.sc-versiones-btn:disabled { opacity: 0.3; cursor: default; }
.sc-versiones-btn:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; border-radius: 3px; }

/* ── La burbuja ────────────────────────────────────────────────────────────
 *
 * El boton flotante que abre el chat, heredado de la barra lateral vieja
 * (#267). Se queda en la misma esquina a proposito: es donde la gente ya sabe
 * buscarlo, y mover un boton que funciona es cobrar por una mejora que no pidio
 * nadie.
 *
 * `z-index` por debajo de los avisos: un chat cerrado no puede tapar un toast.
 */
.sc-burbuja {
    /* La coloca la torre de la esquina (#326), no ella: `position: fixed` con su
       propio `bottom` y su propio z-index es lo que la hacia tapar el menu de
       los modulos. Se conserva el `fixed` como respaldo para cualquier pagina
       que pinte la burbuja sin el layout; dentro de la torre lo pisa
       `.app-torre > *`. */
    position: fixed;
    right: var(--space-4, 1rem);
    /* Sobre la barra de gestos del movil, igual que el campo de escribir. */
    bottom: calc(var(--space-4, 1rem) + env(safe-area-inset-bottom, 0px));
    z-index: 1150;

    width: 52px; height: 52px;
    display: grid; place-items: center;
    border: 1px solid var(--color-border);
    border-radius: 50%;
    background: var(--color-surface-elevated, var(--color-surface));
    color: var(--color-primary);
    cursor: pointer;
    box-shadow: var(--shadow-lg, 0 8px 24px rgb(0 0 0 / 0.3));
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.sc-burbuja:hover { transform: translateY(-2px); }
.sc-burbuja:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }

/* Con el panel abierto la burbuja sobra: el panel ya tiene su boton de cerrar,
   y dejarla flotando encima es un boton pegado sobre la conversacion. */
body:has(.sc-shell.is-abierto) .sc-burbuja { display: none; }

/* El punto de estado. Encendido = hay al menos un equipo conectado.
 *
 * Apagado NO esconde la burbuja, al reves que la version vieja, que solo se
 * pintaba `if (onlineNodesCount > 0)`. Con todos los equipos apagados aquello
 * desaparecia, y eso no dice "no hay ningun equipo encendido": dice "esta app no
 * tiene chat". */
.sc-burbuja-pulso {
    position: absolute; top: 6px; right: 6px;
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--color-text-secondary);
    border: 2px solid var(--color-surface-elevated, var(--color-surface));
}
.sc-burbuja-pulso.is-vivo { background: var(--color-success, #10b981); }

@media (prefers-reduced-motion: reduce) {
    .sc-burbuja { transition: none; }
    .sc-burbuja:hover { transform: none; }
}

/* ── Ver la franja vedada ──────────────────────────────────────────────────
 *
 * La camara no sale en las capturas, asi que "ahi esta tapando" no se puede
 * comprobar mirando una. Con `?sc-seguro=1` en la URL la franja se pinta y se
 * arrastra hasta que despeje la camara; lo que se mida se guarda.
 *
 * En la URL y no en un ajuste escondido a proposito: esto se calibra desde el
 * telefono, donde no hay consola ni inspector.
 */
.sc-zona-segura { display: none; }

.sc-shell[data-sc-seguro="ver"] .sc-zona-segura {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    position: absolute;
    top: 0; left: 0; right: 0;
    height: var(--sc-seguro-arriba);
    min-height: 20px;
    z-index: 20;
    background: repeating-linear-gradient(
        45deg,
        color-mix(in srgb, var(--color-danger, #ef4444) 22%, transparent) 0 6px,
        transparent 6px 12px);
    border-bottom: 1px dashed var(--color-danger, #ef4444);
    /* Se ve pero no estorba: lo de debajo se sigue pudiendo tocar. */
    pointer-events: none;
    font-size: 10px;
    line-height: 1.4;
    color: var(--color-danger, #ef4444);
    font-family: var(--font-mono, monospace);
}

/* El tirador para calibrar SI recibe toque. */
.sc-shell[data-sc-seguro="ver"] .sc-zona-segura-tirador {
    position: absolute;
    left: 0; right: 0; bottom: -14px;
    height: 28px;
    pointer-events: auto;
    cursor: ns-resize;
    touch-action: none;
}
.sc-shell[data-sc-seguro="ver"] .sc-zona-segura-tirador::after {
    content: '';
    position: absolute;
    left: 50%; bottom: 12px;
    width: 44px; height: 4px;
    transform: translateX(-50%);
    border-radius: 2px;
    background: var(--color-danger, #ef4444);
}

/* ── El lienzo: postits y sus pastillas (#254) ───────────────────────────
 *
 * El lienzo es una PROYECCION de la rama visible, no un documento aparte. Lo
 * que se ve aqui son los presentadores de los mensajes que estan en pantalla,
 * el ultimo arriba — el "z-index" es la fecha del mensaje, y por eso no hay
 * nada que guardar.
 *
 * En el hilo, cada uno deja su PASTILLA: el mismo respaldo de texto que
 * tendria si no cupiera en ningun sitio. No se deja el hueco vacio a proposito,
 * porque la conversacion se tiene que seguir entendiendo si se vacia el lienzo,
 * si se lee desde un CLI o si se exporta.
 */

.sc-pastilla {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    padding: 4px 10px;
    border: 1px solid var(--color-border, rgba(255, 255, 255, 0.14));
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font: inherit;
    font-size: 0.82em;
    text-align: left;
    cursor: pointer;
    /* Un titulo largo no puede romper la burbuja. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    transition: background 0.15s ease, border-color 0.15s ease;
}

.sc-pastilla::before {
    content: '';
    flex: 0 0 auto;
    width: 7px; height: 7px;
    border-radius: 2px;
    background: var(--color-primary, #6366f1);
}

.sc-pastilla:hover,
.sc-pastilla:focus-visible {
    background: color-mix(in srgb, var(--color-primary, #6366f1) 12%, transparent);
    border-color: var(--color-primary, #6366f1);
}

/**
 * Los postits se apilan en rejilla (fase 2, #309). Las celdas se anclan a
 * columnas fijas (4 por defecto) con reflow al estrechar el lienzo.
 *
 * **Las medidas viven AQUI y el JS las lee** (#329). Estaban escritas dos veces
 * —`NUM_COLS = 4`, `gap = 12`, `altoFilaBase = 130`, `pad = 24` en `nucleo.js`,
 * y otra vez en este bloque— y al pintar la rejilla habrian sido tres. Tres
 * copias de la misma rejilla es enseñar una celda y soltar en otra.
 */
.sc-carpeta-cuerpo[data-sc="lienzo"] {
    --sc-lienzo-cols: 4;
    --sc-lienzo-gap: 12px;
    --sc-lienzo-fila: 130px;
    --sc-lienzo-pad: 12px;

    display: grid;
    grid-template-columns: repeat(var(--sc-lienzo-cols), minmax(0, 1fr));
    /**
     * Altura de fila FIJA, no `minmax(130px, auto)`.
     *
     * Con `auto` una fila crece si su contenido no cabe, y entonces las filas
     * dejan de medir todas lo mismo: una rejilla pintada encima se desalinea en
     * cuanto hay un postit alto, y el resaltado del destino apunta a un sitio
     * donde la tarjeta no cae. Es una decision, no un detalle: lo que un postit
     * gana en alto lo pide en celdas (`altoCeldas`), que es para lo que existe
     * la rejilla. Lo que no quepa se desplaza dentro del postit, que ya tiene su
     * `.sc-postit-cuerpo { overflow: auto }`.
     */
    grid-auto-rows: var(--sc-lienzo-fila);
    gap: var(--sc-lienzo-gap);
    padding: var(--sc-lienzo-pad);
    align-content: start;
    overflow-y: auto;
    overflow-x: hidden;
    /* Nada de `position` aqui (#319). El cuerpo de carpeta se dimensiona con
       `position: absolute; inset: 0`, y este selector tiene
       mas especificidad: un `position: relative` lo sacaba del posicionamiento,
       asi que `inset` pasaba a ser un empujon y la altura la decidia el
       contenido — el lienzo vacio quedaba en una franja de 154px, encima del
       canto de pestañas. El unico hijo absoluto es `.sc-postit-tirador`, y su
       bloque contenedor es `.sc-postit`, no esto. */
}

/**
 * La rejilla, visible SOLO mientras se arrastra (#329).
 *
 * Se pinta con dos gradientes repetidos en un `::before`, no con un elemento por
 * celda: un lienzo ancho son cientos de nodos para algo decorativo, y habria que
 * recrearlos cada vez que cambia el ancho.
 *
 * La cuenta del paso: el ancho util son `cols` celdas y `cols - 1` huecos, o sea
 * `celda + hueco = (util + hueco) / cols`. Como el `::before` mide exactamente
 * el ancho util —va metido por el padding—, ese `100%` es el util y la formula
 * sale directa. Nada de numeros a mano.
 *
 * En reposo `opacity: 0` y no `display: none`: lo que se quiere es que aparezca
 * con una transicion, y de `none` no se transiciona.
 */
.sc-carpeta-cuerpo[data-sc="lienzo"]::before {
    content: '';
    position: absolute;
    inset: var(--sc-lienzo-pad);
    /* Lo alto lo manda el contenido desplazado, no el hueco visible. */
    height: auto;
    min-height: calc(100% - var(--sc-lienzo-pad) * 2);
    pointer-events: none;
    z-index: 0;

    background-image:
        repeating-linear-gradient(to right,
            var(--sc-lienzo-linea) 0 1px,
            transparent 1px calc((100% + var(--sc-lienzo-gap)) / var(--sc-lienzo-cols))),
        repeating-linear-gradient(to bottom,
            var(--sc-lienzo-linea) 0 1px,
            transparent 1px calc(var(--sc-lienzo-fila) + var(--sc-lienzo-gap)));

    opacity: 0;
    transition: opacity 220ms ease;
}
.sc-carpeta-cuerpo[data-sc="lienzo"] {
    --sc-lienzo-linea: color-mix(in srgb, var(--color-primary, #6366f1) 28%, transparent);
}
.sc-carpeta-cuerpo[data-sc="lienzo"].is-colocando::before { opacity: 1; }

/**
 * Donde va a caer. Un solo elemento que se mueve, no uno por celda.
 *
 * La transicion de `transform`/`width`/`height` es lo que hace que el destino
 * **se deslice** de celda en celda en vez de saltar, que es lo que se pidio.
 */
.sc-lienzo-destino {
    position: absolute;
    left: 0; top: 0;
    /* El borde va DENTRO: sin esto los 2px de cada lado se suman al ancho de la
       celda y el resaltado se mete en el hueco de al lado. */
    box-sizing: border-box;
    pointer-events: none;
    z-index: 1;
    border: 2px solid var(--color-primary, #6366f1);
    border-radius: var(--radius-lg, 12px);
    background: color-mix(in srgb, var(--color-primary, #6366f1) 10%, transparent);
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-primary, #6366f1) 25%, transparent),
                0 0 18px color-mix(in srgb, var(--color-primary, #6366f1) 30%, transparent);
    opacity: 0;
    transition: transform 140ms cubic-bezier(0.2, 0.8, 0.2, 1),
                width 140ms cubic-bezier(0.2, 0.8, 0.2, 1),
                height 140ms cubic-bezier(0.2, 0.8, 0.2, 1),
                opacity 140ms ease;
}
.sc-lienzo-destino.is-visible { opacity: 1; }

/* Quien pide menos movimiento quiere ver lo mismo, no una version peor: la
   rejilla y el destino siguen ahi, pero aparecen y se colocan sin animar. */
@media (prefers-reduced-motion: reduce) {
    .sc-carpeta-cuerpo[data-sc="lienzo"]::before,
    .sc-lienzo-destino { transition: none; }
}

.sc-postit {
    display: flex;
    flex-direction: column;
    position: relative;
    border: 1px solid var(--color-border, rgba(255, 255, 255, 0.12));
    border-radius: var(--radius-lg, 12px);
    background: var(--color-surface, rgba(255, 255, 255, 0.04));
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    padding: 10px;
    /* Sin `min-height`: la altura la manda la celda (#329). Un minimo en pixeles
       peleaba con las filas fijas y sacaba al postit de su celda. */
    min-height: 0;
    max-width: 100%;
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
    outline: 2px solid transparent;
    outline-offset: 2px;
    transition: outline-color 0.15s ease, box-shadow 0.15s ease, transform 0.05s ease;
    box-sizing: border-box;
}

.sc-postit:focus-visible {
    outline-color: var(--color-primary, #6366f1);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
}

.sc-postit.is-arrastrando {
    z-index: 50;
    opacity: 0.9;
    box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35);
    cursor: grabbing;
    user-select: none;
    /* Nada de transicion mientras se arrastra (#325): con `transform 0.05s`, el
       postit perseguia al puntero con 50ms de retraso en cada `pointermove` y se
       notaba blando. Lo que se anima es la llegada al soltar, y esa la pone el
       JS en linea. */
    transition: none;
}

.sc-postit.is-redimensionando {
    user-select: none;
}

.sc-postit-cabecera {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 6px;
    margin-bottom: 6px;
    border-bottom: 1px solid color-mix(in srgb, currentColor 8%, transparent);
    cursor: grab;
    user-select: none;
    font-size: 0.8rem;
    color: var(--color-text-secondary, rgba(255, 255, 255, 0.7));
}

.sc-postit-cabecera:active {
    cursor: grabbing;
}

.sc-postit-grip {
    display: inline-flex;
    align-items: center;
    opacity: 0.6;
    margin-right: 6px;
    color: currentColor;
}

.sc-postit-titulo {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    font-size: 0.8rem;
    font-weight: 500;
}

.sc-postit-descartar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    border: none;
    background: transparent;
    border-radius: var(--radius-sm, 4px);
    color: var(--color-text-secondary, rgba(255, 255, 255, 0.7));
    cursor: pointer;
    padding: 0;
    transition: background 0.15s ease, color 0.15s ease;
}

/* `display: inline-flex` le gana a `[hidden]`, que solo pone `display: none` con
   especificidad de elemento. Sin esto, esconder la ✕ del marco no la esconde. */
.sc-postit-descartar[hidden] { display: none; }

.sc-postit-descartar:hover {
    background: color-mix(in srgb, currentColor 12%, transparent);
    color: var(--color-text, #ffffff);
}

.sc-postit-cuerpo {
    flex: 1;
    overflow: auto;
    display: flex;
    flex-direction: column;
}

.sc-postit-tirador {
    position: absolute;
    right: 2px;
    bottom: 2px;
    width: 14px;
    height: 14px;
    cursor: nwse-resize;
    background: linear-gradient(135deg, transparent 50%, color-mix(in srgb, currentColor 40%, transparent) 50%);
    border-bottom-right-radius: var(--radius-lg, 12px);
    opacity: 0.4;
    transition: opacity 0.15s ease;
}

.sc-postit-tirador:hover {
    opacity: 1;
}

/* Al pulsar su pastilla. Corto: es para encontrarlo, no para celebrarlo. */
.sc-postit.is-destacado {
    outline: 2px solid var(--color-primary, #6366f1);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    .sc-postit.is-destacado { outline-width: 3px; }
}

.sc-lienzo-vacio {
    grid-column: 1 / -1;
    margin: auto;
    padding: 16px;
    text-align: center;
    font-size: 0.82em;
    opacity: 0.55;
}

