/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ COMPONENTE · Menú contextual — GEMELO VANILLA ▓
   v0.1 · 2026-08-17. Ficha: `design-system/COMPONENTE_MENU.md` (v1.0, 2026-07-22).

   ⛔ POR QUÉ EXISTE, y es el hallazgo que lo pidió: el `Menu` **sólo vivía en React**
   (`catalogo-cd/components/Menu.jsx`) y **las tres apps de producción son HTML y CSS a
   secas**. Ninguna podía montarlo. *Un componente que tu producto no puede usar no está
   canonizado: está documentado.* Producción tenía en su lugar `.mc-pop`, `.exv3-menu`,
   `.exv3-docmenu`, `.topbar-dropdown` — cuatro dibujos del mismo objeto, cada uno con su
   propia deriva.

   QUÉ ES. El desplegable **efímero anclado a un botón**: aparece, se elige o se descarta,
   desaparece. Kebab «⋯», botón con etiqueta («Orden», «Filtros») o mitad de un botón
   partido — **todos abren el MISMO popover** (ficha § Regla dura).

   ⛔ NO ES NAVEGACIÓN. Para cambiar de sección van tabs o segmentado, nunca un menú.
   ⛔ NO ES EL `AccountMenu` (el del avatar de cuenta) ni el `Modal` (que sí bloquea).

   QUÉ TRAE ESTA HOJA, y las cuatro cosas eran huecos declarados del catálogo:
     1. El popover con sus TRES anchos del canon (`--w-menu` · `-identidad` · `-ancho`).
     2. ⭐ El **PAR DE MOVIMIENTO** `exds-pop-in` / `exds-pop-out` — decidido el
        2026-08-06 (ficha § «El PAR de movimiento del desplegable») y homologado a un
        solo gesto el 2026-08-10 (`COMPONENTE_MOVIMIENTO.md` § «El gesto del
        DESPLEGABLE», INC-403). **Estaba escrito y nunca construido en el catálogo.**
     3. ⭐ El **ESTADO ACTIVO DEL DISPARADOR** — mientras su panel está abierto el botón
        se ve distinto. No existía **ni la regla**: se abría el panel y el botón no se
        enteraba.
     4. ⭐ Las **ranuras del panel ancho**: cabecera, cuerpo con desplazamiento y **PIE
        PEGAJOSO**. Es lo que convierte al `Menu` en el contenedor del panel de filtros
        sin que el panel invente un contenedor propio.

   REQUISITOS DEL ANFITRIÓN — los dos, o se ve mal en silencio:
     1. Los tokens del canon (`design-system/colors_and_type.css`).
     2. Un reinicio con `box-sizing: border-box`.
   ═══════════════════════════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 1 · EL ENVOLTORIO — de él cuelga el anclaje, y por eso no es decorativo ▓
   `position: relative` es lo que hace que el popover se posicione respecto al BOTÓN y
   no respecto a la página. Sin esto, `left:0` significaría «el borde de la ventana».
   ═══════════════════════════════════════════════════════════════════════════════ */
.exds-menu {
  position: relative;
  display: inline-flex;
  flex: none;
}

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 2 · EL DISPARADOR Y SU ESTADO ACTIVO ▓

   ⛔ ESTA HOJA NO DIBUJA EL DISPARADOR. Lo pone quien monta, y debe ser una pieza del
   catálogo: el `Button` secundario para uno con etiqueta («Filtros»), el `Button`
   sólo-ícono para el kebab. Aquí sólo vive **el estado que el catálogo no tenía**.

   ⭐ EL ESTADO ACTIVO — la regla nueva, y su verdad es `aria-expanded`.
   `FUENTE: plans/handoff toolbar2/toolbar/README.md §5.1 — «Activo: sí. Borde`
   `--color-action-ink + fondo --color-bg-subtle + texto --fg1».`

   ⛔ CUELGA DE `aria-expanded` Y NO DE UNA CLASE, por la misma razón que el chip cuelga
   de `aria-pressed`: así el dibujo no puede desincronizarse de lo que anuncia un lector
   de pantalla, porque **son el mismo atributo**. Una clase `.is-open` paralela es una
   segunda verdad, y dos verdades divergen el primer día.

   ⛔ POR QUÉ HACE FALTA, dicho corto: sin esto se abre un panel de 360 px encima de la
   pantalla y **el botón que lo abrió sigue en reposo**. El usuario tiene que deducir de
   dónde salió el panel mirando dónde está. *Un control que produce una superficie y no
   la reconoce obliga a reconstruir la relación cada vez.*

   ✅ Y ES **UNA** REGLA PARA TODOS LOS DISPARADORES, no una por menú.
   `DECISION-FUENTE: Ricardo 2026-08-17 — «de acuerdo» con una sola marca.`
   El «⋯» se marca igual que «Filtros»: **un aspecto que aprender en vez de dos.** Se
   descartó darle al kebab una marca más suave — *una excepción por pieza convierte una
   regla en una lista de casos, y la lista la tiene que recordar quien monta.*
   ═══════════════════════════════════════════════════════════════════════════════ */
