/* Tema del módulo de Presupuestos
 *
 * POR QUÉ EXISTE ESTE ARCHIVO
 *
 * Las pantallas de /presupuestos se veían como productos distintos. Cuatro
 * cosas concretas, las cuatro medidas en el navegador y no a ojo:
 *
 *   1. El hueco bajo la barra de contexto. Sobre `<main>` compiten SIETE
 *      reglas de relleno superior, todas en el CSS generado y todas con
 *      `!important`. En cuatro pantallas —Fórmulas, Ensambles, Partidas y
 *      Reportes— la que gana vuelve a descontar la altura de la barra sobre un
 *      `<main>` que ya la tenía descontada por el margen: 104 px de hueco
 *      contra 28 px en las otras cuatro.
 *
 *   2. El ancho. 1262 px en unas, 1166 px en otras, y ninguna centrada: el
 *      cálculo se apoya en `100vw`, que cuenta la barra de desplazamiento.
 *
 *   3. Los botones. Seis clases distintas para la misma acción: budget-btn,
 *      budget-resources-primary, budget-assemblies-pending-action,
 *      formula-builder-primary, budget-formulas-library-button y
 *      budget-assembly-edit. Cada una con su color. El de «Nuevo recurso»
 *      salía verde brillante y el de «Abrir presupuestos», oscuro; la misma
 *      acción, dos productos.
 *
 *   4. El color. La primera versión de este archivo inventó un acento propio
 *      en vez de usar el de la aplicación, y dejó a Presupuestos como el único
 *      módulo de otro verde.
 *
 * Va acotado a `body.inside-budget-module` para no tocar el planificador ni la
 * administración, que tienen su propio lenguaje. Las ocho pantallas llevan esa
 * clase, Partidas incluida —hay un comentario en `public/styles.css` que dice
 * lo contrario, pero está desactualizado: comprobado en el navegador, su
 * `<body>` es `inside-app-body inside-budget-module`—. Donde sí se nombra la
 * clase de página es al pelear especificidad, no por falta de ámbito.
 *
 * Sobre los `!important`: las reglas que hay que vencer viven en
 * `public/styles.css`, que se regenera con Sass y borra lo que se le escriba
 * (ver `presupuestos-asistente.css`). No se pueden editar allí, y ganarles por
 * especificidad exigiría selectores más frágiles que el propio `!important`.
 */

/* ── Paleta ───────────────────────────────────────────────────────
 *
 * El acento NO se elige acá: se toma de los tokens que ya usa el resto de la
 * aplicación —`--inside-dark-accent` y `--inside-light-accent`, definidos en
 * `public/styles.css`—. La primera versión de este archivo inventó un verde
 * propio (#17a67f) y el módulo terminó siendo el único con ese color; los
 * botones de Presupuestos no coincidían con los de Control de obra ni con los
 * de Administración.
 *
 * Estas variables existen solo para no repetir el `var(--inside-…)` en cada
 * regla y para que el tema claro cambie en un único sitio. El valor de reserva
 * es el mismo que trae el token, por si esta hoja carga antes que la otra.
 */
