@import url('https://fonts.googleapis.com/css2?family=Figtree:wght@400;500;600;700&family=Literata:opsz,wght@7..72,500;7..72,600&family=IBM+Plex+Mono:wght@400;500;600&display=swap');

/* Diseño del prototipo comercial (Fase 1, 2026-10): tokens, letras y colores
   de ~/Desktop/StayAccounts/prototipo-stayaccounts.html. Tres capas, igual que
   antes, porque hay que soportar "sigue al sistema" Y el interruptor manual:
   la base de :root son los valores CLAROS; el media query cubre "sistema en
   oscuro, sin elección explícita" (con :not([data-theme="light"]) para que
   elegir CLARO le gane al sistema); y [data-theme="dark"] cubre la elección
   explícita de OSCURO. El panel guarda el tema en localStorage('tema') y lo
   aplica en data-theme desde partials/head.ejs — ese mecanismo no cambió.

   Los nombres viejos del panel (--ink, --paper, --line, --ok-fg, --sidebar…)
   siguen existiendo como ALIAS de los tokens nuevos, definidos solo en la
   base: así las ~250 vistas (y sus style="" en línea) heredan el aspecto
   nuevo sin tocarlas una por una. Los alias que apuntan a otro token se
   resuelven en :root, donde también se redefinen los tokens en los bloques
   oscuros, así que siguen al tema correcto.

   Gráficos: serie 1 (ganancia) pasa del oliva viejo al verde azulado de marca;
   el resto de la paleta categórica y las reglas de estado (--ok/--bad/--warn
   nunca como marca de gráfico) se conservan. */
:root {
  /* tokens del prototipo */
  --bg: #F2F6F5; --surface: #FFFFFF; --surface-2: #E8F1EF; --fg: #10292A; --fg-muted: #4B6665; --border: #CCDDDA;
  --primary: #0F3D3E; --on-primary: #FFFFFF; --accent: #1F6F6B; --accent-soft: #D5EAE6;
  --amber: #E39B2D; --amber-soft: #FBEBCF; --on-amber: #5B3704;
  --good: #1B7A47; --good-soft: #DAF0E3; --warn: #8F5A00; --warn-soft: #FBEACB; --bad: #B3321F; --bad-soft: #F8DCD6;
  --side-bg: #0F3D3E; --side-fg: #CDE3E0; --side-hover: #175050; --side-active: #E8F1EF; --side-active-fg: #0F3D3E; --side-brand: #FFFFFF;
  --banner-bg: #E39B2D; --banner-fg: #2E1C02; --focus: #C47A0C; --scrim: rgba(8,26,27,.55);
  --shadow: 0 1px 2px rgba(15,61,62,.10), 0 4px 14px rgba(15,61,62,.07);
  --font-display: "Literata", Georgia, "Times New Roman", serif;
  --font-ui: "Figtree", "Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, Menlo, Consolas, monospace;
  --r: 10px; --sidebar-w: 250px;
  --fg-faint: #5F7776; --surface-3: #F2F7F6; --border-soft: #E1ECEA; --warn-line: #EBD3A0; --side-dim: #8FB3B0; --side-line: rgba(255,255,255,.10);
  --side-wash: rgba(255,255,255,.07); --side-accent: #7FD3CB; --topbar-bg: rgba(255,255,255,.94);
  color-scheme: light;

  /* paleta de gráficos (ver validador de la skill de dataviz) */
  --serie-1: #1F6F6B; --serie-2: #2a78d6; --serie-3: #eb6834; --serie-4: #1baf7a;
  --serie-5: #eda100; --serie-6: #e87ba4; --serie-7: #4a3aa7; --serie-8: #e34948;
  --gr-pos: var(--serie-1); --gr-neg: #a8336a; --gr-mov: #8f8f8c;
  --gr-cero: var(--fg-faint); --gr-texto: var(--fg-muted);

  /* alias hacia atrás: nombres viejos que usan el CSS y las vistas */
  --ink: var(--fg); --ink-soft: var(--fg-muted); --ink-faint: var(--fg-faint);
  --paper: var(--bg); --surface-sunk: var(--surface-3);
  --line: var(--border); --line-soft: var(--border-soft);
  --accent-ink: var(--on-primary); --steel: var(--fg-faint);
  --ok-bg: var(--good-soft); --ok-fg: var(--good);
  --warn-bg: var(--warn-soft); --warn-fg: var(--warn); --warn-border: var(--warn-line);
  --bad-bg: var(--bad-soft); --bad-fg: var(--bad);
  --sidebar: var(--side-bg); --sidebar-line: var(--side-line); --sidebar-text: var(--side-fg);
  --sidebar-text-dim: var(--side-dim); --sidebar-accent: var(--side-accent);
  --sidebar-activo: var(--side-wash); --sidebar-registrar: var(--side-bg);
  --dim: var(--fg-muted); --card: var(--surface); --navy: var(--side-bg); --green: var(--good);
  --warn-text: var(--warn);
  /* nombres que algunas vistas usan sin que existieran (caían a su valor de repuesto) */
  --rojo: var(--bad); --linea: var(--border);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0A191A; --surface: #112627; --surface-2: #173433; --fg: #E2EEEC; --fg-muted: #9DB8B5; --border: #2A4A4C;
    --primary: #3AA79F; --on-primary: #04201F; --accent: #5CC4BB; --accent-soft: #1B4443;
    --amber: #F0B04F; --amber-soft: #3A2A0C; --on-amber: #F6D9A4;
    --good: #5AC98A; --good-soft: #143626; --warn: #F0B04F; --warn-soft: #3A2A0C; --bad: #FF907D; --bad-soft: #401C16;
    --side-bg: #071213; --side-fg: #B5D0CD; --side-hover: #112B2C; --side-active: #173433; --side-active-fg: #E2EEEC; --side-brand: #E8F4F2;
    --banner-bg: #F0B04F; --banner-fg: #2E1C02; --focus: #F0B04F; --scrim: rgba(0,0,0,.62);
    --shadow: 0 1px 2px rgba(0,0,0,.4);
    --fg-faint: #86A3A0; --surface-3: #0F2223; --border-soft: #1F3C3D; --warn-line: #5a4620; --side-dim: #7F9E9B; --side-line: rgba(255,255,255,.08);
    --side-wash: rgba(255,255,255,.06); --side-accent: #5CC4BB; --topbar-bg: rgba(10,25,26,.92);
    --serie-1: #5CC4BB; --serie-2: #3987e5; --serie-3: #d95926; --serie-4: #199e70;
    --serie-5: #c98500; --serie-6: #d55181; --serie-7: #9085e9; --serie-8: #e66767;
    --gr-neg: #dc5f92; --gr-mov: #706b5c;
    color-scheme: dark;
  }
}
:root[data-theme="dark"] {
  --bg: #0A191A; --surface: #112627; --surface-2: #173433; --fg: #E2EEEC; --fg-muted: #9DB8B5; --border: #2A4A4C;
  --primary: #3AA79F; --on-primary: #04201F; --accent: #5CC4BB; --accent-soft: #1B4443;
  --amber: #F0B04F; --amber-soft: #3A2A0C; --on-amber: #F6D9A4;
  --good: #5AC98A; --good-soft: #143626; --warn: #F0B04F; --warn-soft: #3A2A0C; --bad: #FF907D; --bad-soft: #401C16;
  --side-bg: #071213; --side-fg: #B5D0CD; --side-hover: #112B2C; --side-active: #173433; --side-active-fg: #E2EEEC; --side-brand: #E8F4F2;
  --banner-bg: #F0B04F; --banner-fg: #2E1C02; --focus: #F0B04F; --scrim: rgba(0,0,0,.62);
  --shadow: 0 1px 2px rgba(0,0,0,.4);
  --fg-faint: #86A3A0; --surface-3: #0F2223; --border-soft: #1F3C3D; --warn-line: #5a4620; --side-dim: #7F9E9B; --side-line: rgba(255,255,255,.08);
  --side-wash: rgba(255,255,255,.06); --side-accent: #5CC4BB; --topbar-bg: rgba(10,25,26,.92);
  --serie-1: #5CC4BB; --serie-2: #3987e5; --serie-3: #d95926; --serie-4: #199e70;
  --serie-5: #c98500; --serie-6: #d55181; --serie-7: #9085e9; --serie-8: #e66767;
  --gr-neg: #dc5f92; --gr-mov: #706b5c;
  color-scheme: dark;
}


* { box-sizing: border-box; }
/* Bloque 5 — accesibilidad transversal: foco visible en todo lo interactivo
   (antes solo input/select/textarea lo tenían), y respeto por
   prefers-reduced-motion para quien lo pida a nivel de sistema. */
a:focus-visible, button:focus-visible, .nav-link:focus-visible, summary:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001ms !important; animation-iteration-count: 1 !important; transition-duration: 0.001ms !important; }
}
@media print {
  thead { display: table-header-group; }
  .tabla-scroll table thead th { position: static; }
  /* Sin esto, una tabla de más de 70vh se imprimiría recortada dentro de
     su propio recuadro con scroll — al imprimir tiene que verse completa,
     repartida en tantas páginas como haga falta. */
  .tabla-scroll { max-height: none; overflow: visible; }
}
body {
  margin: 0; background: var(--paper); color: var(--ink);
  font-family: var(--font-ui);
  font-size: 14px; line-height: 1.45;
}
h1, h2, h3 { font-family: var(--font-display); font-weight: 600; text-wrap: balance; margin: 0 0 1rem; color: var(--ink); }
a { color: inherit; }

/* ---- shell: sidebar y topbar fijos, main desplazado — así ninguna de las
   19 vistas necesita envolver <main> en un contenedor nuevo (ver nav.ejs). ---- */
aside.sidebar {
  position: fixed; top: 0; left: 0; bottom: 0; width: var(--sidebar-w);
  background: var(--sidebar); color: var(--sidebar-text); border-right: 1px solid var(--sidebar-line);
  display: flex; flex-direction: column; overflow-y: auto; z-index: 10;
}
.topbar {
  position: fixed; top: 0; left: var(--sidebar-w); right: 0; z-index: 5;
  background: var(--topbar-bg); backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--line);
  padding: 0.85rem 2rem; display: flex; align-items: center; justify-content: space-between; gap: 1rem;
}
/* --ink-soft y sin mayúsculas: con --ink-faint en letra de 11 px la ruta no
   llegaba al contraste mínimo (2,6:1 en tema claro; revisión de UX). */
.topbar .crumb { font-size: 0.82rem; color: var(--ink-soft); font-weight: 600; }
.topbar .salir { color: var(--bad-fg); text-decoration: none; font-size: 0.85rem; font-weight: 600; }
.theme-toggle {
  border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft);
  border-radius: 7px; width: 1.9rem; height: 1.9rem; font-size: 0.95rem; line-height: 1;
  cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.theme-toggle:hover { color: var(--ink); border-color: var(--steel); }
.theme-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* max-width subido de 1180px a 1600px (2026-09-10): el tope viejo venía del
   rediseño de la Fase A pensando en texto corrido, pero deja tablas de
   muchas columnas (ej. Resolution Center, P&G detallado) angostas con
   espacio en blanco de sobra a la derecha en cualquier monitor >1500px —
   el usuario lo notó en una tabla real. Las vistas de texto/formulario no
   se ven afectadas: sus tarjetas y grillas ya se ajustan a su contenido,
   no a `main` completo. */
main { margin-left: var(--sidebar-w); padding: 4.4rem 2rem 4rem; max-width: 1600px; }

.hamburger { display: none; }
.sidebar-backdrop { display: none; }

/* Bloque 5.3 (2026-09-07) — antes esto ponía el sidebar en `position:
   static`, que en un aside con 30+ enlaces significa "empujar toda la
   lista completa arriba del contenido" en vez de un menú móvil de
   verdad. Ahora el sidebar se queda `fixed` pero fuera de pantalla
   (`translateX(-100%)`) y un botón ☰ nuevo en el topbar lo desliza —
   mismo patrón sin dependencias que ya usa el ⌘K (toggle de clase +
   `hidden`, sin librería). */
@media (max-width: 900px) {
  aside.sidebar {
    transform: translateX(-100%);
    transition: transform 0.2s ease;
    box-shadow: none;
  }
  body.sidebar-open aside.sidebar { transform: translateX(0); box-shadow: 0 0 0 100vmax rgba(0,0,0,0); }
  .topbar { left: 0; }
  main { margin-left: 0; padding-top: 1.6rem; }
  .hamburger {
    display: inline-flex; align-items: center; justify-content: center;
    border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft);
    border-radius: 7px; width: 2.1rem; height: 2.1rem; font-size: 1.1rem; line-height: 1;
    cursor: pointer; flex: none;
  }
  .sidebar-backdrop {
    display: none; position: fixed; inset: 0; background: rgba(17,19,14,0.45); z-index: 9;
  }
  body.sidebar-open .sidebar-backdrop { display: block; }
}

/* ---- marca ---- */
.brand { padding: 1.5rem 1.4rem 1.2rem; border-bottom: 1px solid var(--sidebar-line); }
.brand-mark { display: flex; align-items: center; gap: 0.7rem; }
.brand-mark svg { width: 32px; height: 32px; flex: none; }
/* Logo real subido en Configuración → Logo del panel: sobre chip blanco, que
   la barra es oscura y casi todos los logos son de tinta oscura.
   El usuario lo seguía viendo pequeño (2026-09-16): con un tope de alto de
   64px, un logo cuadrado se quedaba en 64px de ancho aunque la barra tenga
   213px libres — solo los logos muy apaisados llegaban al tope de ancho.
   Ahora ocupa TODO el ancho disponible y el alto es el que mande el tope;
   `object-fit: contain` centra el dibujo sin deformarlo, y como el chip es
   blanco el sobrante no se nota. Sirve igual para un logo cuadrado, uno
   apaisado o uno vertical. */
.brand-logo {
  width: 100%; max-height: 120px; object-fit: contain;
  background: #fff; border-radius: 0.5rem; padding: 8px 10px; display: block;
}
.brand-word { font-family: var(--font-display); font-weight: 600; font-size: 1rem; letter-spacing: 0.02em; line-height: 1.05; color: var(--sidebar-text); }
.brand-word span { display: block; color: var(--sidebar-text-dim); font-family: var(--font-display); font-weight: 600; }
.brand-sub { margin-top: 0.6rem; font-size: 0.7rem; color: var(--sidebar-text-dim); letter-spacing: 0.04em; text-transform: uppercase; }

/* ---- nav lateral ---- */
nav.groups { padding: 1rem 0.8rem 1.2rem; flex: 1; }
.nav-group { margin-bottom: 1.3rem; }
.nav-group-label { font-size: 0.66rem; letter-spacing: 0.09em; text-transform: uppercase; color: var(--sidebar-text-dim); padding: 0 0.6rem 0.4rem; font-weight: 600; }
.nav-link {
  display: flex; align-items: center; gap: 0.6rem; padding: 0.48rem 0.6rem; border-radius: 7px;
  color: var(--sidebar-text); text-decoration: none; font-size: 0.85rem; font-weight: 500;
  border-left: 3px solid transparent;
}
.nav-link:hover { background: var(--sidebar-activo); }
.nav-link.active { background: var(--sidebar-activo); border-left-color: var(--sidebar-accent); color: var(--sidebar-text); font-weight: 600; }
.nav-link .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--sidebar-text-dim); flex: none; }
.nav-link.active .dot { background: var(--sidebar-accent); }
.nav-badge { margin-left: auto; font-family: var(--font-mono); font-size: 0.66rem; padding: 0.05rem 0.4rem; border-radius: 999px; background: var(--bad-bg); color: var(--bad-fg); }

.sidebar-foot { padding: 1rem 1.4rem 1.3rem; border-top: 1px solid var(--sidebar-line); font-size: 0.74rem; color: var(--sidebar-text-dim); }
.sidebar-foot strong { color: var(--sidebar-text); display: block; font-size: 0.82rem; margin-bottom: 0.15rem; }
.sidebar-foot a { color: var(--sidebar-accent); text-decoration: none; }