.exds-menu-disp[aria-expanded="true"] {
  background: var(--color-bg-subtle);
  border-color: var(--color-action-ink);
  color: var(--fg1);
}

/* EL CONTADOR DEL DISPARADOR · «Filtros ③» — cuántos VALORES hay elegidos en total.
   ⛔ ES EL «Contador» DEL CATÁLOGO MONTADO, no una cajita redibujada
   (`COMPONENTE_BADGE.md` § Contador · `Badge variant="count"` énfasis «respuesta»):
       · forma   `--radius-pill` — ⛔ NUNCA 50 %: con 2+ dígitos se deforma en elipse.
       · ancho   `min-width` = el alto.
       · relleno `0 6px`, el del canon.
       · cifras  tabulares, y el tope «99+» se aplica en el render, no aquí.
   Alto **18**: es el valor que pide el handoff §5.1 y cae dentro del rango 16–22 que la
   ficha del Badge declara. ⚠ A 18 un dígito **no sale círculo** (sale 19 × 18): es la
   aritmética del propio canon, corregida y declarada en `COMPONENTE_BADGE.md` el
   2026-08-17 («círculo perfecto sólo a partir de 19»). No es defecto de esta pieza.
   ⛔ SIN SELECCIÓN NO SE PINTA — ni un «0» ni un guion. Lo apaga el JS con `hidden`, que
   además lo saca del árbol de accesibilidad: un lector no anuncia un contador vacío. */
.exds-menu-disp-n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 18px;
  height: 18px;
  padding: 0 var(--space-1-5);
  border-radius: var(--radius-pill);
  background: var(--color-action-ink);
  color: var(--fg-inverse);
  font: var(--fw-semibold) var(--fs-eyebrow) var(--font-sans);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* ⛔ LA GUARDA, Y NO ES REDUNDANTE — sin ella el `hidden` de arriba NO ESCONDE NADA.
   `[hidden] { display:none }` vive en la hoja del NAVEGADOR, y **cualquier regla de clase
   de autor le gana**: el `display:inline-flex` de arriba pisa al navegador y el contador se
   queda pintado con el atributo puesto. Resultado medido el 2026-08-25 al montar la
   VITRINA: todo disparador sin filtros enseñaba un «0» permanente — el JS ponía `hidden`
   correctamente y la hoja lo desobedecía.
   Es el MISMO defecto que §5 ya documenta para `.exds-menu-pop` («`[hidden]` PIERDE contra
   cualquier `display` de autor»): se aprendió para el panel y se olvidó para el contador.
   *Un `hidden` que no esconde es peor que no tenerlo: quien lo escribe cree que ya está.* */
.exds-menu-disp-n[hidden] { display: none; }

/* EL KEBAB «⋯» · el disparador de sólo-ícono, para cuando no hay etiqueta que poner.
   ⛔ ALTO 40 Y NO 36, y es homologación al catálogo, no gusto (CLAUDE.md regla 0-bis:
   «la app no coincide con el catálogo» es un DEFECTO). La ficha del Menú escribía
   36×36, que **no es escalón** de la escala de control (32 · 40 · 48), y Ricardo fijó
   el 2026-08-17 que la barra de una pantalla de lista va a **40**
   (`PATRON_PANTALLA_LISTA.md` regla dura 6). Además cuadra el anclaje: bajo un botón de
   40 el popover cae a 46 px, que es exactamente el `top:46px` del handoff. */