body.inside-budget-module {
  --pres-acento: var(--inside-dark-accent, #00c896);
  --pres-acento-vivo: var(--inside-dark-accent-hover, #00e0a8);
  /* Texto sobre el acento: el verde de la casa es claro, así que encima va
     tinta oscura. Con blanco no pasa el contraste mínimo. */
  --pres-acento-texto: #04201a;
  --pres-acento-velo: rgba(0, 200, 150, 0.12);
  --pres-borde: rgba(0, 200, 150, 0.28);
  --pres-borde-suave: rgba(0, 200, 150, 0.16);
  --pres-tinta: var(--inside-dark-text, #e8ecef);
  --pres-atenuado: var(--inside-dark-muted, #a6acb3);
  /* Estados. Existen como variable porque los valores de oscuro son claros
     —salmón y ámbar pálidos— y sobre fondo blanco no se leen: medido en
     Recursos maestros con el tema claro puesto desde el botón, el salmón daba
     1,68 y el ámbar 1,33 de contraste, con 4,5 de mínimo. */
  --pres-peligro: #ffb4a8;
  --pres-aviso: #ffd8a0;
}

body.inside-budget-module.theme-light {
  --pres-acento: var(--inside-light-accent, #1a6b5a);
  --pres-acento-vivo: var(--inside-light-accent-2, #2b7c69);
  /* En claro el acento es oscuro y el texto encima va blanco. */
  --pres-acento-texto: #ffffff;
  --pres-acento-velo: var(--inside-light-accent-soft, rgba(26, 107, 90, 0.1));
  --pres-borde: var(--inside-light-border-strong, rgba(26, 107, 90, 0.22));
  --pres-borde-suave: var(--inside-light-accent-soft, rgba(26, 107, 90, 0.1));
  --pres-tinta: var(--inside-light-title, #17312b);
  --pres-atenuado: var(--inside-light-muted, rgba(23, 49, 43, 0.58));
  /* Los mismos estados, en versión legible sobre blanco. */
  --pres-peligro: #a3231f;
  --pres-aviso: #b06a1e;
}

/* ── El espacio de arriba ─────────────────────────────────────────
 *
 * Medido en las ocho pantallas del módulo: en TODAS, `<main>` tiene
 * `margin-top: 152px` y su borde superior cae exactamente en el borde inferior
 * de la barra de contexto. Es decir, el margen ya despeja las dos barras fijas
 * —la superior de 74 px y la de contexto de 78 px— y el relleno superior solo
 * tiene que dar aire.
 *
 * Cuatro pantallas no lo hacían así. Fórmulas, Ensambles, Partidas y Reportes
 * usan este atajo de `public/styles.css`:
 *
 *     padding: calc(var(--inside-app-topbar-height, 74px)
 *                   + var(--budget-workspace-top-gap)) … !important;
 *
 * que vuelve a descontar la barra superior sobre un `<main>` que ya la tenía
 * descontada. Resultado medido: 104 px de hueco bajo la barra contra 28 px en
 * Resumen, Recursos maestros, Dependencias y Plantillas. Los 74 px de más son,
 * literalmente, la altura de la barra contada dos veces.
 *
 * Acá se deja el aire solo: `--budget-workspace-top-gap`. Se usa el token en
 * vez de un número para que los cortes que ya lo bajan a 18 px en pantallas
 * angostas sigan mandando sin repetir la media consulta.
 *
 * Sobre la especificidad: el atajo es `!important` y pesa (0,2,2) por
 * `body.inside-app-body main.budget-formulas-page`, mientras que un
 * `body.inside-budget-module main` a secas pesa (0,1,2) y pierde. Por eso se
 * nombran las clases de página una por una: igualan el peso, y como esta hoja
 * carga después, gana la de acá.
 */
body.inside-budget-module main,
body.inside-app-body main.budget-formulas-page,
body.inside-app-body main.budget-assemblies-page,
body.inside-app-body main.partidas-page,
body.inside-app-body main.reports-page,
/* Fórmulas tiene una séptima regla, más pesada que las otras seis: se apoya en
   ser hermana de la barra superior, (0,3,2). Se repite su forma para igualarla;
   cargando después, gana esta. */
body.inside-app-body .budget-product-topbar ~ main.budget-formulas-page {
  padding-top: var(--budget-workspace-top-gap, 30px) !important;
}

/* Resumen y Partidas montan el contenido dentro de dos cajas intermedias que
   suman su propio relleno superior —27 px la sección y 18 px el contenedor—,
   así que el hueco medido bajo la barra era de 75 px y no de 30. Abajo no se
   tocan: ahí el relleno sí hace falta para que el contenido no muera contra el
   borde de la ventana. */
body.inside-budget-module .budget-app-section,
body.inside-budget-module .budget-app-shell {
  padding-top: 0 !important;
}

/* ── El aire de los lados ─────────────────────────────────────────
 *
 * Los anchos venían de dos sitios distintos: las pantallas de workspace usan
 * `width: min(1320px, 100vw - barra lateral - 48px)` y las otras cuatro
 * heredaban el de `.container`. Medido: 1262 px contra 1166 px. Casi cien
 * píxeles de diferencia, y los bordes de las tarjetas no coincidían al pasar
 * de una pantalla a la siguiente, que es lo que se nota navegando el módulo.
 *
 * El cálculo original arrastra además un error propio: `100vw` incluye el
 * ancho de la barra de desplazamiento y `<main>` no. Sobran ~15 px que se van
 * por la derecha. Medido en Fórmulas: la tarjeta quedaba a 24 px del borde
 * izquierdo y a 9 px del derecho. No saltaba a la vista porque las ocho
 * pantallas estaban descentradas de la misma forma.
 *
 * Acá no se mide contra la ventana sino contra el hueco disponible: el aire lo
 * pone `<main>` con el gutter, y el contenido ocupa el 100 % de lo que queda,
 * con tope de 1320 px. Al no haber `100vw`, la barra de desplazamiento deja de
 * contar y los dos lados quedan iguales.
 */
body.inside-budget-module main,
body.inside-app-body main.budget-formulas-page,
body.inside-app-body main.budget-assemblies-page,
body.inside-app-body main.partidas-page,
body.inside-app-body main.reports-page {
  padding-right: var(--budget-workspace-gutter, 24px) !important;
  padding-left: var(--budget-workspace-gutter, 24px) !important;
}

/* `.budget-workspace-shell` no tenía ninguna regla propia: era solo un gancho
   en el marcado, y por eso esas cuatro pantallas caían al ancho de
   `.container`. Los contenedores del módulo se resuelven todos acá. */
body.inside-app-body .budget-workspace-shell,
body.inside-app-body .budget-formulas-shell,
body.inside-app-body .budget-assemblies-shell,
body.inside-app-body .partidas-shell,
body.inside-app-body .reports-app-shell,
body.inside-budget-module .budget-app-section > .container,
body.inside-budget-module .budget-app-shell--workspace {
  width: min(var(--budget-workspace-max-width, 1320px), 100%) !important;
  max-width: var(--budget-workspace-max-width, 1320px) !important;
  margin-right: auto !important;
  margin-left: auto !important;
}

/* ── Una tarjeta, no dos ──────────────────────────────────────────
 *
 * En el Resumen sin proyecto activo, el aviso «Crea un proyecto» queda dentro
 * de `article.budget-card.budget-start-card`, que ya dibuja tarjeta —fondo,
 * borde y sombra—. El propio `.budget-start-banner` dibuja otra encima, y se
 * ven dos recuadros solapados, uno por dentro del otro.
 *
 * Manda la tarjeta de afuera, que es la que usa el resto del módulo; el aviso
 * pasa a ser su contenido.
 */
body.inside-app-body .budget-card .budget-start-banner {
  border: 0 !important;
  background: none !important;
  box-shadow: none !important;
  padding: 0 !important;
}

/* ── El hueco bajo el encabezado ──────────────────────────────────
 *
 * Medido en las ocho: 20 px en Fórmulas, Ensambles, Plantillas y Reportes;
 * 24 px en Recursos maestros y Dependencias; 42 px en Resumen y 36 px en
 * Partidas.
 *
 * Los dos casos grandes tienen la misma explicación. En esas dos pantallas el
 * encabezado va dentro de `.budget-main`, que es flex en Resumen y grid en
 * Partidas, y ahí los márgenes NO colapsan con el `gap` del contenedor: se
 * suman. 21,6 + 20 = 42 y 16 + 20 = 36, que es exactamente lo medido.
 *
 * La respuesta no es recortar el margen sino dejar un solo espaciador: manda
 * el `gap` del contenedor, ajustado al mismo ritmo de 20 px que usa el resto
 * del módulo, y el encabezado deja de poner el suyo. De paso, la separación
 * entre las tarjetas siguientes de esas dos pantallas queda también en 20.
 *
 * Los 24 px son otra cosa: ahí el margen sí colapsa —max(20, 24)— y basta con
 * igualar el del bloque que sigue.
 */
body.inside-budget-module .budget-main {
  row-gap: 1.25rem !important;
}

body.inside-budget-module .budget-main .modulo-encabezado {
  margin-bottom: 0 !important;
}

body.inside-budget-module .budget-resources-summary {
  margin-top: 1.25rem !important;
}

/* ── Botones ──────────────────────────────────────────────────────
 *
 * Dos formas y nada más: llena para la acción principal de la pantalla, con
 * borde para el resto. Las clases de una sola pantalla se alinean acá en vez de
 * reescribirlas una por una, que es lo que las hizo divergir.
 */
body.inside-budget-module .budget-resources-primary,
body.inside-budget-module .budget-assemblies-pending-action,
body.inside-budget-module .formula-builder-primary,
body.inside-budget-module .budget-formulas-calculate-button,
body.inside-budget-module .budget-btn-primary {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--pres-acento);
  border-radius: 0.6rem;
  background: var(--pres-acento);
  color: var(--pres-acento-texto);
  font: inherit;
  font-size: 0.88rem;
  font-weight: 650;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}

body.inside-budget-module .budget-resources-primary:hover,
body.inside-budget-module .budget-assemblies-pending-action:hover,
body.inside-budget-module .formula-builder-primary:hover,
body.inside-budget-module .budget-formulas-calculate-button:hover,
body.inside-budget-module .budget-btn-primary:hover {
  background: var(--pres-acento-vivo);
  border-color: var(--pres-acento-vivo);
}

body.inside-budget-module .budget-formulas-library-button,
body.inside-budget-module .budget-assembly-edit,
body.inside-budget-module .budget-btn-secondary {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--pres-borde);
  border-radius: 0.6rem;
  background: transparent;
  color: var(--pres-tinta);
  font: inherit;
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  cursor: pointer;
}

body.inside-budget-module .budget-formulas-library-button:hover,
body.inside-budget-module .budget-assembly-edit:hover,
body.inside-budget-module .budget-btn-secondary:hover {
  background: var(--pres-acento-velo);
  border-color: var(--pres-acento);
}

/* Deshabilitado: el mismo gesto en los dos temas. Sin esto, un botón apagado en
   claro quedaba igual de vivo que uno activo. */
body.inside-budget-module .budget-resources-primary[disabled],
body.inside-budget-module .budget-assemblies-pending-action[disabled],
body.inside-budget-module .formula-builder-primary[disabled],
body.inside-budget-module .budget-btn-primary[disabled],
body.inside-budget-module .budget-btn-secondary[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}

/* El botón de riesgo —archivar, eliminar— conserva su aviso, pero con la misma
   forma que los demás para que no parezca de otra aplicación. */
body.inside-budget-module .budget-assembly-edit--riesgo {
  border-color: rgba(255, 138, 120, 0.45);
  color: var(--pres-peligro, #ffb4a8);
}

body.inside-budget-module .budget-assembly-edit--riesgo:hover {
  background: rgba(255, 138, 120, 0.12);
  border-color: rgba(255, 138, 120, 0.7);
}

body.inside-budget-module.theme-light .budget-assembly-edit--riesgo {
  border-color: rgba(176, 42, 24, 0.35);
  color: #a3291a;
}

body.inside-budget-module.theme-light .budget-assembly-edit--riesgo:hover {
  background: rgba(176, 42, 24, 0.08);
}

/* ── `hidden` tiene que ocultar ─────────────────────────────────────
 *
 * El atributo `hidden` de HTML se aplica con `display: none` desde la hoja del
 * navegador, que pierde contra cualquier `display` que escriba el proyecto. Y
 * el módulo escribe muchos: las tarjetas de ensamble son `display: grid`.
 *
 * Consecuencia medida en /presupuestos/ensambles: el buscador y el filtro por
 * familia marcaban las tarjetas con `hidden` —el JavaScript hacía su trabajo—
 * y las tarjetas seguían viéndose, las ocho, con 225 px de alto cada una.
 * Filtrar por «BLOQUES DE CONCRETO» dejaba a la vista las de CONCRETO y ACERO.
 * Nunca funcionó, y no había forma de notarlo desde el código: el JavaScript
 * estaba bien.
 *
 * Esta regla devuelve a `hidden` el significado que dice tener. Va acotada al
 * módulo y con `!important` porque compite justamente contra reglas de autor;
 * sin él volvería a perder.
 *
 * Si alguna vez hace falta un elemento con `hidden` que igual se vea, eso es
 * una contradicción y el arreglo es quitarle el atributo, no esta regla.
 */
body.inside-budget-module [hidden] {
  display: none !important;
}

/* ── El desplegable de tipo de reporte ─────────────────────────────
 *
 * Lo crea el JavaScript y no está dentro de ninguno de los contenedores que el
 * CSS del módulo usa para dar forma a los campos —`.reports-field`,
 * `.reports-filter-field`—, así que salía con los colores por defecto del
 * navegador: fondo rgb(59,59,59) y borde rgb(133,133,133), gris de sistema, al
 * lado de dos desplegables verdes que hacen lo mismo.
 *
 * Antes llevaba además la clase `form-select` de Bootstrap, que tampoco lo
 * estilaba: la hoja de Bootstrap no se carga en el módulo. Quitarla no bastó
 * porque el problema nunca fue la clase de más, sino la de menos.
 *
 * Los valores son los que ya usan sus dos hermanos, medidos en la pantalla.
 */
body.inside-budget-module .reports-report-type-selector select {
  width: 100%;
  min-height: 42px;
  padding: 0.68rem 0.78rem;
  border: 1px solid rgba(128, 185, 171, 0.14);
  border-radius: 0.8rem;
  background: rgba(18, 30, 30, 0.94);
  color: var(--pres-tinta, #e8ecef);
  font: inherit;
}

body.inside-budget-module .reports-report-type-selector label {
  color: var(--pres-acento, #00c896);
  font-size: 0.72rem;
}

body.inside-budget-module.theme-light .reports-report-type-selector select {
  border-color: rgba(16, 36, 31, 0.16);
  background: rgba(255, 255, 255, 0.9);
  color: var(--pres-tinta, #17312b);
}

/* ── El título de «Seleccionar fórmula» se comía los botones ───────
 *
 * La cabecera de esa tarjeta es una fila flex con cuatro cosas: el icono, el
 * bloque de texto —«CONFIGURACIÓN» más el `<h2>`— y los dos botones, «Buscar»
 * y «Crear fórmula». Los dos botones miden 253 px juntos y la tarjeta es la
 * columna angosta de la rejilla, así que no caben los cuatro.
 *
 * El bloque de texto llevaba `min-width: 0`, que le permite encogerse por
 * debajo de su contenido. Medido a 1440 px: la caja del título quedaba en 44 px
 * y su texto seguía midiendo 94, así que se salía y se pintaba ENCIMA del botón
 * de Buscar, invadiéndolo 42 px. A 800 px la caja llegaba directamente a 0.
 *
 * Ya existía un `flex-wrap: wrap` para esto, pero metido dentro de una media
 * query de pantallas angostas. El problema no es de pantallas angostas: pasa
 * también en escritorio, porque lo que no cabe es el contenido, no la ventana.
 *
 * Se saca el envolver de la media query y se le da al texto una base mínima
 * legible: cuando no caben los cuatro en una línea, los botones bajan a la
 * siguiente en vez de que el título desaparezca debajo de ellos.
 */
body.inside-budget-module .budget-formulas-card-heading--with-action {
  flex-wrap: wrap;
}

body.inside-budget-module .budget-formulas-card-heading--with-action > div {
  flex: 1 1 11rem;
}

/* ── La ubicación en el banner ─────────────────────────────────────
 *
 * Cristian, 06/09/2026: «la idea que tengo con Gustavo es que el banner
 * principal sirva como guía también e indique en dónde está uno ubicado».
 *
 * Ocupa el sitio donde antes iba el nombre del módulo a secas, así que no pide
 * espacio nuevo: donde ponía «Presupuestos» ahora pone «Presupuestos › Fórmulas
 * › Creador». Vive en esta hoja porque `partials/head.ejs` la carga en TODAS las
 * páginas, y la miga también.
 *
 * Los tramos intermedios van atenuados y el actual en el color del texto: la
 * jerarquía la da el peso, no una caja alrededor.
 */
/* NI SE ENVUELVE NI SE ENCOGE · corregido 06/09/2026
 *
 * Medido en la aplicación: `.budget-product-topbar-section`, que es quien la
 * contiene, es `inline-flex` con `min-width: 0`. Con `flex-wrap: wrap` la miga
 * se encogía a 0 px de ancho y partía cada tramo en su propia línea: la barra
 * pasaba a 58 px de alto y la ubicación se montaba encima del contexto.
 *
 * `flex: 0 0 auto` la deja ocupar lo que necesita y `nowrap` la mantiene en una
 * línea. Lo que cede espacio cuando falta es el buscador, no esto: saber dónde
 * estás no puede depender del ancho de la ventana. */
.inside-miga {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: nowrap;
  flex: 0 0 auto;
  min-width: 0;
  white-space: nowrap;
}

/* LOS COLORES DEL BANNER · 06/09/2026
 *
 * El banner se escribió dentro de la hoja del módulo y tomó sus variables
 * —`--pres-tinta`, `--pres-atenuado`, `--pres-acento`, `--pres-borde`—, que
 * solo se declaran en `body.inside-budget-module`. Pero el banner se dibuja en
 * las doce secciones, y fuera del módulo esas variables NO EXISTEN: todo caía
 * en el valor de reserva, que era el del tema oscuro.
 *
 * Medido en tema claro, antes de esto:
 *
 *   /perfil                      «Perfil»       rgb(232,236,239) sobre blanco
 *   /planificador/asignaciones   «Asignaciones» igual
 *   → 1,19 de contraste. Con 4,5 de mínimo. Invisibles las dos.
 *
 * Pasó desapercibido porque las pantallas donde se probó —las once del módulo—
 * sí declaran esas variables, y ahí se veía perfecto.
 *
 * Estas heredan del módulo cuando existe, así que sus once pantallas no cambian
 * ni un tono, y caen en los tokens globales de la aplicación cuando no. */
body.inside-app-body {
  --banner-tinta: var(--pres-tinta, var(--inside-dark-text, #e8ecef));
  --banner-atenuado: var(--pres-atenuado, var(--inside-dark-muted, #a6acb3));
  --banner-acento: var(--pres-acento, var(--inside-dark-accent, #00c896));
  --banner-acento-texto: var(--pres-acento-texto, #04201a);
  --banner-borde: var(--pres-borde, rgba(0, 200, 150, 0.28));
}

body.inside-app-body.theme-light {
  --banner-tinta: var(--pres-tinta, var(--inside-light-title, #17312b));
  --banner-atenuado: var(--pres-atenuado, var(--inside-light-muted, rgba(23, 49, 43, 0.58)));
  --banner-acento: var(--pres-acento, var(--inside-light-accent, #1a6b5a));
  --banner-acento-texto: var(--pres-acento-texto, #ffffff);
  --banner-borde: var(--pres-borde, rgba(26, 107, 90, 0.22));
}

.inside-miga-tramo,
.inside-miga-actual {
  font-size: 0.86rem;
  line-height: 1.2;
  white-space: nowrap;
}

.inside-miga-tramo {
  color: var(--banner-atenuado);
  text-decoration: none;
  font-weight: 500;
}

.inside-miga-tramo:hover,
.inside-miga-tramo:focus-visible {
  color: var(--banner-acento);
  text-decoration: underline;
  text-underline-offset: 2px;
}

.inside-miga-actual {
  color: var(--banner-tinta);
  font-weight: 700;
}

/* El contenedor mide 0 px por su `min-width: 0` heredado, así que la miga no
   tiene dónde dibujarse. Se le devuelve el ancho de su contenido solo cuando
   lleva miga dentro, para no alterar las pantallas que aún usan el título de
   sección —la raíz, el login, el Planificador—. */
.budget-product-topbar-section:has(.inside-miga) {
  width: auto;
  flex: 0 0 auto;
  min-width: max-content;
}

.inside-miga-sep {
  color: var(--banner-atenuado);
  opacity: 0.5;
  font-size: 0.8rem;
}

/* En pantallas estrechas la ruta entera no cabe y partirla en dos líneas
   rompería la altura de la barra. Se dejan los dos últimos tramos, que son los
   que dicen dónde estás; los de arriba siguen a un toque de distancia. */
@media (max-width: 820px) {
  .inside-miga-tramo:not(:nth-last-child(-n+3)),
  .inside-miga-sep:not(:nth-last-child(-n+2)) { display: none; }
}

/* ── El contexto plegable ──────────────────────────────────────────
 *
 * Cristian, 06/09/2026: «un check desplegable para poder visualizar y
 * modificar, y si lo quieren dejar fijo un pin, pero como ves, no es necesario
 * siempre».
 *
 * El disparador vive en el banner, junto a la miga: una dice dónde estás y el
 * otro sobre qué. El proyecto y el presupuesto se ven SIEMPRE, aunque el panel
 * esté cerrado; lo que se pliega son los desplegables para cambiarlos.
 */
.inside-contexto-disparador {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  max-width: 300px;
  height: 30px;
  padding: 0 10px;
  border: 1px solid var(--banner-borde);
  border-radius: 999px;
  background: transparent;
  color: var(--banner-tinta);
  font: inherit;
  font-size: 0.78rem;
  cursor: pointer;
  overflow: hidden;
  /* Vive en la celda de acciones, que es `max-content`: crece con el nombre del
     proyecto. El tope es para que un nombre largo no ensanche la celda y le
     coma el sitio al buscador; pasado ahí, el texto se recorta. El suelo es
     para que recortar no lo deje en dos letras, que era justo lo que había que
     poder leer. */
  flex: 0 1 auto;
  min-width: 150px;
  max-width: 280px;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.inside-contexto-disparador:hover {
  border-color: var(--banner-acento);
}

.inside-contexto-disparador:focus-visible {
  outline: 2px solid var(--banner-acento);
  outline-offset: 2px;
}

/* Los nombres de proyecto pueden ser largos; se recortan antes de empujar al
   buscador fuera de la barra. */
.inside-contexto-disparador b,
.inside-contexto-disparador > span:not(.inside-contexto-disparador__flecha) {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.inside-contexto-disparador b { font-weight: 650; }
.inside-contexto-disparador__sep { color: var(--banner-atenuado); opacity: .6; }
.inside-contexto-disparador__flecha {
  color: var(--banner-atenuado);
  font-size: .7rem;
  transition: transform .18s ease;
}
.inside-contexto-disparador[aria-expanded="true"] .inside-contexto-disparador__flecha {
  transform: rotate(180deg);
}

/* El pin va DENTRO del panel: solo tiene sentido cuando está abierto, y fuera
   le robaba sitio al buscador. */
.inside-contexto-pin {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  height: 28px;
  padding: 0 10px;
  margin-left: auto;
  border: 1px solid var(--banner-borde);
  border-radius: 999px;
  background: transparent;
  color: var(--banner-atenuado);
  font: inherit;
  font-size: 0.72rem;
  font-weight: 600;
  cursor: pointer;
  flex-shrink: 0;
}

.inside-contexto-pin:hover { border-color: var(--banner-acento); }
.inside-contexto-pin:focus-visible {
  outline: 2px solid var(--banner-acento);
  outline-offset: 2px;
}
.inside-contexto-pin[aria-pressed="true"] {
  background: var(--banner-acento);
  border-color: var(--banner-acento);
  color: var(--banner-acento-texto);
}

@media (prefers-reduced-motion: reduce) {
  .inside-contexto-disparador__flecha { transition: none; }
}

/* LA PRIMERA PISTA DE LA BARRA TIENE QUE CRECER · 06/09/2026
 *
 * `.budget-product-topbar` es una rejilla de tres pistas —ubicación, buscador,
 * acciones— y hay dos hojas que las fijan con `!important`:
 *
 *   styles.css                minmax(150px, 230px) minmax(180px, 1fr) max-content
 *   presupuestos-creador.css  minmax(300px, 320px) …   (solo el creador de fórmulas)
 *
 * Medido con la pista topada a 230 px: la ubicación ocupa 224 y entraba
 * raspando; una ruta más larga —«Presupuestos › Fórmulas › Creador»— ya no
 * cabía y partía en dos líneas.
 *
 * Entre declaraciones marcadas el desempate es por especificidad antes que por
 * orden de carga, así que no bastaba con cargar después: había que ganarle a
 * (0,3,1). De ahí `html` delante y la clase del módulo, que suman (0,3,2). Se
 * acota a las pantallas con miga: las que aún usan el título de sección se
 * quedan exactamente como estaban.
 *
 * `auto` y no un rango: la pista crece con la ruta que toque. El que cede
 * espacio cuando falta es el buscador, y solo hasta el mínimo de su pista. */
/* TRES SELECTORES, UNA SOLA DECLARACIÓN.

   El primero alcanza a las pantallas que no son de ningún módulo —dashboard,
   perfil, soporte, administración de soporte, herramientas—, que si no se
   quedaban con la rejilla genérica de styles.css: 230 px fijos para la ruta,
   o sea 218 px de hueco con una ruta corta como «Inicio».

   Los otros dos existen porque en sus módulos hay rivales más pesados: (0,3,1)
   en los dos casos —ver más abajo el del `:not()` del Planificador—. En una
   lista de selectores cada uno cuenta su propia especificidad, así que el
   genérico manda donde no hay pelea y los específicos donde sí. */
html body.inside-app-body .budget-product-topbar--con-miga,
html body.inside-app-body.planificador-app-body .budget-product-topbar,
html body.inside-app-body.inside-budget-module .budget-product-topbar--con-miga {
  /* EL BUSCADOR SE COME EL HUECO.

     Cristian, mirando el banner con una ruta corta: «pensemos algo para ese
     espacio muerto en donde va creciendo gradualmente se mete en diferentes
     rutas».

     Antes las tres pistas eran fijas —470 · 600 · 470— para que el buscador
     quedara centrado en la barra. Eso lo dejaba quieto, pero con una ruta corta
     («Presupuestos», 88 px) sobraban casi 400 px de nada entre la ruta y él.

     El hueco es inevitable si el buscador está quieto y la ruta cambia de largo:
     lo uno o lo otro. Se elige que no haya hueco. La primera pista mide la ruta
     y el buscador se queda con lo que sobra, así que crece en las pantallas poco
     profundas y mengua en las hondas. Medido:

       /presupuestos                     ruta  88 px · buscador 726 px
       /presupuestos/ensambles/creador   ruta 239 px · buscador 651 px
       hueco entre la ruta y el buscador: 22 px en las dos

     La tercera mide su contenido: el contexto y los iconos van juntos a la
     derecha y no queda nada suelto entre medias.

     Que el buscador se corra al navegar es lo que Cristian pidió que se viera
     («que se vaya corriendo dependiendo la ruta»). Como cada navegación es una
     carga de documento nueva, una `transition` no llega a dispararse nunca: el
     deslizamiento lo da la transición de vista del final de este archivo. */
  grid-template-columns: max-content minmax(220px, 1fr) max-content !important;
}

/* LA DERECHA VA JUNTA · 06/09/2026
 *
 * Hubo una versión en la que la celda de acciones se quedaba con sobrante y la
 * pastilla de contexto se centraba dentro con `margin-inline: auto`. Tenía
 * sentido mientras el buscador estaba topado a 600 px: sin ese reparto, el hueco
 * se quedaba entero entre el buscador y la pastilla.
 *
 * Desde que el buscador se estira, repartirlo solo servía para volver a abrir
 * huecos, y encima dos: uno antes de la pastilla y otro antes de la luna. La
 * celda vuelve a medir su contenido —contexto e iconos juntos— y todo el
 * sobrante se lo lleva el buscador. Una sola banda, sin vacíos. */

/* La celda mantiene su ancho de contenido: si se la deja encoger a cero, la
   pista de la rejilla se resuelve en su mínimo y el contexto se recorta a un
   par de letras. Medido: con `min-width: 0` aquí, la pista se quedaba en 320 px
   y el disparador en 80. */
html body.inside-app-body.inside-budget-module .budget-product-topbar--con-miga .budget-product-topbar-left {
  min-width: max-content;
}

/* «LISTO PARA TRABAJAR» ES UN AVISO, NO UN PANEL · 06/09/2026
 *
 * La barra de contexto es una rejilla `auto minmax(0,1fr) auto` y el estado cae
 * en la del medio. Con el `width: 100%` que le da styles.css, la pastilla medía
 * 995 x 41 px: un bloque que cruzaba media pantalla para decir dos palabras.
 *
 * Encogida a su contenido y pegada al pin, que es donde la puso la maqueta.
 * Los colores no se tocan: el tema claro ya la resuelve en verde de la casa
 * —#0c5a49 sobre rgba(0,137,110,.07)—, sólo se veía mal por el tamaño. */
.inside-context-bar__status,
.inside-context-formula-status {
  justify-self: end;
  min-width: 0;
}

.inside-context-formula-status__button {
  width: auto;
  min-height: 30px;
  padding: 0.3rem 0.7rem;
}

/* El disco del icono venía a 21 px: casi tan alto como la pastilla entera una
   vez encogida. Se mantiene el disco —distingue de un vistazo el verde de
   «listo» del rojo de «pendientes»— pero a escala. */
.inside-context-formula-status__button > span {
  width: 16px;
  height: 16px;
  flex: 0 0 16px;
  font-size: 0.6rem;
}

/* LO QUE YA DICE LA MIGA · 06/09/2026
 *
 * Fase 3, segunda parte. Ocho pantallas se pintan su propio encabezado en vez
 * de usar `partials/presupuestos/encabezado.ejs`, y usan cinco convenciones de
 * clases distintas para lo mismo:
 *
 *   budget-formulas-v12-kicker          budget-summary-kicker
 *   budget-admin-eyebrow                ensamble-workbench-toolbar__path
 *   dashboard-widget-kicker  ·  tools-app-kicker
 *
 * Varias de ellas se reutilizan dentro de modales y de tarjetas, así que no hay
 * un selector que las alcance a todas sin llevarse por delante algo más. En vez
 * de perseguirlas, se marca en el marcado lo que repite la ubicación y esta
 * regla lo oculta. Se encuentra todo con `grep -rn inside-lo-dice-la-miga`.
 *
 * SE OCULTA, NO SE BORRA. Igual que en el parcial compartido: hay
 * `aria-labelledby` apuntando a estos títulos, y una página sin encabezado de
 * primer nivel es peor accesibilidad, no mejor. `clip-path` lo saca de la vista
 * y lo deja en el árbol de accesibilidad; `position: absolute` lo saca del flujo
 * para que el contenedor no guarde su hueco ni su `gap`.
 *
 * Lo que NO se marca: el saludo del dashboard —«Bienvenido, Cristian» no es una
 * ubicación— y `proyecto-nuevo`, que según de dónde se entre se anuncia como
 * Planificador o como Presupuestos y va con la cuarta fase. */
.inside-lo-dice-la-miga {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* CUANDO ESTÁ PLEGADO, PLEGADO DE VERDAD · 06/09/2026
 *
 * La primera versión se limitaba a poner `hidden` en el panel. Medido en Chrome
 * con las reglas reales, eso deja las cosas a medias, de dos maneras distintas.
 *
 * DENTRO DEL MÓDULO la barra sí desaparece —la regla de la línea 346 de esta
 * misma hoja le devuelve a `hidden` su significado— pero su hueco se queda:
 *
 *     desplegado           barra 78 px · margen de main 152 px · lista 448 px
 *     con `hidden` puesto  barra  0 px · margen de main 152 px · lista 448 px
 *
 * FUERA DEL MÓDULO ni eso. Dashboard, perfil, soporte, administración de soporte
 * y herramientas llevan `inside-app-body` pero no `inside-budget-module`, la
 * barra se les dibuja igual, y allí `hidden` es inerte: `.inside-context-bar`
 * tiene `display: grid` de autor y la regla `[hidden]` del navegador pierde.
 *
 * POR QUÉ UNA CLASE EN <BODY> Y NO `[data-contexto-panel][hidden]`
 *
 * Porque el hueco no lo reserva la barra, sino los que miden a partir de su
 * altura. Son, al menos:
 *
 *   styles.css                    el margen superior de <main>
 *   presupuestos-listados-v13.css `--lista-alto-util`, el alto de la lista
 *   planificador.css              `--planificador-top-offset`
 *
 * y todas la leen de `--inside-context-bar-height`. Puesta la variable a cero,
 * la siguen todos sin tocarlos, y de paso el mecanismo deja de depender de
 * `[hidden]`, así que también funciona en las cinco vistas de fuera del módulo.
 *
 * `body.inside-contexto-plegado` empata en especificidad (0,2,0) con
 * `body.inside-app-body`, que es quien la declara, y con las tres
 * redefiniciones responsive —86, 118 y 216 px—. Gana por orden: esta hoja
 * carga después de styles.css. */
body.inside-contexto-plegado {
  --inside-context-bar-height: 0px;
}

body.inside-contexto-plegado [data-contexto-panel] {
  display: none;
}

/* EL VERDE QUE SOBRABA EN LA BARRA DE CONTEXTO · 06/09/2026
 *
 * styles.css le pone al tema claro
 *
 *     linear-gradient(90deg, rgba(0, 156, 124, .075), transparent 30%),
 *     rgba(250, 253, 252, .98)
 *
 * o sea, un lavado verde en el tercio izquierdo que se va apagando. Partía la
 * barra en dos tonos —verdoso a la izquierda, blanco a la derecha— y en la
 * maqueta la barra es de un solo color. Se queda el fondo, se va el degradado.
 *
 * En oscuro no se toca: allí el mismo lavado apenas se distingue del fondo y
 * es parte del aspecto que ya tenía. */
body.theme-light .inside-context-bar {
  background: rgba(250, 253, 252, 0.98);
}

/* El «›» entre Proyecto y Presupuesto se quedó con el menta del tema oscuro
   —rgba(78, 231, 198, .5)—, que sobre fondo claro casi no se ve y además está
   fuera de la paleta. No tenía redefinición en claro: la regla de styles.css
   es una sola, sin bloque de tema. */
body.theme-light .inside-context-bar__divider {
  color: var(--inside-light-muted, rgba(23, 49, 43, 0.5));
}

/* EL BOTÓN DEL TEMA, COMO SUS VECINOS · 06/09/2026
 *
 * Medido en la fila de acciones del banner:
 *
 *     contexto 268 · MODO OSCURO 116 · idioma 38 · avisos 38 · avatar 38
 *
 * Era el único de texto entre cuatro iconos, y el triple de ancho que cada
 * vecino. No desentonaba por dónde estaba sino por ser texto, así que se queda
 * donde está y pasa a icono: 116 px → 38, y esos 78 se los lleva el buscador.
 *
 * El sol y la luna ya estaban: los intercambia `syncHeaderTheme` en el guion de
 * la cabecera según el tema. Lo único que sobraba era el rótulo.
 *
 * El texto se oculta, no se borra, y sigue en el marcado porque el guion lo
 * escribe en cada cambio de tema. Da igual para un lector de pantalla: el
 * nombre accesible del botón sale de su `aria-label`, que el mismo guion
 * mantiene al día —«Cambiar a modo oscuro» / «Cambiar a modo claro»—, y un
 * `aria-label` gana al texto de dentro.
 *
 * Se oculta por id —(1,0,0)— y no por clase porque hay un
 * `body.theme-light.inside-app-body .budget-product-theme-button > span` con
 * `visibility: visible !important` y `opacity: 1 !important`. Contra eso no se
 * puede ocultar con visibilidad ni con opacidad; con `clip-path` sí, que no lo
 * pelea nadie. */
#budgetHeaderThemeText {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* `html` delante —(0,2,1)— y `min-width`: planificador.css le fija
   `min-width: 116px` con (0,2,0) y carga después, así que empatando en
   especificidad ganaba él y el botón quedaba como una pastilla ancha con un
   icono de 18 px flotando dentro. Por debajo de 1100 px esa misma hoja ya lo
   vuelve un círculo de 36 con `!important`; eso se respeta. */
html body.inside-app-body .budget-product-theme-button {
  width: 38px;
  min-width: 38px;
  height: 38px;
  padding: 0;
  gap: 0;
}

/* EL SOL Y LA LUNA NUNCA SE DIBUJARON · 06/09/2026
 *
 * Descubierto al quitarle el rótulo al botón: se quedó vacío. Medido en el
 * navegador, el `<svg>` daba 0 x 0, y buscando en TODAS las hojas cargadas no
 * hay ni una regla para `.budget-product-theme-symbol`. Un `<svg>` en línea sin
 * `width`/`height` propios y sin CSS que lo dimensione colapsa a cero dentro de
 * un contenedor flex.
 *
 * O sea que el icono llevaba ahí desde siempre, en el marcado y en el guion que
 * lo intercambia, sin verse nunca. Mientras el botón fue de texto no se notó.
 *
 * 18 x 18 es lo que mide la campana de al lado; la bandera es 29 x 20 por su
 * proporción. */
.budget-product-theme-icon .budget-product-theme-symbol {
  display: block;
  width: 18px;
  height: 18px;
}

/* EL BANNER DEL PLANIFICADOR, IGUAL QUE EL DE PRESUPUESTOS · 06/09/2026
 *
 * Cristian: «esa barra del centro se ve muy grande, diria que la dejes del
 * tamaño de la desplegable anterior y lo del buscar de lado izquierdo para que
 * quede homologo visualmente».
 *
 * Medido antes, sobre los 1584 px útiles del banner:
 *
 *     ubicación 250 · contexto del plan 854 · buscador 200 · acciones 186
 *
 * El selector de plan se llevaba más de la mitad y el buscador quedaba
 * arrinconado al final. El contexto ya se movió detrás del buscador en el
 * marcado; aquí van las pistas, con la misma forma que en Presupuestos.
 *
 * `html` delante para llegar a (0,2,2): planificador.css fija estas pistas con
 * `!important` desde (0,2,1) y carga después, así que empatando ganaba él. */
/* NOTA sobre el selector del Planificador de la lista de arriba: su rival no es
   el que parece. Hay un
     body.planificador-app-body .budget-product-topbar:not(.budget-product-topbar--planner-project-create)
   y un `:not()` aporta la especificidad de su argumento, así que suma (0,3,1) y
   le gana a cualquier (0,2,2). Por eso ese selector nombra las dos clases del
   <body> y lleva `html` delante. */


/* Y el contexto del plan se acota. Su rejilla interna repartía 737 px al campo;
   con el tope, el selector queda del tamaño de la pastilla de contexto de
   Presupuestos y el botón de «Nuevo plan» conserva su ancho de contenido. */
html body.inside-app-body.planificador-app-body .inside-context-bar--embedded {
  /* Suelo además del techo: dentro del contenedor flex de acciones se encogía
     hasta 44 px —solo la flecha del desplegable— porque nada le impedía ceder. */
  min-width: 300px;
  max-width: 420px;
  grid-template-columns: minmax(0, 1fr) max-content;
}

/* EL BUSCADOR, CENTRADO EN SU PISTA · 06/09/2026
 *
 * Cristian: «y lo de buscar mas centrado en ambos escenarios».
 *
 * Ocupaba toda su pista, así que quedaba pegado a la ubicación y dejaba un hueco
 * grande antes del contexto. Centrado en la pista, los dos huecos se igualan.
 * El `min()` es para que en ventanas angostas siga ocupando lo que haya. */
html body.inside-app-body .budget-product-search {
  justify-self: stretch;
  /* Marcados los dos, y el tope aparte del ancho: planificador.css fija
     `width: 200px !important` y `max-width: 200px !important` —190 por debajo de
     1800 px—, y contra dos declaraciones marcadas no vale subir el ancho: un
     `max-width` menor lo recorta igual. Se gana por especificidad, (0,2,2)
     contra (0,2,1), que es para lo que está el `html` de delante.

     El `min(100%, …)` es lo que evita que esto lo estire en ventanas angostas:
     ahí manda la pista, no el tope. */
  width: 100% !important;
  max-width: none !important;
}

/* EL SCROLL QUE SOBRABA · 06/09/2026
 *
 * Cristian: «en varios quedaron scrolls inecesarios como por ejemplo en
 * /presupuestos/formulas».
 *
 * `<main>` lleva dos cosas a la vez que no se hablan entre sí:
 *
 *   margin-top: calc(barra superior + barra de contexto)   (styles.css)
 *   min-height: 100vh                                      (.budget-formulas-page
 *                                                           y otras cuatro)
 *
 * o sea, alto de ventana ENTERO más el margen que lo empuja hacia abajo. La
 * página siempre desbordó por el margen: 152 px antes, 74 ahora que la barra de
 * contexto se pliega. No lo causó el plegado, lo dejó a la mitad y a la vista.
 *
 * `min-height` tiene que descontar el cromo fijo que hay encima. Las dos alturas
 * son variables y cambian en los cortes responsive, así que se leen en vez de
 * escribirse.
 *
 * Se acota a `~ main`, que son exactamente los que llevan ese margen: el
 * Planificador no entra, porque su barra de contexto va dentro de la cabecera y
 * no es hermana de <main>.
 *
 * Especificidad (0,2,3) contra (0,1,0) de `.budget-formulas-page` y (0,2,0) de
 * `body.inside-app-body .reports-page`; no hace falta marcar. */
html body.inside-app-body .inside-context-bar ~ main {
  min-height: calc(
    100vh
    - var(--inside-app-topbar-height, 74px)
    - var(--inside-context-bar-height, 78px)
  );
}

/* QUE SE VEA CORRERSE, EN VEZ DE SALTAR · 06/09/2026
 *
 * La ruta crece al entrar en pantallas más hondas y el buscador se corre con
 * ella. Entre una página y otra no hay `transition` que valga —cada navegación
 * es un documento nuevo y el navegador pinta ya la posición final—, así que el
 * deslizamiento tiene que darlo una transición de vista entre documentos.
 *
 * Solo se mueve la cabecera. El contenido se cambia de golpe, sin fundido: en
 * una aplicación de este tamaño un fundido de página entera se lee como
 * lentitud, no como acabado.
 *
 * Los tres nombres tienen que ser únicos en la página, y lo son: hay una miga,
 * un buscador y una celda de acciones.
 *
 * Navegadores que no lo soporten se lo saltan entero y todo queda como antes.
 * Y con `prefers-reduced-motion` se apaga: quien pide menos movimiento no
 * quiere que la barra se deslice en cada clic.
 *
 * SI MOLESTA, se quita borrando este bloque. La regla `@view-transition` retiene
 * la foto de la página anterior hasta que la nueva está lista para pintar, así
 * que con la red lenta puede leerse como que la navegación tarda más. */
@view-transition {
  navigation: auto;
}

.inside-miga { view-transition-name: inside-miga; }
.budget-product-search { view-transition-name: inside-buscador; }
.budget-product-topbar-actions { view-transition-name: inside-acciones; }

::view-transition-group(root) {
  animation-duration: 0s;
}

::view-transition-group(inside-miga),
::view-transition-group(inside-buscador),
::view-transition-group(inside-acciones) {
  animation-duration: 260ms;
  animation-timing-function: cubic-bezier(0.22, 0.61, 0.36, 1);
}

@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }
}

/* =================================================================
   EL FONDO ESTABA PINTADO Y NO SE VEÍA · 09/09/2026
   -----------------------------------------------------------------
   Cristian: «aún no veo fondo en fórmulas». Tenía razón, y mi verificación
   anterior estaba mal: comprobé que el CSS DECLARA la imagen, no que se VEA.
   Son cosas distintas y me quedé con la primera.

   Lo que pasa es de composición, no de reglas de fondo:

     · `body::before` pinta la fotografía y vive en `z-index: -1`.
     · `body` tiene su propio `background-color` opaco —rgb(9,17,17)—.

   Un pseudoelemento en z-index negativo se dibuja DEBAJO del fondo de su
   propio elemento. Así que la foto se pintaba, sí, detrás de una pared.
   Comprobado en las ocho pantallas del módulo: todas igual.

   Se deja el body transparente en tema oscuro para que la capa de abajo
   asome. El suelo no queda al aire: `html` conserva su color oscuro
   —rgb(6,21,20)—, así que si la imagen no cargara, el fondo sigue siendo
   oscuro y nada se ve raro.

   CORRECCIÓN DEL 09/09: acá decía «en tema claro no se toca, ahí la
   foto ya se veía». Era falso, y Cristian lo cazó: «no estoy viendo
   los background en el modo blanco».

   Es EL MISMO defecto, idéntico. Medido en pantalla, en claro:

     · `body::before` está en `z-index: -2` y pinta la fotografía
     · `body` tiene `background-color: rgb(243, 239, 231)`, opaco

   Me confundió que el tema claro además pinta un fondo sobre los
   contenedores de página, así que en algunas pantallas se veía algo
   y di el tema por bueno. En las de creador no hay tal contenedor
   pintado, y ahí quedaba en blanco liso.

   El suelo tampoco queda al aire acá: `html` conserva
   `rgb(244, 247, 249)`, así que si la imagen no cargara el fondo
   sigue siendo claro.
   ================================================================= */

/* Los dos temas se nombran por separado a propósito. Escribirlo como
   `body.inside-app-body` a secas parece lo mismo y no lo es: pesa (0,1,1) y
   pierde contra la regla oscura que hay que vencer, que pesa (0,2,1). El
   `:not(.theme-light)` original no estaba de adorno, aportaba ese peso.
   Comprobado midiendo en pantalla: con la versión corta, en oscuro el body
   volvía a `rgb(9, 17, 17)` y la foto quedaba tapada otra vez. */
body.inside-app-body:not(.theme-light),
body.inside-app-body.theme-light {
  background-color: transparent !important;
}


/* =================================================================
   Y EL SEGUNDO MOTIVO, EN CLARO: DOS VELOS APILADOS · 09/09/2026

   Con el `body` ya transparente, la fotografía asoma pero sigue sin
   leerse. Medido sobre `body::before` en tema claro había DOS
   degradados encima de la imagen:

     · horizontal  0,88 -> 0,70 -> 0,42
     · vertical    0,66 -> 0,30 -> 0,74

   Multiplicados dan casi opacidad total en el borde izquierdo, que
   es justo donde uno mira primero. Es el mismo error que traía el
   tema oscuro y que ya se corrigió allá: se deja UN solo velo,
   vertical, con los mismos valores que funcionan en oscuro.

   El texto no se juega nada: en estas pantallas vive sobre tarjetas
   opacas (--budget-workspace-panel: #ffffff).
   ================================================================= */

body.inside-app-body.theme-light::before {
  background-image:
    linear-gradient(
      180deg,
      rgba(246, 243, 237, 0.62) 0%,
      rgba(246, 243, 237, 0.70) 52%,
      rgba(246, 243, 237, 0.78) 100%
    ),
    url("/assets/hero-building-xray.webp") !important;
  background-position: center, center !important;
  background-size: cover, cover !important;
  background-repeat: no-repeat, no-repeat !important;
  background-attachment: fixed, fixed !important;
}
