@import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500&display=swap');

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  font-family: 'Open Sans', sans-serif;
  background: oklch(0.98 0.003 250);
  color: oklch(0.2 0.01 250);
}

#app { height: 100%; min-height:calc(100vh - 150px)}

/* Scrollbar chica y unificada en todos los navegadores/elementos. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--border, oklch(0.8 0.008 250)) transparent;
}

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--border, oklch(0.8 0.008 250));
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover { background: var(--text-faint, oklch(0.62 0.01 250)); }
::-webkit-scrollbar-corner { background: transparent; }

input::placeholder { color: oklch(0.62 0.01 250); }

button { font-family: inherit; cursor: pointer; }

/*
 * Foco unificado para inputs/textareas y los botones-dropdown (Módulo
 * padre, Tipo, filtros, cambio de estado, etc: todos comparten las clases
 * .status-dropdown-btn / .filter-toggle-btn): el borde toma el color de
 * acento del theme activo (--accent, seteado dinámicamente en
 * Dashboard.svelte según themeHue/darkMode). Fuera del shell (Login,
 * Recuperar) no hay --accent definido, así que cae al valor por defecto
 * (mismo azul/violeta de marca). No usar reglas :focus por componente para
 * esto — este selector global ya cubre cualquier input/dropdown nuevo que
 * use esas clases. Usa !important a propósito: las reglas scoped de cada
 * componente Svelte (`.clase.svelte-hash`) tienen la misma o mayor
 * especificidad y si no, lo pisan silenciosamente.
 */
input:focus, textarea:focus, select:focus,
.status-dropdown-btn:focus, .filter-toggle-btn:focus {
  outline: none !important;
  border-color: var(--accent, oklch(0.55 0.18 250)) !important;
}

.boot-loader {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
}

.boot-logo {
  width: 56px;
  height: 56px;
  animation: boot-pulse 1.6s ease-in-out infinite;
}

.boot-bar {
  width: 160px;
  height: 4px;
  border-radius: 2px;
  background: oklch(0.9 0.005 250);
  overflow: hidden;
  position: relative;
}

.boot-bar-fill {
  position: absolute;
  top: 0;
  left: -40%;
  height: 100%;
  width: 40%;
  border-radius: 2px;
  background: linear-gradient(90deg, oklch(0.5 0.19 320), oklch(0.55 0.18 250), oklch(0.55 0.15 200));
  animation: boot-bar-slide 1.1s ease-in-out infinite;
}

.boot-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: oklch(0.55 0.012 250);
  letter-spacing: 0.02em;
}

@keyframes boot-pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(1.08); opacity: 0.85; }
}

@keyframes boot-bar-slide {
  0% { left: -40%; }
  100% { left: 100%; }
}

/* ---------------------------------------------------------------------------
   Guardado en curso: deshabilita TODOS los botones de guardar de la app.

   Pedido explícito: "unificá todos los editores … siempre poné el botón en
   disabled". Se resuelve con un atributo `data-busy` en el <html> (lo setea
   Dashboard.svelte a partir del store `busy`, que prende `apiFetch()` en cada
   escritura) en vez de una variable `saving` por pantalla: de los 34 editores
   del backoffice, solo 6 deshabilitaban el botón y cada uno a su manera.

   El `!important` es necesario por el mismo motivo ya documentado para la
   regla de `:focus` (ver gotcha #4 en PUPILA_CMS.md §2): las reglas scoped de
   Svelte (`.primary-btn.svelte-hash`) tienen igual o más especificidad y si no
   lo tuviera lo pisarían en silencio.

   `pointer-events: none` corta el click y el `cursor: progress` del contenedor
   avisa que está trabajando. Se excluye `.saved-toast` (no es un control).
--------------------------------------------------------------------------- */
:root[data-busy] .primary-btn,
:root[data-busy] .status-dropdown-item,
:root[data-busy] .save-dropdown-menu button {
  pointer-events: none !important;
  opacity: 0.55 !important;
  cursor: progress !important;
}

:root[data-busy] {
  cursor: progress;
}

/* Estilo del estado deshabilitado, global. Motivo concreto: en Plantillas el
   botón Guardar SÍ tenía `disabled={saving}`, pero de los 34 editores solo 3
   definían `.primary-btn:disabled`, así que en el resto quedaba deshabilitado
   y VISUALMENTE IGUAL — de ahí el reporte "no se pone en disabled". Con esto,
   cualquier botón con el atributo se ve deshabilitado en toda la app. Mismo
   `!important` y mismo motivo que la regla de arriba. */
.primary-btn:disabled,
.secondary-btn:disabled,
button:disabled {
  opacity: 0.55 !important;
  cursor: not-allowed !important;
}