.exds-menu-kebab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 1px solid var(--color-border-default);
  border-radius: var(--radius-button);
  background: var(--color-surface);
  color: var(--fg2);
  box-shadow: var(--shadow-none);   /* ⛔ NINGUNA · misma razón que el botón secundario */
  cursor: pointer;
  transition: background var(--motion-fast) var(--ease-default),
              border-color var(--motion-fast) var(--ease-default),
              color var(--motion-fast) var(--ease-default);
}
.exds-menu-kebab:hover { background: var(--color-bg-subtle); }
.exds-menu-kebab:focus-visible { outline: 2px solid var(--color-border-focus); outline-offset: 2px; }
.exds-menu-kebab svg { width: 20px; height: 20px; display: block; }

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 3 · EL VELO — invisible, y su único trabajo es cerrar ▓
   `position:fixed; inset:0` captura el clic-fuera. ⛔ NO OSCURECE: bloquear la pantalla
   es cosa del `Modal`, y ninguna de estas herramientas toma una decisión que no pueda
   esperar (ficha § Regla dura · handoff §1).
   ═══════════════════════════════════════════════════════════════════════════════ */
/* ⛔ LA CAPA SALE DE LA ESCALA DEL CANON, NO DE UN NÚMERO SUELTO (2026-08-21 · lo cazó
   Ricardo: el «⋯» de Facturación quedaba DEBAJO del cajón de detalle).

   ERA: `40` y `41`. Números que no pertenecían a ninguna escala y que nadie podía rastrear:
   el marco del cajón vive en `--z-cajon` = **900**, así que un menú abierto se le metía
   detrás — medido con el cajón abierto a 1440, el panel iba de 940 a 1164 px y el cajón
   empieza en 1020: **144 px del menú tapados**.

   ES: `--z-desplegable` (1000), que la escala describe literalmente como «cuelga de la
   barra: encima de ella», y el velo un peldaño por debajo. La escala completa vive en
   `design-system/colors_and_type.css` § capas; el velo y el popover son la pareja
   contenedor/contenido, así que el peldaño local se escribe `calc(... + 1)` —nunca un
   número suelto— como manda esa misma sección.

   *No era un defecto de posición: era un defecto de PILA. Y una pila con dos números que
   no salen de la escala no está ordenada, está ordenada por casualidad.* */
.exds-menu-velo {
  position: fixed;
  inset: 0;
  z-index: var(--z-desplegable, 1000);
  background: transparent;
}

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 4 · EL POPOVER ▓

   ⛔ EL EJE VERTICAL SE DERIVA, NO SE TECLEA: `calc(100% + 6px)` = «justo debajo de mi
   disparador, con el aire del canon». La ficha escribía `top: 42px` **bajo un
   disparador de 36**; el handoff escribía `top: 46px` bajo uno de 38. Los dos son el
   mismo 6 px de aire con dos alturas distintas escritas a mano. *Un número que depende
   de otro y se teclea aparte es un número que caduca cuando el otro cambia.*

   ⛔ EL EJE HORIZONTAL LO ESCRIBE EL JS (`menu.js` › `anclar`), y sólo con el popover
   abierto: `left:0` si cabe · `right:0` si no · pegado al borde con 12 px de aire si
   tampoco. El `max-width` de abajo es el techo duro de esa tercera salida.
   ═══════════════════════════════════════════════════════════════════════════════ */
.exds-menu-pop {
  position: absolute;
  top: calc(100% + var(--space-1-5));
  left: 0;
  /* un peldaño LOCAL sobre su propio velo · ver el bloque de la capa, arriba */
  z-index: calc(var(--z-desplegable, 1000) + 1);
  width: var(--w-menu);
  max-width: calc(100vw - 24px);   /* 12 px de aire a cada lado · el clamp del anclaje */
  background: var(--color-surface);
  border: 1px solid var(--color-border-subtle);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  padding: var(--space-1-5);
  /* De dónde CRECE. Lo reescribe el JS con el anclaje real: un popover que crece desde
     una esquina en la que no está anclado se lee como si viniera de otro botón. */
  transform-origin: top left;
}

/* ⛔ LOS TRES ANCHOS SON DEL CANON, Y LA PREGUNTA QUE ELIGE ES «¿QUÉ LLEVA DENTRO?»
   (Ricardo, 2026-08-12 · ficha § Ancho). No «¿cuánto contenido hay?» — esa segunda
   lleva a un ancho por menú, que es de donde venimos: el censo midió **11 anchos para
   15 menús**, seis de ellos separados por 2 a 4 px. */