/* ---- tarjetas / KPI (clases ya usadas por las vistas: card, kpi, label, valor, grid) ---- */
.card { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 1.1rem 1.25rem; box-shadow: var(--shadow); margin-bottom: 1rem; }
/* min(170px, 100%) en vez de 170px a secas: una pista de minmax(170px,·) no
   puede encogerse por debajo de 170px, así que si el espacio disponible fuera
   menor, la rejilla se saldría de su tarjeta. Con min() cede.

   PREVENTIVO, no el arreglo de un defecto observado: a 500px (lo más angosto
   que se pudo medir — Chrome sin ventana no baja de ahí) la página NO se
   desborda, ni con esta regla ni sin ella. Queda porque no puede empeorar
   nada (solo relaja un mínimo) y porque un teléfono real de 360-390px sí
   entra en el rango donde importaría. Sin verificar por debajo de 500px.
   (2026-09-22) */
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(170px, 100%), 1fr)); gap: 0.9rem; }
.kpi { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: 0.95rem 1.05rem; box-shadow: var(--shadow); }
.grid.card > .kpi, .card > .grid > .kpi { box-shadow: none; }
.kpi .label { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-faint); font-weight: 600; margin-bottom: 0.4rem; }
.kpi .valor { font-family: var(--font-mono); font-size: 1.3rem; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }

/* ---- estados / pills (clases ya usadas: estado, ok, revisar, anulado, neutral, falta) ---- */
/* SIN white-space:nowrap acá (hallazgo 1, verificador Opus sobre 59ffbe7):
   esta regla es GLOBAL (185 usos fuera de Reportes) y un nowrap acá impide
   partir una pastilla larga en celular — personal/aseo.ejs, personal/detalle.ejs,
   reportes/comparacion.ejs, liquidez, equilibrio e ingresos-resoluciones,
   entre otras. Medido: scrollWidth 500px (origin/main, sin nowrap) → 749px
   (con nowrap) a 358px de ancho. Donde SÍ hace falta nowrap (una pastilla de
   "Cuadre" en una columna angosta de tabla de Reportes) va scopeado más abajo
   (`table.cifras .estado`), nunca acá. */
.estado { display: inline-flex; align-items: center; gap: 0.3rem; font-size: 0.7rem; font-weight: 600; padding: 0.2rem 0.55rem; border-radius: 999px; background: var(--surface-sunk); color: var(--ink-soft); border: 1px solid var(--line); margin-top: 0.4rem; }
.estado.ok { background: var(--ok-bg); color: var(--ok-fg); border-color: transparent; }
.estado.revisar { background: var(--warn-bg); color: var(--warn-fg); border-color: transparent; }
.estado.anulado { background: var(--bad-bg); color: var(--bad-fg); border-color: transparent; margin: 0; }
.estado.neutral { background: var(--surface-sunk); color: var(--ink-soft); }
/* Mismo rojo que "anulado" pero con su propio nombre: acá no significa que
   algo se anuló, significa que un dato obligatorio (identidad del huésped
   para facturar) todavía no existe — src/routes/facturacion.ts. */
.estado.falta { background: var(--bad-bg); color: var(--bad-fg); border-color: transparent; margin: 0; }

/* ---- tablas ---- */
/* overflow-x: auto SOLO (sin overflow-y) rompe position:sticky: la
   especificación CSS obliga a que el eje sin declarar pase de "visible" a
   "auto" también, así que .tabla-scroll se vuelve un contenedor de scroll
   vertical — pero uno que nunca se desplaza de verdad (crece con su
   contenido), así que "sticky" queda pegado a un scroll que no ocurre y
   nunca se ve fijo. La solución real: declarar overflow-y explícito y
   darle un alto máximo, para que el propio recuadro sea el que desplaza
   (como "inmovilizar filas" en una hoja de cálculo) y sticky se pegue al
   techo de ESE recuadro, no de la página. */
.tabla-scroll { overflow: auto; max-height: 70vh; }
table { width: 100%; border-collapse: collapse; font-size: 0.87rem; }
th, td { text-align: left; padding: 0.55rem 0.6rem; border-bottom: 1px solid var(--line-soft); }
th { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-faint); font-weight: 600; border-bottom: 1px solid var(--line); }
tbody tr:hover { background: var(--surface-sunk); }

/* "Ir a la fila" (buscador de remisiones de /lavanderia, 2026-09-26): la
   fila con el id del ancla (tr[id="fila-envio-..."]) se resalta al llegar
   por el enlace — sin esto, una tabla larga deja al envío encontrado
   perdido entre docenas de filas iguales, aunque el navegador ya haya
   hecho scroll hasta ahí. */
tbody tr:target { background: var(--warn-bg); outline: 2px solid var(--warn-border); outline-offset: -2px; }

/* Revisión de Opus (verificador, 2026-09-26): sin esto, "Ir a la fila" deja
   la fila encontrada TAPADA por la barra sticky de arriba — a 360px la
   barra ocupa unos 67px, así que el navegador para el scroll justo con la
   fila debajo de ella. scroll-margin-top le da a esa fila el mismo colchón
   que la barra ocupa (con margen de sobra), para que el ancla pare debajo. */
tbody tr[id^="fila-envio-"] { scroll-margin-top: 5rem; }

/* Gastos fijos: tras «Ya lo pagué»/«Deshacer» la página vuelve a #fijo-<id>;
   el colchón evita que la fila quede bajo la barra de arriba y el resaltado
   breve ayuda a ubicarla (tbody tr:target ya la pinta; acá solo se anima). */
tbody tr[id^="fijo-"] { scroll-margin-top: 5rem; }
@media (prefers-reduced-motion: no-preference) {
  tbody tr[id^="fijo-"]:target { animation: fijo-destino 2.5s ease-out; }
}
@keyframes fijo-destino { from { background: var(--warn-bg); } to { background: transparent; } }

/* Encabezado fijo al bajar por una tabla larga (P&G, Gastos, Reservas...):
   sticky respecto al recuadro con scroll propio de arriba, no del topbar. */
.tabla-scroll table thead th {
  position: sticky; top: 0; background: var(--surface); z-index: 2;
}

/* Trunca a una sola línea con "…" en vez de partir en muchas — una celda con
   texto largo (razones, evidencia de Conciliación) no debe estirar la fila
   entera. El texto completo va en el atributo title (mismo string, nunca se
   arma dos veces distinto) para verlo al pasar el mouse. */
.truncar { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Precios (revisión 2026-09-26 #6, medido a 358px de ancho): un monto en
   dólares ("USD 29,98") partía en 2 líneas ("USD" / "29,98") en la lista de
   "Le cuesta por noche", la tabla de duración y la tarjeta de Mínimo para
   PriceLabs — nowrap lo deja en una sola línea, igual que .truncar arriba
   pero sin recortar (estos montos ya son cortos, nunca hace falta "…"). */
.monto { white-space: nowrap; }

/* "Mínimo por canal" del detalle de Precios (misma revisión): 7 columnas,
   con la letra chica del escenario bajo el monto — a 358px, dentro de su
   .tabla-scroll (overflow:auto, ver arriba), el navegador la angostaba
   hasta dejar "Descuentos que aplican" de 1 carácter en vez de desplazarla
   de lado. min-width fuerza el ancho real de la tabla para que
   .tabla-scroll la desplace lateralmente (como ya hace con .tabla-fija-1) —
   la PÁGINA en sí sigue sin scroll horizontal, el contenedor es el que
   se desplaza. 680px: mide holgado a 358px con el padding de .card, sin
   imponer un mínimo exagerado en escritorio. */
.tabla-minimo-canal { min-width: 680px; }

/* Portafolio de Precios (revisión Opus 2026-09-25): filas compactas en
   laptop y celular — antes una sola celda imprimía un párrafo entero del
   motor, repetido por fila. Los motivos largos ya no van sueltos en la
   fila: los chips (.estado, .alerta-fila-chips) son pastillas cortas, y el
   motivo completo va en el atributo title o solo en la página del
   apartamento — nunca más de ~2 líneas por celda acá. */
.tabla-compacta th, .tabla-compacta td { padding: 0.45rem 0.55rem; font-size: 0.82rem; vertical-align: top; }
.tabla-compacta .alerta-fila-chips { margin-top: 0.3rem; }
@media (max-width: 640px) {
  .tabla-compacta th, .tabla-compacta td { padding: 0.4rem 0.45rem; font-size: 0.78rem; }
}

/* ---- formularios ---- */
form label { display: block; font-size: 0.72rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-soft); margin: 0.7rem 0 0.32rem; }
input, select, textarea {
  width: 100%; padding: 0.58rem 0.7rem; border: 1px solid var(--line); border-radius: 8px; font-size: 0.9rem;
  font-family: inherit; background: var(--surface); color: var(--ink);
}
input:focus, select:focus, textarea:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent); }
/* Sin esto, un checkbox/radio suelto en una celda de tabla (candidatos de
   conciliación, selección en lote) heredaba el mismo padding/borde/ancho
   100% que un <input type=text> — se veía como una casilla gigante con
   borde en vez de un checkbox normal (hallazgo Opus 2026-09-10). */
input[type="checkbox"], input[type="radio"] { width: auto; padding: 0; border: none; }
button.btn { margin-top: 1.1rem; padding: 0.66rem 1.2rem; border: none; border-radius: 8px; background: var(--accent); color: var(--accent-ink); font-weight: 700; cursor: pointer; font-size: 0.9rem; font-family: var(--font-ui); }
button.btn.green { background: var(--ok-fg); }
button.btn:hover { opacity: 0.92; }
.btn-link { display: inline-block; padding: 0.66rem 1.2rem; border-radius: 8px; background: var(--accent); color: var(--accent-ink); text-decoration: none; font-weight: 700; font-size: 0.9rem; font-family: var(--font-ui); }
.btn-link.secondary { margin: 1.1rem 0 0 0.5rem; background: transparent; color: var(--ink); border: 1px solid var(--line); }
.btn-text { border: 0; background: none; color: var(--accent); padding: 0; margin-left: 0.55rem; cursor: pointer; font: inherit; text-decoration: underline; }
.btn-text.danger { color: var(--bad-fg); }
.btn-text:disabled { color: var(--ink-faint); cursor: default; text-decoration: none; }
.check-label { display: flex; align-items: center; gap: 0.4rem; text-transform: none; font-size: 0.9rem; font-weight: 400; }
.check-label input { width: auto; }
/* El calendario nativo muestra el orden que trae el navegador/SO de quien
   visita (Chrome en Windows con locale en-US, por ejemplo, muestra
   mes/día/año) — HTML no puede forzar un orden distinto ahí. El valor que
   se guarda siempre es correcto (ISO por debajo), pero para que nadie lo
   LEA mal a simple vista, cada campo de fecha lleva esta aclaración
   (inyectada por JS más abajo, para no tocar 15 plantillas a mano) —
   detectando el orden real de CADA navegador con Intl, no un "día/mes/año"
   fijo: un fijo queda al revés justo en el navegador que sí muestra
   mes/día/año, que es el caso real que lo delató (2026-09-13). */
.hint-fecha { display: block; margin-top: 0.2rem; font-size: 0.72rem; color: var(--ink-faint); text-transform: none; }

/* Selector de mes para datos: dos listas (Mes | Año) en una fila, con el mismo
   aspecto de cualquier select del panel (ver partials/selector-mes-dato.ejs).
   Se reduce a una sola columna de ancho completo solo si no caben; el piso de
   min-width evita que se desborde dentro de una celda de tabla. */
.selector-mes-dato { display: flex; gap: 0.5rem; align-items: stretch; max-width: 22rem; }
.selector-mes-dato select {
  width: auto; min-width: 0; min-height: 2.75rem;
  text-transform: none; font-weight: 400; letter-spacing: normal;
}
.selector-mes-dato .selector-mes-dato-mes { flex: 3 1 0; min-width: 6.2rem; }
.selector-mes-dato .selector-mes-dato-anio { flex: 2 1 0; min-width: 4.8rem; }

/* Eco del monto tecleado, con puntos de miles (ver partials/head.ejs,
   data-eco-monto). Va en los campos de plata que aceptan decimales —
   tesorería, abonos, compromisos— donde NO se puede usar data-miles porque
   ese borra el punto decimal y un saldo en dólares perdería los centavos.
   Es solo lectura: el valor que viaja al servidor nunca se toca. */
.hint-monto { display: block; margin-top: 0.2rem; font-size: 0.78rem; font-weight: 600; color: var(--ink-soft); text-transform: none; font-variant-numeric: tabular-nums; }

/* Aclaración corta bajo un campo — mismo nombre y misma regla que
   partials/head-rapido.ejs / head-movil.ejs (esas dos pantallas no incluyen
   este stylesheet, así que la duplican inline; acá queda disponible para el
   resto de vistas de escritorio, ver gasto.ejs / gasto-editar.ejs). */
.file-hint { display: block; margin-top: 0.35rem; color: var(--ink-faint); font-size: 0.78rem; }

/* ---- select buscable (progressive enhancement, ver partials/select-buscable.ejs) ---- */
.select-buscable-wrap { position: relative; }
.select-buscable-lista {
  position: absolute; z-index: 20; top: calc(100% + 2px); left: 0; right: 0; max-height: 220px;
  overflow-y: auto; background: var(--surface); border: 1px solid var(--line); border-radius: 8px;
  box-shadow: var(--shadow);
}
.select-buscable-item { padding: 0.5rem 0.7rem; font-size: 0.88rem; cursor: pointer; }
.select-buscable-item:hover, .select-buscable-item.activo { background: var(--accent); color: var(--accent-ink); }
.select-buscable-vacio { padding: 0.5rem 0.7rem; font-size: 0.85rem; color: var(--ink-faint); }

/* ---- mensajes / avisos ---- */
.msg { padding: 0.7rem 0.95rem; border-radius: 10px; margin-bottom: 1rem; font-size: 0.86rem; background: var(--surface-sunk); color: var(--ink-soft); }
.msg.ok { background: var(--ok-bg); color: var(--ok-fg); }
.msg.error { background: var(--bad-bg); color: var(--bad-fg); }
/* Ni éxito ni error: la escritura SÍ ocurrió (ej. una reserva quedó
   vinculada), pero hay algo que el administrador tiene que revisar a mano — mismo
   amarillo que .estado.revisar. src/routes/facturacion.ts es el primer uso. */
.msg.revisar { background: var(--warn-bg); color: var(--warn-fg); }
.aviso {
  display: flex; gap: 0.7rem; align-items: flex-start; background: var(--warn-bg); color: var(--warn-fg);
  border: 1px solid var(--warn-border); border-radius: 10px; padding: 0.7rem 0.95rem; font-size: 0.82rem; margin-bottom: 1.3rem;
}
.aviso .ico { flex: none; font-family: var(--font-mono); font-weight: 700; }

/* ---- pestañas de reportes (heredado; tabs-reportes.ejs ahora es un breadcrumb, no una barra completa) ---- */
.tabs { display: flex; gap: 0.9rem; flex-wrap: wrap; margin: 0 0 1rem; }
.tabs a { color: var(--ink-soft); text-decoration: none; font-size: 0.9rem; }
.tabs a:hover, .tabs a.active { color: var(--ink); }
.tabs a.active { font-weight: 700; }
.crumb-reportes { font-size: 0.78rem; color: var(--ink-faint); margin: -0.6rem 0 1.1rem; }
.crumb-reportes strong { color: var(--ink-soft); font-weight: 600; }

.mes-selector { display: flex; gap: 0.6rem; align-items: end; max-width: 360px; margin-bottom: 1rem; }
.mes-selector label { flex: 1; margin: 0; }
.mes-selector button { margin: 0; }
.subtabla-actualizar { display: flex; gap: 0.5rem; align-items: center; margin-top: 0.3rem; }
.subtabla-actualizar input { width: 130px; }
.subtabla-actualizar button { padding: 0.35rem 0.6rem; font-size: 0.8rem; margin: 0; }

/* Tesorería: "Guardar los saldos escritos" en un celular angosto no tiene
   sitio para 7 columnas — Tipo y Moneda son las dos que menos hacen falta
   para escribir un saldo nuevo (2026-09-15, ítem 2). */
