/* ═══════════════════════════════════════
   ▓ COMPONENTE · Shell — gemelo vanilla ▓
   v1.0 · 2026-08-23. Ficha: `design-system/COMPONENTE_SHELL.md`

   La CONCHA de las tres aplicaciones: la barra superior, la barra lateral que se colapsa, el
   área de contenido y el cajón acoplado que empuja. No es una pieza más: es el COORDINADOR que
   compone las piezas del marco y posee el movimiento compartido.

   REQUISITOS DEL ANFITRIÓN:
     1. Los tokens del canon (`colors_and_type.css`).
     2. `box-sizing: border-box`.
   ═══════════════════════════════════════

   ⛔ POR QUÉ ESTE GEMELO EXISTE, medido el 2026-08-23: 13 archivos de las tres apps redibujaban
   este marco y 391 menciones dependían de él, así que **una pantalla nueva empezaba inventándose
   su armazón** — y lo que se pierde al reinventar un marco no es su aspecto (eso se copia
   mirando): son sus CONDUCTAS, que nadie recuerda que existían. El `PanelAcoplado` del handoff
   de Contactos perdió nueve.

   ⛔ EL MOVIMIENTO SON DOS VARIABLES Y NADA MÁS, y son INDEPENDIENTES (colapsado + cajón
   coexisten sin conflicto):
     · `--sbw`  240px ↔ 72px            — lo dispara `data-collapsed` en la raíz.
     · `--dw`   0 ↔ `--w-cajon` (420px) — lo dispara `data-cajon` en la raíz.
   El JavaScript SÓLO pone esos dos atributos. No calcula anchos, no escribe estilos y no
   duplica el empuje: *añadir otra forma de empujar deja dos formas de empujar, que es el
   defecto que la homologación del 2026-07-22 cerró.*

   ⛔ LA MARCA: la zona es de este marco (`.exds-tb-brand`), pero el REVEAL isotipo↔wordmark es
   del logo, no de la concha — es una animación de trazado que el kit hace en JavaScript
   (`BrandMark.jsx`, fuente `plans/handoff_anim_isotipo_wordmark/`). Aquí se deja la caja
   preparada y el logo entra como contenido. Ver el README de esta carpeta.

   ⛔ LO QUE ESTA HOJA NO DIBUJA, PORQUE SE MONTA (no se copia): el contador de la barra lateral
   es el `Badge` `data-variant="count"` `ambiente`; la ranura de cuenta es el `account-menu`
   entero; las migas son `Migas`; las pestañas del contenido son `Tabs`; el avatar es `Avatar`.
   `.exds-sb-badge` sólo conserva lo que SÍ es del marco: el enmascarado al colapsar.
   El buscador ⌘K, en cambio, NO es el `Search` del catálogo y se queda aquí: es un DISPARADOR
   de 340 con la tecla, no un campo de 40 con lupa y × — cromo de navegación, declarado como tal
   en los crudos triageados de la ficha. */

