/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║  lp-modal.css — EL MODAL DEL SISTEMA                                     ║
   ║  Design system compartido de LogiPaq · css/ui/                           ║
   ╚══════════════════════════════════════════════════════════════════════════╝

   QUÉ TRAE
   Un solo componente: el chrome del modal —ancho, alto, overlay, cabecera y
   comportamiento en mobile—. NO estila el contenido: lo que va adentro lo
   resuelve cada página (o lp-config.css, si es un patrón compartido).

   DE QUÉ DEPENDE
   De Shoelace 2.20.1 (sus tokens --sl-*) y de nada más. Se puede cargar solo.

   ORDEN DE CARGA — la pagina PRIMERO, los compartidos ULTIMOS
     1. el CSS propio de la pagina (css/napse.css, css/producteca.css, ...)
     2. tema de Shoelace (light.css + dark.css)
     3. css/ui/lp-tokens.css      <- la paleta --e3-*
     4. css/ui/lp-modal.css       <- ESTE
     5. css/ui/lp-config.css      <- componentes de adentro del modal

   Va al reves de la intuicion ("lo especifico pisa a lo general") y estuvo mal
   escrito aca hasta el 09-08-2026: decia la pagina al final, "que puede pisar
   lo de arriba". Es a proposito. css/napse.css tiene ~600 reglas heredadas que
   pintan background, border y color a mano; con la pagina al final cada una
   vuelve a pisar a Shoelace y reaparecen los dos estilos encimados. Con los
   compartidos ultimos manda el design system.

   Por eso una pagina NO puede pisar css/ui/ con una regla suelta. Si necesita
   algo distinto: o el componente compartido gana un modificador
   (.lp-modal--form es exactamente eso), o el estilo no era compartido.

   CÓMO SE USA — copiá esto y anda
   ──────────────────────────────────────────────────────────────────────────
     <sl-dialog id="miModal" class="lp-modal">
       <span slot="label">Título del modal</span>
       <sl-icon-button slot="header-actions" name="gear" label="Configurar"></sl-icon-button>

       <div id="miModal-cuerpo"></div>
     </sl-dialog>

     document.getElementById("miModal").show();   // y .hide()

   Variante para un formulario corto (más angosto y de alto natural):
     <sl-dialog class="lp-modal lp-modal--form"> … </sl-dialog>

   La página TIENE que sincronizar el tema, o el modal sale claro sobre una
   página oscura. Va en el <head>, antes de pintar nada:

     <script>
       (function () {
         var raiz = document.documentElement;
         function sync() {
           raiz.classList.toggle("sl-theme-dark", raiz.getAttribute("data-theme") === "dark");
         }
         sync();
         new MutationObserver(sync).observe(raiz, { attributeFilter: ["data-theme"] });
       })();
     </script>
   ──────────────────────────────────────────────────────────────────────────

   MIGRAR UN MODAL DE BOOTSTRAP SIN TOCAR A QUIEN LO USA
   Se cambia el markup por el de arriba conservando los ids y se carga
   js/ui/lpDialogCompat.js: traduce jQuery(el).modal("show"|"hide"), la clase
   `show` y los eventos show/shown/hide/hidden.bs.modal (nativos Y por jQuery).

   LO USAN HOY
     e3StoresOmni.html  -> js/jsE3Omni/ui/e3OmniModal.js (.lp-modal.e3-omni-dialog)
     napse.html         -> los dos shells del markup (.lp-modal.napse-dialog)

   ESCALERA DE Z-INDEX DEL SITIO
     1030  navbar (css/navbar.css:24)
     1040  sl-dialog          <- tiene que tapar al navbar
     1060  SweetAlert2        <- tiene que tapar al modal (confirmaciones adentro)
     2147483647  toasts de LogiAvisos (css/avisos.css:41)

   Shoelace trae --sl-z-index-dialog en 800, por eso el navbar le quedaba encima.
   Se sube acá y lo hereda cualquier sl-dialog de la página.

   REGLA DE ORO DE ESTE ARCHIVO
   Nada acá adentro nombra a una página. Ni `.napse-body`, ni `.e3-omni-body`,
   ni un id. Una página nueva carga los tres CSS de css/ui/ y funciona SIN
   editarlos. Si algo necesita el nombre de una página, va en el CSS de esa
   página, no acá. */

:root {
  --sl-z-index-dialog: 1040;
}