.exds-menu-pop[data-ancho="acciones"]  { width: var(--w-menu); }            /* 224 · lista de acciones */
.exds-menu-pop[data-ancho="identidad"] { width: var(--w-menu-identidad); }  /* 280 · avatar + nombre + correo */
.exds-menu-pop[data-ancho="ancho"]     { width: var(--w-menu-ancho); }      /* 360 · grupos de filtros con pastillas */

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 5 · ⭐ EL PAR DE MOVIMIENTO — papel 2 de la matriz ▓

   `DECISION-FUENTE: Ricardo 2026-08-06 — paso 6 del plan de efectos: todo desplegable`
   `anclado a un botón entra con exds-pop-in y sale con exds-pop-out.`
   `DECISION-FUENTE: Ricardo 2026-08-10 (INC-403) — UN SOLO GESTO para abrir un panel.`

   ⛔ EL GESTO ES EL DEL 10-AGO, QUE ES EL MÁS RECIENTE Y EL QUE MIDE EL GATE.
   `gate_gesto_desplegable.js` exige literalmente `opacity 0 · scale(.92)
   translateY(-6px)` → `opacity 1 · sin transform`. El censo de aquel día encontró
   **ocho nombres para tres gestos**, los tres a 120 ms y con la misma curva: lo único
   que cambiaba era **la distancia**, y eso es lo que Ricardo percibió como «más suave».
   *Su ojo midió bien y su palabra («lento») no: no era tiempo, era amplitud.*

   ⚠ Y ESTO CORRIGE AL 6-AGO EN UN PUNTO, declarado y no escondido: aquel día la salida
   recorría MENOS que la entrada (`.99` contra `.97`). El canon del 10-ago dice «sale con
   el mismo gesto invertido y `--ease-exit`». Se sigue el más reciente y **la asimetría
   sobrevive donde de verdad importa: en la CURVA** — al abrir esperas a que la cosa
   llegue; al cerrar ya decidiste y sólo quieres que se quite de en medio.
   ⚠ Producción todavía escribe el gesto viejo (`scale(.97)`) en las tres apps: es deuda
   ANTERIOR a este trabajo, la mide `gate_gesto_desplegable` y no se toca desde aquí.

   ⛔ QUIÉN DISPARA LA SALIDA, Y POR QUÉ NO SON LOS SITIOS DE CIERRE: `[hidden]` es
   `display:none` y una caja sin display no anima. El JS pone `hidden` **y** la clase de
   salida; el CSS de abajo **re-muestra la caja los 120 ms que dura** y nada más. Si el
   temporizador que quita la clase fallara, la caja ya está oculta — **se cierra sola**.
   *La alternativa era tocar cada sitio de cierre, y basta olvidar uno para dejar un menú
   pegado encima de la pantalla del analista.*
   ═══════════════════════════════════════════════════════════════════════════════ */
@keyframes exds-pop-in  { from { opacity: 0; transform: scale(.92) translateY(-6px); } to   { opacity: 1; transform: none; } }
@keyframes exds-pop-out { from { opacity: 1; transform: none; } to   { opacity: 0; transform: scale(.92) translateY(-6px); } }

.exds-menu-pop:not([hidden]) {
  animation: exds-pop-in var(--motion-fast) var(--ease-default) both;
}
.exds-menu-pop[hidden].exds-pop-out {
  /* Gana a la hoja del navegador, que le pondría `display:none` y mataría la salida.
     Dura exactamente lo que el temporizador del JS, y no un milisegundo más. */
  display: block;
  animation: exds-pop-out var(--motion-fast) var(--ease-exit) both;
}

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 6 · LA FILA DE MENÚ (`role="menuitem"`) ▓
   Mismos nombres de clase que el kit (`Menu.jsx`) a propósito: dos gemelos con dos
   vocabularios divergen sin que nadie lo vea (`lint_paridad_gemelos`).

   ⚠ DOS VALORES DE LA FICHA SE HOMOLOGAN AL CANON, y los dos por la misma razón —
   no son escalones (CLAUDE.md regla 0-bis · INC-210):
     · radio **7** → `--radius-sm` (6). Ricardo aprobó el 2026-07-21 bajar los sueltos:
       «tener 6 y 8 juntos es el escalón doble que nadie sabe cuándo usar».
     · relleno vertical **9** → `--space-2-5` (10), que es **el que la propia ficha ya
       usa en el eje horizontal**. Un solo valor en vez de dos que difieren en 1 px.
     · tamaño **13.5** → `--fs-body-sm` (13). Ricardo declinó 12.5 el 2026-07-21 por lo
       mismo; el kit ya escribe `var(--fs-body-sm, 13px)`.
   ═══════════════════════════════════════════════════════════════════════════════ */