/* ── LA RAÍZ · dueña del movimiento compartido ──────────────────────────────────────── */
.exds-shell { overflow:hidden; background:var(--color-bg, #FAFBFC); display:flex; flex-direction:column; position:relative; font-family:var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); --shell-header-h:64px; --sbw:240px; --dw:0px; }
/* ⛔ 240↔72 y 0↔--w-cajon SON EL MECANISMO, no un valor cualquiera: las dos variables son
   INDEPENDIENTES (colapsado + cajón coexisten sin conflicto) y las dos animan con
   --motion-slow. Atar el empuje a --w-cajon y no a un número escrito es lo que evita la
   costura: *eran dos nombres para una cosa, y dos nombres es la forma en que un número se
   separa de sí mismo* (producción, 2026-08-07). */
.exds-shell[data-collapsed="true"] { --sbw:72px; }
.exds-shell[data-cajon="true"] { --dw:var(--w-cajon, 420px); }
/* marco=false cuando el Shell LLENA la ventana: ahí borde, radio y sombra dibujan una
   tarjeta flotando sobre nada. */
.exds-shell[data-marco="true"] { border:1px solid var(--color-border-subtle, #E5E7EB); border-radius:var(--radius-lg, 16px); box-shadow:var(--shadow-md, 0 4px 12px -2px rgba(15, 20, 25, 0.06)); }
/* ── LA BARRA LATERAL ───────────────────────────────────────────────────────────────── */
.exds-sb { width:var(--sbw, 240px); flex-shrink:0; background:var(--color-surface, #FFFFFF); border-right:1px solid var(--color-border-subtle, #E5E7EB); display:flex; flex-direction:column; overflow:visible; position:relative; z-index:7; transition: width var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb-scroll { flex:1; overflow-y:auto; overflow-x:hidden; padding:8px 0; }
.exds-sb[data-collapsed="true"] .exds-sb-scroll { overflow:visible; }  /* §7: deja escapar los tooltips de módulo al colapsar */
.exds-sb-item { position:relative; display:flex; align-items:center; gap:12px; width:100%; border:none; background:transparent; text-align:left; cursor:pointer; padding:10px 14px 10px 20px; color:var(--fg2, #4B5563); font:500 var(--fs-body-sm, 13px) var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); letter-spacing:-.005em; transition: color var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), padding var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), gap var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); z-index:0; }
.exds-sb-item::before { content:""; position:absolute; top:1px; bottom:1px; left:8px; right:8px; border-radius:var(--radius-md, 10px); background:transparent; transition: background var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); z-index:-1; }
.exds-sb-item:hover { color:var(--fg1, #0F1419); } .exds-sb-item:hover::before { background:var(--color-bg-subtle, #F2F4F6); }
.exds-sb-item:focus-visible { outline:none; } .exds-sb-item:focus-visible::before { outline:2px solid var(--color-border-focus, #6E7378); outline-offset:2px; }
.exds-sb-item.is-active { color:var(--fg1, #0F1419); } .exds-sb-item.is-active::before { background:var(--color-bg-subtle, #F2F4F6); } .exds-sb-item.is-active .exds-sb-lab { font-weight:600; }
.exds-sb-item.in-path { color:var(--fg1, #0F1419); } .exds-sb-item.in-path .exds-sb-lab { font-weight:600; } .exds-sb-item.in-path::before { background:transparent; }
.exds-sb-ico { flex-shrink:0; display:block; color:inherit; }
.exds-sb-lab { flex:1; white-space:nowrap; overflow:hidden; max-width:170px; opacity:1; transform:translateX(0); transition: opacity var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), max-width var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), transform var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb-chev { flex-shrink:0; color:var(--fg3, #5C6B7A); display:flex; overflow:hidden; max-width:16px; opacity:1; transition: transform var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), opacity var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), max-width var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb-item.is-open .exds-sb-chev { transform:rotate(90deg); }
/* ⛔ EL CONTADOR YA NO SE DIBUJA AQUÍ: SE MONTA (2026-08-23). Esta clase pintaba un contador
   entero a mano —18 de caja, relleno rgba(15,20,25,.06), aro interior de medio píxel, --fg2,
   tabular— que es EXACTAMENTE el Badge data-variant=count enfasis=ambiente del catálogo. Su
   propia hoja lo dice con estas palabras: *era el tratamiento que la barra lateral llevaba
   dibujado a mano; ahora es el que viene de fábrica*. Montarlo lo lleva de 18 a 20 px, que es
   el escalón canónico: es HOMOLOGACIÓN, no una decisión de gusto (regla 0-bis — «la app no
   coincide con el catálogo» es un DEFECTO).
   Lo que SÍ sigue siendo del marco, y por eso se queda: el ENMASCARADO al colapsar. La caja la
   dibuja el Badge; hacerla desaparecer con el riel es del Shell. */
.exds-sb-badge { flex-shrink:0; overflow:hidden; max-width:46px; transition: opacity var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), max-width var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb-badge.no-chev { margin-right:28px; }
.exds-sb-group { display:grid; grid-template-rows:0fr; transition: grid-template-rows var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); position:relative; }
.exds-sb-group.open { grid-template-rows:1fr; }
.exds-sb-group > .exds-sb-groupclip { overflow:hidden; min-height:0; }
/* LINT-ALLOW: 2px · DECISION-FUENTE: Ricardo 2026-08-23 — «de acuerdo con tus recomendaciones»,
   sobre la hoja plans/decisiones/_decision_2026-08-23_radios_shell.html (opción A: dejarlo).
   EL RIEL SE QUEDA EN 2px, Y NO ES UNA CONCESIÓN AL VALOR VIEJO: es que aquí el radio del canon
   NO PUEDE HACER NADA. El riel mide 1.5px de ancho, así que a partir de 0.75px el navegador
   recorta el radio al máximo que cabe — se comprobó con la pieza viva ampliada ×7 y los dos
   lados salen IDÉNTICOS. Poner 6 no cambiaría un píxel: sólo escribiría un número que parece
   distinto, y de paso metería «la punta de una línea» en la escala de esquinas del sistema.
   *Un valor sin nombre no siempre es deuda: a veces es la medida de algo que la escala no mide.*
   ⚠ Y si algún día se quiere cero valores sin nombre, la salida limpia NO es 6 — es
   --radius-pill, que dice literalmente «las puntas redondas» y ya existe. Queda apuntado. */
/* ⛔ EL RIEL TIENE NOMBRE DESDE EL 2026-08-27 (decisión 1.5 de Ricardo · cosecha de
   .sbx-rail del backoffice, 5 menciones vivas). Antes era SÓLO un pseudo-elemento del grupo,
   y un pseudo no se puede sustituir: la app que ya lo pinta con un <span> propio no tenía
   contra qué homologarse, así que su riel iba a seguir siendo suyo para siempre.
   Los dos dibujan lo MISMO, y por eso van en la misma regla. Cuando el marcado trae el
   elemento, el pseudo se aparta — si no, saldrían dos rieles superpuestos de 1.5px, que a
   ojo se ve igual y en un diff de estilos NO se ve en absoluto. */
.exds-sb-rail, .exds-sb-group::before { content:""; position:absolute; left:28px; top:2px; bottom:6px; width:1.5px; background:var(--color-border-subtle, #E5E7EB); border-radius:2px; }
.exds-sb-group:has(> .exds-sb-rail)::before { content:none; }
.exds-sb-sub { position:relative; display:flex; align-items:center; gap:10px; width:100%; border:none; background:transparent; text-align:left; cursor:pointer; padding:9px 14px 9px 51px; color:var(--fg2, #4B5563); font:500 var(--fs-body-sm, 13px) var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); transition: color var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); z-index:0; }
.exds-sb-sub::before { content:""; position:absolute; top:1px; bottom:1px; left:40px; right:8px; border-radius:var(--radius-md, 10px); background:transparent; transition: background var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); z-index:-1; }
.exds-sb-sub:hover { color:var(--fg1, #0F1419); } .exds-sb-sub:hover::before { background:var(--color-bg-subtle, #F2F4F6); }
/* EL UNICO SITIO del Shell donde el outline del canon NO cabe. El ::before del sub-item deja
   1px de colchon (top:1px/bottom:1px) y su contenedor .exds-sb-groupclip recorta en vertical
   (overflow:hidden, padding 0): un anillo de 2px con 2px de separacion se saldria 4px y el
   navegador cortaria 3px del primer y del ultimo submodulo de cada grupo abierto. Dibujado
   hacia dentro se ve entero y conserva grosor y token del canon (COMPONENTE_FOCO.md).
   Medido, no supuesto: 1px de colchon contra 4px de anillo.
   LINT-ALLOW: #6E7378 · anillo de foco inset (--color-border-focus) porque el contenedor recorta */
.exds-sb-sub:focus-visible { outline:none; } .exds-sb-sub:focus-visible::before { box-shadow: inset 0 0 0 2px var(--color-border-focus, #6E7378); }
.exds-sb-sub.is-active { color:var(--fg1, #0F1419); font-weight:600; } .exds-sb-sub.is-active::before { background:var(--color-bg-subtle, #F2F4F6); }
/* El sub-ítem tiene su propio tope de etiqueta: sangra 51px, así que le queda menos riel. */
.exds-sb-sub .exds-sb-lab { max-width:150px; }
.exds-sb[data-collapsed="true"] .exds-sb-item { padding:11px 0 11px 26px; gap:0; }
.exds-sb[data-collapsed="true"] .exds-sb-item::before { left:14px; right:14px; }
.exds-sb[data-collapsed="true"] .exds-sb-lab { max-width:0; opacity:0; transform:translateX(-8px); }
.exds-sb[data-collapsed="true"] .exds-sb-chev, .exds-sb[data-collapsed="true"] .exds-sb-badge { max-width:0; opacity:0; }
.exds-sb[data-collapsed="true"] .exds-sb-badge { padding:0; }
.exds-sb[data-collapsed="true"] .exds-sb-group { grid-template-rows:0fr !important; }
.exds-sb-foot { border-top:1px solid var(--color-border-subtle, #E5E7EB); flex-shrink:0; height:60px; display:flex; align-items:center; justify-content:flex-end; padding:0 14px; transition: padding var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb[data-collapsed="true"] .exds-sb-foot { justify-content:center; padding:0; }
.exds-sb-collapse { width:40px; height:40px; padding:0; border:none; background:transparent; cursor:pointer; border-radius:var(--radius-md, 10px); display:flex; align-items:center; justify-content:center; color:var(--fg2, #4B5563); transition: color var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), background var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb-collapse:hover { color:var(--fg1, #0F1419); background:var(--color-bg-subtle, #F2F4F6); }
.exds-sb-collapse:focus-visible { outline:2px solid var(--color-border-focus, #6E7378); outline-offset:2px; }
/* La doble flecha gira 180° al colapsar. Era un transform en línea calculado en React; en CSS
   lo hereda el gemelo sin escribir una línea de JavaScript. */
.exds-sb-collapse svg { transition: transform var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb[data-collapsed="true"] .exds-sb-collapse svg { transform:rotate(180deg); }
/* ── Tooltips OSCUROS: sidebar → a la derecha · topbar → abajo. Aparecen SOLO con el sidebar
     colapsado (los ítems quedan sin texto); el botón de colapsar SIEMPRE (nunca tiene texto). ── */
.exds-tt { position:relative; }
.exds-tt::after { content:attr(data-tip); position:absolute; background:var(--color-bg-inverse, #0F1419); color:var(--fg-inverse, #FFFFFF); font:500 var(--fs-caption, 12px)/1 var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); padding:8px 12px; border-radius:var(--radius-sm, 6px); white-space:nowrap; box-shadow:var(--shadow-md, 0 4px 12px -2px rgba(15, 20, 25, 0.06)); opacity:0; pointer-events:none; z-index:var(--z-tooltip, 1400); transition: opacity var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-tt-down::after { top:calc(100% + 8px); left:50%; transform:translateX(-50%) translateY(-4px); }
.exds-tt-down:hover::after { opacity:1; transform:translateX(-50%) translateY(0); transition-delay:380ms; }
/* ⛔ EL RÓTULO DE LA BARRA PLEGADA VA CLARO — NO EL DE LA BARRA SUPERIOR (2026-08-27).
   `DECISION-FUENTE: Ricardo, 2026-08-27 — pregunta 1 de la cosecha, opción B: «los dos claros».`

   Con la barra plegada, pasar el ratón sobre un módulo enseña su nombre. Si el módulo NO tiene
   submódulos sale este rótulo; si SÍ los tiene sale `.exds-sb-flyout`, que es blanco. **Son el
   mismo gesto, en el mismo sitio, a un centímetro de distancia** — y uno era negro y el otro
   blanco. *El usuario no lee «tooltip» y «flyout»: lee «esto está mal».*

   ⛔ Y EL ALCANCE ES SÓLO ÉSTE, no «todos los tooltips»: `.exds-tt-down` —el de la barra
   superior— sigue OSCURO, porque ahí no compite con ningún desplegable. La piel es la DEL
   FLYOUT, no una nueva: superficie + borde sutil, para que las dos caras del mismo gesto sean
   la misma familia.

   No es un cambio del producto: **producción ya lo hacía así** desde el 2026-08-06 (`.sbx-tip`
   en el backoffice, `.sb-tooltip` en el portal), con su declaración de excepción. El que estaba
   fuera de línea era el catálogo. *Una excepción declarada en la app que el catálogo nunca
   absorbió es una decisión viviendo en el sitio equivocado.* */
.exds-tt-right::after {
  left:calc(100% + 12px); top:50%; transform:translateY(-50%) translateX(-4px);
  background:var(--color-surface, #FFFFFF);
  color:var(--fg1, #0F1419);
  border:1px solid var(--color-border-subtle, #E5E7EB);
}
.exds-tt-right:hover::after { opacity:1; transform:translateY(-50%) translateX(0); transition-delay:380ms; }
.exds-shell:not([data-collapsed="true"]) .exds-tt:not([data-tip-always])::after { display:none; }
@media (prefers-reduced-motion:reduce){ .exds-tt::after{ transition: none; } }
/* ── EL DESPLEGABLE DEL RIEL PLEGADO · COSECHADO, NO INVENTADO (2026-08-27) ─────────────
   DECISION-FUENTE: Ricardo, 2026-08-27, decisión 1.6 — se cosecha ANTES de sustituir nada,
   porque si no el backoffice pierde una función viva al adoptar el gemelo.
   Esta ficha lo declaraba desde julio como «pendiente, no construido… si lo necesitas NO lo
   inventes aquí». No se ha inventado: sale de .sbx-flyout* del backoffice (17 menciones
   vivas), midiendo las DOS reglas que la cascada suma allí — la base de S230 y la
   homologación de S276, que es la que fija el ancho de 212 y las píldoras.

   QUÉ ES: con la lateral PLEGADA los módulos se quedan sin texto, así que un módulo CON
   submódulos no tiene dónde enseñarlos. Al pasar el ratón sale esta tira a su derecha con el
   nombre del módulo y sus submódulos. Un módulo PLANO (sin hijos) no saca desplegable: saca
   tooltip. Son el mismo gesto en el mismo sitio y por eso comparten piel.

   ⛔ DOS COSAS QUE NO SE VEN EN EL CSS Y SIN LAS CUALES LA PIEZA NO FUNCIONA:
     · vive en el BODY y se posiciona con position:fixed leyendo el rectángulo del módulo —
       no puede colgar de la lateral, que recorta (overflow) y quedaría cortado;
     · se cierra con RETARDO (140ms) y el propio desplegable cancela ese retardo al recibir el
       ratón. Sin eso, el hueco de 8px entre el ícono y la tira lo cierra a media travesía y
       es IMPOSIBLE llegar a pulsarlo. Esto está en shell.js, no aquí.

   ⚠ Lo que NO se cosecha aquí, porque es de otra pieza: el tooltip claro del módulo plano
   (.sbx-tip). Su excepción al canon está declarada en producción con GATE-ALLOW y su dueña es
   COMPONENTE_CAPA_EFIMERA.md — ver la pregunta abierta en la ficha del Shell. ── */
.exds-sb-flyout { position:fixed; z-index:var(--z-desplegable, 1000); width:212px; min-width:0; background:var(--color-surface, #FFFFFF); border:1px solid var(--color-border-subtle, #E5E7EB); border-radius:var(--radius-md, 10px); box-shadow:var(--shadow-md, 0 4px 12px -2px rgba(15, 20, 25, 0.06)); padding:0; overflow:hidden; font-family:var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); opacity:0; pointer-events:none; transform:translateX(-6px); transition: opacity var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), transform var(--motion-base, 250ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb-flyout.show { opacity:1; transform:translateX(0); pointer-events:auto; }
.exds-sb-flyout-head { font:600 var(--fs-caption, 12px) var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); color:var(--fg1, #0F1419); text-transform:none; letter-spacing:normal; padding:11px 16px 8px; margin-bottom:0; border-bottom:1px solid var(--color-border-subtle, #E5E7EB); }
.exds-sb-flyout-list { padding:var(--space-1-5, 6px); }
.exds-sb-flyout-item { display:block; width:100%; box-sizing:border-box; margin:1px 0; padding:8px 12px; border:none; background:transparent; text-align:left; cursor:pointer; position:relative; white-space:nowrap; border-radius:var(--radius-sm, 6px); font:500 var(--fs-body-sm, 13px) var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); color:var(--fg2, #4B5563); transition: background var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), color var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-sb-flyout-item:hover { background:var(--color-bg-subtle, #F2F4F6); color:var(--fg1, #0F1419); }
/* Decisión C de Ricardo (2026-07-29): la barra de 3px sale — el PESO DE LETRA ya marcaba la
   selección, y dos marcadores para lo mismo es uno de más. Por eso el activo lleva
   box-shadow:none explícito: apaga cualquier marcador heredado, no es una sombra que falte. */
.exds-sb-flyout-item.is-active { background:var(--color-bg-subtle, #F2F4F6); color:var(--fg1, #0F1419); font-weight:600; box-shadow:var(--shadow-none, none); }
/* El foco NO viene de producción: allí este ítem no lo declara. Se añade porque es un
   <button> y el canon del foco no admite un control sin anillo — es homologación al canon,
   no una decisión de aspecto (regla 0-bis). Mismo anillo que el sub-ítem de la lateral. */
/* AQUI EL ANILLO DEL CANON CABE, y conviene decir por que se llego a pensar lo contrario:
   `.exds-sb-flyout` tiene overflow:hidden y padding:0, asi que leer sus dos reglas lleva
   derecho a «el item esta a ras». No lo esta: la holgura la pone un INTERMEDIARIO,
   `.exds-sb-flyout-list` (padding:6px), y el item mide 198 dentro de 212. El anillo necesita
   4 (2 de grosor + 2 de separacion) y hay 6. Medido pintandolo y muestreando los cuatro lados:
   sale entero por los cuatro. *Al auditar un recorte se recorre la cadena entera de ancestros,
   no solo el que tiene overflow.* */
.exds-sb-flyout-item:focus-visible { outline:2px solid var(--color-border-focus, #6E7378); outline-offset:2px; }
@media (prefers-reduced-motion:reduce){ .exds-sb-flyout { transition:none; } }
/* ── Topbar (FUNDIDA en el Shell, ya no es export del catálogo) ── */
/* La barra de 64px: marca sobre el riel de la lateral · divisoria · migas · buscador ⌘K ·
   clúster de íconos + cuenta. Se corre a la izquierda cuando el cajón empuja (--dw). */
.exds-tb { position:relative; height:var(--shell-header-h, 64px); flex-shrink:0; background:var(--color-surface, #FFFFFF); border-bottom:1px solid var(--color-border-subtle, #E5E7EB); display:flex; align-items:center; gap:16px; padding:0 18px; padding-right:calc(18px + var(--dw, 0px)); transition: padding-right var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
/* Zona de marca: ancha como la lateral y en absoluto, para que el isotipo quede centrado en
   el riel de 72 al colapsar. */
.exds-tb-brand { position:absolute; left:0; top:0; height:var(--shell-header-h, 64px); width:var(--sbw, 240px); overflow:hidden; pointer-events:none; z-index:1; transition: width var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); color:var(--color-action-ink, #21272C); }
/* La marca anima su POSICIÓN, no sólo su forma: −16px colapsada (X centrada en el riel de 72)
   → 18px expandida (margen de logo). En el kit ese left lo corre BrandMark dentro del mismo
   tween por fotograma, así que aquí sólo va la caja. */
.exds-tb-brand > svg { position:absolute; top:20px; }
/* Divisoria anclada al borde de la lateral: se mueve con ella. */
.exds-tb-div { width:1px; height:var(--shell-header-h, 64px); background:var(--color-border-subtle, #E5E7EB); flex-shrink:0; margin-left:calc(var(--sbw, 240px) - 19px); margin-right:20px; transition: margin-left var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-tb-right { display:flex; align-items:center; gap:12px; flex-shrink:0; margin-left:auto; }
.exds-tb-icons { display:flex; align-items:center; gap:2px; }
.exds-tb-iglyph { width:19px; height:19px; display:block; }
/* El punto de aviso del ícono: no lleva número, sólo dice «hay algo». */
.exds-tb-dot { position:absolute; box-sizing:content-box;  /* ⛔ 2026-08-29 · SIN ESTO EL ANILLO SE COME EL PUNTO: con border-box, width:7px incluye los 1,5px de borde por lado y lo que llega al ojo son ~4px, no 7. Es el «dot muy pequeño» que reportó Ricardo. El TAMAÑO (7 fuera de la escala 6/8) es decisión suya y va aparte. */ top:8px; right:9px; width:7px; height:7px; border-radius:var(--radius-pill, 999px); background:var(--color-emerald, #0B8F6B); border:1.5px solid var(--color-surface, #FFFFFF); }
/* ⛔ 38 → 40 EL 2026-08-27 · DECISION-FUENTE: Ricardo, sobre la hoja
   `plans/decisiones/_decision_alto_topbar_2026-08-27.html` (opción B).
   La barra superior toma la medida MEDIA del Button (32 · 40 · 48), que es la que el
   catálogo ya documenta para el avatar. El 38 no estaba en NINGUNA de las dos escalas
   —ni la del botón con nombre (32·40·48) ni la del botón de ícono (20·28·32)— y lo
   compartían las DOS apps: no era una desviación del portal, era una medida sin nombre
   que la siguiente pantalla iba a copiar sin saber de dónde venía.
   Aquí es donde vive la medida: las apps la heredan de esta clase y NO la vuelven a
   escribir. */
.exds-tb-icon { position:relative; width:40px; height:40px; border-radius:var(--radius-md, 10px); border:none; background:transparent; color:var(--fg2, #4B5563); display:flex; align-items:center; justify-content:center; cursor:pointer; transition: color var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), background var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-tb-icon:hover, .exds-tb-icon.is-on { color:var(--fg1, #0F1419); background:var(--color-bg-subtle, #F2F4F6); }
.exds-tb-icon:focus-visible { outline:2px solid var(--color-border-focus, #6E7378); outline-offset:2px; }
/* ⛔ EL BUSCADOR ⌘K NO ES EL Search DEL CATÁLOGO, y la frontera está declarada en la ficha
   (crudos triageados): aquél es un CAMPO de 40px con lupa y ×; éste es un DISPARADOR de 340
   que abre la paleta de comandos. Su caja la define el marco, no la escala de Search.
   Se centra en la zona de CONTENIDO, no en la ventana: por eso el left descuenta medio riel
   de la lateral y medio empuje del cajón — así el buscador sigue centrado sobre lo que se ve
   mientras el marco se mueve por debajo. A ancho real recentra; no se oculta (invención 5 del
   handoff, descartada: era artefacto de una demo angosta). */
.exds-tb-search { display:flex; align-items:center; gap:9px; background:var(--color-bg-subtle, #F2F4F6); border:1px solid transparent; border-radius:var(--radius-md, 10px); padding:8px 13px; width:340px; max-width:100%; cursor:pointer; position:absolute; left:calc(50% + var(--sbw, 240px)/2 - var(--dw, 0px)/2); transform:translateX(-50%); transition: left var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-tb-search:hover { border-color:var(--color-border-default, #D1D5DB); }
.exds-tb-search:focus-visible { outline:2px solid var(--color-border-focus, #6E7378); outline-offset:2px; }
.exds-tb-ph { font-size:var(--fs-body-sm, 13px); color:var(--fg3, #5C6B7A); flex:1; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; text-align:left; }
/* ⛔ LA TECLA PASA DE 4px AL RADIO DEL CANON (Ricardo 2026-08-23).
   DECISION-FUENTE: Ricardo — «de acuerdo con tus recomendaciones», sobre la hoja
   plans/decisiones/_decision_2026-08-23_radios_shell.html (opción B: homologar).
   Aquí SÍ se ve —a 19px de alto la esquina es visible, y la lupa ×7 de la hoja lo enseñaba—,
   y aun así gana el sistema sobre la convención del teclado: *un valor sin nombre en el
   catálogo es un valor que la siguiente pantalla copia sin saber de dónde vino.* Era el único
   sitio del Shell donde 4px medía una esquina, así que se iba a heredar desde aquí.
   Se descartó declarar 4px en el canon como «radio de los elementos diminutos»: habría añadido
   un peldaño a la escala para una sola pieza. */
.exds-tb-kbd { font:500 var(--fs-eyebrow, 11px)/1 var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace); color:var(--fg3, #5C6B7A); background:var(--color-surface, #FFFFFF); border:1px solid var(--color-border-subtle, #E5E7EB); border-radius:var(--radius-sm, 6px); padding:3px 5px; }
/* ── El cuerpo: la fila lateral + contenido, que el cajón EMPUJA a la izquierda ────────── */
.exds-main { flex:1; min-height:0; display:flex; margin-right:var(--dw, 0px); transition: margin-right var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-content { flex:1; min-width:0; background:var(--color-bg-content, #F6F7F8); overflow:auto; position:relative; display:flex; flex-direction:column; }
/* ── EL CAJÓN ACOPLADO · regla dura del proyecto: sin velo, sin sombra. Se define por su
     BORDE, su encabezado va EN la línea de 64px de la barra superior, y al abrir EMPUJA la
     barra y el contenido en vez de flotar encima. ─────────────────────────────────────── */
.exds-drawer { position:absolute; top:0; right:0; bottom:0; width:var(--dw, 0px); overflow:hidden; background:var(--color-surface, #FFFFFF); border-left:1px solid var(--color-border-subtle, #E5E7EB); z-index:8; outline:none; transition: width var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }

/* ⛔ 2026-08-29 · LA GUARDA QUE FALTABA. Estas cuatro son las unicas del Shell que mueven la
   DISPOSICION entera al abrir el cajon (--dw): el contenido, la topbar, el buscador y el panel.
   Habia guarda de movimiento reducido para .exds-tt, .exds-sb-flyout, .exds-drawer-swap y
   .exds-drawer-hbtn —las cuatro piezas pequenas— y ninguna para las que de verdad desplazan
   media pantalla. Quien pide "reducir movimiento" al sistema lo pide sobre todo por esto.
   No lo cazo ningun gate: lint_movimiento sale verde sin verlo. */
@media (prefers-reduced-motion:reduce){ .exds-main, .exds-tb, .exds-tb-search, .exds-drawer { transition:none; } }
/* El panel de dentro mide su ancho FINAL siempre: así el contenido no se re-maqueta mientras
   la caja de fuera crece. */
.exds-drawer-panel { position:absolute; top:0; right:0; bottom:0; width:var(--w-cajon, 420px); display:flex; flex-direction:column; }
.exds-drawer-head { height:var(--shell-header-h, 64px); flex-shrink:0; border-bottom:1px solid var(--color-border-subtle, #E5E7EB); display:flex; align-items:center; justify-content:space-between; padding:0 12px 0 20px; }
.exds-drawer-title { font:600 var(--fs-body, 14px) var(--font-sans, "Inter", -apple-system, "system-ui", sans-serif); color:var(--fg1, #0F1419); letter-spacing:-.005em; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.exds-drawer-body { flex:1; overflow-y:auto; overflow-x:hidden; padding:20px; }
/* ── Papel 6 · Cambio de contenido del drawer (sube+fade, base + curva estándar) ── */
@keyframes exds-drawer-in { from { opacity:0; transform:translateY(10px); } to { opacity:1; transform:none; } }
.exds-drawer-swap { animation: exds-drawer-in var(--motion-slow, 400ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1))  /* ⛔ HOMOLOGADO 2026-08-06: usaba el gesto de CAJÓN a 200 ms. Un cajón entra en 400 —
     recorre media pantalla, y una superficie grande que va rápido se lee como un golpe.
     Lo destapó declarar el PAPEL de esta pieza: el gate por nombre no lo veía. */; }
@media (prefers-reduced-motion:reduce){ .exds-drawer-swap { animation:none; } }
/* ── El BOTÓN DE CABECERA del cajón · una sola pieza para todos (Ricardo 2026-07-29) ── */
.exds-drawer-hbtn { width:var(--drawer-btn-size,32px); height:var(--drawer-btn-size,32px); flex-shrink:0; padding:0; border:none; background:transparent; border-radius:var(--radius-sm, 6px); color:var(--fg3, #5C6B7A); cursor:pointer; display:flex; align-items:center; justify-content:center; transition: color var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), background var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)), transform var(--motion-fast, 120ms) var(--ease-default, cubic-bezier(0.32, 0.72, 0, 1)); }
.exds-drawer-hbtn:hover { color:var(--fg1, #0F1419); background:var(--color-bg-subtle, #F2F4F6); }
.exds-drawer-hbtn:active { transform:scale(.96); }
.exds-drawer-hbtn:focus-visible { outline:2px solid var(--color-border-focus, #6E7378); outline-offset:2px; }
@media (prefers-reduced-motion:reduce){ .exds-drawer-hbtn { transition:none; } }