﻿/* Blazor le da foco al primer <h1> en cada navegación (FocusOnNavigate, por accesibilidad):
   sin esto el navegador dibuja su anillo de foco alrededor del título de cada pantalla.
   Se neutraliza el recuadro SIN sacar el foco, que es lo que usan los lectores de pantalla.
   Regla global del monorepo: design-system/REGLAS-GLOBALES.md */
h1:focus,
h1:focus-visible {
    outline: none;
}

:root {
    --po-bg: #0f172a;
    --po-bg-2: #111c34;
    --po-surface: #ffffff;
    --po-surface-2: #f1f5f9;
    --po-ink: #0f172a;
    --po-ink-soft: #475569;
    --po-primary: #2563eb;
    --po-primary-d: #1d4ed8;
    --po-accent: #38bdf8;
    --po-border: #e2e8f0;
    --po-ok: #16a34a;
    --po-warn: #d97706;
    --po-danger: #dc2626;
    --po-sidebar-w: 244px;
    /* El alto de la topbar es un dato COMPARTIDO: la cabecera de pantalla (`.po-page-head`) y el
       grupo pegajoso se anclan justo debajo. Estaba escrito 54px en los tres lugares, y la escala
       compacta de tablet (ver "PANTALLA MEDIANA") necesita bajarlo sin desalinear los otros dos. */
    --po-topbar-h: 54px;
}

html, body {
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    background: var(--po-surface-2);
    color: var(--po-ink);
    margin: 0;
}

a { color: var(--po-primary); text-decoration: none; }
a:hover { text-decoration: underline; }

h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -.01em; }

.btn-primary { --bs-btn-bg: var(--po-primary); --bs-btn-border-color: var(--po-primary); --bs-btn-hover-bg: var(--po-primary-d); --bs-btn-hover-border-color: var(--po-primary-d); }
.btn-outline-primary { --bs-btn-color: var(--po-primary); --bs-btn-border-color: var(--po-primary); --bs-btn-hover-bg: var(--po-primary); --bs-btn-hover-border-color: var(--po-primary); }
.text-primary { color: var(--po-primary) !important; }