@media (max-width: 760px) {
  .tabla-tesoreria th:nth-child(2), .tabla-tesoreria td:nth-child(2),
  .tabla-tesoreria th:nth-child(3), .tabla-tesoreria td:nth-child(3) { display: none; }
  .tabla-tesoreria input[type="number"] { width: 100%; }
}

.barra-rent { width: 100%; min-width: 90px; height: 0.5rem; background: var(--surface-sunk); border-radius: 999px; overflow: hidden; border: 1px solid var(--line-soft); }
.barra-rent-fill { height: 100%; background: var(--accent); border-radius: inherit; }

/* T12 (Tanda 2 de Reportes, 2026-09-24) — el bloque `.chart*` que vivía acá
   (SVG a mano de la vista VIEJA de /reportes/comparativo: `.chart-wrap`,
   `.chart`, `.chart-ingreso/-utilidad/-axis/-label/-legend`,
   `.chart-divergente`, `.chart-var-pos/-neg`, `.chart-legend-pos/-neg`) se
   borró: T9 reescribió comparativo.ejs sobre gr-barras/gr-leyenda (sin SVG,
   sin `.chart-*`) — confirmado con `grep -rn "chart-\|class=\"chart" src
   test` que no queda ningún uso real (solo quedan menciones en pruebas que
   verifican la AUSENCIA de estas clases: reportes-portada.test.ts y
   reportes-comparativo-vista.test.ts, ninguna las usa como selector). */

/* Bloque 5.5 — utilidades para los patrones de `style=` inline más
   repetidos (el v4 midió 267, subiendo de los 217 que el v3 había
   bajado). Se agregan las clases para frenar el crecimiento en
   pantallas NUEVAS; no se barrieron las ocurrencias ya existentes en
   40+ vistas — el riesgo de una migración masiva no compensaba el
   beneficio puramente cosmético en esta ronda. */
.nota { color: var(--ink-soft); font-size: 0.9rem; }
.nota-chica { color: var(--ink-soft); font-size: 0.85rem; }
.subtitulo-card { margin-top: 0; font-size: 1rem; }
.fila-entre { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }

.accesos { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(150px, 100%), 1fr)); gap: 0.85rem; }
.acceso { display: block; color: var(--ink); text-decoration: none; margin: 0; transition: border-color .12s ease; }
.acceso:hover { border-color: var(--accent); }

.fila-anulada { background: var(--bad-bg); color: var(--bad-fg); }
.acciones-gasto { white-space: nowrap; }
.acciones-gasto form { display: inline; }

/* ---- buscador ⌘K (Bloque 5) — índice construido en vivo desde los
   .nav-link ya renderizados en el sidebar, nunca una lista aparte que
   se pueda desincronizar de la navegación real ---- */
.cmdk-overlay {
  position: fixed; inset: 0; z-index: 100; background: rgba(17,19,14,0.55);
  display: flex; align-items: flex-start; justify-content: center; padding-top: 12vh;
}
.cmdk-overlay[hidden] { display: none; }
.cmdk-panel {
  width: min(92vw, 560px); background: var(--surface); border: 1px solid var(--line);
  border-radius: 12px; box-shadow: var(--shadow); overflow: hidden;
}
.cmdk-input {
  width: 100%; border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  padding: 0.9rem 1.1rem; font-size: 1rem; background: var(--surface); color: var(--ink);
}
.cmdk-input:focus { outline: none; }
.cmdk-list { list-style: none; margin: 0; padding: 0.4rem; max-height: 55vh; overflow-y: auto; }
.cmdk-item {
  padding: 0.55rem 0.75rem; border-radius: 8px; font-size: 0.9rem; color: var(--ink);
  text-decoration: none; display: block; cursor: pointer;
}
.cmdk-item.activo, .cmdk-item:hover { background: var(--accent); color: var(--accent-ink); }
.cmdk-empty { padding: 1rem 1.1rem; color: var(--ink-faint); font-size: 0.88rem; }
.cmdk-hint {
  padding: 0.5rem 1.1rem; border-top: 1px solid var(--line-soft); font-size: 0.72rem;
  color: var(--ink-faint); display: flex; gap: 0.9rem;
}
.cmdk-hint kbd {
  font-family: var(--font-mono); background: var(--surface-sunk); border: 1px solid var(--line);
  border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.7rem;
}

/* ---- tablas ordenables (Bloque 5) — opt-in vía data-sortable en <table> ---- */
table[data-sortable] th[data-key] { cursor: pointer; user-select: none; white-space: nowrap; }
table[data-sortable] th[data-key]:hover { color: var(--ink); }
table[data-sortable] th[data-key] .sort-arrow { display: inline-block; width: 0.9em; opacity: 0.5; }
table[data-sortable] th[data-key].sort-activo .sort-arrow { opacity: 1; }

/* ---- calendario de aseo (2026-09-10) ----
   Cada día es un <details>: los que ya pasaron se pliegan. Cada cambio de una
   fila se guarda solo (con JavaScript; sin él queda el botón Guardar). En el
   celular la tabla de siete columnas obligaba a deslizar de lado: debajo de
   760px cada limpieza es una tarjeta con sus rótulos. */
.agenda-ayuda { font-size: 0.8rem; color: var(--ink-soft); margin: 0 0 1rem; max-width: 72ch; }
.solo-js { display: none; }
.agenda-js .solo-js { display: inline; }
.agenda-js .guardar-fila { display: none; }
details.dia [hidden] { display: none !important; }
details.dia > summary { list-style: none; cursor: pointer; }
details.dia > summary::-webkit-details-marker { display: none; }
details.dia[open] > summary { margin-bottom: 0.75rem; }
.dia-titulo { margin: 0; font-size: 0.95rem; display: flex; justify-content: space-between; align-items: center; gap: 0.5rem 1rem; flex-wrap: wrap; }
.dia-nombre, .dia-cuenta { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.dia-nombre::before { content: '▸'; color: var(--ink-faint); font-size: 0.8rem; transition: transform 0.15s ease; }
details.dia[open] .dia-nombre::before { transform: rotate(90deg); }
.dia-titulo .estado { margin-top: 0; }
.dia-total { font-family: var(--font-ui); font-size: 0.75rem; font-weight: 600; color: var(--ink-faint); }
.tabla-agenda-wrap { overflow-x: auto; }
.tabla-agenda td { vertical-align: top; }
.tabla-agenda select, .tabla-agenda input[type="text"] { min-width: 8.5rem; }
.tabla-agenda .sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.apto-edificio { display: block; font-size: 0.72rem; color: var(--ink-faint); }
.hora-label { display: flex; align-items: center; gap: 0.4rem; margin: 0 0 0.25rem; font-size: 0.72rem; color: var(--ink-soft); white-space: nowrap; }
.hora-label input { width: 6.5rem; padding: 0.35rem 0.5rem; }
/* La hora que se asume cuando el campo está vacío: es lo que ve quien limpia. */
.hora-asumida { font-size: 0.68rem; color: var(--ink-faint); font-style: italic; }
.barra-kunas button.btn { margin-top: 0; }
@media (max-width: 560px) {
  .barra-kunas { gap: 0.5rem 0.75rem; }
  .barra-kunas button.btn { padding: 0.34rem 0.7rem; font-size: 0.78rem; }
}

.hora-vacia {
  color: var(--ink-faint); background: none; border: 0; padding: 0 0.15rem;
  font: inherit; cursor: pointer; border-bottom: 1px dotted var(--ink-faint);
}
.hora-vacia:hover, .hora-vacia:focus-visible { color: var(--ink); }
.nota-express { display: block; font-size: 0.7rem; color: var(--ink-faint); }
.estado.chip-estado { margin: 0 0 0.35rem; }
.select-estado { font-size: 0.8rem; padding: 0.35rem 0.5rem; }
.guardado { display: block; min-height: 1rem; font-size: 0.72rem; color: var(--ink-faint); white-space: nowrap; }
.guardado.ok { color: var(--ok-fg); }
.guardado.error { color: var(--bad-fg); white-space: normal; max-width: 14rem; }
.form-borrar { margin: 0.3rem 0 0; }
.form-borrar .btn-text { margin-left: 0; font-size: 0.75rem; }
.dia-acciones { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.5rem 1.25rem; margin-top: 0.75rem; padding-top: 0.6rem; border-top: 1px dashed var(--line); }
.agregar-dia > summary { list-style: none; cursor: pointer; color: var(--accent); font-weight: 600; font-size: 0.85rem; }
.agregar-dia > summary::-webkit-details-marker { display: none; }
.agregar-dia[open] { flex-basis: 100%; order: 2; }
.whatsapp .btn-text { font-size: 0.85rem; }
.whatsapp .btn-text:first-child { margin-left: 0; }
.copiado { margin-left: 0.55rem; font-size: 0.75rem; color: var(--ok-fg); }
.texto-whatsapp { margin-top: 0.6rem; font-size: 0.8rem; font-family: var(--font-mono); }
@media (max-width: 760px) {
  .tabla-agenda thead { display: none; }
  .tabla-agenda, .tabla-agenda tbody, .tabla-agenda tr, .tabla-agenda td { display: block; width: 100%; }
  .tabla-agenda tr { border: 1px solid var(--line); border-radius: 10px; padding: 0.7rem 0.85rem; margin-bottom: 0.6rem; }
  .tabla-agenda tbody tr:hover { background: transparent; }
  .tabla-agenda td { border: 0; padding: 0.3rem 0; }
  .tabla-agenda td[data-label]::before {
    content: attr(data-label); display: block; margin-bottom: 0.15rem;
    font-size: 0.66rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--ink-faint);
  }
  .tabla-agenda td.celda-express::before { display: inline; margin-right: 0.5rem; }
  .tabla-agenda td.celda-express .nota-express { display: inline; margin-left: 0.4rem; }
  .tabla-agenda td.celda-horas { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; }
  .tabla-agenda td.celda-horas::before { flex-basis: 100%; }
  .tabla-agenda select, .tabla-agenda input[type="text"] { min-width: 0; }
  .guardado { white-space: normal; }
}

/* ---- navegación por secciones (2026-09-10): 9 secciones en el menú, las
   páginas de cada una como pestañas arriba del contenido, la ruta
   "Sección › Página" clicable y "← Volver a …" en toda sub-página. Todo sale
   de src/navegacion.ts vía partials/nav.ejs. ---- */
.topbar-izq, .topbar-der { display: flex; align-items: center; gap: 0.75rem; min-width: 0; }
.topbar-der { gap: 0.9rem; flex: none; }
.topbar nav.crumb { display: flex; align-items: center; gap: 0.45rem; min-width: 0; overflow: hidden; white-space: nowrap; }
.topbar .crumb a { color: var(--ink-soft); text-decoration: none; }
.topbar .crumb a:hover { color: var(--accent); text-decoration: underline; text-underline-offset: 3px; }
.topbar .crumb [aria-current] { color: var(--ink); overflow: hidden; text-overflow: ellipsis; }
.topbar .crumb .sep { color: var(--ink-soft); }
.volver {
  display: inline-flex; align-items: center; gap: 0.3rem; flex: 0 1 auto; min-width: 0;
  padding: 0.32rem 0.75rem; border: 1px solid var(--line); border-radius: 7px;
  background: var(--surface); color: var(--ink); text-decoration: none;
  font-size: 0.83rem; font-weight: 600; white-space: nowrap;
}
.volver:hover { border-color: var(--accent); color: var(--accent); }

.registrar { display: flex; gap: 0.5rem; padding: 1rem 0.8rem 0; }
.registrar a {
  flex: 1; text-align: center; padding: 0.5rem 0.4rem; border-radius: 7px;
  border: 1px solid var(--sidebar-line); color: var(--sidebar-text); text-decoration: none;
  font-family: var(--font-ui); font-weight: 700; font-size: 0.82rem;
}
.registrar a:first-child { background: var(--sidebar-accent); border-color: var(--sidebar-accent); color: var(--sidebar-registrar); }
.registrar a:hover { filter: brightness(1.12); }
.nav-group-pie { border-top: 1px solid var(--sidebar-line); padding-top: 0.9rem; }

.seccion-tabs { margin-left: var(--sidebar-w); max-width: 1600px; padding: 4.3rem 2rem 0; }
.seccion-tabs ~ main { padding-top: 1.2rem; }
.pestanas { display: flex; align-items: flex-end; gap: 0.15rem; overflow-x: auto; border-bottom: 1px solid var(--line); scrollbar-width: thin; }
.pestanas a {
  display: inline-flex; align-items: center; gap: 0.4rem; white-space: nowrap;
  padding: 0.55rem 0.8rem; margin-bottom: -1px; border-bottom: 2px solid transparent;
  color: var(--ink-soft); text-decoration: none; font-size: 0.87rem; font-weight: 600;
}
.pestanas a:hover { color: var(--ink); }
.pestanas a.activa { color: var(--accent); border-bottom-color: var(--accent); }
.pestanas .nav-badge { margin-left: 0; }
.pestanas-grupo {
  align-self: center; padding: 0 0.55rem 0 0.1rem; white-space: nowrap;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-soft);
}
.pestanas a.todos { margin-left: auto; color: var(--accent); }

/* Inicio de Reportes: los 22 reportes a la vista, por subgrupo. */
.directorio-reportes { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(190px, 100%), 1fr)); gap: 1rem 1.4rem; }
.directorio-reportes h3 {
  margin: 0 0 0.45rem; font-family: var(--font-ui); font-weight: 700;
  font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-soft);
}
.directorio-reportes ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
.directorio-reportes a { color: var(--accent); text-decoration: none; font-weight: 500; }
.directorio-reportes a:hover { text-decoration: underline; }

@media (max-width: 900px) {
  /* El topbar fixed tapaba el comienzo de la página en el celular (main solo
     deja 1,6rem arriba). Pegado arriba pero dentro del flujo, no tapa nada. */
  .topbar { position: sticky; padding: 0.7rem 1rem; }
  .seccion-tabs { margin-left: 0; padding: 0.5rem 1rem 0; }
  .volver .volver-a { display: none; }
}
@media (max-width: 560px) {
  .topbar .volver + .crumb { display: none; }
}
@media print {
  aside.sidebar, .topbar, .seccion-tabs, .sidebar-backdrop { display: none; }
  main { margin-left: 0; padding-top: 0; }
}

/* ---- revisión de UX (2026-09-10): foco visible sobre el menú oscuro (el
   azul de --accent no llega a 3:1 sobre él en tema claro), foco de las
   pestañas sin recorte, toques de 44 px en el celular, el menú cerrado fuera
   del orden de tabulación, "+ Gasto" a un toque en el celular, las vistas
   internas de una pantalla como control segmentado (no otra fila de
   pestañas) y la pantalla de error con salida. ---- */
aside.sidebar a:focus-visible, aside.sidebar button:focus-visible { outline-color: var(--sidebar-accent); }
.pestanas a:focus-visible { outline-offset: -3px; }
.topbar-registrar { display: none; }
/* Un destino largo ("Retención en la fuente") se recorta con "…" en vez de
   montarse sobre los botones de la derecha. */