.exds-mi {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  width: 100%;
  padding: var(--space-2-5);
  border: 0;
  background: none;
  border-radius: var(--radius-sm);
  text-align: left;
  cursor: pointer;
  font: var(--fw-medium) var(--fs-body-sm) var(--font-sans);
  color: var(--fg1);
  transition: background var(--motion-fast) var(--ease-default);
}
.exds-mi:hover { background: var(--color-bg-subtle); }
/* ⛔ MISMA GUARDA, MISMA RAZÓN: una opción se esconde con `hidden` cuando el contexto no la
   permite (el ejemplo canónico monta así «Nuevo contrato»), y el `display:flex` de arriba le
   ganaba al navegador — la opción prohibida seguía pinchable. */
.exds-mi[hidden] { display: none; }
.exds-mi:focus-visible { outline: 2px solid var(--color-border-focus); outline-offset: -2px; }

/* ⭐ LO ELEGIDO (`role="menuitemradio"` + `aria-checked="true"`) — cosechado del kit el
   2026-08-24, que es cuando el gate pudo VERLO: la hoja en línea de `Menu.jsx` no entraba en
   el denominador y estas dos reglas llevaban semanas fuera de la comparación.

   ⛔ EL DIBUJO NO SE INVENTA: es el que el catálogo YA usa en `OptionList` — fondo
   `--color-bg-subtle` + peso 600. Se copia el patrón.

   ⛔ Y EL PESO NO ES ADORNO, ES EL SEGUNDO CANAL. Si lo elegido se distinguiera SÓLO por un
   gris de 1,10 contra el blanco, quien no distingue ese gris —o mira de reojo— no tendría cómo
   saber cuál está puesta. Misma regla que las pastillas de estado.
   ⚠ El hover baja un peldaño (`-bg-sunken`) porque el reposo ya ocupa el `-bg-subtle`: sin eso,
   pasar el ratón por encima de la opción elegida no se notaría. */
.exds-mi[aria-checked="true"] { background: var(--color-bg-subtle); font-weight: 600; }
.exds-mi[aria-checked="true"]:hover { background: var(--color-bg-sunken); }

/* El ícono va a la izquierda, 16 × 16, un peldaño por debajo del texto en tinta: el
   ícono acompaña, no manda. Sale de la fuente única (`COMPONENTE_ICONO.md`). */
.exds-mi svg { width: 16px; height: 16px; display: block; flex: none; color: var(--fg2); }
.exds-mi-txt { flex: 1 1 auto; min-width: 0; }

/* El contador a la derecha de una fila («Actividad 12»). Sólo texto, sin pastilla: una
   pastilla dentro de una fila de menú es ruido — el número ya está solo en su columna. */
.exds-mi-n { font-variant-numeric: tabular-nums; font-size: var(--fs-caption); color: var(--fg3); flex: none; }

/* DESTRUCTIVO · «Rechazar expediente». Tinta e ícono en la BASE `--color-error`, y su hover va
   al tinte del mismo semántico — nunca al gris de los demás.
   ⛔ AQUÍ DECÍA `--color-error-deep`, y lo corrige §0-quater de `COMPONENTE_SEMANTICOS.md`
   (`DECISION-FUENTE:` Ricardo 2026-08-20): en REPOSO esta fila va sobre el blanco del menú, no
   sobre un tinte, así que le toca la base. Y no se añade un `-deep` para el hover a propósito:
   `-deep` estaba *reservado* a lo que va encima de su tinte y no *exigido*, porque con el rojo
   viejo la base sobre `--color-error-tint` medía 5,08:1 y pasaba AA. **Con #DA342B mide 3,97 y ya
   no pasa**, así que aquí el `-deep` dejó de ser una reserva y pasó a ser obligatorio (4,55).

   ⛔ Y ESTE COMENTARIO AFIRMÓ LA REGLA ANTES DE QUE EXISTIERA. Se escribió el 2026-08-29 diciendo
   *«por eso el :hover sube a -deep»* y **la regla no estaba**: sólo había `background`. Lo cazó
   `revisor-qa` leyendo el comentario contra el código, no el código contra la pantalla.
   *Un comentario que describe la intención en vez del hecho se lee igual que uno cierto, y por eso
   apaga la pregunta del siguiente que pase.* Es el mismo defecto que este branch bautizó tres
   archivos más allá, y lo cometió el mismo día quien lo bautizó. */