/* Y ADEMÁS declarado sobre el host: css/avisos.css pone el mismo token en
   `:root` con 999999 (piso para sus diálogos) y suele cargar después, así que
   por herencia el modal se iba a 999999 y los diálogos satélite que un módulo
   cuelga del body con su propio token (1200-1350, p. ej. los de Stock Placeit)
   se abrían DETRÁS. Una custom property puesta en el elemento le gana a la
   heredada de :root sin importar el orden de las hojas. La escalera queda:
   modal 1040 < diálogos de módulo 1200-1350 < diálogos de avisos 999999. */
.lp-modal {
  --sl-z-index-dialog: 1040;
}

/* ── con el modal abierto, la pagina no scrollea ni muestra su barra ───────
   Shoelace marca <html class="sl-scroll-lock"> mientras hay un dialogo
   abierto, pero su bloqueo va sobre el <body>, y en estas paginas el que
   scrollea es el <html>: medido con el modal abierto, la pagina seguia
   corriendo y la barra vertical seguia a la vista al lado del overlay.

   Se ata el bloqueo a la clase que Shoelace ya pone —no a `:has()`, que
   ademas pide Chrome 105— asi vale para cualquier dialogo del sistema.

   Hay que reservar el ancho de la barra o la pagina de atras salta ~15px a la
   derecha al abrirse el modal. Se hace con `padding-right`, NO con
   `scrollbar-gutter: stable`: el gutter reserva el espacio DIBUJANDO la pista
   vacia de la barra, o sea que se sigue viendo una barra —gris y sin pulgar,
   pero ahi— que es justo lo que se queria sacar.

   El ancho no se adivina: Shoelace ya lo mide y lo deja en
   `--sl-scroll-lock-size`, inline sobre el <html>, al mismo tiempo que pone la
   clase. El 0px del fallback cubre a los navegadores con barras superpuestas
   (macOS, mobile), donde no hay nada que compensar. */
/* Un contenedor que adentro tiene SOLO el gate de acceso se estira al alto del
   cuerpo del modal, para que el gate pueda centrarse de verdad.

   Sin esto, un módulo que inyecta el gate en un <div> propio —#napseReusableModalBody
   de napse.html— deja ese div con alto automático, o sea el alto de la tarjeta:
   el `min-height: 100%` del gate se resuelve contra `auto`, no hace nada, y la
   tarjeta queda pegada arriba con medio panel vacío abajo.

   Va acá y no en lp-config.css porque depende del modal: el cuerpo del
   <sl-dialog> es lo que tiene alto definido, y los hijos ranurados son hijos
   directos del <sl-dialog>. */
.lp-modal > :has(> .lp-gate) {
  height: 100%;
}

html.sl-scroll-lock {
  overflow: hidden;
  padding-right: var(--sl-scroll-lock-size, 0px);
}

/* ── ANTES DE QUE SHOELACE CARGUE ─────────────────────────────────────────
   Los modales se montan en el <body> ni bien arranca la pagina, pero el
   autoloader de Shoelace registra `sl-dialog` recien cuando lo VE en el DOM y
   trae el modulo por red. En ese rato el navegador lo trata como una etiqueta
   desconocida —un `display: inline` cualquiera— y PINTA SUS HIJOS: el contenido
   del modal aparece suelto al final de la pagina, abajo del footer.

   Se veia clarito en e3StoresOmni.html: al recargar, la tarjeta de "Acceso
   restringido — Ingresá la clave" quedaba colgada abajo de todo hasta que
   terminaba de bajar Shoelace.

   `:not(:defined)` matchea solo mientras el custom element no esta registrado,
   y deja de aplicar solo. No hace falta JS ni una clase que alguien tenga que
   sacar. Va sin `.lp-modal` a proposito: el problema es de CUALQUIER dialogo,
   incluidos los que arma LogiAvisos. */
sl-dialog:not(:defined),
sl-drawer:not(:defined),
sl-alert:not(:defined) {
  display: none;
}

/* Shoelace trae el ancho por defecto en 31rem (~496px), que queda chico para
   tablas y formularios. --width es la custom property oficial del componente. */
.lp-modal {
  --width: min(1100px, 92vw);
}

/* Shoelace oscurece con --sl-overlay-background-color, que por defecto es muy
   suave (~43% de negro) y no trae blur. Se oscurece mas y se desenfoca el fondo
   para que el modal despegue de la pagina. */
.lp-modal::part(overlay) {
  background-color: rgba(15, 23, 42, 0.62);
  backdrop-filter: blur(8px) saturate(120%);
  -webkit-backdrop-filter: blur(8px) saturate(120%);
}

.sl-theme-dark .lp-modal::part(overlay) {
  background-color: rgba(2, 6, 23, 0.72);
}

/* Alto fijo, tambien con el modal vacio: asi no salta de tamaño cada vez que
   carga o cambia el contenido. Va height y max-height con el mismo valor porque
   Shoelace ya trae su propio max-height y si no, gana el suyo. */