/* ─── AUTH ─── */
.po-auth-wrap {
    min-height: 100vh;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: radial-gradient(1200px 600px at 20% -10%, #1e3a8a 0%, var(--po-bg) 55%);
    padding: 24px;
}
.po-auth-card {
    width: 100%; max-width: 420px;
    background: var(--po-surface); border-radius: 16px;
    box-shadow: 0 20px 60px rgba(2,6,23,.45);
    padding: 32px 32px 28px;
}
.po-auth-brand {
    display: flex; align-items: center; gap: 10px; justify-content: center;
    font-size: 1.4rem; color: var(--po-primary); margin-bottom: 18px;
}
.po-auth-brand .bi { font-size: 1.6rem; }
.po-auth-brand strong { color: var(--po-ink); }
.po-auth-title { font-size: 1.25rem; text-align: center; margin-bottom: 18px; }
.po-auth-foot { color: #94a3b8; font-size: .8rem; margin-top: 16px; letter-spacing: .04em; }

/* ─── SHELL ─── */
.po-shell { display: flex; min-height: 100vh; }
.po-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.po-sidebar {
    width: var(--po-sidebar-w); flex-shrink: 0;
    background: linear-gradient(180deg, var(--po-bg) 0%, var(--po-bg-2) 100%);
    color: #cbd5e1; display: flex; flex-direction: column;
    position: sticky; top: 0; height: 100vh;
}
.po-sidebar-brand {
    display: flex; align-items: center; gap: 10px;
    padding: 18px 20px; font-size: 1.2rem; color: #fff;
    border-bottom: 1px solid rgba(255,255,255,.07);
}
.po-sidebar-brand .bi { color: var(--po-accent); font-size: 1.4rem; }
.po-sidebar-brand strong { color: var(--po-accent); }
.po-nav-scroll { flex: 1; overflow-y: auto; padding: 12px 10px; }
.po-navlink {
    display: flex; align-items: center; gap: 12px;
    padding: 10px 14px; border-radius: 9px; color: #cbd5e1;
    font-size: .92rem; margin-bottom: 3px; transition: background .12s, color .12s;
}
.po-navlink:hover { background: rgba(255,255,255,.06); color: #fff; text-decoration: none; }
.po-navlink.active { background: var(--po-primary); color: #fff; box-shadow: 0 4px 14px rgba(37,99,235,.45); }
.po-navlink .bi { font-size: 1.05rem; width: 20px; text-align: center; }
.po-nav-sep { font-size: .68rem; text-transform: uppercase; letter-spacing: .12em; color: #64748b; padding: 14px 14px 6px; }
.po-sidebar-foot { padding: 12px 16px; border-top: 1px solid rgba(255,255,255,.07); }
.po-badge-empresa { font-size: .78rem; color: #94a3b8; display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ─── Badge de plan (junto a la marca) ─── */
.po-plan-badge {
    margin-left: auto; font-size: .62rem; font-weight: 700; letter-spacing: .06em;
    text-transform: uppercase; padding: 2px 8px; border-radius: 999px;
    line-height: 1.4; white-space: nowrap; text-decoration: none;
    border: 1px solid transparent; transition: filter .12s;
}
.po-plan-badge:hover { text-decoration: none; filter: brightness(1.12); }
.po-plan-free { background: rgba(148,163,184,.18); color: #cbd5e1; border-color: rgba(148,163,184,.35); }
.po-plan-basic { background: rgba(56,189,248,.16); color: #7dd3fc; border-color: rgba(56,189,248,.4); }
.po-plan-pro { background: rgba(37,99,235,.22); color: #93b4fb; border-color: rgba(37,99,235,.5); }
.po-plan-enterprise { background: linear-gradient(135deg, var(--po-primary), var(--po-accent)); color: #fff; border-color: transparent; box-shadow: 0 2px 8px rgba(37,99,235,.45); }

/* ─── Grupos colapsables del NavMenu ─── */
.po-nav-group { margin-top: 4px; }
.po-nav-grouptoggle {
    display: flex; align-items: center; gap: 12px; width: 100%;
    padding: 10px 14px; border: 0; background: transparent; border-radius: 9px;
    color: #7e8ba0; font-size: .7rem; font-weight: 600; text-transform: uppercase; letter-spacing: .1em;
    cursor: pointer; transition: background .12s, color .12s;
}
.po-nav-grouptoggle:hover { background: rgba(255,255,255,.05); color: #e2e8f0; }
.po-nav-grouptoggle > .bi:first-child { font-size: 1rem; width: 20px; text-align: center; }
.po-chevron { margin-left: auto; font-size: .78rem !important; width: auto !important; transition: transform .18s; }
.po-nav-grouptoggle.open .po-chevron { transform: rotate(180deg); }
.po-nav-groupitems { display: none; padding-left: 8px; }
.po-nav-groupitems.show { display: block; }
.po-nav-groupitems .po-navlink { font-size: .88rem; }

/* ─── TOPBAR ─── */
.po-topbar {
    display: flex; align-items: center; gap: 14px;
    background: var(--po-bg); color: #e2e8f0;
    padding: 0 18px; height: var(--po-topbar-h);
    position: sticky; top: 0; z-index: 40;
}
.po-topbar-empresa { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: .95rem; }
.po-topbar-empresa .bi { color: var(--po-accent); }
.po-topbar-logo { display: none; height: 30px; max-width: 140px; object-fit: contain; border-radius: 4px; background: #fff; padding: 2px 4px; }
.po-topbar-user { margin-left: auto; display: flex; align-items: center; gap: 12px; }
.po-user-name { font-size: .85rem; color: #cbd5e1; }
.po-navtoggle { display: inline-flex; align-items: center; color: #e2e8f0; border: 0; background: transparent; }
.po-navtoggle:hover { color: #fff; }

.po-content { padding: 22px 26px 60px; width: 100%; max-width: none; }

/* ─── SIDEBAR COLAPSADO (solo desktop; en mobile el toggle abre/cierra) ─── */
@media (min-width: 861px) {
    .po-shell.po-nav-collapsed { --po-sidebar-w: 66px; }
    .po-nav-collapsed .po-sidebar-brand span,
    .po-nav-collapsed .po-navlink span,
    .po-nav-collapsed .po-nav-sep,
    .po-nav-collapsed .po-plan-badge,
    .po-nav-collapsed .po-nav-grouptoggle span,
    .po-nav-collapsed .po-chevron,
    .po-nav-collapsed .po-sidebar-foot { display: none; }
    .po-nav-collapsed .po-sidebar-brand { justify-content: center; padding-left: 0; padding-right: 0; }
    .po-nav-collapsed .po-navlink { justify-content: center; padding-left: 0; padding-right: 0; }
    .po-nav-collapsed .po-navlink .bi { width: auto; }
    .po-nav-collapsed .po-nav-scroll { padding: 12px 8px; }
    /* En el rail colapsado los grupos se muestran siempre como íconos (ignora estado abierto/cerrado) */
    .po-nav-collapsed .po-nav-grouptoggle { justify-content: center; padding-left: 0; padding-right: 0; }
    .po-nav-collapsed .po-nav-grouptoggle > .bi:first-child { width: auto; }
    .po-nav-collapsed .po-nav-groupitems { display: block; padding-left: 0; }
}

/* ─── COMPONENTES ─── */
/* Cabecera de página pegajosa (2026-08-29, pedido recurrente): el título y las acciones que lleva
   adentro (el botón "Nuevo cliente", el buscador transversal de la ficha, "Ver el impacto"…) se
   pierden de vista al scrollear una grilla larga sin paginación — el catálogo de costos es el caso
   que lo destapó: se edita un precio en una fila de abajo y hay que volver a scrollear arriba para
   confirmar. Pegada justo debajo de la topbar (mismos 54px) y con fondo opaco para no dejar ver la
   tabla pasando detrás. El z-index (35) la deja por encima de todo el contenido normal de la
   pantalla (tablas, cards) — el `thead` pegajoso de más abajo vive DEBAJO de este bloque (se mide
   su alto en vivo para correrlo, no para taparlo detrás): ver la nota completa en "CABECERAS DE
   TABLA PEGAJOSAS" y en `pone-ui.js`. */
.po-page-head {
    display: flex; align-items: center; gap: 14px; margin-bottom: 20px; flex-wrap: wrap;
    position: sticky; top: var(--po-topbar-h); z-index: 35; background: var(--po-surface-2);
}
.po-page-head h1 { margin: 0; }
.po-page-head .po-spacer { flex: 1; }

/* ─── La cabecera fija se COMPACTA al scrollear (2026-08-30) ──────────────────────────────────────
   Regla de producto de Gustavo: el bloque que queda quieto —topbar + título + filtros + cabecera de
   tabla— **no puede pasar del cuarto superior de la pantalla, y mejor si es menos**. Con todo a
   tamaño completo son ~190px de 744, o sea justo en el límite, y en las pantallas con más botones se
   pasa.

   La salida no es achicar todo siempre —arriba de todo, quieto, el título grande y su explicación
   sirven— sino **encogerlo cuando el usuario ya está leyendo la lista**: apenas hay scroll, el
   subtítulo se va (ya se leyó) y el título baja de tamaño. Al volver arriba, vuelve entero.
   `data-scroll` lo pone `pone-ui.js`; la transición evita el salto brusco. */
.po-page-head, .po-page-head h1 { transition: padding .12s ease, font-size .12s ease; }
:root[data-scroll] .po-page-head { margin-bottom: 8px; }
:root[data-scroll] .po-page-head h1 { font-size: 1.15rem; }
:root[data-scroll] .po-page-head h1 .bi { font-size: 1rem; }
/* El subtítulo de la pantalla: se selecciona por estructura y no por una clase propia porque son 37
   pantallas y ninguna la tiene. Lo que se esconde es SÓLO la explicación de la pantalla — los avisos
   viven en `.alert`, fuera del `.po-page-head`. */
:root[data-scroll] .po-page-head > .text-muted { display: none; }
/* Segundo nivel, si aun así el bloque se pasa del cuarto de pantalla (lo mide el JS): antes que
   recortar la lista, se recorta la cabecera.
   ⚠️ Achicar sólo el título no alcanza —medido: 7px de 162—. Lo que ocupa de verdad son la topbar
   (54), la barra de filtros (49) y el `thead` (28), así que el ahorro sale de los tres. Bajar
   `--po-topbar-h` acá recalcula solo el `top` de todo lo que cuelga de ella, que por eso es una
   variable y no un número repetido. */
:root[data-head-min] { --po-topbar-h: 46px; }
:root[data-head-min] .po-page-head h1 { font-size: 1rem; }
:root[data-head-min] .po-page-head { gap: 8px; margin-bottom: 6px; }
:root[data-head-min] .po-filtros { padding: 5px 0 4px; }
:root[data-head-min] .po-filtros .form-control-sm, :root[data-head-min] .po-filtros .btn-sm { padding-top: .1rem; padding-bottom: .1rem; }
:root[data-head-min] .table-responsive thead th { padding-top: .2rem; padding-bottom: .2rem; }

/* Grupo pegajoso: para cuando además del título hay un aviso que también tiene que quedar a mano
   (ej. el banner "N líneas cotizadas con otro precio" del catálogo de costos, con su botón "Ver el
   impacto"). Se envuelven `.po-page-head` y el aviso juntos en este div en vez de apilar dos sticky
   con un offset calculado a mano: el alto del aviso varía (una o dos líneas según el ancho de
   pantalla) y un offset fijo se desalinearía. Adentro del grupo, `.po-page-head` deja de pegarse por
   su cuenta — pega el grupo entero, como un solo bloque. */
.po-sticky-group { position: sticky; top: var(--po-topbar-h); z-index: 35; background: var(--po-surface-2); }
.po-sticky-group .po-page-head { position: static; }

.po-card { background: var(--po-surface); border: 1px solid var(--po-border); border-radius: 14px; padding: 18px 20px; }
.po-card + .po-card { margin-top: 16px; }

/* ─── La barra de FILTROS de un listado viaja con la cabecera (2026-08-30) ────────────────────────
   Pedido de Gustavo: al scrollear tienen que quedar quietos el título, los filtros Y la cabecera de
   la tabla — no dos de los tres. Faltaba justamente el del medio: el buscador y los botones viven
   dentro de `.po-card`, fuera del `.po-sticky-group`, así que se iban con el scroll y para volver a
   filtrar había que subir hasta arriba.

   Se pega por su cuenta, con el offset que mide `pone-ui.js` en `--po-filtros-top` (topbar +
   cabecera de pantalla, que cambia por pantalla y por ancho). El respaldo del `calc()` deja la barra
   un poco corrida si el JS no llegó a correr, nunca tapada — mismo criterio que `--po-thead-top`.
   ⚠️ El fondo tiene que ser OPACO: es lo único que impide ver las filas pasar por detrás. */
.po-filtros {
    position: sticky; top: var(--po-filtros-top, calc(var(--po-topbar-h) + 54px)); z-index: 30;
    background: var(--po-surface); padding: 10px 0 8px; margin-bottom: 0 !important;
}
/* El `.po-card` no puede recortar ni crear su propio contexto de scroll: con `overflow` distinto de
   `visible` el sticky se ancla al card —que no scrollea por dentro— y deja de pegarse. Es la misma
   trampa que ya mordió con `.table-responsive` (ver "CABECERAS DE TABLA PEGAJOSAS"). */
.po-card:has(.po-filtros) { overflow: visible; }

/* ─── Vista Enterprise (pantalla de planes) ─── */
.po-enterprise { border-color: #bfdbfe; box-shadow: 0 6px 24px rgba(37,99,235,.1); }
.po-enterprise-head { display: flex; align-items: center; gap: 16px; margin-bottom: 8px; }
.po-enterprise-head > .bi { font-size: 2.2rem; color: var(--po-accent); }
.po-ent-stat { background: linear-gradient(135deg, #eff6ff, #f0f9ff); border: 1px solid #dbeafe; border-radius: 12px; padding: 14px 12px; text-align: center; height: 100%; }
.po-ent-num { font-weight: 700; font-size: 1.05rem; color: var(--po-primary-d); }
.po-ent-lbl { color: var(--po-ink-soft); font-size: .8rem; margin-top: 2px; }
.po-ent-lbl .bi { color: var(--po-primary); }

/* KPIs compactos: son el encabezado del proyecto, no su contenido. Ocupando media pantalla
   empujaban las etapas —que es lo que el gerente viene a mirar— abajo del pliegue. */
.po-kpis { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 10px; margin-bottom: 14px; }
.po-kpi { background: var(--po-surface); border: 1px solid var(--po-border); border-radius: 10px; padding: 9px 12px; }
.po-kpi-label { color: var(--po-ink-soft); font-size: .68rem; text-transform: uppercase; letter-spacing: .04em; }
.po-kpi-value { font-size: 1.15rem; font-weight: 700; margin-top: 1px; white-space: nowrap; }
.po-kpi .bi { color: var(--po-primary); }

/* ⚠️ Los dos grises de la grilla los ponía Bootstrap y ninguno servía (2026-08-30, junto con el
   fondo propio del `thead`):
   · el separador de fila venía en `#dee2e6`, que NO es el borde del producto — la misma línea se
     dibujaba de dos colores según viniera de una tabla o de una card;
   · el hover era `#f8fafc`, un 3% de diferencia contra el blanco: invisible justo donde más sirve,
     que es seguir una fila de ocho columnas de punta a punta sin perder el renglón.
   El hover pasa a un azul muy lavado de la familia del primario: se ve, y no compite con el ámbar y
   el rojo que la grilla usa para decir cosas (vencido, sobrecosto). */
.table { --bs-table-hover-bg: #eff5ff; --bs-border-color: var(--po-border); }
/* Cabeceras ordenables (componente SortTh) */
.po-sort-th { cursor: pointer; user-select: none; white-space: nowrap; }
.po-sort-th:hover { color: var(--po-primary); }
.po-sort-ico { font-size: .68rem; margin-left: 5px; opacity: .35; }
.po-sort-ico.po-sort-on { opacity: 1; color: var(--po-primary); }
.table thead th { font-size: .74rem; text-transform: uppercase; letter-spacing: .05em; color: var(--po-ink-soft); border-bottom: 2px solid var(--po-border); }
.table td { vertical-align: middle; }

/* ─── DENSIDAD DE LAS GRILLAS — una fila es un RENGLÓN, no una tarjeta (2026-08-30) ──────────────
   Pedido de Gustavo mirando `/recursos`: **80px de alto para mostrar un renglón de texto** es
   desperdiciar la pantalla y obligar a scrollear de más. Vale para TODAS las tablas del producto, no
   sólo para la que se reportó, así que va acá y no en una pantalla.

   ⚠️ Lo que hacía alta la fila NO era el padding de la celda. Eran dos cosas, y la primera pesaba el
   doble que la segunda:
   1. **La celda de acciones no declara `nowrap`** —son 48 en el producto—, así que con dos botones
      (editar + eliminar) en una columna angosta se APILAN en dos renglones y estiran la fila entera.
      Medido en `/recursos`: 80px, de los cuales ~40 eran el segundo botón.
   2. El `line-height: 1.5` heredado, que es interlineado para leer de corrido; una grilla se barre
      con la vista. Es el mismo 1.35 que ya se usaba en el tramo de tablet.
   Y se comprime el CROMO, nunca el dato: el `.btn-sm` de una grilla no necesita el padding de un
   botón de formulario. El texto no se achica. */
.po-card .table > :not(caption) > * > * { padding-top: .3rem; padding-bottom: .3rem; }
.po-card .table { line-height: 1.35; }
/* `:has()` en vez de tocar 48 `.razor`: una celda cuyo contenido directo son botones no parte la
   fila en dos. Si mañana aparece otra, ya nace comprimida. */
.po-card .table td:has(> .btn) { white-space: nowrap; }
.po-card .table .btn-sm { padding: .12rem .4rem; font-size: .8rem; line-height: 1.35; }
/* Sin esto los íconos de acción quedan pegados y se leen como un bloque, no como botones distintos. */
.po-card .table .btn-sm + .btn-sm { margin-left: 2px; }
/* ⚠️ La compensación táctil de tablet (`@media (pointer: coarse)`, al final del archivo) sigue
   valiendo y le devuelve el alto al botón: achicar el blanco donde se apoya el dedo es lo único que
   esta densidad NO puede hacer. */

/* ─── `.table-responsive` NO puede ser scroll container en desktop (2026-08-29) ───
   Bootstrap le pone `overflow-x: auto`, y por la regla de CSS que empareja los dos ejes (si uno no
   es `visible`, el otro también pasa a `auto`) el contenedor termina siendo scrolleable EN LOS DOS
   EJES — aunque nunca llegue a scrollear de verdad en Y, porque su alto es intrínseco (crece con el
   contenido: `scrollHeight == clientHeight` siempre). Eso lo convierte igual en el ANCESTRO DE
   ANCLAJE del `thead` pegajoso de más abajo, y como ese ancestro nunca scrollea internamente en Y, el
   sticky nunca se despega: el `thead` viaja pegado al contenido en vez de quedarse arriba.

   Medido en Azure el 29/08 en `/catalogo-costos` (el primer intento, con sólo `top: 0` en el thead,
   NO alcanzó): `rectTop: -637` — el thead no estaba tapado detrás de la topbar, estaba scrolleado
   fuera de la pantalla. La nota vieja de acá decía que alcanzaba con `top: 0` porque la topbar lo
   tapaba por z-index; medido en pantalla real eso no ocurre: el sticky nunca llega a activarse.

   La solución es que el contenedor DEJE DE SER scroll container: `overflow: clip` (a diferencia de
   `hidden`) no crea contexto de scroll — mismo motivo por el que `html, body` ya usan
   `overflow-x: clip` contra la topbar (ver el bloque TELÉFONO, más abajo). Con esto el `thead` se
   ancla directo al VIEWPORT real, y ahí sí un `top` positivo lo despega y lo baja de verdad — ver la
   nota de "CABECERAS DE TABLA PEGAJOSAS" para cómo se usa ese offset (bajarlo hasta que se VEA,
   debajo de la topbar y del `.po-page-head`, no cubierto detrás).

   El costo: una tabla más ancha que su tarjeta se recorta en vez de deslizar de costado EN DESKTOP.
   Ninguna grilla del producto lo necesita hoy (las anchas resuelven el ancho por columna, no por
   scroll) — si alguna vez aparece una que sí, revisar acá primero. En MÓVIL sigue haciendo falta
   deslizar (el reporte de costos lo hace a propósito, ver RESPONSIVE), así que el `@media` de más
   abajo reactiva `overflow-x: auto` — y ahí el `thead` vuelve a no pegarse: un contenedor no puede
   deslizar de costado Y anclar un sticky vertical al mismo tiempo, es una cosa o la otra. */
.table-responsive { overflow-x: clip; overflow-y: clip; }

/* ─── CABECERAS DE TABLA PEGAJOSAS (todas las grillas, 2026-08-29) ───
   Generaliza lo que primero se probó a mano en la planilla de insumos (`.po-insumos`, más abajo):
   cualquier tabla dentro de `.table-responsive` fija su `thead` al scrollear, para no perder de
   vista qué es cada columna en una grilla larga y sin paginación — el catálogo de costos es el caso
   que lo pidió: se edita un costo varias filas más abajo y sin la cabecera a la vista no se sabe qué
   columna es cuál.

   ⚠️⚠️ El pedido de Gustavo es que la cabecera de la grilla se VEA mientras scrollea, no sólo que
   quede anclada: con `top: 0` el thead se pega, pero JUSTO DETRÁS de la topbar y del `.po-page-head`
   (medido en Azure: `.po-sticky-group` termina en y=84 y el thead se ancla a y=0, tapado los 84px de
   arriba) — el título "SKU / ARTÍCULO / COSTO UNIT." no se ve en ningún momento. Con la regla de
   arriba, `table-responsive` ya no es scroll container, así que el `thead` se ancla al viewport real
   (o al contenedor con overflow genuino más cercano, como el `max-height:320px` de `EtapasCard`) — y
   eso significa que un `top` en positivo SÍ desplaza de verdad, así que se usa para bajar la cabecera
   hasta quedar justo debajo del bloque superior, en vez de para taparla.

   El alto de ese bloque superior varía por pantalla (84px en el catálogo, más en `ProyectoDetalle`
   con buscador y varios botones, y en móvil puede wrappear a 3-4 filas), así que un número fijo se
   desalinea. `--po-thead-top` lo mide `pone-ui.js` en vivo (topbar + `.po-sticky-group` o, si no hay
   grupo, `.po-page-head`) y lo recalcula en cada render de Blazor y en cada resize — ver la nota
   completa en `pone-ui.js`. El `84px` de acá es el RESPALDO si el JS no llegó a correr todavía (o
   está deshabilitado): deja la cabecera un poco corrida en vez de invisible, que es la instrucción
   explícita — nunca tapada.

   El z-index (5) ya no necesita ganarle a la topbar (40) ni al `.po-page-head` (35): no se superponen
   más, el thead vive DEBAJO de los dos. Sigue por encima del cuerpo de la tabla, que scrollea sin
   z-index propio. En móvil, donde `.table-responsive` vuelve a ser scroll container por el scroll
   horizontal, nada de esto aplica y el thead no se pega (ver la nota de arriba).

   ⚠️ Fondo propio, distinto del cuerpo (2026-08-30, pedido de Gustavo: "una grilla más respetable").
   Antes era `var(--po-surface)`, el mismo blanco que las filas, y la tabla se leía como texto suelto
   en vez de como grilla — más notorio todavía siendo sticky, porque encima flota igual de "invisible"
   sobre lo que tapa. Pasa a `var(--po-surface-2)`, el gris-slate que el producto YA usa como
   superficie secundaria (fondo de página, badges, KPI label, fila "abriendo") — no es un tono nuevo,
   es reusar el que ya separa "cromo" de "dato". Aplica una sola vez acá y llega a las ~30 pantallas
   con tabla sin tocar un solo .razor, porque todas comparten este selector. No cambia alto ni padding:
   el presupuesto del bloque quieto (1/4 de pantalla) no se mueve. */
.table-responsive thead th,
.table-responsive thead td {
    position: sticky;
    top: var(--po-thead-top, 84px);
    z-index: 5;
    background: var(--po-surface-2);
}

/* ⚠️ Adentro de un MODAL, no. `--po-thead-top` se mide contra la PÁGINA (topbar + cabecera de
   pantalla) y el modal flota en su propio contexto, así que ese offset empuja la cabecera sobre el
   cuerpo de su tabla y TAPA las primeras filas. Lo destapó el historial de precios (30/08): con dos
   filas, la más vieja quedaba escondida debajo del `thead` y el modal parecía tener una sola —
   estaba en el DOM y no se veía. Un modal es corto por definición: la cabecera no necesita
   perseguir ningún scroll. */
.po-modal .table-responsive thead th,
.po-modal .table-responsive thead td {
    position: static;
}

.po-badge { display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: .74rem; font-weight: 600; }
.po-badge-plan { background: #eff6ff; color: var(--po-primary-d); border: 1px solid #bfdbfe; }
.po-est-0 { background: #f1f5f9; color: #475569; }
.po-est-1 { background: #dbeafe; color: #1d4ed8; }
.po-est-2 { background: #fef3c7; color: #b45309; }
.po-est-3 { background: #dcfce7; color: #15803d; }
.po-est-4 { background: #fee2e2; color: #b91c1c; }

.po-tipo-chip { display: inline-flex; align-items: center; gap: 6px; font-size: .82rem; color: var(--po-ink-soft); }
.po-tipo-chip .bi { color: var(--po-primary); }

.po-money { font-variant-numeric: tabular-nums; font-weight: 600; }
.po-desvio-pos { color: var(--po-danger); }
.po-desvio-neg { color: var(--po-ok); }

/* ─── Importador de artículos ─────────────────────────────────────────────────────────────────── */
/* El número del paso: el flujo es bajá → subí → revisá → confirmá, y sin el número se lee como tres
   cards sueltas en vez de como una secuencia con un orden que importa. */
.po-paso-num {
    flex: 0 0 auto; width: 30px; height: 30px; border-radius: 50%;
    background: var(--po-primary); color: #fff; font-weight: 700; font-size: .9rem;
    display: flex; align-items: center; justify-content: center;
}
/* Una fila rechazada se ve rechazada: se lista igual —desaparecer se lee como "el sistema la
   perdió"— pero en gris y sobre rojo lavado, para que no se confunda con lo que sí va a entrar. */
.po-fila-rechazada { background: #fef2f2; color: var(--po-ink-soft); }
.po-import-trabajando { border-left: 3px solid var(--po-primary); }

.po-empty { text-align: center; color: var(--po-ink-soft); padding: 40px 20px; }
.po-empty .bi { font-size: 2.4rem; color: #cbd5e1; display: block; margin-bottom: 8px; }

/* ─── SMART SEARCH (typeahead) ─── */
.po-smartsearch { position: relative; }
.po-ss-box { position: relative; }
.po-ss-icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: #94a3b8; font-size: .85rem; pointer-events: none; }
.po-ss-input { padding-left: 30px; padding-right: 30px; }
.po-ss-clear { position: absolute; right: 6px; top: 50%; transform: translateY(-50%); border: 0; background: transparent; color: #94a3b8; cursor: pointer; padding: 2px 4px; line-height: 1; }
.po-ss-clear:hover { color: var(--po-danger); }
.po-ss-dropdown { position: absolute; z-index: 1200; top: calc(100% + 2px); left: 0; right: 0; background: #fff; border: 1px solid var(--po-border); border-radius: 10px; box-shadow: 0 12px 32px rgba(2,6,23,.18); max-height: 280px; overflow-y: auto; }
.po-ss-item { display: flex; flex-direction: column; align-items: flex-start; width: 100%; text-align: left; border: 0; background: transparent; padding: 8px 12px; cursor: pointer; }
.po-ss-item:hover { background: var(--po-surface-2); }
.po-ss-item-text { font-size: .9rem; color: var(--po-ink); }
.po-ss-item-sub { font-size: .76rem; color: var(--po-ink-soft); }
.po-ss-empty { padding: 10px 12px; color: var(--po-ink-soft); font-size: .85rem; }

/* GeoPicker: el buscador geo + combos ahora es el componente compartido de IntegraIA.Shared,
   con su CSS propio (_content/IntegraIA.Shared/geopicker.css). Las clases po-geo-* se eliminaron. */

/* ─── ICON PICKER ─── */
.po-iconpicker { position: relative; }
.po-ip-box { position: relative; }
.po-ip-trigger { display: flex; align-items: center; gap: 8px; text-align: left; cursor: pointer; }
.po-ip-trigger:disabled { cursor: not-allowed; opacity: .6; }
.po-ip-cur-icon { font-size: 1rem; color: var(--po-primary); flex: 0 0 auto; }
.po-ip-cur-desc { font-size: .9rem; color: var(--po-ink); flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.po-ip-cur-code { font-size: .72rem; color: var(--po-ink-soft); flex: 0 0 auto; }
.po-ip-caret { font-size: .7rem; color: #94a3b8; flex: 0 0 auto; }
.po-ip-backdrop { position: fixed; inset: 0; z-index: 1190; }
.po-ip-dropdown { position: absolute; z-index: 1200; top: calc(100% + 2px); left: 0; right: 0; background: #fff; border: 1px solid var(--po-border); border-radius: 10px; box-shadow: 0 12px 32px rgba(2,6,23,.18); overflow: hidden; }
.po-ip-search { position: relative; padding: 8px; border-bottom: 1px solid var(--po-border); }
.po-ip-search .bi-search { position: absolute; left: 18px; top: 50%; transform: translateY(-50%); color: #94a3b8; font-size: .85rem; pointer-events: none; }
.po-ip-search input { padding-left: 28px; }
.po-ip-list { max-height: 260px; overflow-y: auto; }
.po-ip-item { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; border: 0; background: transparent; padding: 7px 12px; cursor: pointer; }
.po-ip-item:hover { background: var(--po-surface-2); }
.po-ip-item.sel { background: color-mix(in srgb, var(--po-primary) 12%, #fff); }
.po-ip-item-icon { flex: 0 0 28px; display: flex; align-items: center; justify-content: center; font-size: 1.15rem; color: var(--po-primary); }
.po-ip-item-desc { display: flex; flex-direction: column; min-width: 0; }
.po-ip-item-name { font-size: .88rem; color: var(--po-ink); }
.po-ip-item-code { font-size: .72rem; color: var(--po-ink-soft); }
.po-ip-empty { padding: 10px 12px; color: var(--po-ink-soft); font-size: .85rem; }

/* ─── LOGO EMPRESA (form) ─── */
.po-logo-box { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.po-logo-preview { height: 72px; max-width: 220px; object-fit: contain; border: 1px solid var(--po-border); border-radius: 10px; padding: 6px 10px; background: #fff; }
.po-logo-empty { height: 72px; width: 120px; display: flex; align-items: center; justify-content: center; border: 1px dashed var(--po-border); border-radius: 10px; color: #94a3b8; font-size: 1.6rem; background: var(--po-surface-2); }
.po-logo-actions { display: flex; flex-direction: column; gap: 6px; min-width: 240px; }

/* ─── GANTT ─── */
.po-gantt { border: 1px solid var(--po-border); border-radius: 10px; overflow: hidden; font-size: .8rem; }
.po-gantt-head { display: flex; align-items: stretch; background: var(--po-surface-2); border-bottom: 1px solid var(--po-border); }
.po-gantt-row { display: flex; align-items: stretch; min-height: 34px; border-bottom: 1px solid #eef2f7; }
.po-gantt-row:last-child { border-bottom: 0; }
.po-gantt-labelcol { width: 210px; flex-shrink: 0; display: flex; align-items: center; gap: 7px; padding: 4px 12px; border-right: 1px solid var(--po-border); background: #fff; }
.po-gantt-head .po-gantt-labelcol { background: var(--po-surface-2); font-weight: 600; color: var(--po-ink-soft); text-transform: uppercase; font-size: .68rem; letter-spacing: .05em; }
.po-gantt-label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.po-gantt-sub { color: var(--po-ink-soft); font-size: .72rem; margin-left: auto; white-space: nowrap; }
.po-gantt-track { position: relative; flex: 1; min-width: 0; height: auto; }
.po-gantt-head .po-gantt-track { height: 26px; }
.po-gantt-bucket { position: absolute; top: 0; height: 100%; display: flex; align-items: center; padding-left: 5px; font-size: .68rem; color: var(--po-ink-soft); border-left: 1px solid var(--po-border); white-space: nowrap; overflow: hidden; }
.po-gantt-grid { position: absolute; top: 0; bottom: 0; width: 1px; background: #f1f5f9; }
.po-gantt-bar { position: absolute; top: 50%; transform: translateY(-50%); height: 20px; border-radius: 5px; display: flex; align-items: center; padding: 0 7px; color: #fff; box-shadow: 0 1px 3px rgba(2,6,23,.18); overflow: hidden; }
.po-gantt-bar-text { font-size: .7rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Barra de resumen (etapa madre): fina y con las puntas marcadas, para que no compita con el
   trabajo real de las hojas. */
.po-gantt-bar-resumen { height: 8px; border-radius: 2px; opacity: .85; box-shadow: none; }
/* Hito de compromiso: rombo sobre la línea. Si la barra lo pasa, se pinta rojo — que el rombo quede
   a la izquierda del final de la barra es todo el mensaje. */
.po-gantt-hito { position: absolute; top: 50%; width: 11px; height: 11px; margin-left: -5.5px; transform: translateY(-50%) rotate(45deg); background: #0f172a; border: 1.5px solid #fff; border-radius: 2px; z-index: 2; }
/* Incumplido: el rombo NO se pinta de rojo — quedaría rojo sobre barra roja y desaparecería justo
   cuando más hay que verlo. Se lo despega con un anillo. */
.po-gantt-hito-mal { box-shadow: 0 0 0 1.5px #dc2626; }
.po-gantt-foot { display: flex; padding: 4px 12px; background: var(--po-surface-2); border-top: 1px solid var(--po-border); }
.po-gantt-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; }
.po-gantt-legend span { display: inline-flex; align-items: center; gap: 6px; font-size: .78rem; color: var(--po-ink-soft); }
.po-gantt-legend i { width: 12px; height: 12px; border-radius: 3px; display: inline-block; }

/* ─── ACORDEÓN DE SECCIONES (ficha de proyecto) ─── */
/* El título ES el control: un botón aparte al lado del título obliga a apuntar a un blanco chico
   para algo que el usuario piensa como "abrir/cerrar esta sección". */
.po-sec-toggle { cursor: pointer; user-select: none; display: inline-flex; align-items: center; gap: 8px; }
.po-sec-toggle:hover { color: var(--po-primary); }
.po-sec-count { font-size: .72rem; font-weight: 600; color: var(--po-ink-soft); background: var(--po-surface-2); border: 1px solid var(--po-border); border-radius: 999px; padding: 1px 8px; }
/* Chip de etapa en la grilla de recursos: responde "¿a qué etapa pega este costo?" de un vistazo. */
.po-etapa-chip { display: inline-block; max-width: 260px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; background: rgba(37,99,235,.08); border: 1px solid rgba(37,99,235,.22); color: var(--po-primary); border-radius: 6px; padding: 1px 7px; font-size: .75rem; }

/* ─── REORDENAR ETAPAS (drag & drop entre hermanas) ─── */
.po-drag-handle { color: #94a3b8; cursor: grab; margin-right: 4px; }
.po-drag-handle:active { cursor: grabbing; }
.po-etapa-row[draggable="true"] { cursor: default; }
/* La fila que se está arrastrando se atenúa; la de destino marca el borde donde va a caer. */
.po-drag-src { opacity: .45; }
.po-drag-over td { border-top: 2px solid var(--po-primary) !important; background: rgba(37,99,235,.05); }

/* Edición inline de una asignación dentro de la fila desplegada de su etapa. */
.po-inline-edit { display: inline-flex; align-items: center; gap: 6px; margin-left: 10px; }
.po-comp-row-edit { background: rgba(37,99,235,.06); }

/* Chevron del árbol: control propio, separado del badge de detalle. */
.po-tree-toggle { display: inline-flex; align-items: center; justify-content: center; width: 20px; cursor: pointer; color: var(--po-ink-soft); }
.po-tree-toggle:hover { color: var(--po-primary); }
.po-badge-click { cursor: pointer; }
.po-badge-click:hover { background: rgba(37,99,235,.12); }

/* ─── NIVELES DE PERMISO ─── */
.po-nivel-0 { color: #64748b; }
.po-nivel-1 { border-left: 3px solid #64748b; }
.po-nivel-2 { border-left: 3px solid var(--po-primary); }
.po-nivel-3 { border-left: 3px solid var(--po-ok); }

/* ─── MODAL (confirmación / danger zone) ─── */
.po-modal-backdrop { position: fixed; inset: 0; background: rgba(2,6,23,.55); z-index: 2000; display: flex; align-items: center; justify-content: center; padding: 16px; }
.po-modal { background: #fff; border-radius: 14px; width: 100%; max-width: 480px; box-shadow: 0 24px 60px rgba(2,6,23,.4); overflow: hidden; }
/* Formularios con varias columnas (alta/edición de etapa): 480px los aplasta en una sola. */
.po-modal.po-modal-wide { max-width: 860px; }
/* La caja recibe foco (tabindex="-1") para poder escuchar Escape; el anillo del navegador ahí no
   aporta nada, el modal ya es evidente. */
.po-modal:focus, .po-modal:focus-visible { outline: none; }
.po-modal-head { display: flex; align-items: center; gap: 10px; padding: 16px 20px; border-bottom: 1px solid var(--po-border); font-weight: 700; font-size: 1.05rem; }
.po-modal-head .bi { font-size: 1.3rem; }
/* Cruz de cierre: el click en el fondo NO cierra (IDENTIDAD.md regla 13), así que esta es una de las
   salidas reales del modal y tiene que estar siempre visible, no aparecer al hacer hover. */
.po-modal-x { margin-left: auto; border: 0; background: transparent; color: var(--po-ink-soft); line-height: 1; padding: 4px 6px; border-radius: 8px; cursor: pointer; }
.po-modal-x .bi { font-size: 1rem; }
.po-modal-x:hover:not(:disabled) { background: var(--po-surface-2); color: var(--po-ink); }
.po-modal-x:disabled { opacity: .4; cursor: default; }
.po-modal-body { padding: 18px 20px; }
.po-modal-body ul { margin: 8px 0; padding-left: 20px; color: var(--po-ink-soft); }
.po-modal-foot { display: flex; justify-content: flex-end; gap: 8px; padding: 14px 20px; background: var(--po-surface-2); border-top: 1px solid var(--po-border); }

.po-error-ui { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: #fee2e2; color: #991b1b; padding: 10px 16px; z-index: 1000; box-shadow: 0 -2px 10px rgba(0,0,0,.1); }
.po-error-ui .reload, .po-error-ui .dismiss { margin-left: 12px; cursor: pointer; }

/* ─── ANÁLISIS IA (chat foto→BOM) ─── */
.po-analisis-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
.po-chat { display: flex; flex-direction: column; }
.po-chat-thread { display: flex; flex-direction: column; gap: 10px; max-height: 52vh; overflow-y: auto; padding: 4px; margin-bottom: 12px; }
.po-msg { max-width: 85%; padding: 10px 13px; border-radius: 12px; font-size: .9rem; line-height: 1.4; }
.po-msg-usuario { align-self: flex-end; background: var(--po-primary); color: #fff; border-bottom-right-radius: 3px; }
.po-msg-agente { align-self: flex-start; background: var(--po-surface-2); color: var(--po-ink); border: 1px solid var(--po-border); border-bottom-left-radius: 3px; }
.po-msg-sistema { align-self: center; background: #fffbeb; color: #92400e; font-size: .82rem; }
.po-msg-typing { font-style: italic; color: var(--po-ink-soft); }
/* Markdown renderizado de las respuestas del agente: mismo aire que un párrafo suelto, sin los
   márgenes de bloque que meterían aire de más adentro de la burbuja. */
.po-msg-md > :first-child { margin-top: 0; }
.po-msg-md > :last-child { margin-bottom: 0; }
.po-msg-md p { margin-bottom: .5rem; }
.po-msg-md ul, .po-msg-md ol { margin-bottom: .5rem; padding-left: 1.15rem; }
.po-msg-md li + li { margin-top: .15rem; }
.po-msg-md code { background: rgba(0,0,0,.05); padding: 1px 4px; border-radius: 4px; font-size: .88em; }
.po-msg-md pre { background: rgba(0,0,0,.05); padding: 8px 10px; border-radius: 8px; overflow-x: auto; }
.po-msg-md table { width: 100%; font-size: .85em; border-collapse: collapse; }
.po-msg-md th, .po-msg-md td { border: 1px solid var(--po-border); padding: 3px 6px; }
.po-fotos { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.po-foto-item { position: relative; }
.po-foto-thumb { width: 64px; height: 64px; object-fit: cover; border-radius: 8px; border: 1px solid var(--po-border); }
.po-foto-file { width: 64px; height: 64px; border-radius: 8px; border: 1px solid var(--po-border); background: var(--po-surface-2); display: flex; align-items: center; justify-content: center; color: var(--po-primary); font-size: 1.7rem; text-decoration: none; }
.po-foto-file:hover { background: #e8eefc; }
.po-foto-del { position: absolute; top: -6px; right: -6px; width: 20px; height: 20px; border-radius: 50%; border: none; background: var(--po-danger); color: #fff; font-size: .8rem; line-height: 1; cursor: pointer; }
.po-foto-add { display: flex; gap: 8px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.po-foto-add .form-control { max-width: 240px; }
.po-composer { display: flex; gap: 8px; align-items: flex-end; }
.po-composer textarea { flex: 1; resize: vertical; }
.po-bom-table th { font-size: .78rem; color: var(--po-ink-soft); }
.po-sku { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .72rem; background: var(--po-surface-2); border: 1px solid var(--po-border); border-radius: 4px; padding: 0 4px; color: var(--po-ink-soft); }
/* ── Datos que se editan tocándolos, en la grilla del catálogo ──────────────────────────────────
   SKU y costo son la misma clase de cosa —un dato que se toca— y por lo tanto se ven igual: sin
   fondo, sin borde, y con una sola señal, el hover (subrayado punteado + azul). Dice "esto se toca"
   sin gritarlo en las 36 filas, y no compite con los botones de la fila.
   ⚠️ El SKU tenía el chip gris `.po-sku` y el costo no: dos formatos para dos columnas que hacen lo
   mismo. Reporte de Gustavo (30/08). Se resolvió sacándole el fondo acá —no poniéndoselo al costo—
   por lo mismo que el badge de origen: en esta grilla el relleno confunde. `.po-sku` sigue igual
   donde el código NO se edita (fichas de cliente, proveedor, componente, etapa). */
.po-dato-edit { text-decoration: none !important; color: inherit !important; }
.po-dato-edit:hover { text-decoration: underline dotted !important; text-underline-offset: 3px; color: var(--po-primary) !important; }
.po-dato-edit:hover .po-money, .po-dato-edit:hover .po-sku-plano { color: var(--po-primary); }
.po-sku-plano { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: .78rem; color: var(--po-ink-soft); }
.po-sku-input { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; text-transform: uppercase; }

/* ⚠️ El ORIGEN es un dato, no una acción: iba en un badge con fondo y en una grilla donde el SKU y el
   costo SÍ se editan con un click, el recuadro relleno se leía como algo tocable. Ahora es un ícono
   con color y tooltip — el mismo recurso con el que DigitalOne dice de qué dispositivo salió una
   fichada (huella, cara, QR, PIN). Sin fondo, sin borde y sin cursor de mano. */
.po-ico-origen { font-size: 1.05rem; line-height: 1; }
.po-ico-web { color: #0f766e; }   /* teal: el azul es del producto y el gris se pierde */
.po-linea-promovida { background: #f0fdf4; }
.po-linea-promovida td { color: var(--po-ink-soft); }

/* ─── REPORTE COSTOS: desglose por componente ─── */
.po-rep-chevron { color: var(--po-ink-soft); font-size: .72rem; margin-right: 6px; transition: transform .18s; display: inline-block; }
.po-rep-chevron.open { transform: rotate(90deg); }
.po-rep-chevron-spacer { display: inline-block; width: 12px; margin-right: 6px; }
/* inline-block + nowrap: el texto del badge ("2 subetapas ocultas") se partia en dos lineas y
   rompia la fila de la etapa. */
.po-comp-badge { display: inline-block; white-space: nowrap; background: var(--po-surface-2); color: var(--po-ink-soft); font-weight: 600; font-size: .68rem; margin-left: 8px; padding: 1px 7px; border-radius: 999px; }
.po-comp-row > td { background: var(--po-surface-2); border-top: 0; padding-top: .3rem; padding-bottom: .3rem; }
.po-comp-row:hover > td { background: #e8eef7; }
.po-comp-name { padding-left: 30px !important; }
.po-comp-name i { color: var(--po-accent); font-size: .8rem; }
/* Nombre de una linea de receta que ademas es puerta al item (recurso o componente): hereda el
   color del texto para no llenar la tabla de azul, y se delata al pasar por encima. */
.po-link-item { color: inherit; text-decoration: none; }
.po-link-item:hover { color: var(--po-primary); text-decoration: underline; }

/* ─── ETAPAS: árbol, avance e incidencia ─── */
/* Barras compactas dentro de la fila: avance = azul primario (progreso de ejecución),
   incidencia = celeste acento (peso estructural, mismo criterio que .po-comp-name i). */
.po-progress-mini { display: inline-block; vertical-align: middle; width: 64px; height: 6px; border-radius: 999px; background: var(--po-surface-2); overflow: hidden; border: 1px solid var(--po-border); }
.po-progress-mini-fill { display: block; height: 100%; background: var(--po-primary); border-radius: 999px; }
.po-incidencia-mini .po-progress-mini-fill { background: var(--po-accent); }
/* Consumido que superó lo pactado en una contratación. La barra se clampea al 100% y cambia de
   color; cuánto se pasó va en texto al lado, porque una barra desbordada no dice el número.
   Avisa, no bloquea: es la misma doctrina que la holgura negativa del plan. */
.po-progress-mini-danger .po-progress-mini-fill { background: var(--po-danger); }
.po-progress-mini-label { display: inline-block; margin-left: 6px; font-size: .74rem; color: var(--po-ink-soft); vertical-align: middle; }
.po-etapa-row { cursor: pointer; }
.po-etapa-nombre i.po-etapa-tipo { color: var(--po-accent); font-size: .8rem; }
/* Fila de un registro anulado: presente pero fuera de circulación. */
.po-row-anulada > td { opacity: .55; }
.po-row-anulada:hover > td { opacity: .8; }

/* ─── EDITOR DE FLUJO DE ESTADOS (3 cajas + drag & drop) ───
   Caja de disponibles a la izquierda y dos destinos (estados anteriores / posteriores). Se puede
   arrastrar entre cajas o usar los botones de mover uno / mover todos. */
.po-flow-grid { display: grid; grid-template-columns: 1fr auto 1fr; gap: 12px; align-items: start; }
.po-flow-col { display: flex; flex-direction: column; min-width: 0; }
.po-flow-col > label { font-size: .78rem; font-weight: 600; color: var(--po-ink-soft); margin-bottom: 4px; }
.po-flow-box { border: 1px solid var(--po-border); border-radius: 10px; background: #fff; min-height: 170px; max-height: 260px; overflow-y: auto; padding: 6px; }
/* Feedback de drop: el borde punteado aparece sólo mientras se arrastra encima. */
.po-flow-box.po-drop { border-style: dashed; border-color: var(--po-primary); background: #f5f8ff; }
.po-flow-item { display: flex; align-items: center; gap: 6px; padding: 5px 8px; border-radius: 8px; cursor: grab; font-size: .84rem; border: 1px solid transparent; }
.po-flow-item:hover { background: var(--po-surface-2); }
.po-flow-item.po-sel { background: #e8eef7; border-color: var(--po-primary); }
.po-flow-item:active { cursor: grabbing; }
.po-flow-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
.po-flow-btns { display: flex; flex-direction: column; gap: 4px; justify-content: center; padding-top: 22px; }
.po-flow-btns .btn { padding: 2px 8px; line-height: 1.4; }
.po-flow-empty { color: var(--po-ink-soft); font-size: .78rem; text-align: center; padding: 18px 6px; }
@media (max-width: 860px) {
    .po-flow-grid { grid-template-columns: 1fr; }
    .po-flow-btns { flex-direction: row; justify-content: center; padding-top: 0; }
}

/* ─── ZONA PELIGROSA ───
   Se separa del resto de la pantalla ANTES de que el usuario lea una palabra. El bloque verde
   ("esto no se toca") usa el semáforo real del producto: el contraste rojo/verde es lo que evita
   el malentendido de creer que borra todo el sistema. */
.po-danger-zone { border: 1px solid var(--po-danger); border-radius: 14px; overflow: hidden; }
.po-danger-zone-head { background: #fef2f2; color: var(--po-danger); padding: 14px 20px; font-weight: 700; display: flex; align-items: center; gap: 8px; border-bottom: 1px solid var(--po-danger); }
.po-danger-zone-body { padding: 18px 20px; background: #fff; }
.po-danger-zone-scope { display: flex; align-items: flex-start; gap: 8px; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 10px; padding: 10px 14px; margin-bottom: 14px; font-size: .85rem; color: #166534; }
.po-danger-zone-scope .bi { color: var(--po-ok); flex-shrink: 0; margin-top: 2px; }
/* Resaltado del buscador transversal del detalle de proyecto. */
.po-search-hit { background: color-mix(in srgb, var(--po-warn) 22%, #fff); border-radius: 3px; padding: 0 2px; }

/* ─── RESPONSIVE ─── */
@media (max-width: 980px) {
    .po-analisis-grid { grid-template-columns: 1fr; }
}
@media (max-width: 860px) {
    .po-sidebar { position: fixed; left: calc(-1 * var(--po-sidebar-w)); z-index: 60; transition: left .2s; }
    .po-nav-open .po-sidebar { left: 0; }
    .po-content { padding: 16px 14px 60px; }
}

/* ─── MODO SIMPLE: selector del alta y planilla de insumos ─── */
/* El modo se elige entre dos tarjetas y no en un <select>: la diferencia entre "simple" y
   "estructurado" no se entiende por el rótulo, hay que poder leer qué implica cada una antes
   de decidir. Cada tarjeta resume con 2 bullets + una etiqueta "para quién es", y la elegida
   lleva un check visible: no hace falta leer el párrafo completo para saber cuál está activa. */
.po-modo-opt { position: relative; display: flex; gap: 10px; align-items: flex-start; padding: 12px 14px; border: 1px solid var(--po-border); border-radius: 8px; cursor: pointer; height: 100%; background: var(--po-surface); transition: border-color .15s, background .15s; }
.po-modo-opt:hover { border-color: var(--po-primary); }
.po-modo-opt.activa { border-color: var(--po-primary); background: #eff6ff; box-shadow: inset 0 0 0 1px var(--po-primary); }
.po-modo-opt input { margin-top: 3px; flex: 0 0 auto; accent-color: var(--po-primary); }
.po-modo-opt .bi { color: var(--po-primary); }
.po-modo-check { position: absolute; top: 10px; right: 12px; color: var(--po-primary); font-size: 1rem; }
.po-modo-body { display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1 1 auto; }
.po-modo-head { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding-right: 18px; }
.po-modo-head strong { font-size: .95rem; }
.po-modo-tag { margin-left: auto; font-size: .66rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--po-ink-soft); background: var(--po-surface-2); border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
.po-modo-opt.activa .po-modo-tag { color: var(--po-primary-d); background: #dbeafe; }
.po-modo-features { margin: 0; padding-left: 18px; font-size: .8rem; color: var(--po-ink-soft); }
.po-modo-features li { margin-bottom: 2px; }

/* Aviso de conversión ("¿el trabajo creció?"): tip de menor jerarquía que una .po-card de contenido
   real, por eso NO es blanca ni de borde sólido — borde punteado celeste (acento de marca) para que
   se lea como sugerencia, no como otra sección de datos a la par de la planilla o el equipo. */
.po-modo-nudge { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; border: 1px dashed #bae6fd; background: #f6fbff; border-radius: 14px; padding: 14px 18px; margin-bottom: 16px; }
.po-modo-nudge > .bi { font-size: 1.3rem; color: var(--po-accent); flex-shrink: 0; }

/* Chip de modo en la cabecera del detalle: tono neutro (informativo, no un estado ni un plan). */
.po-badge-modo { background: var(--po-surface-2); color: var(--po-ink-soft); border: 1px solid var(--po-border); }

.po-insumos > thead th { font-size: .78rem; color: var(--po-ink-soft); font-weight: 600; }
/* Cabecera pegajosa: este fue el caso que destapó el bug de offset dentro de `.table-responsive`
   (Bicicletero Doble, 2026-08-13) — el `top: 0` que lo arregla y por qué ahora es la regla GENERAL
   de todas las grillas del producto, ver "CABECERAS DE TABLA PEGAJOSAS" más arriba. */
.po-insumos > tbody > tr > td { vertical-align: top; }
/* La fila nueva se distingue de la ya guardada: en una planilla que mezcla ambas, sin esto no se
   sabe qué está persistido y qué se pierde si se recarga la página. El acento a la izquierda queda
   visible incluso si la fila se sale de la pantalla por el scroll horizontal. */
.po-insumos > tbody > tr.po-insumo-nueva > td { background: #fffbeb; }
.po-insumos > tbody > tr.po-insumo-nueva > td:first-child { border-left: 3px solid var(--po-warn); }
.po-insumo-meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
/* Mismo formato (píldora, tamaño chico) para las dos anotaciones de la columna Descripción, así se
   escanean igual de rápido: gris = ya identificado en el catálogo, azul = se va a crear al guardar. */
.po-insumo-tag { display: inline-flex; align-items: center; gap: 4px; font-size: .68rem; font-weight: 600; padding: 1px 7px; border-radius: 999px; white-space: nowrap; }
.po-insumo-tag-nuevo { background: #eff6ff; color: var(--po-primary-d); }
.po-insumo-tag-catalogo { background: var(--po-surface-2); color: var(--po-ink-soft); }

/* Barra de acciones al pie de la planilla: se separa como un "footer" propio (mismo criterio que
   .po-modal-foot) para que no se confunda con una fila más de la tabla. Guardar queda a la derecha,
   Agregar renglón a la izquierda — separar "seguir cargando" de "confirmar". */
.po-insumos-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 14px -20px -18px; padding: 12px 20px; background: var(--po-surface-2); border-top: 1px solid var(--po-border); border-radius: 0 0 14px 14px; }
/* .po-spacer sólo empuja dentro de .po-page-head (ver esa regla arriba); acá se repite el mismo
   contrato acotado a esta barra para no tocar el comportamiento del resto del producto. */
.po-insumos-actions .po-spacer { flex: 1; }

/* ─── CERTIFICADOS DE AVANCE ───
   La cascada de un certificado no es una escalera: el anticipo y el fondo de reparo se calculan los
   DOS sobre el Sub Total 1. Por eso las dos filas van dentro de .po-cert-cascada-grupo, con un
   conector a la izquierda que las ata al mismo origen — dibujadas como escalones, se leen como si el
   fondo de reparo saliera del subtotal de arriba, que da otro número. El IVA sí se apila, y por eso
   queda afuera del grupo. */
.po-cert-cascada { margin-top: 10px; padding: 10px 14px; background: var(--po-surface-2); border: 1px solid var(--po-border); border-radius: 10px; max-width: 560px; margin-left: auto; }
.po-cert-cascada-row { display: grid; grid-template-columns: 1fr 70px 150px; align-items: center; gap: 8px; padding: 3px 0; font-size: .86rem; }
.po-cert-cascada-row > :last-child { text-align: right; }
.po-cert-cascada-row > :nth-child(2) { text-align: right; color: var(--po-ink-soft); font-size: .8rem; }
.po-cert-cascada-row.subtotal { border-top: 1px solid var(--po-border); font-weight: 600; padding-top: 5px; }
/* El número más grande de la pantalla: es a lo que se llega y lo que se firma. */
.po-cert-cascada-row.total { border-top: 2px solid var(--po-ink); margin-top: 4px; padding-top: 7px; font-size: 1.02rem; font-weight: 700; }
.po-cert-cascada-nota { font-size: .74rem; color: var(--po-ink-soft); padding: 4px 0 2px; }
.po-cert-cascada-grupo { border-left: 2px solid var(--po-border); padding-left: 10px; margin-left: 2px; }
/* Los % y los importes editables no ensanchan la fila: entran en la misma grilla de 3 columnas. */
.po-cert-pct { width: 68px; display: inline-block; padding: 1px 4px; height: auto; font-size: .8rem; text-align: right; }
.po-cert-imp { width: 140px; display: inline-block; padding: 1px 6px; height: auto; font-size: .82rem; }
.po-cert-avance { white-space: nowrap; }
.po-cert-lineas > tbody > tr > td { vertical-align: middle; }
.po-cert-detalle { padding: 4px 0 8px; }

/* Loader de la ficha de proyecto. Ocupa alto real para que la página no parezca vacía mientras
   corren las consultas: en un proyecto grande son varios segundos y un renglón de texto no alcanza. */
.po-cargando-ficha { display: flex; flex-direction: column; align-items: center; justify-content: center;
    min-height: 46vh; text-align: center; color: var(--po-ink); }
/* Fila de la lista mientras se abre: el clic ya entró y el proyecto está cargando. */
.po-row-abriendo { background: var(--po-surface-2); opacity: .75; }
/* Prefijo de moneda en un KPI que muestra un valor por moneda (avance certificado bimonetario). */
.po-kpi-moneda { font-size: .62rem; font-weight: 600; color: var(--po-ink-soft); letter-spacing: .04em;
    margin-right: 4px; vertical-align: middle; }

/* ── Situación económica y financiera (lectura del directorio) ───────────────────────────────── */
.po-veredicto { background: var(--po-surface-2); border: 1px solid var(--po-border); border-radius: 8px;
    padding: 12px 14px; }
.po-veredicto-linea { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; padding: 3px 0; }
.po-veredicto-moneda { font-size: .66rem; font-weight: 700; letter-spacing: .06em; color: var(--po-ink-soft);
    border: 1px solid var(--po-border); border-radius: 4px; padding: 1px 6px; }
.po-vered-deben { color: var(--po-primary); }
.po-fin-tabla .po-fin-grupo td { font-size: .68rem; text-transform: uppercase; letter-spacing: .06em;
    color: var(--po-ink-soft); background: var(--po-surface-2); font-weight: 700; padding-top: 8px; }
.po-fin-tabla .po-fin-total td { font-weight: 700; border-top: 2px solid var(--po-border); }
.po-fin-ok { color: var(--po-success, #16a34a); }
.po-fin-deben { color: var(--po-primary); font-weight: 700; }

/* ── Densidad de la ficha de proyecto ─────────────────────────────────────────────────────────
   Una obra tiene 29 etapas, 20 certificados y 22 documentos: cada píxel de alto que se gana es un
   renglón más de lo que el usuario vino a mirar. Se comprime el cromo, nunca el dato. */
.po-ficha-cab { padding-top: 6px; padding-bottom: 8px; }
.po-ficha-toggle { cursor: pointer; user-select: none; display: flex; align-items: center; gap: 7px;
    padding: 2px 0; }
.po-ficha-toggle:hover { color: var(--po-primary); }
.po-card { padding: 12px 14px; }
.po-kpis { gap: 8px; margin-bottom: 10px; }
.po-kpi { padding: 7px 11px; }
.po-kpi-value { font-size: 1.06rem; }
.table-sm > :not(caption) > * > * { padding-top: .3rem; padding-bottom: .3rem; }
/* Lo que hace alta una fila no es el padding de la celda sino lo que se le mete adentro: un botón
   con su padding propio, una barrita con margen, un badge. Se comprimen ESOS, no el dato. */
.po-card .table-sm .btn-sm { padding: .1rem .38rem; font-size: .78rem; line-height: 1.35; }
/* Los iconos de acción quedan pegados sin esto: se leen como un bloque y no como botones distintos. */
.po-card .table-sm .btn-sm + .btn-sm { margin-left: 2px; }
.po-card .table-sm .po-progress-mini { margin-bottom: 0; vertical-align: middle; }
.po-card .table-sm .po-comp-badge { padding: 0 5px; }
/* Incidencia sobre el proyecto completo: al lado del relativo, no en un segundo renglón. */
.po-inc-abs { font-size: .68rem; color: var(--po-ink-soft); margin-left: 6px; }

/* ── Escala de texto de las tablas de la ficha ────────────────────────────────────────────────
   El dato baja de 16 a 15 px. Es UNA prueba acotada a la ficha de proyecto (por eso la clase
   propia y no `.po-card .table`): una obra tiene 29 etapas y 22 documentos, y ahí el píxel por
   fila se nota; en una pantalla de seis renglones sólo se leería peor.

   Lo secundario NO se toca a mano: `.small` de Bootstrap es `em`, así que sigue al padre y baja
   solo a ~13 px. Los rótulos, badges y códigos están en `rem` —anclados al root— y se quedan
   donde están: si bajaran todos juntos, la jerarquía se conservaría en proporción pero el
   escalón más chico caería por debajo de los 10 px, que ya no se lee. */
.po-tabla-ficha { font-size: .94rem; }
/* Los importes: más chicos que el texto y SIN negrita. El peso 600 de `.po-money` está pensado para
   un total suelto, no para una grilla donde cada fila trae cuatro; ahí todo grita y no destaca nada.
   Se conserva `tabular-nums`, que es lo que hace que las columnas de cifras se alineen. */
.po-tabla-ficha .po-money { font-weight: 400; font-size: .86rem; }
/* El encabezado tampoco necesita peso: ya se distingue por versalita, color y el filete inferior. */
.po-tabla-ficha thead th { font-weight: 500; }
/* Nombre de una etapa hoja: mismo cuerpo que la madre, sin el peso. */
.po-etapa-hoja { font-weight: 400; }
.po-page-head { margin-bottom: 8px; }
/* KPI en alerta (plazo vencido): el borde tiñe sin gritar. */
.po-kpi-alerta { border-color: var(--po-danger, #dc2626); background: color-mix(in srgb, var(--po-danger, #dc2626) 4%, var(--po-surface)); }
/* KPI cuyo valor es una FRASE, no un número. El `nowrap` de .po-kpi-value existe para que
   "ARS 628,685,264" no se parta al medio; aplicado a "vencido hace 2 años y 5 meses" corta el texto
   contra el borde de la card y el usuario lee media respuesta. */
.po-kpi-frase { white-space: normal; font-size: 1rem; line-height: 1.2; text-wrap: balance; }
/* Aviso de que el indicador todavía no tiene contra qué medirse. Ámbar, no rojo: no es un error. */
.po-desvio-aviso { color: var(--po-warning, #b45309); }

/* ── Resumen para dirección ─────────────────────────────────────────────────────────────────────
   Las cuatro respuestas van en cajas del mismo tamaño y peso: ninguna es "la principal", el orden
   ya dice cuál se lee primero. La pregunta va arriba y en texto normal —no en mayúsculas como los
   KPI de la ficha— porque acá el rótulo ES una pregunta y tiene que leerse como tal. */
.po-res-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; }
.po-res-caja { display: flex; flex-direction: column; gap: 4px; }
.po-res-preg { font-size: .9rem; font-weight: 600; color: var(--po-ink); }
/* El valor no lleva nowrap: acá un importe puede venir en dos monedas, y cortarlo contra el borde
   haría que se lea media respuesta. Es la lección de los KPI cuyo valor es una frase. */
.po-res-valor { font-size: 1.5rem; font-weight: 700; line-height: 1.15; }
.po-res-ayuda { font-size: .78rem; color: var(--po-ink-soft); margin-top: auto; }
/* Destaque sobrio: un borde, no un fondo de color. La caja tiene que seguir siendo legible. */
.po-res-destaca { border-color: var(--po-primary); box-shadow: inset 3px 0 0 var(--po-primary); }
.po-res-alerta { border-color: #f59e0b; box-shadow: inset 3px 0 0 #f59e0b; }
.po-res-alertas { padding-left: 18px; }
.po-res-alertas li + li { margin-top: 4px; }

@media (max-width: 575px) {
    .po-res-valor { font-size: 1.28rem; }
}

/* La descripción del proyecto en el resumen: es prosa larga, no un dato de tabla, así que respira
   distinto del resto de las cards. */
.po-res-descripcion p { line-height: 1.5; color: var(--po-ink); }

/* Aviso informativo de menor jerarquía que una card: explica una decisión del sistema (por qué los
   importes van separados por moneda) sin competir visualmente con los datos. */
.po-tip { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 10px; padding: 10px 14px;
          font-size: .85rem; color: #1e3a8a; }
.po-tip .bi { color: var(--po-primary); margin-right: 4px; }

/* El titular del resumen: una sola frase, del tamaño de un subtítulo. No es un h1 —el nombre del
   proyecto ya lo es— pero tiene que ganarle en peso al resto del texto de la pantalla. */
.po-res-titular { font-size: 1.05rem; font-weight: 600; color: var(--po-ink);
                  border-left: 4px solid var(--po-primary); padding: 8px 0 8px 12px; }

/* ── Las cajas se abren ─────────────────────────────────────────────────────────────────────────
   La afordancia va en la lupa y en el hover, no en un borde permanente: las cinco cajas son
   clickeables, así que marcarlas todas todo el tiempo no distingue nada y sólo agrega ruido a la
   pantalla que se le muestra a alguien de afuera. */
.po-res-abrible { cursor: pointer; transition: box-shadow .12s, border-color .12s; }
.po-res-abrible:hover { border-color: var(--po-primary); box-shadow: 0 2px 10px rgba(37,99,235,.14); }
.po-res-abrible:focus-visible { outline: 2px solid var(--po-primary); outline-offset: 2px; }
.po-res-preg { display: flex; align-items: center; gap: 6px; }
/* La lupa vive apagada y se enciende con el hover: presente para que se sepa que hay algo abajo,
   callada para que no compita con la pregunta. */
.po-res-lupa { margin-left: auto; font-size: .78rem; color: var(--po-ink-soft); opacity: .45; }
.po-res-abrible:hover .po-res-lupa { opacity: 1; color: var(--po-primary); }

/* ── De dónde sale este número ──────────────────────────────────────────────────────────────────
   La cuenta escrita para que la lea alguien de afuera del proyecto. Prioridad absoluta a que los
   importes se puedan seguir con el dedo: columna derecha, tabulares, y los cortes marcados con
   filete en vez de color —esto se imprime y se adjunta a un expediente—. */
.po-origen { max-height: 68vh; overflow-y: auto; }
.po-origen-def { font-size: .95rem; color: var(--po-ink); margin-bottom: 12px; }
/* Los datos del papel del que cuelga todo: rótulo y valor en dos columnas, no en prosa. */
.po-origen-doc { display: grid; grid-template-columns: auto 1fr; gap: 2px 12px; margin: 0 0 14px;
                 font-size: .84rem; background: var(--po-surface-2); border-radius: 8px; padding: 10px 12px; }
.po-origen-doc dt { color: var(--po-ink-soft); font-weight: 500; }
.po-origen-doc dd { margin: 0; color: var(--po-ink); }
.po-origen-bloque + .po-origen-bloque { margin-top: 18px; }
.po-origen-moneda { font-size: .75rem; font-weight: 700; letter-spacing: .08em; color: var(--po-ink-soft);
                    text-transform: uppercase; margin-bottom: 4px; }
.po-origen-tabla { width: 100%; border-collapse: collapse; font-size: .88rem; }
.po-origen-tabla td { padding: 4px 6px; vertical-align: top; }
.po-origen-concepto { width: 100%; }
.po-origen-fecha { white-space: nowrap; color: var(--po-ink-soft); font-size: .8rem; text-align: right; }
.po-origen-importe { white-space: nowrap; text-align: right; font-variant-numeric: tabular-nums; }
/* La referencia del documento (período, número externo) va debajo del concepto: es lo que permite
   encontrar el papel en una carpeta, pero no compite con el renglón. */
.po-origen-ref { display: block; font-size: .78rem; color: var(--po-ink-soft); }
/* Detalle: los documentos que componen un subtotal. Indentados, para que se lea que cuelgan de él. */
.po-origen-detalle > td:first-child { padding-left: 20px; }
.po-origen-detalle { color: var(--po-ink-soft); }
.po-origen-suma > td, .po-origen-resta > td { border-top: 1px solid var(--po-border); }
.po-origen-subtotal > td { border-top: 1px solid var(--po-border); font-weight: 600; }
.po-origen-resultado > td { border-top: 2px solid var(--po-ink); border-bottom: 2px solid var(--po-ink);
                            font-weight: 700; font-size: .95rem; }
.po-origen-info > td { color: var(--po-ink-soft); font-style: italic; }
/* La diferencia sin explicar se marca en ámbar: no es un error de cálculo, es algo para ir a mirar. */
.po-origen-diferencia > td { color: #b45309; }
.po-origen-notas { margin: 14px 0 0; padding-left: 18px; font-size: .82rem; color: var(--po-ink-soft); }
.po-origen-notas li + li { margin-top: 3px; }
.po-origen-fuentes { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px;
                     padding-top: 12px; border-top: 1px solid var(--po-border); }
.po-origen-fuentes-rotulo { font-size: .82rem; color: var(--po-ink-soft); }
/* Marca de "abre al lado". Va en el botón y no en cada renglón: repetida veinte veces en la columna
   de conceptos ensuciaría la cuenta, que es lo que se vino a leer. */
.po-origen-fuera { font-size: .7rem; opacity: .55; margin-left: 2px; }
.po-origen-pie { font-size: .78rem; color: var(--po-ink-soft); margin: 10px 0 0; }

/* ─── PANTALLA MEDIANA: tablet y notebook chica (30/08/2026) ────────────────────────────────────
   Gustavo abrió el catálogo de costos desde una tablet y todo entraba grande: el menú se comía 244px
   de los ~1280 disponibles, las filas venían con el aire de una pantalla de 1920, y la descripción
   del artículo («Bulón inox A2 M8 con tuerca y arandela») se partía en dos renglones, así que cada
   fila medía el doble y en la primera pantalla entraban seis artículos de 36.

   Entre el teléfono (860px, que ya estaba resuelto escondiendo columnas) y el desktop no había
   nada: **una tablet de 1280 recibía el layout de escritorio entero**. Este es ese escalón, y la
   pieza central es `html { font-size }`. Bootstrap y todo el CSS del producto están escritos en
   `rem`, así que bajar la raíz achica de una sola vez la letra, los `padding` de los botones, los
   `gap` y los altos de fila — de manera proporcional, sin desalinear nada. Lo que va aparte es lo
   que está escrito en `px` por buenas razones: el ancho del menú, el alto de la topbar y el aire
   de las tarjetas.

   Se elige 1400px como techo (y no 1280) para que la notebook de 1366 —el otro caso real— entre
   también. De 1401 para arriba no cambia una línea: el desktop es lo verificado y no se toca.

   ⚠️ NO se esconde ninguna columna acá, a diferencia del teléfono. En una tablet la grilla entra
   entera y quien la abre está trabajando, no dando un vistazo: lo que sobraba era el aire, no las
   columnas. Esconder datos en 1280px sería resolver un problema que no existe. */
@media (max-width: 1400px) {
    html { font-size: 15px; }

    :root {
        --po-sidebar-w: 212px;
        --po-topbar-h: 48px;
    }

    .po-content { padding: 16px 18px 48px; }
    .po-card { padding: 14px 16px; border-radius: 12px; }
    .po-card + .po-card { margin-top: 12px; }
    .po-page-head { gap: 10px; margin-bottom: 14px; }

    /* El menú es lo que más espacio devuelve: son píxeles que la grilla no tiene. */
    .po-sidebar-brand { padding: 14px 16px; font-size: 1.1rem; }
    .po-nav-scroll { padding: 10px 8px; }
    .po-navlink { padding: 8px 12px; gap: 10px; }
    .po-nav-grouptoggle { padding: 8px 12px; gap: 10px; }
    .po-nav-sep { padding: 10px 12px 5px; }
    .po-sidebar-foot { padding: 10px 14px; }
    .po-topbar { padding: 0 14px; gap: 10px; }

    /* Los KPI son el encabezado de la ficha, no su contenido: con la caja más angosta entran cinco
       en la fila en vez de tres y las etapas suben por encima del pliegue, que en una tablet
       apaisada son ~700px de alto y se llenan enseguida. */
    .po-kpis { grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }
    .po-kpi { padding: 8px 10px; }

    /* El aire lateral de la celda es lo que hace que la descripción del artículo entre en UN
       renglón; el alto de fila lo resuelve solo el `rem` de arriba. La regla iguala en
       especificidad a la de Bootstrap y gana por orden (app.css va después). */
    .table > :not(caption) > * > * { padding-left: .5rem; padding-right: .5rem; }

    /* El interlineado de 1,5 es de texto para leer de corrido; una grilla es para barrer con la
       vista. En las celdas que llegan a dos renglones —«Precio desde», que lleva la fecha y abajo
       quién la tocó— es lo que decide el alto de TODA la fila. Medido: 55 → 51px promedio, y la
       tabla del catálogo pasa de 2047 a 1882px. No toca los botones: Bootstrap les fija el suyo. */
    .table { line-height: 1.35; }

    /* ⚠️ Acá el ancho no lo pedían los datos, lo pedía el RÓTULO. Con `table-layout: auto` una
       cabecera que no puede partirse fija el piso de ancho de toda la columna: medido en el
       catálogo a 1280px, «EN PROYECTOS» se reservaba 118px para mostrar un «1», y «COSTO UNIT.» y
       «PRECIO DESDE» otro tanto — 130px que le faltaban a la descripción del artículo, que es la
       única columna cuyo contenido es realmente largo. El resultado era que el rótulo entraba en un
       renglón y CADA UNA de las 36 filas se partía en tres.
       Dejándolos envolver, el rótulo ocupa dos renglones UNA vez y las filas bajan a la mitad. El
       `nowrap` sigue vigente en desktop, donde el ancho sobra. */
    .po-sort-th { white-space: normal; }

    /* El SKU es monoespaciado y hay slugs de 40 caracteres (`VARILLA-DE-APORTE-TIG-INOX-…`): sin
       techo, uno solo le fija el ancho a la columna entera. Se achica y se le permite cortar, pero
       NO se trunca con puntos suspensivos — un SKU es la llave del artículo y a medias no sirve. */
    .po-sku { font-size: .68rem; overflow-wrap: anywhere; }
}

/* Tablet en vertical y notebooks de 1024: un escalón más. Sigue sin esconderse nada — la grilla
   empieza a deslizarse de costado recién en el tramo de teléfono. */
@media (max-width: 1200px) {
    html { font-size: 14px; }
    :root { --po-sidebar-w: 196px; }
    .po-content { padding: 14px 14px 44px; }
    .table > :not(caption) > * > * { padding-left: .45rem; padding-right: .45rem; }
}

/* ⚠️ Achicar la letra achica también el blanco donde se apoya el dedo, y una tablet se toca. En un
   dispositivo táctil los botones de acción de las grillas (el lápiz, el reloj del historial) vuelven
   a un alto cómodo: se recupera el aire vertical del botón sin devolverle el horizontal a la fila,
   que es lo que se vino a ganar. `pointer: coarse` deja fuera a la notebook de 1366, que se maneja
   con mouse y no lo necesita. */
@media (max-width: 1400px) and (pointer: coarse) {
    .btn-sm, .btn-group-sm > .btn { padding-top: .35rem; padding-bottom: .35rem; }
    .table .btn-sm { min-width: 2rem; }
}

/* ─── TELÉFONO ─────────────────────────────────────────────────────────────────────────────────
   Escrito el 18/08/2026, cuando quedó claro que la dirección iba a entrar desde el celular y no
   desde una notebook. Todo va dentro de media queries: el desktop ya está verificado y no se toca.

   El defecto que se veía primero era la TOPBAR. Es la única pieza que aparece en las 24 pantallas,
   y con una razón social larga ("TALLERES NAVALES DARSENA NORTE S A C I Y N TANDANOR SACI Y N")
   más el mail completo del usuario, la fila de 54px no entra en 390px: se partía en tres renglones
   que tapaban el título de la pantalla, y empujaba el botón de salir fuera del viewport —con scroll
   horizontal en TODA la página, no sólo en la barra—. El nombre se corta con puntos suspensivos:
   quién sos y en qué empresa estás ya lo sabe el que entró, y el botón de salir tiene que estar
   siempre al alcance del pulgar. */
@media (max-width: 860px) {
    .po-topbar { padding: 0 10px; gap: 8px; }
    .po-topbar-empresa { flex: 0 1 auto; min-width: 0; }
    .po-topbar-empresa > span { max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .po-topbar-empresa .bi, .po-topbar-logo { flex-shrink: 0; }
    .po-topbar-user { flex: 0 0 auto; gap: 8px; }
    .po-user-name { display: inline-block; max-width: 120px; overflow: hidden; text-overflow: ellipsis;
                    white-space: nowrap; vertical-align: middle; }

    /* Cinturón: `clip` en vez de `hidden` porque `hidden` crea un contenedor de scroll y le rompe
       el `position: sticky` a la topbar. Si algún día una tabla ancha se escapa de su envoltorio,
       la página no se desplaza de costado. */
    html, body { overflow-x: clip; }

    /* En desktop `.table-responsive` pasa a `overflow: clip` para que el `thead` pegajoso se pueda
       anclar al viewport real (ver la nota completa donde se define `.table-responsive thead th`).
       Acá, en cambio, las tablas SÍ necesitan deslizar de costado (ver la segunda pasada de TELÉFONO,
       más abajo: el reporte de costos lo hace a propósito). Reactivar `overflow-x: auto` reintroduce
       el contexto de scroll, así que en móvil el `thead` deja de pegarse — es la contracara esperada:
       un contenedor no puede deslizar de costado Y anclar un sticky vertical al mismo tiempo. */
    .table-responsive { overflow-x: auto; overflow-y: auto; }

    /* Un campo con letra menor a 16px hace que iOS haga zoom solo al tocarlo, y la pantalla queda
       en otra escala hasta que el usuario la achica con dos dedos. Pasa en cada buscador y en el
       chat del asistente. */
    .form-control, .form-select, .form-control-sm, .form-select-sm, textarea.form-control { font-size: 16px; }
}

/* Teléfonos angostos: el nombre de la empresa cede el lugar. Queda el logo (o el ícono), que es
   suficiente para saber dónde estás parado. */
@media (max-width: 430px) {
    .po-topbar-empresa > span { display: none; }
    .po-user-name { max-width: 96px; }
}

/* El menú lateral se abre encima del contenido, así que necesita un fondo que diga "esto está
   abierto" y que se pueda tocar para cerrarlo —en un teléfono no hay lugar adonde ir con el dedo—.
   z-index 55: arriba del contenido, abajo del propio sidebar (60), lejos del modal (2000). */
.po-nav-backdrop { display: none; position: fixed; inset: 0; background: rgba(2,6,23,.5); z-index: 55; }
.po-nav-open .po-nav-backdrop { display: block; }

/* ─── TELÉFONO, segunda pasada: las filas de listado se comprimían en vez de deslizar ───────────
   Con la topbar y el menú ya resueltos, quedaba el defecto que se ve en el Panel, que es el
   aterrizaje. Las tablas viven dentro de .table-responsive pero sin ancho mínimo, así que en los
   ~300px libres del celular el navegador las achica hasta que entran: "ADIF - Cruce Alto Nivel RP
   N 24 - General Rodriguez" se partía en cinco renglones y la razón social del cliente en otros
   seis, con la fila ocupando media pantalla.

   Quien entra desde el teléfono es la dirección, que mira y no opera: lo que vino a buscar —qué
   obra, en qué estado y cuánta plata— tiene que entrar en la primera pantalla sin arrastrar el
   dedo. Por eso las columnas secundarias se esconden en vez de apretarse. Nada de lo escondido
   queda inaccesible: Tipo, Responsable y Unidades siguen en la ficha de la obra, a un toque de la
   misma fila. */
@media (max-width: 860px) {
    .po-col-movil-oculta { display: none; }

    /* La razón social es la que más se dispara. Se lee entera en la ficha del cliente; en un
       listado alcanza con reconocer de quién es la obra. El texto completo queda en el title. */
    .po-col-cliente-clamp { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
    .po-col-proyecto-clamp { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }

    /* ── Reporte de costos: acá se desliza, no se esconde todo ──
       Es un reporte, no una pantalla de vistazo, así que el scroll horizontal está bien. Lo que
       estaba mal era que el importe de Presupuesto caía JUSTO sobre el borde del recorte y se leía
       "ARS 120.013.53…" — un número cortado a la mitad en la pantalla de la plata es lo peor que
       puede pasar. Dándole piso de ancho a la columna del proyecto, Presupuesto entra entero en la
       primera vista y lo que sigue se ve deslizando, siempre con celdas completas.
       Estado sale de la vista de teléfono junto con Real y % ejec.: es el único reporte donde la
       pregunta es cuánta plata, y el estado ya se lee en el Panel y en el listado de proyectos. */
    table.po-tabla-costos th:first-child,
    table.po-tabla-costos td:first-child { min-width: 150px; }

    /* El chevron de expandir tiene que quedar al lado del nombre y no arriba: el nombre pasa a ser
       una caja de tres renglones (el clamp), que es de bloque, así que el ícono lo rodea flotando. */
    .po-tabla-costos .po-rep-chevron,
    .po-tabla-costos .po-rep-chevron-spacer { float: left; margin-right: 5px; }
}

/* El dato de una columna que se esconde no se pierde: baja adentro de la celda del proyecto, que
   en teléfono es la única que queda ancha. La fila pasa a leerse como una ficha —nombre, cliente,
   estado— en vez de como una grilla apretada. */
.po-solo-movil { display: none; }
@media (max-width: 860px) {
    .po-solo-movil { display: block; margin-top: 4px; }

    /* El ancho real de una tabla no es el de la pantalla sino el de la tarjeta que la contiene:
       con los 20px de padding de la .po-card por lado, en un teléfono de 390px quedan 313. Achicar
       el aire lateral de las celdas es lo que hace entrar la columna de los importes sin cortarla. */
    .table > :not(caption) > * > * { padding-left: .4rem; padding-right: .4rem; }
}

/* La descripción del proyecto se escribe en un textarea, con renglones en blanco entre párrafos.
   HTML los colapsa, así que un texto de cuatro párrafos llegaba a la pantalla como un ladrillo.
   `pre-line` respeta los saltos que puso quien escribió y sigue reacomodando el texto al ancho
   —a diferencia de `pre`, que lo dejaría con scroll lateral en el teléfono—. */
.po-texto-parrafos { white-space: pre-line; }

/* Aviso de que el catálogo se movió y los proyectos quedaron atrás. Va en ámbar y no en rojo: no
   es un error, es trabajo pendiente — y quien lo lee es quien acaba de cambiar el precio. */
.po-impacto-aviso { border-left: 4px solid #f59e0b; }

/* ─── Panel desplegable dentro de una card (2026-09-11) ───────────────────────────────────────────
   Para una acción que necesita confirmarse mostrando datos —«guardar como componente» muestra la
   receta antes de crearla—. No es un modal a propósito: lo que se confirma es lo que está en la
   misma pantalla, y taparlo con un diálogo obliga a recordar de memoria lo que se acaba de ver. */
.po-inline-panel {
    background: var(--po-surface-2);
    border: 1px solid var(--po-border);
    border-radius: 12px;
    padding: 14px 16px;
}
.po-inline-panel h3 { margin-bottom: 6px; }
.po-inline-panel .table { background: var(--po-surface); }