/* ---------------------------------------------------------------------------
   El menú `⋮` de una fila no puede quedar recortado por la tabla
   ---------------------------------------------------------------------------
   Reportado: "en todos los listados, cuando hay pocos registros, el desplegable
   de los 3 puntos queda dentro del overflow de la tabla y no se ven las
   opciones".

   La causa son dos decisiones correctas que se pisan entre sí:

     - `.table-wrap` declara `overflow-x: auto` en los 29 listados — es parte de
       la convención de columnas (subgrid + scroll horizontal en pantallas
       angostas, ver `check-columnas.js`). **Por spec, si un eje es distinto de
       `visible` el otro se computa a `auto`**, así que la tabla también recorta
       en vertical. Medido: `overflow-y` computado = `auto` en los 29.
     - `.actions-menu` es `position: absolute` dentro de la fila, centrado
       (`top: 50%`) en 24 listados o colgando (`top: 100%`) en Abonos/Lectores.

   Arriba lo que sobresale lo absorbe el renglón de encabezado; abajo no hay
   nada, así que se recorta en la ÚLTIMA fila — y con uno o dos registros TODAS
   las filas son la última, que es por qué el síntoma se ve "cuando hay pocos".

   Medido en pantalla real antes de escribir esto (el menú se sale de la caja):
   General 12px · Portales 11px (1 sola fila) · Autores 13px · Ediciones 14px ·
   Servicios 14px · Perfiles 14px · Notas 26px (3 items) · Abonos 61px y
   Lectores 63px (los dos que cuelgan). Peor caso: 63px.

   El arreglo: mientras hay un menú abierto, la tabla le hace lugar. El menú
   sólo existe en el DOM cuando está abierto (`{#if menuAbierto === row.id}` en
   los 26 listados), así que `:has()` alcanza y el padding desaparece al
   cerrarlo — no queda un hueco permanente al pie de ninguna tabla.

   Por qué acá y no en cada componente: la convención está copiada a mano en 29
   archivos y ya se pagó dos veces que se despeguen entre sí (ver "Barra de
   título" y "Buscador y desplegable de Filtros" en PUPILA_CMS.md). Una sola
   regla no se puede desincronizar. No hace falta `!important` (a diferencia de
   las dos reglas de arriba): ningún `.table-wrap` declara `padding`, así que no
   compite con nada scoped.

   Se descartó `overflow: visible` bajo el mismo `:has()`: en una tabla con
   scroll horizontal activo, pasar a `visible` resetea el `scrollLeft` y la
   tabla saltaría al inicio al abrir el menú.

   Después del arreglo, medido de nuevo: de 10 menús recortados quedó **1**, y
   es de 2px ARRIBA en Notas (el único listado con 3 items). Se deja así a
   propósito: 2px se comen el borde redondeado, no un item, y taparlos pide un
   `padding-top`, que mueve la tabla entera 16px hacia abajo cada vez que se
   abre un menú — un salto visible en las 29 pantallas para tapar 2px en una.

   **Si algún día un menú suma items y vuelve a recortarse, este número es lo
   único que hay que subir** — y `.docs/devtools/browser/listado-menu-acciones.mjs`
   lo mide solo, listado por listado. */
.table-wrap:has(.actions-menu),
.table-wrap:has(.status-dropdown-menu) {
  /* Hacia ARRIBA se sale poco (2px en Notas, 7px en Bloques: los menús de 3
     items, que el renglón de encabezado ya casi alcanza a absorber), pero un
     `padding-top` solo movería la tabla entera hacia abajo al abrir un menú.
     El `margin-top` negativo del mismo valor lo compensa: el contenido queda
     exactamente donde estaba y lo único que se corre es el borde de la
     tarjeta, 16px hacia arriba y por debajo de la barra sticky del título,
     que tiene fondo propio. Sin salto. */
  padding-top: 16px;
  margin-top: -16px;
}

.table-wrap:has(.actions-menu) {
  padding-bottom: 72px;
}

/* El desplegable de estado inline (Borrador/En revisión/Publicado/Programado)
   es el caso peor y NO estaba cubierto por la regla de arriba, que pedía
   `.actions-menu`: mide 142px, cuelga entero debajo de la fila y en Notas se
   salía **134px**. Lo encontró el barrido de
   `.docs/devtools/browser/desplegables-recortados.mjs`, no la lectura — es
   exactamente el mismo bug en otro control, y la razón de que ese script no
   busque clases conocidas sino cualquier flotante que se salga de su caja. */
.table-wrap:has(.status-dropdown-menu) {
  padding-bottom: 160px;
}