.volver-destino { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.subpestanas {
  display: inline-flex; flex-wrap: wrap; gap: 0.25rem; padding: 0.25rem; margin: 0 0 1rem;
  border: 1px solid var(--line); border-radius: 9px; background: var(--surface-sunk);
}
.subpestanas a {
  padding: 0.42rem 0.85rem; border-radius: 6px; color: var(--ink-soft);
  text-decoration: none; font-weight: 600; font-size: 0.86rem;
}
.subpestanas a:hover { color: var(--ink); }
.subpestanas a[aria-current] { background: var(--surface); color: var(--ink); box-shadow: var(--shadow); }
.subpestanas a:focus-visible { outline-offset: -2px; }

.pantalla-error { max-width: 62ch; }
.pantalla-error .error-codigo {
  margin: 0 0 0.3rem; font-family: var(--font-mono); font-size: 0.78rem;
  letter-spacing: 0.06em; color: var(--ink-soft);
}
.pantalla-error h1 { font-size: 1.35rem; margin-bottom: 0.5rem; }
.error-acciones { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem; margin-top: 1.1rem; }
.error-acciones .btn-link { margin: 0; }
.error-acciones button.btn-link { border: 0; cursor: pointer; }
main.sin-menu { margin: 0 auto; padding-top: 3rem; max-width: 720px; }

@media (max-width: 900px) {
  aside.sidebar { visibility: hidden; transition: transform 0.2s ease, visibility 0s linear 0.2s; }
  body.sidebar-open aside.sidebar { visibility: visible; transition: transform 0.2s ease; }
  .hamburger, .topbar .theme-toggle { width: 2.75rem; height: 2.75rem; }
  .volver, .topbar-registrar { min-height: 2.75rem; }
  /* .btn-text (padding:0) dejaba "Marcar leída"/"Silenciar"/"Editar" junto al
     "Anular" en rojo, y las acciones de conciliación, con un toque casi del
     tamaño del texto subrayado (2026-09-15, ítem 3). */
  .btn-text { display: inline-flex; align-items: center; min-height: 2.75rem; padding: 0 0.5rem; }
  .acciones-gasto { display: flex; gap: 0.75rem; align-items: center; }
  .topbar-registrar {
    display: inline-flex; align-items: center; padding: 0 0.8rem; border-radius: 7px; white-space: nowrap;
    background: var(--accent); color: var(--accent-ink); text-decoration: none; font-weight: 700; font-size: 0.85rem;
  }
  .topbar-der { gap: 0.5rem; }
  .nav-link { padding-top: 0.75rem; padding-bottom: 0.75rem; }
  .pestanas a { padding-top: 0.8rem; padding-bottom: 0.8rem; }
  .pestanas a.todos { order: -1; margin-left: 0; }
  .subpestanas { display: flex; margin-top: 0.75rem; }
  .subpestanas a { flex: 1 1 auto; text-align: center; padding: 0.7rem 0.6rem; }
}
@media (max-width: 560px) {
  /* "Salir" también está al pie del menú: aquí deja sitio a "+ Gasto". */
  .topbar .salir { display: none; }
}

/* ---- revisión de diseño (2026-09-16) ------------------------------------
   Cuatro cosas que se repetían a mano en 40+ vistas o que solo se notan en
   un celular de 390 px de ancho:

   1. `.cabecera-pagina` / `.acciones-cabecera`: 26 vistas abren con el mismo
      `style="display:flex; justify-content:space-between…"` y un par de
      botones al lado. Como `.btn-link.secondary` trae `margin: 1.1rem 0 0`
      (pensado para ir DEBAJO de un formulario), en esas filas de cabecera
      los botones secundarios quedaban 17 px más abajo que el primario.
   2. Toques de 44 px en el celular para campos y botones: el mínimo que
      Apple y Google recomiendan. Un `input` de 0,58rem de relleno mide unos
      36 px — se falla con el pulgar, que es como se usa esto de verdad.
   3. Márgenes laterales: 2 rem a cada lado en un celular se come 64 de los
      390 px justo donde hay tablas anchas.
   4. `.oculta-movil` para las columnas que no hacen falta para decidir nada
      (moneda cuando todo es COP, marcas de tiempo, retención) y
      `details.ayuda` para la explicación larga que estorba a partir de la
      segunda visita a la pantalla. ------------------------------------- */
.cabecera-pagina { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem 1rem; flex-wrap: wrap; }
.cabecera-pagina > h1 { margin-bottom: 0.6rem; }
.acciones-cabecera { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.acciones-cabecera .btn-link,
.acciones-cabecera .btn-link.secondary,
.acciones-cabecera button.btn { margin: 0; }
/* Un <form> con un solo button.btn adentro (p. ej. "Leer PriceLabs ahora" en
   /precios, 2026-09-26): sin esto, el form es su propia caja de bloque y
   queda más alto/desalineado que los .btn-link de al lado. Mismo patrón que
   .alerta-mas-lista form (más abajo en este archivo). */
.acciones-cabecera form { display: contents; }

/* La explicación larga, plegada: quien ya sabe cómo funciona la pantalla no
   tiene que pasar por encima de tres párrafos cada vez que entra. El texto
   sigue en la página (y en el buscador del navegador), solo empieza cerrado. */
details.ayuda { margin: 0 0 1.1rem; }
details.ayuda > summary {
  display: inline-flex; align-items: center; gap: 0.35rem; min-height: 2.25rem;
  cursor: pointer; list-style: none;
  color: var(--accent); font-weight: 600; font-size: 0.86rem;
}
details.ayuda > summary::-webkit-details-marker { display: none; }
details.ayuda > summary::before { content: '›'; display: inline-block; transition: transform 0.15s ease; }
details.ayuda[open] > summary::before { transform: rotate(90deg); }
details.ayuda > p, details.ayuda > ul { color: var(--ink-soft); font-size: 0.88rem; max-width: 72ch; }
details.ayuda > p:first-of-type { margin-top: 0.4rem; }

/* Estado vacío con salida: "no hay nada" a secas deja a quien lo lee sin
   saber si está mal configurado o si de verdad no hay nada que hacer. */
.vacio-acciones { display: flex; flex-wrap: wrap; gap: 0.5rem 1rem; align-items: center; margin-top: 0.6rem; }

@media (max-width: 760px) {
  .oculta-movil { display: none; }
}

/* Columnas secundarias que sobran en escritorio angosto (1280px, el ancho
   común de un portátil): a esa medida /pyg tenía 9 columnas y "Cuadre" (la
   más importante para decidir algo) se cortaba dentro de la tarjeta. Ocultar
   Margen bruto y U. antes imp. — ya redundantes con EBITDA/Utilidad neta,
   siguen en el CSV — hasta 1440px, más ancho que el punto de corte de
   .oculta-movil (760px) porque acá el problema no es el celular. */
@media (max-width: 1440px) {
  .oculta-mediano { display: none; }
}

@media (max-width: 900px) {
  /* Campos y botones que se alcanzan con el pulgar. Las casillas y los radios
     quedan fuera: ahí el área tocable la da el <label>, y estirarlos los
     dibujaría como rectángulos gigantes dentro de las tablas. */
  input:not([type="checkbox"]):not([type="radio"]), select, textarea { min-height: 2.75rem; }
  button.btn, .btn-link {
    min-height: 2.75rem; display: inline-flex; align-items: center; justify-content: center;
  }
}

@media (max-width: 560px) {
  main { padding-left: 1rem; padding-right: 1rem; }
  .seccion-tabs { padding-left: 1rem; padding-right: 1rem; }
  .card { padding: 0.9rem 1rem; }
  /* Botones de ancho completo: apilados, uno debajo de otro, se tocan mejor
     que tres pastillas angostas peleando por la misma línea. */
  .acciones-cabecera { width: 100%; }
  .acciones-cabecera > .btn-link, .acciones-cabecera > button.btn { flex: 1 1 10rem; }
}

/* ─────────────────────────────────────────────────────────────────────
   Reportes financieros (2026-09-22)
   Todo se dibuja con SVG en el servidor: ninguna librería de gráficos,
   ningún JS nuevo en el navegador. Los colores salen de los tokens de
   arriba, así que el modo oscuro y la impresión funcionan sin reglas
   aparte. El color semántico (verde/rojo) se gasta SOLO en la variación
   y en las cifras negativas — si todo es de color, nada resalta.
   ───────────────────────────────────────────────────────────────────── */

.delta {
  font-family: var(--font-mono); font-size: 0.72rem; font-weight: 600;
  padding: 0.12rem 0.4rem; border-radius: 999px; white-space: nowrap;
  font-variant-numeric: tabular-nums; border: 1px solid transparent;
}
.delta.bien { background: var(--ok-bg); color: var(--ok-fg); }
.delta.mal { background: var(--bad-bg); color: var(--bad-fg); }
/* --ink-soft, no --ink-faint (hallazgo 7 / E15 contraste): --ink-faint da
   3,16:1 en tema claro — bajo el mínimo de 4,5:1 para texto chico. --ink-soft
   da 6,43:1 claro / 7,36:1 oscuro. */
.delta.plano { background: var(--surface-sunk); color: var(--ink-soft); border-color: var(--line-soft); }
.delta-nota { font-size: 0.68rem; color: var(--ink-soft); }

/* Encabezado de sección de reporte: título, periodo y acción al mismo nivel. */
.rep-cabeza { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.5rem 1rem; margin: 0 0 0.75rem; }
.rep-cabeza h2 { margin: 0; font-size: 1.05rem; }
.rep-periodo { font-family: var(--font-mono); font-size: 0.78rem; color: var(--ink-soft); }

/* T12 (Tanda 2 de Reportes, 2026-09-24) — la primera frase de la respuesta
   (B.1/B.2 del plan: "En Agosto 2026, el resultado real fue..."/"N de M
   meses ya tienen..."), destacada pero SIN color de estado — el signo/tono
   va en los .delta de más abajo, nunca acá. comparativo.ejs (T9) y pace.ejs
   (T10) ya traían la clase esperando esta regla (antes con estilo en línea
   en pace.ejs, quitado en su revisión de Opus). max-width en ch (no rem):
   ~70 caracteres de ancho de línea legible, cualquiera sea el tamaño de
   fuente del usuario. */
.rep-respuesta { font-size: 1rem; font-weight: 600; color: var(--ink); margin: 0 0 0.9rem; max-width: 70ch; }

/* Tablas de cifras: números a la derecha, totales separados. */
table.cifras td.num, table.cifras th.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
table.cifras td.neg { color: var(--bad-fg); }
table.cifras tfoot td { border-top: 2px solid var(--line); font-weight: 600; }
/* T12 (Tanda 2 de Reportes, 2026-09-24) — mismo problema que el hallazgo 9
   de más abajo (tr.hito), en el <tfoot>: la celda "Totales" de comparativo.ejs
   (T9) es un <th scope="row"> (las 6 cifras ya son <td class="num">, así que
   SÍ heredan la línea de arriba); ese <th> no cae bajo `table.cifras tfoot td`
   y le sigue ganando la regla GLOBAL `th` (0,68rem, mayúsculas, gris) — sin
   esto, "Totales" queda chico y gris SIN línea superior, mientras el resto de
   la fila (los <td>) sí la tiene: la línea de totales visualmente empezaba en
   la 2ª columna. También alcanza a pyg-apartamento.ejs (Tanda 1, mismo
   `table.cifras`), cuyo tfoot es enteramente <th> — su fila de suma pasa de
   verse como un encabezado pequeño a verse como un total real. */
/* Hallazgo 4 (revisión de Opus): el `th` global lleva `border-bottom: 1px
   solid var(--line)` (más oscuro) y el `td` normal lleva `var(--line-soft)`
   (más claro) — sin fijar acá el mismo `--line-soft` que ya usan los `td`
   vecinos, la raya INFERIOR de la fila de Totales se veía partida (más
   oscura bajo la 1ª columna, tanto en pantalla como al imprimir). */
table.cifras tfoot th { border-top: 2px solid var(--line); border-bottom-color: var(--line-soft); font-weight: 600; font-size: inherit; text-transform: none; letter-spacing: normal; color: var(--ink); }
table.cifras tbody tr.destacada td { background: var(--surface-sunk); font-weight: 600; }
/* Nowrap SOLO acá (nunca en `.estado` global — hallazgo 1): una pastilla de
   "Cuadre"/"cuadra" en una columna angosta de una tabla de Reportes (/pyg,
   /pyg-detallado, /pyg-apartamento) sí necesita no partirse a la mitad. */
table.cifras .estado { white-space: nowrap; }
/* Hallazgo 9: la regla GLOBAL `th` (más abajo del archivo, línea ~287) fija
   font-size 0.68rem + mayúsculas — que también le gana a un <th scope="row">
   dentro de una fila de total (tr.hito), aunque `tr.hito th[scope="row"]`
   (más abajo) ya le ponga negrita: esa regla no pisa font-size/text-transform,
   así que sin esto un total quedaba en 10,88px EN MAYÚSCULAS mientras sus
   renglones de al lado miden 13,92px normal. */
table.cifras tr.hito th { font-size: inherit; text-transform: none; letter-spacing: normal; }

/* ─────────────────────────────────────────────────────────────────────
   Reportes financieros — base compartida T1 (2026-09-22)
   Segunda vuelta de los gráficos: reemplaza por completo al primer intento
   (.kpi-fin, .spark*, .cascada*, .casc-*, .compo* — retirados en T6, sin
   usos en src/ ni test/ ya). .chart-* (comparativo.ejs, la vista VIEJA)
   quedó retirado en T12, Tanda 2: comparativo.ejs ya no usa SVG. Estos son
   gráficos en HTML semántico (tabla real, barras <div>/<span> con posición en %) en vez
   de SVG generado a mano: mismo criterio de siempre (los tokens de color de
   arriba, así que oscuro e impresión salen sin reglas aparte), pero con una
   tabla de verdad debajo de cada barra, sin scroll lateral en el celular.
   NINGUNA vista suma/resta/divide una cifra acá: todo llega ya calculado
   por src/calc/graficos.ts o src/reportes/vista-resultados.ts. ───────── */

/* Visualmente oculto, disponible para lectores de pantalla. Hasta ahora
   solo existía anidado en .tabla-agenda (public/estilos.css, Bloque de
   aseo) — esta versión es la misma receta, pero global, para el <caption
   class="sr-only"> de gr-barras y cualquier otro texto de contexto que no
   tenga que verse. No se toca la versión anidada: sigue funcionando igual,
   nada más queda redundante con esta. */
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

.gr-panel { display: flex; flex-direction: column; gap: 1rem; }
.gr-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(7rem, 100%), 1fr)); gap: 0.9rem; }

/* Cifra destacada: 'IBM Plex Sans' 600 con cifras PROPORCIONALES (no
   tabular-nums) — es un número grande y suelto, no una columna de números
   que tenga que alinear dígito con dígito; tabular ahí se ve suelto/raro. */
.gr-cifra {
  font-family: var(--font-ui); font-weight: 600;
  font-variant-numeric: proportional-nums; min-width: 0; color: var(--ink);
  font-size: clamp(1.05rem, .9rem + 1vw, 1.4rem); line-height: 1.15;
}
.gr-cifra--hero { font-size: 3rem; }
.gr-cifra.neg { color: var(--bad-fg); }
.gr-cifra-etiqueta { font-size: 0.72rem; color: var(--ink-soft); }
.delta-periodo { color: var(--ink-soft); font-size: 0.75rem; }

/* Mini-tendencia de gr-cifra: ancho/alto FIJOS en px (los pasa quien
   incluye el parcial: 160×40 en el hero, 96×28 en una tarjeta) — nunca
   preserveAspectRatio="none": con tamaño fijo no hace falta estirar. */