.lp-modal::part(panel) {
  height: min(80vh, 820px);
  max-height: min(80vh, 820px);
}

/* El header y el footer quedan fijos; scrollea solo el contenido.

   El `color` NO es decorativo: todo lo que va adentro del modal es light DOM y
   hereda del <body> de la pagina, que en este sitio lo pinta Bootstrap 4.5
   (`body{color:#212529}`). En modo oscuro eso daba texto casi negro sobre panel
   negro —medido 1.12:1— en cualquier elemento que no declarara su propio color.
   Con esto quedan blindados todos los modales del sistema de una. */
/* `position: relative` NO es decorativo: hace del cuerpo el marco de referencia
   de todo lo que adentro este posicionado en absoluto.

   Un modal de este sitio suele traer una capa de "cargando" con
   `position: absolute; inset: 0` y un `backdrop-filter` — la
   `.napse-modal-loader`, por ejemplo—. En los modales de Bootstrap esa capa se
   media contra el `div.modal-body`, que era su ancestro posicionado. Al pasar a
   sl-dialog ese div desaparece, y sin nadie posicionado en el medio el `inset:
   0` se resuelve contra el VIEWPORT: la capa tapa la pantalla entera y el blur
   se come tambien el modal y la pagina. Se ve como si el overlay estuviera roto.

   Con esto la capa queda encerrada en el cuerpo del modal, que es donde va. */
.lp-modal::part(body) {
  position: relative;
  overflow-y: auto;
  color: var(--sl-color-neutral-900);
}

/* ── mientras carga, el cuerpo no scrollea ni muestra su barra ────────────
   La capa de "cargando" tapa el cuerpo entero, pero el cuerpo SEGUIA siendo
   scrolleable: quedaba la barra vertical del modal a la derecha, con las
   flechitas del sistema, al lado de un contenido borroso que no se puede ni
   leer ni tocar. Se ve como un error.

   Mientras la capa este visible el cuerpo va en `overflow: hidden`. Al
   ocultarse la capa —le sacan el atributo `hidden`— la barra vuelve sola.

   Los nombres de clase son los que ya existen en el markup de las paginas.
   `napse-modal-loader` esta tanto en napse.html como en producteca.html: el
   prefijo dice de donde salio, no de quien es, igual que `e3cfg-*`. Si
   aparece una tercera, se suma a esta lista.

   `:has()` pide Chrome 105. Si hiciera falta soportar mas atras, la
   alternativa es que el JS que muestra la capa ponga una clase en el dialogo. */
.lp-modal:has(> .lp-gate:not([hidden])),
.lp-modal:has(> .napse-modal-loader:not([hidden]))::part(body),
.lp-modal:has(> .producteca-detail-modal-loader:not([hidden]))::part(body),
.lp-modal:has(> .cmeli-stats-overlay:not([hidden]))::part(body) {
  overflow: hidden;
}

/* ── cabecera ──────────────────────────────────────────────────────────────
   El titulo NO se leia en modo oscuro. Son dos cosas distintas y hacen falta
   las dos:

   1. `::part(title)` es el <h2> del shadow DOM. Sin color propio hereda el que
      trae el panel y, con una pagina que pinta su propio color de texto, salia
      negro sobre panel negro. Es el mismo arreglo que ya estaba en
      css/avisos.css para `.lp-dialog::part(title)`, que nunca llego aca porque
      el modal del sistema es `.lp-modal`, no `.lp-dialog`.

   2. El texto del titulo suele venir en un `<span slot="label">`, que es LIGHT
      DOM: no hereda del panel sino del <body> de la pagina. Por eso hay que
      pintarlo aparte — con arreglar solo el ::part() se seguia sin ver. */

.lp-modal::part(title) {
  color: var(--sl-color-neutral-900);
  font-size: var(--sl-font-size-large);
  font-weight: var(--sl-font-weight-semibold);
}

.lp-modal > [slot="label"],
.lp-modal > [slot="label"] * {
  color: var(--sl-color-neutral-900);
}

.lp-modal::part(header) {
  border-bottom: 1px solid var(--sl-color-neutral-200);
}

/* La cruz de cerrar y los botones de la cabecera: el gris neutro del tema, no
   el color heredado de la pagina. */
.lp-modal::part(close-button__base) {
  color: var(--sl-color-neutral-500);
}

.lp-modal::part(close-button__base):hover {
  color: var(--sl-color-neutral-900);
}

/* Un formulario corto crece con el contenido en vez de usar el alto fijo de
   arriba: con tres campos, 820px de alto es todo aire. */