.exds-mi--danger { color: var(--color-error, #DA342B); }
.exds-mi--danger svg { color: var(--color-error, #DA342B); }
.exds-mi--danger:hover { background: var(--color-error-tint); color: var(--color-error-deep); }
/* ⛔ EL ÍCONO SE QUEDA EN LA BASE, y aquí me pasé de largo (2026-08-29). Al subir el TEXTO a
   `-deep` subí también su `svg`, y el canon exime a los glifos: `DECISION-FUENTE: Ricardo,
   2026-08-09 — un punto o un ícono puede quedar por debajo del contraste porque NUNCA es el único
   portador del significado; la palabra va al lado.* Un ícono no es texto pequeño: es refuerzo.
   Aplicar la regla del texto a lo que la tiene EXENTA no es ser prudente, es no leer la excepción.
   La regla se retira; el `svg` hereda `--color-error` de la línea de arriba. */

/* DESHABILITADO · ⛔ NO es un `<button>` sino un `<div aria-disabled>` (ficha § Estados):
   un botón deshabilitado no recibe el foco, y entonces **nadie puede leer el motivo**.
   Sin hover, con el cursor que lo dice y con el motivo en un tooltip (`data-tt`). */
.exds-mi--off { color: var(--fg4); cursor: not-allowed; }
.exds-mi--off svg { color: var(--fg4); }
.exds-mi--off:hover { background: none; }

/* DIVISORIA · separa el grupo normal del ítem destructivo o de un grupo condicional.
   ⚠ La ficha escribía `margin: 5px 4px`. **5 no es escalón** (los vecinos son 4 y 6) y
   está justo en medio de los dos, así que elegir uno sería inventar. Se toma el 4 que la
   propia ficha ya usa en el eje horizontal: un solo valor en vez de dos que difieren en
   1 px, que es el mismo criterio que se aplicó al relleno de la fila. */
.exds-mi-sep {
  height: 1px;
  background: var(--color-border-subtle);
  margin: var(--space-1);
  border: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 7 · ⭐ LAS RANURAS DEL PANEL ANCHO — cabecera · cuerpo · PIE PEGAJOSO ▓

   Sólo se encienden con `data-panel="si"`. Un menú de acciones no las quiere: son tres
   cajas de más para una lista de cuatro filas.

   ⛔ EL PIE ES **PEGAJOSO POR MAQUETACIÓN, NO POR `position: sticky`.** El popover es
   una columna flex con tres hijos: la cabecera y el pie no se encogen (`flex: none`) y
   el cuerpo se queda con lo que sobra y **desplaza dentro de sí** (`min-height: 0`, sin
   lo cual una caja flex no baja de su alto natural y el desplazamiento se va al panel
   entero). *`sticky` habría dejado el pie flotando sobre el contenido con el borde
   superior mordiendo la última fila de chips; así el pie es una franja de verdad.*
   ═══════════════════════════════════════════════════════════════════════════════ */
/* ⛔ `:not([hidden])` NO ES ADORNO — sin él el panel NO SE ESCONDE NUNCA, y el defecto
   es invisible al leer. `[hidden]` es una regla de la hoja del NAVEGADOR, y **cualquier
   `display` de autor la gana, tenga la especificidad que tenga**: escribir
   `.exds-menu-pop[data-panel="si"] { display: flex }` a secas deja el panel pintado con
   el atributo puesto — abierto para el ojo y cerrado para el árbol de accesibilidad.
   Se cazó EJECUTANDO, con la comprobación de `prefers-reduced-motion` (2026-08-17): era
   la única de las diez que llegaba a mirar el `display` de la caja cerrada. *Nueve
   comprobaciones en verde sobre un panel que no se cerraba.* */
.exds-menu-pop[data-panel="si"]:not([hidden]),
.exds-menu-pop[data-panel="si"][hidden].exds-pop-out {
  display: flex;
  flex-direction: column;
}
.exds-menu-pop[data-panel="si"] {
  padding: 0;                       /* cada ranura pone el suyo */
  /* Techo de alto: lo que quede de ventana menos el aire de la barra y del borde. Es
     MEDIDA DE CAJA, no estilo: el canon no tokeniza alturas de contenido — el mismo
     trato que el tope de 168 px del resumen de `SeccionFiltros`. */
  max-height: calc(100vh - 220px);
}

.exds-menu-cab {
  flex: none;
  padding: var(--space-3-5) var(--space-4) var(--space-2);
  font: var(--fw-semibold) var(--fs-h3) var(--font-sans);
  color: var(--fg1);
  /* ⚠ El handoff escribía 15/600. **15 no es escalón de texto** del canon (los vecinos
     son 14 y 16); `--fs-button-lg` sí vale 15 pero es de BOTÓN, y esto es un título.
     Gana el catálogo: `--fs-h3`. */
}

.exds-menu-cuerpo {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 0;      /* ⛔ sin esto la caja flex no baja de su alto natural: no desplaza */
  overflow-y: auto;
  padding: 0 var(--space-2) var(--space-2);
}

/* ⭐ EL PIE — el conteo a la IZQUIERDA, «Limpiar» a la DERECHA.
   `FUENTE: plans/handoff toolbar2/toolbar/README.md §1 y §5.1.`

   ⛔ Y NO LLEVA «APLICAR», que es la mitad de la regla: **el filtro se aplica en vivo**.
   *Un primario ink que sólo cierra es una promesa falsa* — el usuario cree que hasta
   pulsarlo no ha pasado nada, y lleva veinte segundos viendo la lista ya filtrada
   detrás. Si algún día alguien echa de menos el botón, lo que falta no es el botón: es
   que el filtrado dejó de ser en vivo.

   ⛔ EL TEXTO DEL CONTEO LO ESCRIBE QUIEN MONTA. «121 contratos» lleva dentro un
   sustantivo de negocio, y esta pieza no sabe qué está filtrando
   (`CONTRATO_FILTRADO_PANTALLA_LISTA.md` §1). Aquí sólo hay una caja y una tipografía. */
.exds-menu-pie {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-2-5) var(--space-3);
  border-top: 1px solid var(--color-border-subtle);
  background: var(--color-surface);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}
.exds-menu-pie-n {
  font: var(--fw-regular) var(--fs-body-sm) var(--font-sans);
  color: var(--fg3);
  font-variant-numeric: tabular-nums;
}

/* «Limpiar» · ES LA MISMA PIEZA que el «Limpiar» de la fila de pastillas
   (`pastilla-filtro.css` › `.exds-fpill-clear`): texto, no un botón con caja.
   ⛔ Un `Button` —aunque fuera fantasma— traería altura de acción y competiría con lo
   que hay al lado, y **deshacer nunca debe pesar más que lo hecho** (7-bis). */
.exds-menu-pie-limpiar {
  display: inline-flex;
  align-items: center;
  flex: none;
  height: 30px;
  padding: 0 var(--space-2-5);
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--fg2);
  font: var(--fw-medium) var(--fs-body-sm) var(--font-sans);
  cursor: pointer;
  transition: background var(--motion-fast) var(--ease-default),
              color var(--motion-fast) var(--ease-default);
}
.exds-menu-pie-limpiar:hover { background: var(--color-bg-subtle); color: var(--fg1); }
.exds-menu-pie-limpiar:focus-visible { outline: 2px solid var(--color-border-focus); outline-offset: 2px; }

/* ═══════════════════════════════════════════════════════════════════════════════
   ▓ 8 · MOVIMIENTO APAGADO — sin excepción (regla 4 de la matriz, no negociable) ▓
   ⛔ Y APAGARLO NO DEJA SIN INFORMACIÓN: el panel sigue apareciendo y desapareciendo,
   el disparador sigue marcándose activo y el foco sigue volviendo. Lo único que se
   quita es el gesto.
   ⚠ La salida se vuelve INSTANTÁNEA a propósito: `display:none` de vuelta. El JS
   también lee el ajuste y pone su temporizador a cero, así que las dos mitades —la que
   dibuja y la que limpia— se apagan a la vez. *Una guarda que sólo está en el CSS deja
   al temporizador esperando 120 ms por una animación que nadie va a ver.*
   ═══════════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .exds-menu-pop:not([hidden]) { animation: none; }
  .exds-menu-pop[hidden].exds-pop-out { display: none; animation: none; }
  .exds-menu-kebab,
  .exds-mi,
  .exds-menu-pie-limpiar { transition: none; }
}