.gr-tendencia { display: block; overflow: visible; }
.gr-tendencia-linea { fill: none; stroke: var(--gr-mov); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* Color por signo, no siempre oliva (hallazgo 10): el punto final de la
   tendencia se veía en oliva (--gr-pos) aunque la serie terminara en
   pérdida (utilidad negativa) o fuera la de la tarjeta de Costos (donde el
   punto no es "ganancia": es un movimiento/costo, --gr-mov). gr-tendencia.ejs
   agrega .gr-rol-neg o .gr-rol-mov según corresponda; sin ninguna de las dos,
   el default sigue siendo oliva (una serie de ganancia sin pérdidas). */
.gr-tendencia-punto { r: 4; fill: var(--gr-pos); stroke: var(--surface); stroke-width: 2; }
.gr-tendencia-punto.gr-rol-neg { fill: var(--gr-neg); }
.gr-tendencia-punto.gr-rol-mov { fill: var(--gr-mov); }

/* ---- gr-barras: tabla semántica con barra HTML (sin SVG) ---- */
table.gr-barras { width: 100%; border-collapse: collapse; font-size: 0.87rem; }
table.gr-barras caption { text-align: left; }
@media (min-width: 561px) {
  /* CSS grid SOBRE una tabla real: thead/tbody/tr quedan "display:contents"
     (no pintan caja propia) y th/td entran directo a la grilla de la
     <table> — la tabla sigue siendo una tabla para un lector de pantalla
     (los roles de accesibilidad de table/row/cell no dependen del layout
     visual), pero se alinea en columnas sin depender del ancho automático
     de <table>. */
  table.gr-barras { display: grid; grid-template-columns: minmax(8rem, 30%) 1fr auto; align-items: center; row-gap: 0.4rem; }
  table.gr-barras.gr-mostrar-pct { grid-template-columns: minmax(8rem, 30%) 1fr auto auto; }
  table.gr-barras thead, table.gr-barras tbody, table.gr-barras tr { display: contents; }
  table.gr-barras th[scope="row"], table.gr-barras td { padding: 0.3rem 0.6rem; border-bottom: 1px solid var(--line-soft); }
  table.gr-barras th[scope="row"] { text-align: left; font-weight: 500; color: var(--ink); }
  /* Hallazgo N3 (2ª verificación): sin esto, cada celda de la fila mide
     SU PROPIA altura de contenido (el `th` de la etiqueta ~26px, la
     `td.gr-pista` de 14px+padding ~24px) y `align-items:center` (arriba)
     las centra dentro de la fila de grid en vez de estirarlas — el
     `border-bottom` de cada celda queda entonces al pie de SU PROPIA
     caja, a una altura distinta según la celda, así que cada fila
     imprimía DOS líneas divisorias en vez de una. `align-items:stretch`
     hace que las tres/cuatro celdas de una fila compartan la misma
     altura (la de la más alta); `display:flex` + `align-items:center`
     en cada celda mantiene el contenido centrado DENTRO de esa altura
     compartida (con `justify-content:flex-end` en las columnas
     numéricas, que ya no alinean con `text-align` una vez son flex). La
     pista pasa a `height:auto` para poder estirarse: la barra (top:50%+
     translateY(-50%), position:absolute) y el cero (.gr-cero,
     top:0;bottom:0) siguen centrados/cubriendo toda la altura nueva sin
     cambios propios. */
  table.gr-barras { align-items: stretch; }
  table.gr-barras th[scope="row"], table.gr-barras td { display: flex; align-items: center; }
  table.gr-barras td.gr-pista { height: auto; }
}
@media (max-width: 560px) {
  /* Sin scroll lateral: cada fila pasa a ser su propia mini-grilla de dos
     filas ("etq val" arriba, "pista pista" abajo, ocupando el ancho
     completo) — el contrato de gr-barras (tabla + barras HTML) se mantiene,
     solo cambia cómo se acomodan las celdas. */
  table.gr-barras, table.gr-barras tbody { display: block; }
  table.gr-barras tr {
    display: grid; grid-template-columns: 1fr auto; row-gap: 0.3rem; column-gap: 0.6rem;
    grid-template-areas: "etq val" "pista pista";
    padding: 0.55rem 0; border-bottom: 1px solid var(--line-soft);
  }
  table.gr-barras th[scope="row"] { grid-area: etq; text-align: left; font-weight: 500; color: var(--ink); }
  /* Clases explícitas (gr-valor/gr-pct), nunca selectores por posición del
     mismo tipo de etiqueta: el primer <td> real de la fila es td.gr-pista
     (va ANTES que los .num en el marcado), así que contar por posición de
     tipo hacía que "el primero" no casara con nada y "el segundo" apagara
     el valor en COP en vez del % — en celular la cascada (mostrarPct:false)
     se quedaba sin ninguna cifra visible, solo la barra. Ver notas de
     T2/T4/T5 para T6. */
  table.gr-barras td.gr-valor { grid-area: val; }
  table.gr-barras td.gr-pct { display: none; }
  table.gr-barras td.gr-pista { grid-area: pista; }
  /* A.5.2 (hallazgo del planificador Opus sobre las capturas): th/td
     conservaban el border-bottom GLOBAL (`th, td { ... }` más abajo en el
     archivo) además del que ya trae `table.gr-barras tr` de esta misma
     regla — en la mini-grilla de dos filas de acá arriba eso pintaba TRES
     líneas sueltas por fila (una bajo la etiqueta, una bajo el valor, una
     bajo la pista) en vez de una sola línea divisoria por fila. Se apaga
     acá, scopeado a gr-barras en celular — la línea que sí debe verse es
     la de `tr`, ya declarada arriba. */
  table.gr-barras th[scope="row"], table.gr-barras td { border-bottom: 0; }
}
table.gr-barras td.num {
  font-family: var(--font-mono); font-variant-numeric: tabular-nums;
  text-align: right; color: var(--ink);
  /* Hallazgo N3: en el layout de grid de escritorio (≥561px) esta celda pasa
     a `display:flex` (arriba) para poder estirarse a la altura de la fila —
     `text-align` ya no alinea el número, así que hace falta el equivalente
     en flex. En celular, sin `display:flex`, esta línea no hace nada. */
  justify-content: flex-end;
}
/* A.5.1 (hallazgo del planificador Opus sobre las capturas): la regla
   GLOBAL `th` (más abajo en el archivo) fija font-size:0.68rem +
   mayúsculas, y le gana a los `table.gr-barras th[scope="row"]` de arriba
   (que solo tocan text-align/font-weight/color, nunca tamaño ni
   mayúsculas) — los rótulos de cada fila (Ingresos, Costo directo...)
   salían en mayúsculas de 10,88px en vez del tamaño normal de la tabla
   (13,92px). Sin @media: aplica igual en escritorio y celular. Ojo: esto
   es un selector DISTINTO de `table.cifras tr.hito th` (arriba, hallazgo
   3.9) — cada uno normaliza su propia tabla, sin pisarse. */
table.gr-barras th[scope="row"] { font-size: inherit; text-transform: none; letter-spacing: normal; }
tr.hito th[scope="row"], tr.hito td.num { font-weight: 600; color: var(--ink); }

.gr-pista { position: relative; height: 14px; min-width: 6rem; vertical-align: middle; }
/* top:50% + translateY(-50%), no top:1px fijo (hallazgo 3, punto 8): la
   barra es un hijo `position:absolute` de `.gr-pista`, así que
   `vertical-align` (arriba, en td.gr-pista) no la mueve — con top:1px fijo
   quedaba pegada arriba de la celda de 38px de /pyg-detallado en vez de
   centrada. Centrada por transform funciona igual en la pista de 14px de
   la portada/ranking (barra de 12px), donde ya se veía bien. */
.gr-barra { position: absolute; top: 50%; transform: translateY(-50%); height: 12px; min-width: 2px; border-radius: 2px; }
/* Punta de datos redondeada, base cuadrada — la punta es hacia donde crece
   la barra: derecha si suma, izquierda si resta (baja el acumulado). */
.gr-barra.hacia-der { border-radius: 0 4px 4px 0; }
.gr-barra.hacia-izq { border-radius: 4px 0 0 4px; }
.gr-cero { position: absolute; top: 0; bottom: 0; width: 1px; background: var(--gr-cero); }
.gr-rol-pos { background: var(--gr-pos); }
.gr-rol-neg { background: var(--gr-neg); }
.gr-rol-mov { background: var(--gr-mov); }

/* ---- gr-bala: bullet chart de una fila por mes (T7, Tanda 2, 2026-09-23) ----
   Reutiliza table.gr-barras entero (mismo grid de escritorio / mini-grilla
   de celular, mismas clases gr-valor/gr-pct/gr-pista/gr-cero/gr-barra) con
   la clase gr-bala encima — lo único nuevo de verdad es la marca de meta
   (.gr-meta), una línea vertical DENTRO de la misma pista. */
@media (min-width: 561px) {
  /* D1 (revisión de Opus sobre las capturas de escritorio, 2026-09-23):
     en ≥561px el `th` de gr-barras es `display:flex` EN FILA (hallazgo N3
     de la Tanda 1, arriba) — el mes y `.gr-detalle` (display:block, pero
     dentro de un contenedor flex eso solo controla su caja interna, no la
     dirección del eje principal del padre) quedaban uno AL LADO del otro
     sin separación: "Septiembre 2026en curso". Scopeado solo a gr-bala (el
     th normal de gr-barras no tiene .gr-detalle, así que no hace falta
     tocarlo): pone el detalle DEBAJO del mes, como ya se veía bien en
     celular (ahí el th no es flex, así que `.gr-detalle{display:block}`
     ya caía en su propia línea sin ayuda). */
  table.gr-barras.gr-bala th[scope="row"] {
    flex-direction: column; align-items: flex-start; justify-content: center;
  }
  /* D2 (misma revisión): una `.gr-pista` con altura FIJA de 20px (antes,
     sin @media) le ganaba en especificidad a `table.gr-barras td.gr-pista
     { height: auto; }` del bloque de escritorio de gr-barras (arriba,
     hallazgo N3) — con la pista sin poder estirarse a la altura real de
     la fila, su border-bottom quedaba a una altura distinta que el de las
     demás celdas: una raya suelta bajo la barra, ~11px más arriba que la
     de mes/valor/%. En escritorio la pista vuelve a estirarse como
     cualquier celda de gr-barras (height:auto heredado); min-height solo
     asegura que nunca quede más baja que la marca de 20px. */
  table.gr-barras.gr-bala td.gr-pista { min-height: 20px; }
}
/* La marca va en --ink, NUNCA un --gr-*: no es una serie de dato, es una
   referencia neutral (ver el comentario de la paleta arriba y (F) del plan
   de la Tanda 2). Contraste de --ink contra la barra oliva (--gr-pos):
   3,17:1 claro, 2,44:1 oscuro — NO alcanza por sí solo, así que la marca
   lleva un anillo de 1px del color de la SUPERFICIE (box-shadow, no
   border: así no le resta ancho a los 2px reales) y mide 20px, más alta
   que la barra de 12px, para que sus puntas (arriba y abajo) siempre
   asomen sobre la superficie en vez de quedar enteramente adentro de la
   barra oliva.
   D2: top:50%+height:20px+translate(-50%,-50%), NO top:0;bottom:0 — con
   top/bottom la marca estiraba su alto al de la CELDA (que ahora puede
   medir más de 20px en escritorio, estirada a la altura de la fila), así
   que su centro ya no coincidía con el centro de la barra de 12px
   (siempre centrada por su cuenta con top:50%+translateY(-50%)). Fijando
   el alto en 20px y centrando por transform, la marca queda centrada
   igual que la barra sin importar cuánto mida la celda. */
.gr-meta {
  position: absolute; top: 50%; height: 20px; width: 2px; transform: translate(-50%, -50%);
  background: var(--ink); border-radius: 1px; box-shadow: 0 0 0 1px var(--surface);
}
.gr-detalle { display: block; font-size: 0.75rem; font-weight: 400; color: var(--ink-soft); }
/* Muestra de la marca en la leyenda: una línea angosta, no el rectángulo
   ancho de las demás muestras (gr-leyenda-muestra base) — para que se lea
   como la misma marca de la pista, no como una cuarta categoría de color. */
.gr-leyenda-muestra.gr-leyenda-meta { width: 2px; height: 0.9rem; border-radius: 1px; background: var(--ink); }
@media (max-width: 560px) {
  /* En celular la pista NO se estira por flex (es su propia área de grid
     en la mini-grilla de dos filas, ver el bloque de celular de gr-barras
     arriba) — necesita su propia altura fija para alojar la marca de
     20px, sin depender de ningún ancestro. Scopeado a ≤560px (D2): a
     ≥561px esta regla es justo la que rompía el estirado de la celda. */
  table.gr-barras.gr-bala .gr-pista { height: 20px; }
  /* B.2, punto 3 del plan: en celular el valor en COP se oculta (ya está en
     la tabla gemela) y el % ocupa su lugar arriba de la pista — selector
     MÁS ESPECÍFICO que `table.gr-barras td.gr-valor`/`td.gr-pct` (el bloque
     base de arriba), así que gana ahí sin tocar el comportamiento normal
     de gr-barras (donde sigue siendo el valor el que se ve). */
  table.gr-barras.gr-bala td.gr-valor { display: none; }
  table.gr-barras.gr-bala td.gr-pct { display: block; grid-area: val; }
}

/* ---- gr-columnas: columnas verticales, mismo criterio sin SVG ----
   El espacio para el rótulo va en margin-top/margin-bottom (FUERA de la
   caja), no en padding: un padding-top acá infla la "caja de referencia"
   (padding box) que usa .gr-cols-cero para su top:%, mientras que las
   barras (dentro de .gr-col, que llena solo la caja de CONTENIDO) siguen
   midiendo % contra 11rem — dos bases distintas para el mismo % = la línea
   de cero queda desalineada contra la base de las barras (bug verificado:
   ~10px de desfase). Con margin en vez de padding, .gr-cols mide 11rem
   limpios y el % de la cero y el % de las barras usan la misma caja. */
.gr-cols {
  position: relative; height: 11rem; margin: 1.4rem 0 1.3rem;
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; align-items: end;
}
.gr-cols-cero { position: absolute; left: 0; right: 0; height: 1px; background: var(--gr-cero); }
.gr-col {
  position: relative; height: 100%; display: flex; align-items: flex-end; justify-content: center;
  border-radius: 4px;
}
.gr-col:hover, .gr-col:focus { background: var(--surface-sunk); }
.gr-col:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.gr-cols-barra {
  position: absolute; width: min(24px, 60%); left: 50%; transform: translateX(-50%);
  border-radius: 4px 4px 0 0; /* la punta de datos es arriba: crece desde el cero hacia el valor */
}
/* La clase de rol va en LA BARRA (.gr-cols-barra), nunca en .gr-col: .gr-col
   mide height:100% de toda la columna, así que si el rol se pintaba ahí
   (como antes) el fondo cubría la columna ENTERA y todas las barras se
   veían del mismo alto — el bug quedaba oculto porque el color de fondo
   era el mismo en toda la caja. */
.gr-cols-barra.gr-rol-neg { border-radius: 0 0 4px 4px; } /* baja: la punta es abajo */
.gr-col-rotulo { position: absolute; font-size: 0.7rem; color: var(--gr-texto); white-space: nowrap; }
/* Ancladas al borde de la BARRA (min(12px,30%) es la mitad del ancho de
   `.gr-cols-barra`, min(24px,60%)), no al borde de la COLUMNA (hallazgo 5):
   con una serie corta a 1280px la columna es mucho más ancha que la barra
   centrada adentro, así que anclar a `left:0`/`right:0` de la columna
   dejaba el rótulo flotando hasta ~80px lejos de su propia barra. Con el
   cálculo de acá, el rótulo empieza justo en el borde de la barra y crece
   hacia el centro del gráfico — igual que antes evita el scroll lateral en
   el celular (que salía de anclar al borde de la TARJETA), pero ahora
   también queda pegado a su dato en pantallas anchas. */
.gr-col.tip-izq .gr-col-rotulo { left: calc(50% - min(12px, 30%)); text-align: left; }
.gr-col.tip-der .gr-col-rotulo { left: auto; right: calc(50% - min(12px, 30%)); text-align: right; }
/* Hallazgo 5: a ≤560px, el rótulo de la última columna SOLO por ser la
   última (no por ser el máximo ni el mínimo) puede tapar la barra vecina —
   el valor exacto ya está en la tabla de al lado, así que en celular no
   hace falta. `.rotulo-extra` la pone gr-columnas.ejs únicamente en ese
   caso (nunca en el máximo/mínimo, que siempre se rotulan). */
@media (max-width: 560px) {
  .gr-col-rotulo.rotulo-extra { display: none; }
}
/* Tooltip SOLO CSS — nunca la única forma de leer el valor: el mismo texto
   ya está en data-tip/aria-label, que el lector de pantalla lee sin hover. */
.gr-col:hover::after, .gr-col:focus::after {
  content: attr(data-tip); position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
  margin-bottom: 0.3rem; background: var(--ink); color: var(--surface); font-size: 0.72rem;
  padding: 0.3rem 0.55rem; border-radius: 6px; white-space: nowrap; z-index: 5; pointer-events: none;
}
.gr-col.tip-izq:hover::after, .gr-col.tip-izq:focus::after { left: 0; transform: none; }
.gr-col.tip-der:hover::after, .gr-col.tip-der:focus::after { left: auto; right: 0; transform: none; }
.gr-cols-eje { display: flex; margin-top: 0.35rem; font-size: 0.7rem; color: var(--ink-soft); }
.gr-cols-eje span { flex: 1; text-align: center; }

/* ---- leyenda ---- */
.gr-leyenda { display: flex; flex-wrap: wrap; gap: 0.4rem 1rem; list-style: none; margin: 0.6rem 0 0; padding: 0; font-size: 0.8rem; color: var(--ink-soft); }
.gr-leyenda li { display: flex; align-items: center; gap: 0.35rem; }
.gr-leyenda-muestra { width: 0.75rem; height: 0.5rem; border-radius: 2px; flex: none; }

/* Primera columna fija al hacer scroll lateral de una tabla ancha (P&G por
   apartamento con muchas propiedades, por ejemplo). white-space:nowrap +
   min-width: sin esto, el mes ("Agosto 2026") o el código del apartamento
   ("AP01 — Edificio Mandato Centro 101") partían en 2-4 líneas porque la tabla
   real de auto-layout los angostaba para que las 7-9 columnas cupieran en
   el 100% del contenedor — nowrap fuerza a la tabla a crecer más allá del
   100% (activa el scroll lateral de .tabla-scroll, que es lo esperado en
   una tabla con .tabla-fija-1) en vez de partir el texto. */
/* Hallazgo N1 (2ª verificación): la regla GLOBAL `table { border-collapse:
   collapse; }` (arriba) hace que Chrome NO pinte el `box-shadow` de la
   celda fija de abajo (la que tapa la franja de padding de la .card) —
   con bordes colapsados el navegador recorta la sombra al borde
   compartido de la celda, así que al desplazar la tabla de lado
   asomaban cifras ("01", "98", "55"...) en el margen izquierdo. Scopeado
   SOLO a `table.tabla-fija-1` (nunca la regla global: solo 2 usos, ambos
   en Reportes — pyg.ejs y pyg-apartamento.ejs), separa los bordes sin
   dejar huecos entre celdas (border-spacing:0) y deja que box-shadow
   vuelva a pintar. */
table.tabla-fija-1 { border-collapse: separate; border-spacing: 0; }
.tabla-fija-1 td:first-child, .tabla-fija-1 th:first-child {
  position: sticky; left: 0; background: var(--surface); z-index: 1;
  white-space: nowrap; min-width: 9rem;
  /* Tapa la franja de padding de la .card por la que asomaban cifras al
     desplazar (hallazgo 4): una sombra sólida del ancho del padding de la
     tarjeta, del mismo color que la superficie, a la izquierda de la celda
     fija. */
  box-shadow: -1.25rem 0 0 var(--surface);
}
/* Hallazgo 4: `.tabla-fija-1 th:first-child` (arriba) tiene MÁS especificidad
   que `.tabla-scroll table thead th` (z-index:2, más abajo en el archivo) —
   dos clases contra una clase + tres tipos — así que la esquina ("MES")
   quedaba con z-index:1 y el scroll (lateral o vertical) la tapaba con la
   celda vecina o con la fila de abajo. Esta regla, más específica todavía
   (clase + thead + th + :first-child), le vuelve a ganar. */
.tabla-fija-1 thead th:first-child { z-index: 3; }

/* Hallazgo 10 (menor): en /pyg-apartamento, la primera columna
   ("AP01 — Edificio Mandato Centro 101") no tiene techo de ancho — solo el piso
   de min-width:9rem de arriba — así que a 358px llegaba a ocupar ~220 de
   324px, dejando casi nada para el resto de la tabla con scroll. Un techo
   angosto en celular (donde el espacio importa) sin tocar el piso ni el
   escritorio: el texto se trunca con "…" en vez de empujar toda la fila. */
@media (max-width: 480px) {
  .tabla-fija-1 td:first-child, .tabla-fija-1 th:first-child {
    max-width: 45vw; overflow: hidden; text-overflow: ellipsis;
  }
}

.estado .ico { font-family: var(--font-mono); font-weight: 700; }

@media print {
  /* A.5.3 (hallazgo del planificador Opus): la leyenda (gr-leyenda.ejs)
     dibuja su muestra de color con `background`, igual que las barras — sin
     esto se imprimía sin color, así que la leyenda dejaba de explicar el
     color de un gráfico que sí se ve.
     .gr-meta/.gr-leyenda-meta (T7, Tanda 2): la marca de la bala también
     pinta con `background`, no con un color semántico — sin esto, la marca
     desaparecería en la vista previa de impresión igual que le pasaba a la
     leyenda antes del A.5.3. */
  .gr-barra, .gr-cols-barra, .gr-tendencia-punto, .gr-leyenda-muestra,
  .gr-meta, .gr-leyenda-meta,
  .gr-cero, .gr-cols-cero {
    print-color-adjust: exact; -webkit-print-color-adjust: exact;
  }
}

/* Alto contraste (Windows "Contraste alto" / forced-colors): sin esto, el
   modo forzado apaga TODOS los colores de fondo no semánticos y las barras
   desaparecen contra la superficie — quedaría una tabla con números pero
   sin el propio gráfico. Mismo motivo para .gr-meta/.gr-leyenda-meta (T7,
   Tanda 2): la marca de la bala es también un `background`, no un color
   semántico del sistema. */
@media (forced-colors: active) {
  .gr-barra, .gr-cols-barra, .gr-tendencia-linea, .gr-tendencia-punto,
  .gr-cero, .gr-cols-cero, .gr-leyenda-muestra, .gr-meta, .gr-leyenda-meta {
    forced-color-adjust: none;
  }
  /* Hallazgo 10 (revisión de Opus): con forced-color-adjust:none de arriba,
     el propio color de autor (var(--ink), oscuro) queda intacto — pero en
     el tema oscuro de alto contraste eso lo pinta casi invisible sobre un
     fondo casi negro (Canvas). CanvasText es el color de TEXTO que el
     propio sistema ya eligió para leerse sobre Canvas en cualquier tema, así
     que reemplaza a --ink solo acá, solo bajo forced-colors.
     R4 (2ª revisión de Opus): la muestra de la leyenda es
     `.gr-leyenda-muestra.gr-leyenda-meta { background: var(--ink) }` (dos
     clases, especificidad 0,2,0, más arriba en este archivo) — un selector
     de una sola clase (`.gr-leyenda-meta`, 0,1,0) pierde contra esa regla
     SIN IMPORTAR el orden en la hoja, porque la especificidad manda antes
     que el orden. Por eso la marca (`.gr-meta`, que si compite 0,1,0 contra
     0,1,0 y gana por ir después) sí se veía bien, pero la muestra de la
     leyenda seguía en --ink, casi invisible sobre Canvas. */
  .gr-meta, .gr-leyenda-muestra.gr-leyenda-meta {
    background: CanvasText;
  }
  .gr-meta {
    box-shadow: 0 0 0 1px Canvas;
  }
}

/* === Alertas (rediseño) === */
/* 2026-09-23 — el administrador: "no me gusta como están". Antes eran tres tablas de
   5 columnas, una fila gigante por alerta con botón grande + tres botones
   de texto repetidos 35 veces. Ahora: un resumen de cifras arriba, cada
   tipo de alerta (mismo `codigo`) agrupado en un solo renglón con su
   cantidad, y por caso una tarjeta angosta con UNA acción principal — las
   otras tres (marcar leída/silenciar) quedan chicas y juntas adentro de un
   "Más". Colores de franja por severidad: mismos tokens que ya usa
   partials/head-movil.ejs para /movil/alertas (--bad-fg/--warn-fg), así que
   los dos rediseños (escritorio y celular) leen el semáforo igual; AMARILLO
   ("Para revisar") usa --ink-faint, no un tercer color de estado — ese
   nombre ya es "más para mirar que para preocuparse", no una tercera alarma
   de color. Todo por variables, sin colores nuevos: los dos temas heredan
   solo cambiando los tokens de arriba. */

.alertas-resumen { margin-bottom: 1.1rem; }
a.kpi.alertas-resumen-item {
  display: block; text-decoration: none; color: inherit;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}
a.kpi.alertas-resumen-item:hover { box-shadow: 0 2px 4px rgba(24,27,20,0.08), 0 10px 20px -10px rgba(24,27,20,0.22); }
.kpi.alertas-resumen-item.vacio { opacity: 0.6; }
.alertas-resumen-item.sev-ROJO .valor { color: var(--bad-fg); }
.alertas-resumen-item.sev-NARANJA .valor { color: var(--warn-fg); }
.alertas-resumen-item.sev-AMARILLO .valor { color: var(--ink-faint); }

.alertas-severidad .subtitulo-card { margin-bottom: 0.7rem; }
.lista-alertas { display: flex; flex-direction: column; gap: 0.6rem; }

/* La franja de color reemplaza la pastilla roja repetida en cada fila de la
   tabla vieja — ya dice la severidad el título de la sección, así que acá
   solo hace falta un borde, no otra pastilla más. */
.alerta-fila {
  display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: flex-start; justify-content: space-between;
  padding: 0.75rem 0.9rem; border: 1px solid var(--line); border-left: 4px solid var(--line);
  border-radius: 10px; background: var(--surface);
}
.alerta-fila.sev-ROJO { border-left-color: var(--bad-fg); }
.alerta-fila.sev-NARANJA { border-left-color: var(--warn-fg); }
.alerta-fila.sev-AMARILLO { border-left-color: var(--ink-faint); }

.alerta-fila-cuerpo { flex: 1 1 18rem; min-width: 0; }
.alerta-fila-titulo { font-weight: 700; font-size: 0.92rem; }
/* Recorte a dos líneas con el texto completo en el atributo title (mismo
   criterio que .truncar más arriba) — nunca se pierde información, solo se
   deja de mostrar de entrada. */
.alerta-fila-detalle {
  margin: 0.25rem 0 0; color: var(--ink-soft); font-size: 0.85rem; line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.alerta-fila-chips { display: flex; flex-wrap: wrap; align-items: center; gap: 0.4rem; margin-top: 0.5rem; }
.alerta-fila-chips .estado { margin-top: 0; }
.alerta-fila-fecha { font-size: 0.74rem; color: var(--ink-faint); }

/* Motivo de "Costos: pendientes" en /precios (auditoría 2026-09-26, punto E)
   — antes solo vivía en un `title` (tooltip), invisible en el celular o en
   una captura de pantalla. Mismo patrón de recorte que .alerta-fila-detalle. */
.costos-motivo {
  margin: 0.25rem 0 0; color: var(--ink-soft); font-size: 0.76rem; line-height: 1.3; max-width: 22rem;
  display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.alerta-fila-acciones { display: flex; flex-direction: column; align-items: flex-end; gap: 0.4rem; flex: 0 0 auto; }
.alerta-fila-acciones .btn-link { margin: 0; }

/* El mismo patrón visual que details.ayuda (más arriba en este archivo):
   flechita chica que rota al abrir, para que "Más" no se sienta como un
   componente nuevo aunque sea chico y quieto a propósito — nunca el mismo
   peso que el botón principal. */
.alerta-mas { margin: 0; }
.alerta-mas > summary {
  display: inline-flex; align-items: center; gap: 0.25rem; min-height: 1.6rem;
  cursor: pointer; list-style: none; color: var(--ink-soft); font-weight: 600; font-size: 0.78rem;
}
.alerta-mas > summary::-webkit-details-marker { display: none; }
.alerta-mas > summary::before { content: '›'; display: inline-block; transition: transform 0.15s ease; }
.alerta-mas[open] > summary::before { transform: rotate(90deg); }
.alerta-mas-lista { display: flex; flex-wrap: wrap; align-items: center; gap: 0 0.3rem; margin-top: 0.3rem; }
.alerta-mas-lista form { display: contents; }
.alerta-mas-lista .btn-text { margin-left: 0; }

/* Un tipo con varios casos: título + cantidad primero, acción compartida (si
   existe) al lado, y el detalle de cada caso adentro del <details>. */
.grupo-alerta { border: 1px solid var(--line-soft); border-radius: 10px; padding: 0.7rem 0.9rem; }
.grupo-alerta-cab { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.6rem; }
.grupo-alerta-titulo-linea { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.grupo-alerta-titulo { font-weight: 700; font-size: 0.94rem; }
.grupo-alerta-cab .btn-link { margin: 0; }
.grupo-alerta-expandir { margin-top: 0.55rem; }
.grupo-alerta-expandir > summary {
  display: inline-flex; align-items: center; gap: 0.3rem; min-height: 1.9rem;
  cursor: pointer; list-style: none; color: var(--accent); font-weight: 600; font-size: 0.82rem;
}
.grupo-alerta-expandir > summary::-webkit-details-marker { display: none; }
.grupo-alerta-expandir > summary::before { content: '›'; display: inline-block; transition: transform 0.15s ease; }
.grupo-alerta-expandir[open] > summary::before { transform: rotate(90deg); }
.grupo-alerta-items { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 0.55rem; }

@media print {
  /* Igual que .tabla-scroll arriba: en papel se ve todo, sin recorte de
     texto ni casos escondidos detrás de un <details> cerrado. */
  .alerta-fila-detalle { -webkit-line-clamp: unset; line-clamp: unset; display: block; overflow: visible; }
  .grupo-alerta-expandir, .alerta-mas { display: block; }
}

@media (max-width: 900px) {
  /* Mismo mínimo de 44px del resto del panel (ver el comentario de
     .btn-text más arriba) para los dos toggles nuevos — no son botones de
     acción, pero igual se tocan con el pulgar. */
  .alerta-mas > summary, .grupo-alerta-expandir > summary { min-height: 2.75rem; }
  .alerta-fila { flex-direction: column; align-items: stretch; }
  .alerta-fila-acciones { align-items: flex-start; }
}

/* Hallazgo de revisión (2026-09-23): el recorte a dos líneas de arriba
   confía en el atributo title para el texto completo — pero en el celular
   no hay hover que muestre un title, así que el resto del detalle quedaba
   ilegible (información perdida, no solo escondida). Mismo punto de corte
   que .oculta-movil (760px, más arriba en este archivo): de ahí para abajo
   ya es "celular" para el resto de la hoja de estilos, así que el detalle
   se ve completo, sin recorte. En escritorio el recorte sigue, porque ahí
   sí hay mouse para el tooltip. */
@media (max-width: 760px) {
  .alerta-fila-detalle { -webkit-line-clamp: unset; line-clamp: unset; display: block; overflow: visible; }
}
/* === fin Alertas === */

/* === Reportes: portada — directorio arriba + exportar (T-portada-G, 2026-09-24) === */
/* Reemplaza el bloque "Exportar reportes" del 2026-09-24: ese botón cambió de
   lugar y de marcado (ver src/views/reportes/index.ejs), así que su única
   regla (.exportar-reportes-nota) ya no la usa nada.

   El directorio pasa de pie de página a ser lo primero que se ve (pedido de
   El administrador: "que sea al principio, no a lo último"). Ya NO es un .card: un
   solo nivel de énfasis — el borde/superficie de .card vive en cada tarjeta
   de GRUPO, no también en la sección que las contiene (evita la caja dentro
   de la caja). */
.rep-directorio { margin: 0 0 1rem; }
.rep-directorio-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; align-items: start; margin-top: 0.75rem; }
/* 1 columna en celular, 2 desde 700px, 3 desde 1100px — sin auto-fit: los
   anchos exactos evitan que una tarjeta con un grupo de nombre largo (p. ej.
   "Ventas y ocupación") rompa la cuenta de columnas a medio camino. */
@media (min-width: 700px) {
  .rep-directorio-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1100px) {
  .rep-directorio-grid { grid-template-columns: repeat(3, 1fr); }
}
.rep-directorio-grupo { margin-bottom: 0; }
.rep-directorio-grupo h3 {
  margin: 0 0 0.5rem; font-family: var(--font-ui); font-weight: 700;
  font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--ink-soft);
}
.rep-directorio-lista { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.65rem; }
/* Enlace grande y fácil de tocar, con su pregunta debajo en gris — un solo
   color (--accent, el mismo de siempre para enlaces), sin ícono ni emoji. */
.rep-directorio-enlace { display: block; padding: 0.15rem 0; color: var(--accent); text-decoration: none; font-weight: 600; }
.rep-directorio-enlace:hover { text-decoration: underline; }
.rep-directorio-descripcion { margin: 0.15rem 0 0; color: var(--ink-soft); font-size: 0.82rem; line-height: 1.35; }

/* "Descargar todo en Excel": ahora el pie del directorio, separado por una
   línea suave — nunca la acción principal de la pantalla (esa es leer el
   resumen, que sigue debajo). Reusa .btn-link.secondary, ya definido más
   arriba, resetenado su margen propio (pensado para .mes-selector). */
.rep-exportar {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.75rem;
  margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--line);
}
.rep-exportar-texto { margin: 0; font-weight: 600; }
.rep-exportar-form { display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: end; margin: 0; }
.rep-exportar-form label { margin: 0; }
.rep-exportar-form button { margin: 0; }

/* El CSV de la tendencia del resumen (antes vivía suelto arriba de la
   página, en tabs-reportes.ejs) — junto al periodo que describe. */
.rep-resumen-csv { white-space: nowrap; }

@media (max-width: 560px) {
  .rep-exportar { flex-direction: column; align-items: stretch; }
  .rep-exportar-form { flex-direction: column; align-items: stretch; }
}
/* === fin Reportes: portada === */

/* === Exportar reportes === */
/* Botón "Descargar todo en Excel" de /reportes (2026-09-24). Reusa
   .mes-selector (fila label+input+botón) y .btn-link.secondary (ya
   definidos más arriba) — acá solo se ajusta el espacio entre el botón y
   su nota de una línea, para que no se vean pegados. */
.exportar-reportes-nota { margin-top: -0.4rem; margin-bottom: 1.1rem; }
/* === fin Exportar === */

/* ── Tanda 3: Tablero por apartamento / Contratos por vencer ── */
/* Revisión de Opus (2026-09-25): las dos tablas nuevas (9-10 columnas cada
   una) no cabían completas a 1280px dentro de .tabla-scroll con el padding
   normal de th/td (0.55rem 0.6rem) — quedaban con scroll lateral interno
   aunque .tabla-scroll ya existe justo para permitirlo quedando corto por
   poco. Acotado a estas dos tablas (nunca .cifras global, que usan otros
   14 reportes): mismo padding vertical, menos padding horizontal. */
.rep3-compacta th, .rep3-compacta td { padding-left: 0.35rem; padding-right: 0.35rem; }
/* Fecha corta ("1 sep 2026") en la columna Vence de Contratos: sin esto, una
   columna angosta la partía entre palabras ("1 sep" / "2026"). */
.rep3-nowrap { white-space: nowrap; }
/* === fin Tanda 3: Tablero por apartamento / Contratos por vencer === */

/* ── Tanda 3: Gastos mes a mes (2026-09-25) ──────────────────────────────
   Pedido del administrador tras revisar las capturas: en la columna "Diferencia",
   una diferencia NEGATIVA (el renglón gastó MENOS que su promedio) es una
   buena noticia — nunca se pinta con table.cifras td.neg (--bad-fg, rojo,
   reservado para "esto empeoró"). Se muestra en el mismo tono secundario
   que el resto de las cifras de contexto (--ink-soft, el que ya usan .nota
   y .msg); una diferencia positiva se queda en el color normal del texto,
   sin clase — la única alerta real de este reporte es la píldora "Subió"
   (.estado.revisar), nunca el color de la cifra. */
.rep3-diferencia-baja { color: var(--ink-soft); }
/* === fin Tanda 3: Gastos mes a mes === */

/* ── Tanda 4: Gastos sin factura ──────────────────────────────────────────
   Este reporte tiene DOS selectores (Mes y Desde) en el mismo
   form.mes-selector — el max-width:360px de esa clase (pensado para un
   solo <select>) los deja apretados y corta el texto del mes ("Septiembre
   2026 (en curso" sin el paréntesis final, revisión de Opus 2026-09-25). Se
   ensancha solo para esta forma (clase adicional, nunca tocando
   .mes-selector global, que usan otros 20 reportes con un único selector).
   A 358px, en cambio, ni ensanchado caben los dos <select> más el botón en
   una sola fila: se apilan (columna) para que la página no scrollee de lado. */
.rep4-selector-doble { max-width: 620px; }
@media (max-width: 480px) {
  .rep4-selector-doble { flex-direction: column; align-items: stretch; max-width: none; }
}
/* Medido con CDP a 1280px (scratchpad/verif-rep4-sin-factura): la tabla
   principal (8 columnas: fecha, descripción, categoría, apartamento, valor,
   quién pagó, origen, soporte) quedaba 8px más ancha que su .tabla-scroll
   con el padding normal de th/td (0.55rem 0.6rem) — mismo hallazgo que
   Tablero/Contratos en la Tanda 3 (.rep3-compacta), acotado acá a esta
   tabla, nunca a .cifras global. */
.rep4-compacta th, .rep4-compacta td { padding-left: 0.4rem; padding-right: 0.4rem; }
/* === fin Tanda 4: Gastos sin factura === */

/* === Facturación: tablas que encajan y buscador de filas (2026-09-30) ===
   El administrador: "que todo encaje, que no se tenga que mover horizontal la barra".
   `.tabla-ajustada` es OPT-IN (solo las tablas de Facturación la llevan; no
   toca los valores de .tabla-scroll ni de ninguna otra tabla): letra de
   12,5px, relleno angosto y el texto se parte en líneas en vez de estirar la
   columna. Solo lo corto y numérico (fechas, montos, códigos) va con
   .nowrap. El ancho mínimo de cada columna queda en lo que mide su palabra
   más larga, así que a ~1100px de contenido la suma cabe sin barra; en
   pantallas más angostas el .tabla-scroll de afuera vuelve a desplazarse de
   lado (lo aceptable). El encabezado sigue fijo (sticky de .tabla-scroll). */
.tabla-ajustada { width: 100%; font-size: 0.78rem; }
.tabla-ajustada th, .tabla-ajustada td { padding: 0.4rem 0.35rem; vertical-align: top; overflow-wrap: break-word; }
.tabla-ajustada th { font-size: 0.64rem; letter-spacing: 0.02em; }
.tabla-ajustada .nowrap, .nowrap { white-space: nowrap; }
/* El recuadro (.card) de una tabla ajustada pierde parte de su relleno
   lateral: son ~24px más para la tabla, que en una laptop de 1366px
   decide entre que encaje o se desplace. */
.card.tabla-scroll:has(> .tabla-ajustada) { padding-left: 0.6rem; padding-right: 0.6rem; }
.tabla-ajustada .sub { font-size: 0.72rem; color: var(--ink-soft); line-height: 1.3; }
.tabla-ajustada .estado { font-size: 0.66rem; padding: 0.1rem 0.4rem; margin-top: 0.2rem; }
.tabla-ajustada td input[type="text"], .tabla-ajustada td input.input-compacto { width: 100%; min-width: 0; font-size: 0.75rem; padding: 0.25rem 0.4rem; }
.tabla-ajustada td input:not([type="checkbox"]):not([type="radio"]), .tabla-ajustada td select { max-width: 100%; }
.tabla-ajustada td button { font-size: 0.75rem; }
.tabla-ajustada td form { margin: 0; }
.tabla-ajustada td form + form { margin-top: 0.2rem; }
/* Variante para tablas de muchísimas columnas de diagnóstico (p. ej. IVA
   que cobra Airbnb, 28 columnas): más apretada aún. */
.tabla-ajustada.tabla-ajustada-densa { font-size: 0.72rem; }
.tabla-ajustada.tabla-ajustada-densa th, .tabla-ajustada.tabla-ajustada-densa td { padding: 0.3rem 0.25rem; }
.tabla-ajustada.tabla-ajustada-densa th { font-size: 0.58rem; letter-spacing: 0; }

/* Una fila que el buscador oculta (atributo hidden) desaparece aunque otra
   regla de tabla le hubiera puesto display. */
tr[hidden] { display: none; }

.buscador-tabla { display: flex; align-items: center; flex-wrap: wrap; gap: 0.4rem 0.75rem; margin: 0.6rem 0; }
.buscador-tabla input[type="search"] { flex: 1 1 18rem; width: auto; max-width: 36rem; }
.buscador-tabla .btn-text { margin-left: 0; }
.buscador-tabla-contador { font-size: 0.85rem; color: var(--ink-soft); }
.buscador-tabla-nota { flex-basis: 100%; margin: 0; font-size: 0.75rem; color: var(--ink-soft); }
/* === fin Facturación: tablas que encajan y buscador === */

/* === Fase 3a: reclamos y mantenimiento === */
.chips-estado { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 1rem; }
.chips-estado a { display: inline-flex; align-items: center; gap: 0.35rem; padding: 0.3rem 0.7rem; border-radius: 999px; border: 1px solid var(--line); background: var(--surface); color: var(--ink-soft); font-size: 0.78rem; font-weight: 600; text-decoration: none; }
.chips-estado a.activo { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); }
.chips-estado a .n { font-family: var(--font-mono); font-size: 0.72rem; opacity: 0.85; }
.estado.rojo { background: var(--bad-bg); color: var(--bad-fg); border-color: transparent; }
.estado.naranja, .estado.amarillo { background: var(--warn-bg); color: var(--warn-fg); border-color: transparent; }
.pasos-reclamo { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 1rem; padding: 0; list-style: none; counter-reset: paso; }
.pasos-reclamo li { flex: 1 1 7rem; padding: 0.55rem 0.7rem; border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--ink-faint); font-size: 0.8rem; font-weight: 600; counter-increment: paso; }
.pasos-reclamo li::before { content: counter(paso) ". "; font-family: var(--font-mono); }
.pasos-reclamo li.hecho { color: var(--ok-fg); background: var(--ok-bg); border-color: transparent; }
.pasos-reclamo li.actual { color: var(--accent-ink); background: var(--accent); border-color: var(--accent); }
.pasos-reclamo li.rechazado { color: var(--bad-fg); background: var(--bad-bg); border-color: transparent; }
.dos-columnas { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(320px, 100%), 1fr)); gap: 1rem; align-items: start; }
.lista-simple { margin: 0; padding: 0; list-style: none; }
.lista-simple li { padding: 0.5rem 0; border-bottom: 1px solid var(--line-soft); font-size: 0.86rem; }
.lista-simple li:last-child { border-bottom: 0; }
.lista-simple .cuando { color: var(--ink-faint); font-size: 0.74rem; }
.tablero { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(280px, 100%), 1fr)); gap: 1rem; align-items: start; }
.tablero-col { background: var(--surface-sunk); border: 1px solid var(--line); border-radius: 12px; padding: 0.8rem; }
.tablero-col h2 { font-size: 0.95rem; margin: 0 0 0.7rem; display: flex; justify-content: space-between; }
.tarjeta-tarea { background: var(--surface); border: 1px solid var(--line); border-left: 4px solid var(--steel); border-radius: 10px; padding: 0.7rem 0.8rem; margin-bottom: 0.7rem; font-size: 0.84rem; }
.tarjeta-tarea.prio-URGENTE { border-left-color: var(--bad-fg); }
.tarjeta-tarea.prio-ALTA { border-left-color: var(--warn-fg); }
.tarjeta-tarea.prio-MEDIA { border-left-color: var(--accent); }
.tarjeta-tarea h3 { font-size: 0.92rem; margin: 0 0 0.3rem; }
.tarjeta-tarea .meta { color: var(--ink-soft); font-size: 0.76rem; display: flex; flex-wrap: wrap; gap: 0.3rem 0.7rem; margin-bottom: 0.4rem; }
.tarjeta-tarea .acciones { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin-top: 0.5rem; }
.tarjeta-tarea .acciones form { margin: 0; }
.tarjeta-tarea .acciones button { border: 1px solid var(--line); background: var(--surface); color: var(--ink); border-radius: 8px; padding: 0.25rem 0.6rem; font-size: 0.76rem; cursor: pointer; }
.tarjeta-tarea .acciones a { font-size: 0.78rem; }
.tarjeta-tarea .vencida { color: var(--bad-fg); font-weight: 600; }
/* === fin Fase 3a === */