.lp-modal--form {
  --width: min(560px, 92vw);
}

.lp-modal--form::part(panel) {
  height: auto;
  max-height: 88vh;
}

/* Contenido corto que SÍ necesita el ancho del modal estándar (una grilla de
   dos columnas, por ejemplo) pero no su alto fijo: crece con el contenido
   con el MISMO tope que el default de arriba, min(80vh, 820px), así nunca
   se pasa de alto en una pantalla chica. A diferencia de .lp-modal--form,
   ÉSTA NO TOCA EL ANCHO — --form además angosta el modal a 560px, que le
   queda chico a una grilla de dos columnas. Ejemplo real: el modal de
   "Detalle de la venta" de meliAr.html (js/meliAr/meliArTarjetaVista.js),
   cuyos paneles miden menos que el alto fijo y dejaban medio modal de aire
   abajo. napse.html y e3StoresOmni.html no usan esta clase y siguen con el
   alto fijo de siempre — no se les toca el comportamiento por defecto. */
.lp-modal--auto-height::part(panel) {
  height: auto;
  max-height: min(80vh, 820px);
}

/* Modal ANGOSTO. El ancho por defecto, min(1100px, 92vw), está pensado para una
   tabla; un modal de una sola columna —una lista de opciones, un formulario
   corto, un paso de asistente— a 1100 px deja tres palabras por renglón
   perdidas en el ancho.

   Va ACÁ y no en el CSS de la página a propósito: css/ui/ carga ÚLTIMO y le
   gana a las hojas de página, así que una regla suelta en styles.css que
   intente pisar `--width` no tiene efecto. La forma de pedir algo distinto es
   un modificador del componente compartido. */
.lp-modal--angosto {
  --width: min(620px, 92vw);
}

/* Modificador: a PANTALLA COMPLETA (24-09-2026). La segunda excepción al alto fijo, después de la
   consulta SQL de napse.html (.napse-sql-fs-modal): «Devoluciones Novogar» de posventaOmni.html abre
   así y un botón de la cabecera lo achica al tamaño normal (se recuerda por PC). Sin el modificador,
   el modal es el de siempre. */
.lp-modal.lp-modal--pantalla {
  --width: 100vw;
}
.lp-modal.lp-modal--pantalla::part(base) {
  padding: 0;
}
.lp-modal.lp-modal--pantalla::part(panel) {
  width: 100vw;
  max-width: 100vw;
  height: 100dvh;
  max-height: 100dvh;
  border-radius: 0;
}

/* En mobile ocupa la pantalla entera. --width sola no alcanza: solo controla el
   ancho, y el alto, el padding del overlay y los bordes redondeados viven en el
   shadow DOM del componente, al que solo se llega por ::part(). */
@media (max-width: 768px) {
  .lp-modal {
    --width: 100%;
  }

  /* Sin esto queda el margen del overlay alrededor del panel. */
  .lp-modal::part(base) {
    padding: 0;
  }

  .lp-modal::part(panel) {
    width: 100%;
    max-width: 100%;
    height: 100dvh;
    max-height: 100dvh;
    border-radius: 0;
  }
}

/* ── El modal ES el contenedor: nada adentro vuelve a ser tarjeta ──────────
   (21-08-2026)

   `css/theme.css:119` le pone la "tarjeta de vidrio" iOS —fondo translúcido,
   borde, sombra de 60 px, radio 22, margen automático y padding— a TODO
   `<form>`, `<main>` y `.lpq-container` de CUALQUIER página, y lo hace con
   `!important`. `css/styles.css:174` hace algo parecido con `<form>`.

   Eso está bien para el cuerpo de una página, pero adentro de un `<sl-dialog>`
   se ve como una caja adentro de otra caja: dos bordes paralelos con sus dos
   sombras, y el padding doble comiéndose el ancho útil. Apareció en la
   Reputación de Mercado Libre, cuyo dashboard era un `<main>`.

   Ese caso se arregló en el origen (pasó a `<div role="region">`), pero la
   trampa la pisa cualquiera que escriba un `<form>` adentro de un modal, que es
   lo más natural del mundo. Así que se neutraliza acá, de una vez y para todas
   las pantallas: `css/ui/` carga ÚLTIMO y le gana a las hojas de página.

   Va con `!important` porque contra `!important` no hay especificidad que
   alcance. El padding NO se toca: un `<form>` adentro del modal puede querer el
   suyo, y el que molestaba venía junto con el resto de la tarjeta. */
.lp-modal form,
.lp-modal main {
  background: transparent !important;
  border: 0 !important;
  border-radius: 0 !important;
  box-shadow: none !important;
  margin: 0 !important;
  max-width: none;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}