/* ═══════════════════════════════════════════════════════════════════════
   FASE 1 · Diseño del prototipo comercial (2026-10)
   Solo apariencia. Va al final a propósito: gana por orden a las reglas
   viejas de igual especificidad, y los <style> propios de cada vista (que
   cargan después de este archivo) siguen ganando como siempre.
   Referencia: ~/Desktop/StayAccounts/prototipo-stayaccounts.html
   ═══════════════════════════════════════════════════════════════════════ */
html { -webkit-text-size-adjust: 100%; }
body { font-size: 15px; line-height: 1.5; background: var(--bg); color: var(--fg); overflow-x: hidden; }
h1, h2, h3 { font-family: var(--font-display); font-weight: 600; line-height: 1.25; letter-spacing: 0; }
h1 { font-size: 1.45rem; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }
a:focus-visible, button:focus-visible, .nav-link:focus-visible, summary:focus-visible,
input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.mono, code, kbd { font-family: var(--font-mono); }

/* ---- menú lateral: teal oscuro, marca arriba, grupos en mayúscula pequeña ---- */
aside.sidebar { background: var(--side-bg); color: var(--side-fg); border-right: 0; }
.brand { padding: 1.1rem 1rem 0.9rem; border-bottom: 1px solid var(--side-line); }
.brand-word { font-family: var(--font-display); font-weight: 600; font-size: 1.1rem; letter-spacing: 0; color: var(--side-brand); }
.brand-word span { font-family: var(--font-display); font-weight: 600; color: var(--side-dim); font-size: 0.78rem; letter-spacing: 0.12em; }
.brand-sub { color: var(--side-dim); }
nav.groups { padding: 0.7rem 0.6rem 1rem; }
.nav-group { margin-bottom: 1rem; display: flex; flex-direction: column; gap: 2px; }
.nav-group-label { font-size: 0.7rem; letter-spacing: 0.09em; text-transform: uppercase; color: var(--side-dim); padding: 0.7rem 0.65rem 0.25rem; font-weight: 600; }
.nav-link { gap: 0.65rem; padding: 0.5rem 0.65rem; border-radius: 8px; border-left: 0; color: var(--side-fg); font-size: 0.92rem; font-weight: 500; }
.nav-link:hover { background: var(--side-hover); }
.nav-link.active { background: var(--side-active); color: var(--side-active-fg); font-weight: 600; border-left: 0; }
.nav-link .dot { width: 6px; height: 6px; background: var(--side-dim); opacity: 0.8; }
.nav-link.active .dot { background: var(--side-active-fg); opacity: 1; }
.nav-badge { font-family: var(--font-ui); font-size: 0.65rem; font-weight: 700; padding: 1px 7px; border-radius: 99px; background: var(--amber); color: var(--on-amber); }
.nav-group-pie { border-top: 1px solid var(--side-line); padding-top: 0.7rem; }
.registrar { padding: 0.9rem 0.6rem 0; }
.registrar a { border-radius: 8px; font-family: var(--font-ui); font-weight: 600; border-color: var(--side-line); color: var(--side-fg); }
.registrar a:first-child { background: var(--amber); border-color: var(--amber); color: var(--banner-fg); }
.registrar a:hover { filter: none; background: var(--side-hover); }
.registrar a:first-child:hover { background: var(--amber); filter: brightness(1.08); }
.sidebar-foot { border-top: 1px solid var(--side-line); color: var(--side-dim); }
.sidebar-foot strong { color: var(--side-brand); }
.sidebar-foot a { color: var(--side-accent); }
aside.sidebar a:focus-visible, aside.sidebar button:focus-visible { outline-color: var(--amber); }

/* ---- barra superior ---- */
.topbar { background: var(--topbar-bg); border-bottom: 1px solid var(--border); }
.topbar .salir { color: var(--bad); }
.theme-toggle, .hamburger { background: var(--surface); border-color: var(--border); border-radius: 8px; color: var(--fg-muted); }
.theme-toggle:hover { color: var(--fg); border-color: var(--accent); }
.volver { border-radius: 8px; }
.topbar-registrar { background: var(--primary); color: var(--on-primary); }

/* ---- encabezado de página: título en Literata, subtítulo gris, acciones a la derecha ---- */
.cabecera-pagina { align-items: flex-end; margin-bottom: 0.4rem; }
.cabecera-pagina > h1 { margin-bottom: 0.4rem; }
main > h1 { margin-bottom: 0.5rem; }
main > h1 + p, .cabecera-pagina + p, main > h1 + .nota, main > h1 + .nota-chica { color: var(--fg-muted); }

/* ---- tarjetas ---- */
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); box-shadow: var(--shadow); padding: 1rem 1.1rem; }
.card > h2, .card > h3 { margin-bottom: 0.65rem; }
.acceso:hover { border-color: var(--accent); }

/* ---- mosaicos de cifras (tiles) ---- */
.kpi { background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 0.85rem 0.95rem; box-shadow: var(--shadow); min-width: 0; }
.kpi .label { font-size: 0.8rem; text-transform: none; letter-spacing: 0; color: var(--fg-muted); font-weight: 500; margin-bottom: 0.1rem; }
.kpi .valor { font-family: var(--font-display); font-size: 1.5rem; font-weight: 600; letter-spacing: 0; font-variant-numeric: tabular-nums; margin: 0.1rem 0; overflow-wrap: anywhere; }
.gr-cifra { font-family: var(--font-display); }
.big { font-family: var(--font-display); font-variant-numeric: tabular-nums; }

/* ---- tablas ---- */
.tabla-scroll { border-radius: var(--r); }
table { font-size: 0.9rem; }
th, td { padding: 0.55rem 0.65rem; border-bottom: 1px solid var(--border); }
th { background: var(--surface-2); font-size: 0.76rem; letter-spacing: 0.04em; color: var(--fg-muted); font-weight: 600; border-bottom: 1px solid var(--border); }
.tabla-scroll table thead th { background: var(--surface-2); }
tbody tr:hover { background: var(--surface-3); }
tbody tr:last-child td { border-bottom: 0; }
th.num, td.num { text-align: right; font-variant-numeric: tabular-nums; }
td.num { white-space: nowrap; }
.monto, .money { font-variant-numeric: tabular-nums; }
tfoot td, tfoot th { font-weight: 700; background: var(--surface-2); }
table.cifras td.num, table.cifras th.num { font-family: var(--font-ui); font-variant-numeric: tabular-nums; }
tbody tr[id]:target { background: var(--warn-bg); }

/* ---- botones: primario teal, secundario, pequeño ---- */
button.btn, .btn-link {
  background: var(--primary); color: var(--on-primary); border: 1px solid var(--primary);
  border-radius: 8px; padding: 0.5rem 0.95rem; font-family: var(--font-ui); font-weight: 600; font-size: 0.9rem;
  line-height: 1.4; box-shadow: none;
}
button.btn { margin-top: 1rem; }
.btn-link { padding: 0.5rem 0.95rem; }
button.btn:hover, .btn-link:hover { opacity: 1; filter: brightness(1.12); }
button.btn.green { background: var(--good); border-color: var(--good); color: #fff; }
.btn-link.secondary, button.btn.secondary, button.btn.secundario {
  background: var(--surface); color: var(--fg); border: 1px solid var(--border);
}
.btn-link.secondary { margin: 1rem 0 0 0.5rem; }
.btn-link.secondary:hover, button.btn.secondary:hover, button.btn.secundario:hover { background: var(--surface-2); filter: none; }
button.btn:disabled, .btn-link[aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
.btn-text { color: var(--accent); }
.btn-text.danger { color: var(--bad); }
.btn.sm, .btn-link.sm, button.btn.sm { padding: 0.25rem 0.65rem; font-size: 0.82rem; }

/* ---- chips / insignias: good · warn · bad · info · amber ---- */
.estado { padding: 0.12rem 0.6rem; font-size: 0.76rem; background: var(--surface-2); color: var(--fg-muted); border-color: var(--border); }
.estado.ok { background: var(--good-soft); color: var(--good); }
.estado.revisar, .estado.naranja, .estado.amarillo { background: var(--warn-soft); color: var(--warn); }
.estado.anulado, .estado.falta, .estado.rojo { background: var(--bad-soft); color: var(--bad); }
.estado.neutral { background: var(--surface-2); color: var(--fg-muted); border-color: var(--border); }
.estado.info { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.estado.amber { background: var(--amber-soft); color: var(--on-amber); border-color: transparent; }
.chips-estado a.activo, .pasos-reclamo li.actual { background: var(--primary); color: var(--on-primary); border-color: var(--primary); }

/* ---- formularios ---- */
form label { font-size: 0.82rem; font-weight: 600; text-transform: none; letter-spacing: 0; color: var(--fg); margin: 0.6rem 0 0.25rem; }
input, select, textarea { background: var(--surface); border: 1px solid var(--border); border-radius: 8px; padding: 0.45rem 0.65rem; color: var(--fg); }
input:focus, select:focus, textarea:focus { outline: 2px solid var(--focus); outline-offset: 1px; border-color: var(--focus); }
input::placeholder, textarea::placeholder { color: var(--fg-faint); }
input[type="checkbox"], input[type="radio"] { accent-color: var(--primary); }
.select-buscable-item:hover, .select-buscable-item.activo, .cmdk-item.activo, .cmdk-item:hover { background: var(--accent-soft); color: var(--fg); }

/* ---- avisos / alertas ---- */
.msg { border-radius: 8px; background: var(--surface-2); color: var(--fg); }
.msg.ok { background: var(--good-soft); color: var(--good); }
.msg.error { background: var(--bad-soft); color: var(--bad); }
.msg.revisar { background: var(--warn-soft); color: var(--warn); }
.aviso { background: var(--amber-soft); color: var(--on-amber); border: 0; border-radius: 8px; }

/* ---- pestañas ---- */
.pestanas { border-bottom-color: var(--border); }
.pestanas a { color: var(--fg-muted); }
.pestanas a:hover { color: var(--fg); }
.pestanas a.activa, .pestanas a.todos { color: var(--accent); border-bottom-color: var(--accent); }
.pestanas a.todos { border-bottom-color: transparent; }
.subpestanas { background: var(--surface-2); border-color: var(--border); }
.subpestanas a[aria-current] { background: var(--primary); color: var(--on-primary); box-shadow: none; }

/* ---- barras de progreso y estados de pasos ---- */
.barra-rent { background: var(--surface-2); border-color: var(--border); }
.barra-rent-fill { background: var(--accent); }
.pasos-reclamo li.hecho { background: var(--good-soft); color: var(--good); }

/* ---- pantalla de entrada ---- */
body.pantalla-entrada { min-height: 100vh; display: grid; place-items: center; padding: 1.5rem 1rem; }
body.pantalla-entrada main.entrada { width: min(400px, 100%); margin: 0; padding: 0; max-width: none; }
.entrada-marca { display: flex; flex-direction: column; align-items: center; gap: 0.6rem; margin-bottom: 1.2rem; text-align: center; }
.entrada-icono { width: 64px; height: 64px; border-radius: 16px; display: block; }
.entrada-logo-img { max-width: 160px; max-height: 80px; object-fit: contain; border-radius: 10px; background: #fff; padding: 6px 10px; }
.entrada-marca h1 { margin: 0; font-size: 1.6rem; color: var(--fg); }
.entrada-empresa { margin: 0; color: var(--fg-muted); font-weight: 600; font-size: 0.95rem; }
.entrada-card { padding: 1.4rem 1.4rem 1.5rem; margin: 0; }
.entrada-card h1, .entrada-card h2 { font-size: 1.1rem; margin-bottom: 0.4rem; }
.entrada-card p { color: var(--fg-muted); font-size: 0.9rem; }
.entrada-card button.btn { width: 100%; margin-top: 1.1rem; padding: 0.65rem 1rem; }

/* ---- móvil: el menú existente (☰ → panel lateral) con el aspecto nuevo ---- */
@media (max-width: 900px) {
  body { overflow-x: hidden; }
  main, .seccion-tabs { max-width: 100vw; }
  body.sidebar-open aside.sidebar { box-shadow: 0 0 0 100vmax var(--scrim); }
  .sidebar-backdrop { background: var(--scrim); }
}
@media (max-width: 560px) {
  h1 { font-size: 1.3rem; }
  .kpi .valor { font-size: 1.3rem; }
}

/* Una tabla ancha dentro de una tarjeta se desplaza dentro de la tarjeta, no
   estira la página (a 400px la página no debe tener scroll horizontal). */
.card:has(> table) { overflow-x: auto; }

/* ═══ Fase 2 (2026-10): menú agrupado, marca y encabezado de página ═══ */

/* ---- marca: cuadro con iniciales (o el logo) + StayAccounts + la empresa ---- */
.brand-mark { gap: 10px; }
.brand-icono, .brand-logo {
  width: 34px; height: 34px; border-radius: 9px; flex: none;
}
.brand-icono { display: block; }
.brand-logo { max-height: none; object-fit: contain; background: #fff; padding: 3px; }
.brand-textos { min-width: 0; color: var(--side-brand); }
.brand-nombre { display: block; font-family: var(--font-display); font-size: 1.05rem; font-weight: 600; line-height: 1.1; }
.brand-empresa { display: block; font-size: 0.75rem; opacity: 0.75; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- menú: grupos con título, ícono de línea y etiqueta "Nuevo" ---- */
.nav-link .ico { flex: none; }
.nav-link .nav-txt { flex: 1; min-width: 0; }
.nav-link .nav-badge { margin-left: 0; }
.nav-nuevo { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--amber); }
.nav-group + .nav-group { margin-top: 0.2rem; }

/* ---- encabezado de página unificado (partials/encabezado-pagina.ejs) ---- */
.phead { display: flex; flex-wrap: wrap; gap: 8px 16px; align-items: flex-end; justify-content: space-between; margin: 0 0 1.1rem; }
.phead > div:first-child { min-width: 0; }
.phead h1 { margin: 0; overflow-wrap: anywhere; }
.phead p { margin: 2px 0 0; color: var(--fg-muted); max-width: 70ch; }
.phead-acciones { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.phead-acciones .btn-link, .phead-acciones button.btn { margin: 0; }

/* ---- Inicio: mosaicos sin tarjeta dentro de tarjeta, atención y cierre ---- */
.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr)); gap: 12px; margin-bottom: 1.1rem; }
.tile { display: block; background: var(--surface); border: 1px solid var(--border); border-radius: var(--r); padding: 14px; box-shadow: var(--shadow); min-width: 0; text-decoration: none; color: inherit; }
a.tile:hover { border-color: var(--accent); }
.tile .l { font-size: 0.8rem; color: var(--fg-muted); }
.tile .v { font-family: var(--font-display); font-size: 1.5rem; font-weight: 600; margin: 2px 0; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.tile .s { font-size: 0.8rem; color: var(--fg-muted); }
.tile .delta-periodo { font-size: 0.8rem; color: var(--fg-muted); }
.seccion-titulo { margin: 1.4rem 0 0.6rem; font-size: 1.05rem; }
.g2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr)); gap: 16px; align-items: start; margin-bottom: 1.1rem; }
.g2 > .card { margin: 0; }
.card-cabecera { display: flex; flex-wrap: wrap; gap: 6px 12px; align-items: center; justify-content: space-between; margin-bottom: 0.65rem; }
.card-cabecera h2 { margin: 0; }
.lista-atencion { list-style: none; margin: 0; padding: 0; }
.lista-atencion li { display: flex; gap: 12px; align-items: center; justify-content: space-between; padding: 10px 0; border-top: 1px solid var(--border); min-width: 0; }
.lista-atencion li:first-child { border-top: 0; }
.lista-atencion li > a:not(.btn-link) { color: inherit; text-decoration: none; min-width: 0; flex: 1; text-align: left; }
.lista-atencion li > a:not(.btn-link):hover b { text-decoration: underline; }
.lista-atencion li > a:not(.btn-link) b { display: block; font-weight: 600; }
.lista-atencion .detalle { font-size: 0.82rem; color: var(--fg-muted); }
.chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px; border-radius: 99px; font-size: 0.76rem; font-weight: 600; white-space: nowrap; background: var(--surface-2); color: var(--fg-muted); border: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.chip.good { background: var(--good-soft); color: var(--good); border-color: transparent; }
.chip.warn { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.chip.bad { background: var(--bad-soft); color: var(--bad); border-color: transparent; }
.chip.info { background: var(--accent-soft); color: var(--accent); border-color: transparent; }
.chip.amber { background: var(--amber-soft); color: var(--on-amber); border-color: transparent; }
.barra { height: 8px; border-radius: 99px; background: var(--surface-2); overflow: hidden; border: 1px solid var(--border); margin: 10px 0; }
.barra > span { display: block; height: 100%; background: var(--amber); border-radius: 99px; }

/* ajustes tras ver las capturas de la Fase 2 */
.nav-group { margin-bottom: 0.35rem; }
.nav-group-label { padding: 0.55rem 0.65rem 0.2rem; }
.nav-link { padding-top: 0.4rem; padding-bottom: 0.4rem; }
.lista-atencion li { flex-wrap: wrap; }
.lista-atencion li > .chip:first-child { min-width: 2.2rem; justify-content: center; }
@media (max-width: 560px) {
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .tile { padding: 12px; }
  .tile .v { font-size: 1.3rem; }
  .lista-atencion li > a:not(.btn-link) { flex-basis: 10rem; }
}

/* ═══ Fase 3 (2026-10): menú compacto, enlaces de tabla y repaso ═══ */
@media (min-width: 901px) {
  .nav-group-label { padding: 0.4rem 0.65rem 0.1rem; }
  .nav-link { padding-top: 0.25rem; padding-bottom: 0.25rem; }
  .nav-group { gap: 1px; margin-bottom: 0.2rem; }
  nav.groups { padding-top: 0.4rem; padding-bottom: 0.6rem; }
}
.nav-link .nav-txt { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* enlaces dentro de tablas: color de acento, sin subrayado; subrayado al pasar el mouse */
td a:not(.btn-link):not(.btn):not(.btn-text), th a:not(.btn-link):not(.btn):not(.btn-text) { color: var(--accent); text-decoration: none; }
td a:not(.btn-link):not(.btn):not(.btn-text):hover, th a:not(.btn-link):not(.btn):not(.btn-text):hover { text-decoration: underline; text-underline-offset: 2px; }
.btn-text { text-decoration: none; }
.btn-text:hover:not(:disabled) { text-decoration: underline; text-underline-offset: 2px; }

/* Selectores de fecha/filtro en una fila: en pantallas angostas pasan a línea siguiente */
.mes-selector { flex-wrap: wrap; }
.mes-selector label { min-width: 0; }
@media (max-width: 560px) { .mes-selector label { flex: 1 1 8rem; } }

/* Mosaicos de cifras directamente dentro de una tarjeta: sin tarjeta dentro de tarjeta */
.card:has(> .kpi:first-child), .grid.card:has(> .kpi) { background: transparent; border: 0; box-shadow: none; padding: 0; }

/* Fechas y cifras de tabla no parten línea; los enlaces de acción tampoco */
td.fecha, td.num, td:has(> a.btn-text:only-child) { white-space: nowrap; }

/* Chips de estado = chip del prototipo */
.estado { display: inline-flex; align-items: center; gap: 4px; padding: 2px 9px; font-size: 0.76rem; font-weight: 600; border-radius: 99px; line-height: 1.4; max-width: 100%; }
.phead + p, .phead + .nota, .phead + .nota-chica { color: var(--fg-muted); margin-top: -0.5rem; }

.msg, .aviso { overflow-wrap: anywhere; }
@media (max-width: 560px) { .grid:has(> .kpi) { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; } .grid:has(> .kpi) .kpi { padding: 0.7rem 0.75rem; } }
