/* styles.css — mismo sistema de diseño de los mockups de PowerPoint
   (colores, tarjetas, botones, pills, toggles) aplicado a la app real.
   Convertido a variables CSS para soportar modo claro/oscuro — el atributo
   data-theme="dark" en <html> activa el segundo bloque de variables. */

:root {
  --bg-page: #F5F6FA;
  --text-main: #161B33;
  --sidebar-bg: #161B33;
  --sidebar-item: #AFB6D6;
  --sidebar-item-active-bg: #232A52;
  --sidebar-item-hover-bg: #1D2547;
  --topbar-bg: #fff;
  --topbar-border: #E6E8EE;
  --card-bg: #fff;
  --card-shadow: 0 2px 10px rgba(20,20,60,.06);
  --border-soft: #E3E5EE;
  --border-softer: #EEF0F6;
  --muted: #8A90A6;
  --field-bg: #fff;
  --table-header: #8A90A6;
  --table-border: #EEF0F6;
  --btn-ghost-bg: #fff;
  --btn-secondary-bg: #EAF0FA;
  --btn-secondary-color: #2F5496;
  --toggle-off: #D7DEEE;
  --avatar-bg: #D7DEEE;
  --bell-panel-bg: #fff;
  --bell-item-hover: #F7FAFF;
  --bell-item-border: #F2F3F8;
  --input-error-bg: #FEF4F4;

  /* ---------------------------------------------------------------- gráficos
     Paleta de datos VALIDADA (ver client/src/charts/tokens.js). No se cambia
     un valor de aquí sin volver a pasar el validador: el orden de los tonos y
     su separación bajo daltonismo son el motivo de que existan estos hex y no
     otros más bonitos. */
  --viz-1: #2F5496;   /* azul     · la serie principal, la de la marca */
  --viz-2: #E0651F;   /* naranja  */
  --viz-3: #0E9F7E;   /* verde azulado */
  --viz-4: #8A5CF0;   /* violeta  */
  --viz-5: #B87800;   /* ámbar    · solo con etiqueta directa */
  --viz-6: #D6558C;   /* magenta  · solo con etiqueta directa */

  --viz-seq-1: #93B2E2; --viz-seq-2: #7196D2; --viz-seq-3: #4F79BC;
  --viz-seq-4: #3A5F9C; --viz-seq-5: #25436F;

  --viz-div-bajo:  #0E9F7E;
  --viz-div-medio: #C7CBD8;
  --viz-div-alto:  #C2410C;

  --viz-bien: #16743F; --viz-atencion: #B87800; --viz-critico: #B42318;
  --viz-neutro: #8A90A6;

  --viz-superficie: #FFFFFF;
  --viz-rejilla: #EDEFF5;
  --viz-eje: #C9CCD8;
  --viz-tinta: #161B33;
  --viz-tinta-suave: #6B7189;
  --viz-pista: #F0F2F8;      /* el canal vacío de una barra de progreso */
  /* Fondos de las etiquetas de estado. Existen porque los hex que había
     escritos a mano en las pantallas NO cambiaban con el tema: un chip rosa
     claro con texto rojo claro sobre una tarjeta oscura es ilegible, y así
     estaba media app en modo noche. */
  --viz-bien-bg: #E7F8EE; --viz-atencion-bg: #FFF4D6; --viz-critico-bg: #FDECEC;


  /* Paneles hundidos dentro de una tarjeta: el desglose de una factura, el
     informe de un agente, lo que se va a borrar en una confirmación. Sin
     estas variables cada pantalla los pintaba con un blanco fijo — y en modo
     noche se convertían en un bloque blanco con texto blanco encima. */
  --bg-soft: #F7F9FC;
  --bg-raised: #FFFFFF;

  /* Y el panel de "esto pide algo de ti": la barra de facturas pendientes del
     cliente, el recálculo de tasas. */
  --attention-bg: #FFF9E9;
  --attention-border: #F0D9A0;
  --attention-text: #8A5A00;
}
html[data-theme="dark"] {
  --bg-page: #0E1226;
  --text-main: #E8E9F0;
  --sidebar-bg: #0A0D1E;
  --sidebar-item: #8991B8;
  --sidebar-item-active-bg: #1A2040;
  --sidebar-item-hover-bg: #151A33;
  --topbar-bg: #161B33;
  --topbar-border: #262C4D;
  --card-bg: #1A1F3B;
  --card-shadow: 0 2px 14px rgba(0,0,0,.35);
  --border-soft: #2C3358;
  --border-softer: #262C4D;
  --muted: #8991B8;
  --field-bg: #12162C;
  --table-header: #8991B8;
  --table-border: #262C4D;
  --btn-ghost-bg: #1A1F3B;
  --btn-secondary-bg: #202850;
  --btn-secondary-color: #8FB4F0;
  --toggle-off: #2C3358;
  --avatar-bg: #262C4D;
  --bell-panel-bg: #1A1F3B;
  --bell-item-hover: #202850;
  --bell-item-border: #262C4D;
  --input-error-bg: #3A1F22;

  /* --------------------------------------------------- gráficos (tema oscuro)
     NO es el claro invertido: son los mismos seis tonos re-escalonados contra
     la superficie #1A1F3B y validados otra vez contra ella. */
  --viz-1: #5385DB;
  --viz-2: #D96F33;
  --viz-3: #22A382;
  --viz-4: #9B6BEB;
  --viz-5: #B0851F;
  --viz-6: #D9639A;

  --viz-seq-1: #3A5896; --viz-seq-2: #4A72BE; --viz-seq-3: #5E8FDB;
  --viz-seq-4: #84AAE8; --viz-seq-5: #AAC6F2;

  --viz-div-bajo:  #22A382;
  --viz-div-medio: #4A5170;
  --viz-div-alto:  #D9722F;

  --viz-bien: #3FA76B; --viz-atencion: #B08F00; --viz-critico: #DE4F63;
  --viz-neutro: #8991B8;

  --viz-superficie: #1A1F3B;
  --viz-rejilla: #262C4D;
  --viz-eje: #3A4266;
  --viz-tinta: #E8E9F0;
  --viz-tinta-suave: #A2A9C9;
  --viz-pista: #232A52;
  --viz-bien-bg: #14301F; --viz-atencion-bg: #2E2513; --viz-critico-bg: #351A1E;


  --bg-soft: #12162C;
  --bg-raised: #232A52;

  --attention-bg: #2E2513;
  --attention-border: #5A4A1E;
  --attention-text: #F0C572;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html, body, #root { height: 100%; }
body {
  font-family: Arial, "Helvetica Neue", sans-serif;
  background: var(--bg-page);
  color: var(--text-main);
  transition: background .15s ease, color .15s ease;
}
a { color: inherit; }
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; font-size: 13.5px; }

/* ---------- LAYOUT ---------- */
.app-shell { display: flex; min-height: 100vh; }
.sidebar {
  width: 250px; flex: 0 0 auto; background: var(--sidebar-bg); padding: 26px 0;
  display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; overflow: hidden;
}
.sb-brand { color: #fff; font-weight: 800; font-size: 18px; padding: 0 26px 26px 26px; }
.sb-role { color: #7C87B8; font-size: 11px; font-weight: 700; padding: 0 26px 18px; text-transform: uppercase; letter-spacing: .04em; }
.sb-item {
  display: flex; align-items: center; gap: 12px; padding: 13px 26px; color: var(--sidebar-item); font-weight: 600;
  font-size: 14px; text-decoration: none; border: none; background: none; width: 100%; text-align: left;
}
.sb-item .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--sidebar-item); flex: 0 0 auto; }
.sb-item.active { background: var(--sidebar-item-active-bg); color: #fff; }
.sb-item.active .dot { background: #5FA8FF; }
.sb-item:hover:not(.active) { background: var(--sidebar-item-hover-bg); color: #fff; }
.sb-item.highlight { background: rgba(245, 166, 35, 0.14); color: #FFD9A0; font-weight: 800; }
.sb-item.highlight .dot { background: #F5A623; width: 9px; height: 9px; }
.sb-item.highlight:hover:not(.active) { background: rgba(245, 166, 35, 0.24); color: #fff; }
.sb-item.highlight.active { background: #F5A623; color: #1E2761; }
.sb-item.highlight.active .dot { background: #1E2761; }
.sb-highlight-divider { border: none; border-top: 1px solid rgba(255,255,255,0.12); margin: 10px 26px 6px; }
.sb-group-header {
  display: flex; align-items: center; justify-content: space-between; width: 100%;
  padding: 16px 26px 8px; background: none; border: none; color: #7C87B8; font-weight: 800;
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; text-align: left; cursor: pointer;
}
.sb-group-header:hover { color: #AFB6D6; }
.sb-spacer { flex: 1; }

/* El menú se desplaza; el pie NO. Antes el scroll estaba en el cajón
   entero y el botón de cerrar sesión, que vive al final, se cruzaba con la
   barra inferior del celular: había que pelearse con el dedo para llegar,
   y muchas veces no se llegaba. Ahora el pie está anclado y siempre a la
   vista, y el relleno que lo separa de la barra va sobre un elemento que
   no se desplaza, donde el navegador sí lo respeta siempre. */
.sb-scroll { position: relative; flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.sb-pie {
  flex: 0 0 auto;
  border-top: 1px solid rgba(255, 255, 255, .12);
  padding-top: 10px;
  background: var(--sidebar-bg);
}

/* Cuando el menu tiene mas destinos de los que caben, el borde del pie deja
   de ser una linea limpia y se convierte en un desvanecido: es la senal
   universal de "sigue hacia abajo". Sin ella, el menu simplemente terminaba
   — y dos pantallas reales (Preguntas frecuentes y Seguridad) dejaban de
   existir para quien no adivinara que ahi se podia desplazar. */
.sb-pie { position: relative; }
.sb-pie::before {
  content: "";
  position: absolute; left: 0; right: 0; bottom: 100%;
  height: 44px;
  background: linear-gradient(to top, var(--sidebar-bg) 30%, transparent);
  pointer-events: none;
  opacity: 0;
  transition: opacity .18s ease;
}
.sidebar.hay-mas .sb-pie::before { opacity: 1; }
.sidebar.hay-mas .sb-pie { border-top-color: transparent; }
.sb-mas-cue {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(100% + 2px);
  display: flex; align-items: center; gap: 5px;
  padding: 3px 10px; border-radius: 999px;
  background: var(--sidebar-bg);
  box-shadow: 0 0 0 4px var(--sidebar-bg);
  font-size: 10px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
  color: #7C87B8; pointer-events: none; opacity: 0; transition: opacity .18s ease;
}
.sidebar.hay-mas .sb-mas-cue { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .sb-pie::before, .sb-mas-cue { transition: none; }
}
.sb-cuenta { display: none; }
.sb-logout { margin: 10px 16px 0; }
.sb-instalar { margin: 10px 16px 0; }
.sb-instalar:empty { display: none; }

.main-area { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.topbar {
  height: 70px; background: var(--topbar-bg); border-bottom: 1px solid var(--topbar-border); display: flex; align-items: center;
  justify-content: space-between; padding: 0 32px; position: sticky; top: 0; z-index: 20;
}
/* El título puede ser largo (ej. "Delegaciones de aprobación") y el lado
   derecho (idioma, tema, campana, avatar) tiene ancho fijo — en pantallas
   angostas, sin esto, el título empujaba el topbar entero más ancho que
   la pantalla (scroll horizontal). Se deja que el título se achique y
   corte con "…" en vez de desbordar. */
.topbar-title-row { flex: 1 1 auto; min-width: 0; }
.topbar-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.main { flex: 1; padding: 32px 40px; max-width: 1280px; width: 100%; }

/* ---------- CALLOUT (numerito guía, igual que en el mockup) ---------- */
.callout { position: relative; }
.callout::before {
  content: attr(data-n);
  position: absolute; top: -11px; left: -11px; width: 26px; height: 26px; border-radius: 50%;
  background: #1E2761; color: #fff; font: 800 13px Arial; display: flex; align-items: center; justify-content: center;
  box-shadow: 0 3px 8px rgba(0,0,0,.35); z-index: 5; border: 2px solid #fff;
}

/* ---------- COMPONENTES ---------- */
.card { background: var(--card-bg); border-radius: 16px; padding: 20px; box-shadow: var(--card-shadow); }
/* Arregla un problema clásico de flexbox: por defecto, un item de flex no se
   encoge por debajo del ancho mínimo de su contenido (ej. una tabla ancha
   adentro), aunque tenga flex-shrink. Esto hacía que varias tarjetas en
   fila se salieran de la pantalla en celular en vez de apilarse bien. */
.row.wrap > .card { min-width: 0; }
.h1 { font-size: 24px; font-weight: 800; color: var(--text-main); }
.h2 { font-size: 16px; font-weight: 800; color: var(--text-main); }
.muted { color: var(--muted); font-size: 12.5px; font-weight: 600; }
.pill { border-radius: 999px; padding: 6px 14px; font-size: 12px; font-weight: 700; display: inline-block; }
.btn-primary {
  background: #2F5496; color: #fff; border: none; border-radius: 12px; font-weight: 800; font-size: 14px;
  padding: 12px 20px; text-align: center;
}
.btn-primary:disabled { opacity: .55; cursor: not-allowed; }
.btn-secondary {
  background: var(--btn-secondary-bg); color: var(--btn-secondary-color); border: none; border-radius: 12px; font-weight: 800; font-size: 13.5px;
  padding: 11px 18px; text-align: center;
}
.btn-ghost {
  background: var(--btn-ghost-bg); color: var(--text-main); border: 1.5px solid var(--border-soft); border-radius: 12px; font-weight: 700; font-size: 13.5px;
  padding: 10px 16px;
}
.btn-danger { background: #FDECEC; color: #B42318; border: none; border-radius: 12px; font-weight: 800; font-size: 13.5px; padding: 10px 16px; }
html[data-theme="dark"] .btn-danger { background: #3A1F22; color: #F29A9A; }
.btn-sm { padding: 7px 12px; font-size: 12.5px; }
.row { display: flex; align-items: center; }
.between { justify-content: space-between; }
.wrap { flex-wrap: wrap; }
.gap6{gap:6px;} .gap8{gap:8px;} .gap10{gap:10px;} .gap12{gap:12px;} .gap14{gap:14px;} .gap16{gap:16px;} .gap20{gap:20px;}
.col { display: flex; flex-direction: column; }
.avatar { width: 38px; height: 38px; border-radius: 50%; background: var(--avatar-bg); display: flex; align-items: center; justify-content: center; font-weight: 800; color: #2F5496; font-size: 13px; flex: 0 0 auto; }
.icon-badge { width: 38px; height: 38px; border-radius: 11px; display: flex; align-items: center; justify-content: center; font-size: 17px; font-weight: 800; color: #fff; flex: 0 0 auto; }

.toggle { width: 44px; height: 26px; border-radius: 999px; position: relative; flex: 0 0 auto; border: none; background: var(--toggle-off); }
.toggle.on { background: #2F5496; }
.toggle::after { content: ""; position: absolute; top: 3px; width: 20px; height: 20px; border-radius: 50%; background: #fff; box-shadow: 0 1px 3px rgba(0,0,0,.3); left: 3px; transition: left .12s ease; }
.toggle.on::after { left: 21px; }
.toggle:disabled { opacity: .55; cursor: not-allowed; }

.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 12px; font-weight: 700; color: var(--muted); }
.field .required-mark { color: #B42318; font-weight: 800; }
.field input, .field select, .field textarea {
  border: 1.5px solid var(--border-soft); border-radius: 10px; padding: 11px 14px; font-weight: 600; color: var(--text-main); background: var(--field-bg); width: 100%;
}
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: #2F5496; }
.field .error { color: #B42318; font-size: 11.5px; font-weight: 700; }
.field.has-error input, .field.has-error select, .field.has-error textarea { border-color: #B42318; background: var(--input-error-bg); }

table.data-table { width: 100%; border-collapse: collapse; }
table.data-table th { text-align: left; font-size: 11.5px; color: var(--table-header); font-weight: 700; text-transform: uppercase; letter-spacing: .03em; padding: 10px 12px; border-bottom: 2px solid var(--table-border); }
table.data-table td { padding: 12px; border-bottom: 1px solid var(--table-border); font-size: 13.5px; color: var(--text-main); }
table.data-table tr:last-child td { border-bottom: none; }

.banner { border-radius: 12px; padding: 14px 18px; font-size: 13px; font-weight: 700; }
.banner.info { background: #EAF0FA; color: #2F5496; }
.banner.warn { background: #FFF4D6; color: #92610A; }
.banner.error { background: #FDECEC; color: #B42318; }
.banner.success { background: #E7F8EE; color: #16743F; }
html[data-theme="dark"] .banner.info { background: #1A2A4D; color: #8FB4F0; }
html[data-theme="dark"] .banner.warn { background: #3D2F0A; color: #F0C572; }
html[data-theme="dark"] .banner.error { background: #3A1F22; color: #F29A9A; }
html[data-theme="dark"] .banner.success { background: #163A26; color: #7ED9A3; }

.empty-state { text-align: center; padding: 48px 20px; color: var(--muted); }

/* ---------- NOTIFICATION BELL ---------- */
.bell-wrap { position: relative; }
.bell-btn { width: 40px; height: 40px; border-radius: 12px; border: 1.5px solid var(--border-soft); background: var(--card-bg); display: flex; align-items: center; justify-content: center; font-size: 18px; position: relative; color: var(--text-main); }
.bell-dot { position: absolute; top: -4px; right: -4px; background: #B42318; color: #fff; font-size: 10px; font-weight: 800; border-radius: 999px; min-width: 17px; height: 17px; display: flex; align-items: center; justify-content: center; padding: 0 4px; border: 2px solid var(--card-bg); }
.bell-panel { position: absolute; right: 0; top: 50px; width: 360px; max-height: 420px; overflow-y: auto; background: var(--bell-panel-bg); border-radius: 14px; box-shadow: 0 20px 50px rgba(20,20,60,.2); border: 1px solid var(--border-softer); z-index: 50; }
.bell-item { padding: 14px 16px; border-bottom: 1px solid var(--bell-item-border); }
.bell-item.unread { background: var(--bell-item-hover); }
.bell-item:last-child { border-bottom: none; }

.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: #1E2761; padding: 20px; }
.login-card { background: #fff; border-radius: 20px; padding: 40px; width: 100%; max-width: 400px; box-shadow: 0 30px 80px rgba(0,0,0,.35); }

.chart-bars { display: flex; align-items: flex-end; gap: 8px; height: 200px; }
.chart-bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 200px; }
.chart-bar { width: 64%; border-radius: 5px 5px 0 0; }

.mobile-menu-btn { display: none; font-size: 18px; padding: 8px 12px; }
.sidebar-backdrop { display: none; }

/* ---------- Selector de tema (claro/oscuro) ---------- */
.theme-toggle-btn {
  width: 40px; height: 40px; border-radius: 12px; border: 1.5px solid var(--border-soft); background: var(--card-bg);
  display: flex; align-items: center; justify-content: center; color: var(--text-main); flex: 0 0 auto;
}

/* ====================================================================
   MÓVIL
   ====================================================================
   La app se usa en el celular tanto como en el escritorio: el especialista
   registra sus horas desde el teléfono y el gerente aprueba desde el suyo.
   Hasta acá había UNA regla de móvil en toda la hoja, y el resto del
   comportamiento en pantalla chica era casualidad.

   Lo que sigue es el sistema, no parches. Cada bloque dice qué rompía. */

/* ---------- 1. El zoom de iOS: el más caro de todos ----------
   Safari en iPhone hace zoom automático sobre cualquier campo cuya letra
   mida menos de 16px, y NO se devuelve al salir: la persona queda con la
   app ampliada y desplazada, y tiene que pellizcar para volver. Toda la
   app tenía los campos a 13.5px, así que pasaba en cada formulario.

   La salida NO es `user-scalable=no` —eso le quita a la gente la
   posibilidad de ampliar, que es una herramienta de accesibilidad real—
   sino subir la letra a 16px donde el zoom se dispara. */
@media (max-width: 860px) {
  input, select, textarea,
  .field input, .field select, .field textarea {
    font-size: 16px;
  }
}

/* ---------- 2. La muesca y la barra de gestos ----------
   Con la app instalada en un iPhone, el contenido llega hasta el borde
   físico de la pantalla. Sin esto, el título del topbar queda debajo del
   reloj y los botones de abajo debajo de la barra de gestos. */
/* El topbar no solo se aparta de la muesca: CRECE hacia arriba para
   ocuparla. Con `black-translucent`, el contenido de la app arranca en el
   píxel 0 de la pantalla —debajo del reloj y la batería—, así que la
   franja del reloj tiene que ser parte de la barra y no del contenido. */
.topbar {
  padding-left: max(32px, env(safe-area-inset-left));
  padding-right: max(32px, env(safe-area-inset-right));
  padding-top: env(safe-area-inset-top);
  height: calc(70px + env(safe-area-inset-top));
}
.sidebar { padding-top: max(26px, env(safe-area-inset-top)); padding-bottom: max(26px, env(safe-area-inset-bottom)); }

/* ---------- 3. La altura que miente ----------
   `100vh` en un navegador móvil cuenta la pantalla ENTERA, incluida la
   barra de direcciones que está encima. El resultado: la app siempre mide
   unos 60px más que el hueco real y aparece un scroll vertical fantasma.
   `dvh` es la altura que de verdad queda. Se deja `vh` primero como
   respaldo para los navegadores que no lo entienden. */
.app-shell { min-height: 100vh; min-height: 100dvh; }
.sidebar { height: 100vh; height: 100dvh; }
.login-wrap { min-height: 100vh; min-height: 100dvh; }

/* ---------- 4. Nada empuja la página a lo ancho ----------
   Un solo elemento más ancho que la pantalla hace que TODA la página se
   desplace de lado, y el efecto se siente como si la app estuviera rota
   aunque el resto esté bien. */
/* AQUÍ HABÍA UN `overflow-x: clip` Y HUBO QUE QUITARLO.
   La intención era buena: que un solo elemento demasiado ancho no hiciera
   deslizar la página entera de lado. El efecto real fue el contrario de lo
   que se buscaba — cuando algo SÍ se pasaba de ancho, el recorte lo volvía
   a la vez invisible e inalcanzable: se veía cortado y no había forma de
   llegar a ello.

   Es una tapadera, no un arreglo. Y en un piloto la diferencia importa: una
   página que se desliza de lado es fea pero USABLE, y además se diagnostica
   sola porque se ve qué la está empujando. Una recortada es un misterio.

   Lo que evita el deslizamiento es que nada sea más ancho que la pantalla:
   las reglas de abajo (`.row > * { min-width: 0 }`, las pills, las tablas
   con su propio scroll, la rejilla del tablero). Si algo se escapa, que se
   vea. */
.pill { max-width: 100%; overflow-wrap: anywhere; }
input[type="file"] { max-width: 100%; }
img, svg, video { max-width: 100%; }

/* Contenedor estándar de tabla ancha. Existe para que en el JSX se lea
   `<div className="tabla-scroll">` y se note cuándo falta, en vez de
   repetir el mismo `style` a mano en doce sitios y olvidarlo en dos. */
.tabla-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; max-width: 100%; }

@media (max-width: 860px) {
  /* ---------- 5. El menú lateral ---------- */
  .sidebar { position: fixed; left: -270px; width: 260px; transition: left .18s ease; z-index: 100; box-shadow: 0 0 40px rgba(0,0,0,.3); overscroll-behavior: contain; }
  .sidebar.open { left: 0; }
  .sidebar-backdrop { display: block; position: fixed; inset: 0; background: rgba(10,12,30,.45); z-index: 90; }

  .topbar { padding-left: max(14px, env(safe-area-inset-left)); padding-right: max(14px, env(safe-area-inset-right)); gap: 8px; }
  .topbar-user-text { display: none; }
  .main { padding: 16px; padding-left: max(16px, env(safe-area-inset-left)); padding-right: max(16px, env(safe-area-inset-right)); }

  .bell-panel { position: fixed; left: 12px; right: 12px; width: auto; top: calc(74px + env(safe-area-inset-top)); max-height: 70vh; max-height: 70dvh; }

  /* ---------- 6. Las filas que no envolvían ----------
     `.row` es `display:flex` sin `flex-wrap`, y está en casi todas las
     pantallas: dos botones con texto largo, un select ancho junto a una
     etiqueta, una pill junto a un nombre de archivo. En el escritorio
     entra todo; en 320px útiles, no — y como flex no envuelve por
     defecto, en vez de bajar de línea se sale de la pantalla.

     Era el defecto más repetido de las dos auditorías: unas cuarenta
     incidencias en veintiséis archivos. Envolver por defecto en móvil las
     arregla todas a la vez, y donde de verdad haga falta una sola línea
     está `.row.nowrap`. */
  .row:not(.nowrap) { flex-wrap: wrap; }

  /* Y el complemento: un hijo de flex no se encoge por debajo del ancho
     de su contenido salvo que se le diga. Sin esto, una tarjeta con una
     tabla adentro se niega a achicarse y arrastra la fila entera.
     Va sobre `.row > *` y no sobre `.row.wrap > *`: en el celular envuelven
     TODAS las filas, no solo las que traen la clase escrita. */
  .row > * { min-width: 0; }

  /* ---------- 7. Lo que se toca con el dedo ----------
     Apple pide 44px, Android 48dp. `.btn-sm` medía 29px de alto, y hay
     una docena de botones que solo llevan un icono de 13px dentro: eran
     blancos de ~30px para un dedo de ~45px. No es incomodidad, es error
     de pulsación — y en una pantalla de aprobaciones, pulsar mal tiene
     consecuencias. */
  .btn-primary, .btn-secondary, .btn-ghost, .btn-danger, .btn-sm {
    min-height: 44px;
  }
  .btn-sm { padding: 10px 14px; font-size: 13px; }
  /* El interruptor mide 44x26. Agrandarlo con relleno para llegar a los
     44 de alto habria movido la perilla, que se posiciona a 3px del borde:
     habria quedado flotando encima de la pastilla. Se agranda solo el
     BLANCO que recibe el dedo, con un pseudo-elemento invisible, y el
     dibujo se queda exactamente donde estaba. */
  .toggle { position: relative; }
  .toggle::before {
    content: ""; position: absolute; left: 0; right: 0;
    top: -9px; bottom: -9px;
  }

  /* ---------- 8. El texto diminuto ----------
     Quince archivos escriben `fontSize: 9.5` o `10` en línea, casi siempre
     dentro de una pill. Diez píxeles en un celular no se leen: se
     adivinan. El `!important` es feo y es a propósito — es la única forma
     de ganarle a un estilo en línea, y la alternativa era tocar cuarenta
     sitios y volver a olvidarlo en el próximo. */
  .pill { font-size: 11.5px !important; padding: 5px 11px; }

  /* Red de seguridad para TODA tabla ancha. Doce sitios de la app arman a
     mano su `<div style={{overflowX:"auto"}}>` alrededor de la tabla, y en
     seis se olvidó — es lo que pasa siempre con lo que hay que acordarse
     de repetir. En vez de envolver seis a mano y confiar en que el próximo
     se acuerde, la regla se pone una vez aquí.
     `display:block` sobre una tabla es el truco de siempre: convierte la
     caja en un bloque que puede desplazarse sin romper la rejilla interna
     de filas y columnas. */
  table.data-table, table.viz-tabla {
    display: block;
    max-width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* Y el segundo motivo del desborde del tablero, que sobrevivía al primero.
     Los bloques anchos piden `grid-column: span 2` por estilo en línea. En
     una rejilla que en el celular tiene UNA sola columna, pedir dos no se
     ignora: CSS Grid crea una segunda columna implícita y la rejilla vuelve
     a medir el doble de la pantalla.

     El `!important` es feo y es la única forma de ganarle a un estilo en
     línea. Va sobre los hijos y no sobre un componente concreto para que
     valga también para el bloque ancho que alguien añada dentro de un año:
     en una rejilla de una columna, nada abarca dos. */
  .tablero > *, .tablero-2 > * { grid-column: span 1 !important; }

  /* Las tarjetas de impresión (cuenta de cobro, factura) usan 32px de
     relleno fijo: en 360px eso deja 256px de contenido. */
  #invoice-print-area, .card-impresion { padding: 16px !important; }

  .login-card { padding: 26px 22px; }
  .h1 { font-size: 21px; }
  .empty-state { padding: 32px 14px; }
  .modal-cerrar { min-width: 44px; min-height: 44px; }
}

/* En pantallas de 320px (iPhone SE, y varios Android de gama baja que
   siguen en uso en los equipos de campo) cualquier `minWidth` en línea
   desborda. */
@media (max-width: 400px) {
  .row > *, .card { min-width: 0 !important; }

  /* PERO ENCOGERSE NO ES ENVOLVER, y era lo que estaba pasando.
     El `min-width: 0 !important` de arriba anula el `minWidth: 200` en linea
     de las tarjetas de indicadores. Sin ese suelo, cuatro tarjetas con
     `flex: 1` no bajan de linea: se reparten el ancho a partes iguales y se
     quedan en 80 px cada una. El numero de 24px no cabe, se sale de su caja
     y se pisa con el de al lado. En el portal del cliente, en un iPhone de
     390px, se leia «4.680.0», «3.001M4», «64.1%X» — importes de un
     presupuesto, cortados a la mitad y superpuestos.

     El diagnostico se demoro porque la pagina NO se desliza de lado: nada
     desborda el documento. Lo que desborda es el contenido DENTRO de cada
     tarjeta, y eso no lo delata ninguna medida de ancho.

     Con la base al 100 %, la fila hace lo que se esperaba desde el principio:
     una tarjeta por linea. En un telefono de 390px es ademas lo correcto —
     cuatro cifras de siete digitos no caben de otra forma. */
  .row:not(.nowrap) > .card { flex-basis: 100% !important; }
  /* Solo los lados. `padding: 12px` a secas pisaba el hueco de abajo que
     deja sitio a la barra inferior, y el boton de guardar de cada
     formulario volvia a quedar tapado — justo en las pantallas mas
     chicas, que son las que menos margen tienen. */
  .main { padding-left: 12px; padding-right: 12px; padding-top: 12px; }
}

/* ====================================================================
   BARRA INFERIOR DEL CELULAR
   ====================================================================
   Cinco ranuras fijas, pegadas abajo, dentro del área segura. Va abajo y
   no arriba por una razón física: en un teléfono de 6 pulgadas sostenido
   con una mano, el pulgar alcanza cómodamente el tercio inferior de la
   pantalla y no llega a la esquina superior izquierda, que es justo donde
   estaba el único menú que había. */
/* El aviso de version nueva. Va abajo y estrecho a proposito: interrumpe lo
   justo para que se vea, y no tapa el formulario que la persona esta
   llenando. Un aviso que hay que cerrar para poder seguir trabajando se
   cierra sin leerlo.

   En el telefono se sube por encima de la barra inferior de navegacion, que
   ya ocupa ese borde. */
.aviso-version {
  position: fixed; left: 50%; transform: translateX(-50%);
  bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 96;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  max-width: calc(100vw - 32px);
  padding: 10px 12px 10px 18px;
  background: var(--card-bg);
  border: 1.5px solid var(--border-soft);
  border-radius: 14px;
  box-shadow: 0 12px 34px rgba(20, 20, 60, .18);
}
.aviso-version-texto { font-size: 13.5px; font-weight: 700; }
.aviso-version-acciones { display: flex; gap: 8px; }
@media (max-width: 860px) {
  .aviso-version { bottom: calc(72px + env(safe-area-inset-bottom)); }
}

.barra-movil { display: none; }
.solo-movil { display: none; }

@media (max-width: 860px) {
  .barra-movil {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 95;
    background: var(--topbar-bg);
    border-top: 1px solid var(--topbar-border);
    padding-bottom: env(safe-area-inset-bottom);
    box-shadow: 0 -2px 14px rgba(20, 20, 60, .07);
  }
  .bm-item {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; padding: 8px 2px 7px; min-height: 56px;
    background: none; border: none; text-decoration: none;
    color: var(--muted); font-weight: 700; font-size: 10.5px;
    -webkit-tap-highlight-color: transparent;
  }
  /* El destino activo no se marca solo con color: lleva además una barra
     arriba. Un daltónico y una pantalla al sol necesitan la misma pista, y
     ninguno de los dos la recibe del color solo. */
  .bm-item.activo { color: var(--viz-1); }
  .bm-item.activo::before {
    content: ""; position: absolute; top: 0; left: 50%; transform: translateX(-50%);
    width: 34px; height: 3px;
    border-radius: 0 0 3px 3px; background: var(--viz-1);
  }
  .bm-item { position: relative; }
  .bm-icono { position: relative; display: flex; }
  .bm-texto { line-height: 1.1; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .bm-punto {
    position: absolute; top: -5px; right: -8px; background: #B42318; color: #fff;
    font-size: 9.5px; font-weight: 800; border-radius: 999px; min-width: 16px; height: 16px;
    display: flex; align-items: center; justify-content: center; padding: 0 4px;
    border: 2px solid var(--topbar-bg);
  }

  /* El contenido tiene que terminar ENCIMA de la barra. Sin esto, la
     última fila de cualquier tabla y el botón de guardar de cualquier
     formulario quedan tapados — y el de guardar es siempre el último. */
  .main { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }

  .solo-movil { display: flex; }
  .solo-escritorio { display: none !important; }
  .mobile-menu-btn { display: none; }

  /* Idioma y tema, uno al lado del otro. Apilados ocupaban 100px fijos de
     un pie que ya no se desplaza: eran 100px que le quitaban al menú. */
  .sb-ajustes { flex-direction: row; gap: 8px; margin: 0 16px 10px; }
  .sb-ajustes .btn-ghost {
    flex: 1 1 0; min-width: 0; justify-content: center; display: flex; align-items: center;
    gap: 5px; padding: 10px 8px; font-size: 12.5px; white-space: nowrap; overflow: hidden;
  }

  /* El hueco para la barra inferior va en el pie, que no se desplaza. */
  .sb-pie { padding-bottom: calc(64px + env(safe-area-inset-bottom)); }
  .sb-logout { font-weight: 800; }

  .sb-cuenta {
    display: flex; align-items: center; gap: 10px;
    margin: 0 16px 14px; padding: 10px 12px;
    background: var(--sidebar-item-active-bg); border-radius: 12px;
  }
  .sb-cuenta-nombre { color: #fff; font-weight: 800; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .sb-cuenta-correo { color: var(--sidebar-item); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}

/* ====================================================================
   MODAL — hoja inferior en el celular
   ====================================================================
   Centrado vertical y 85vh de alto máximo funcionan en un escritorio. En
   un teléfono, al abrirse el teclado la ventana se encoge a la mitad y los
   botones de Cancelar/Guardar quedan debajo del teclado, sin forma de
   llegar a ellos: el formulario se puede llenar pero no enviar.

   Una hoja anclada abajo resuelve las dos cosas: los botones quedan justo
   encima del teclado, y el gesto de cerrar (deslizar hacia abajo, o tocar
   la parte oscura de arriba) es el que la gente ya conoce del sistema. */
.modal-fondo {
  position: fixed; inset: 0; background: rgba(10, 12, 30, .5);
  display: flex; align-items: center; justify-content: center;
  z-index: 200; padding: 20px;
}
.modal-caja { width: 100%; max-height: 85vh; max-height: 85dvh; overflow-y: auto; position: relative; }
.modal-agarre { display: none; }
.modal-cerrar {
  background: none; border: none; cursor: pointer; font-size: 22px; line-height: 1;
  color: var(--muted); padding: 4px; display: flex; align-items: center; justify-content: center;
}

@media (max-width: 860px) {
  .modal-fondo { align-items: flex-end; padding: 0; }
  .modal-caja {
    border-radius: 20px 20px 0 0;
    max-height: 92dvh;
    padding-bottom: calc(20px + env(safe-area-inset-bottom));
    animation: modal-sube .18s ease-out;
  }
  .modal-agarre {
    display: block; width: 40px; height: 4px; border-radius: 999px;
    background: var(--border-soft); margin: -6px auto 12px;
  }
  @keyframes modal-sube { from { transform: translateY(14%); } to { transform: translateY(0); } }
}
@media (prefers-reduced-motion: reduce) {
  .modal-caja { animation: none !important; }
}

/* ---------- El puente hacia el bot de WhatsApp ----------
   Deliberadamente NO es un botón verde de WhatsApp. Un bloque verde de marca
   dentro de una app azul marino se lee como un anuncio, y además compite con
   la acción principal de la pantalla — que es el formulario que tiene al
   lado. Esto es una RUTA ALTERNATIVA, y tiene que verse más callada que la
   ruta principal, no más gritada. */
.bot-tarjeta {
  border: 1.5px solid var(--border-soft);
  border-radius: 14px;
  padding: 16px;
  background: var(--bg-soft);
}
.bot-cabecera { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.bot-marca {
  width: 34px; height: 34px; border-radius: 10px; flex: 0 0 auto;
  display: flex; align-items: center; justify-content: center;
  background: var(--btn-secondary-bg); color: var(--viz-1);
}
.bot-marca-listo { background: var(--viz-bien-bg); color: var(--viz-bien); }
.bot-marca-pendiente { background: var(--viz-atencion-bg); color: var(--viz-atencion); }

/* El aviso de vincular WhatsApp. Comparte el lenguaje de la tarjeta del bot
   (misma marca redonda, mismo tono de atención) pero es una banda ancha, no
   una tarjeta de columna: va en el camino de la persona, no en un panel
   lateral, y tiene que leerse de un vistazo sin robarle la pantalla al
   contenido que la persona vino a ver. */
.aviso-wa { border-left: 4px solid var(--viz-atencion); }
.bot-titulo { font-weight: 800; font-size: 14px; }
.bot-texto { font-size: 13px; line-height: 1.6; margin: 0 0 12px; color: var(--text-main); }
.bot-boton {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  text-decoration: none; width: 100%;
}
.bot-ejemplos { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border-softer); }
.bot-ejemplos-titulo { font-size: 11.5px; font-weight: 700; color: var(--muted); margin-bottom: 6px; }
.bot-ejemplo { font-size: 12.5px; color: var(--muted); font-style: italic; line-height: 1.6; }
.bot-rapidas { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.bot-rapidas .btn-ghost { text-decoration: none; display: inline-flex; align-items: center; }

/* ---------- Una fila de tabla, convertida en ficha ---------- */
.ficha-movil {
  border: 1.5px solid var(--border-soft); border-radius: 14px; padding: 14px;
  background: var(--card-bg);
}
.ficha-titulo { font-weight: 800; font-size: 14.5px; margin-bottom: 10px; overflow-wrap: anywhere; }
.ficha-datos { display: grid; grid-template-columns: minmax(96px, 40%) 1fr; gap: 7px 12px; margin: 0; }
.ficha-datos > div { display: contents; }
.ficha-datos dt {
  font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .03em;
}
.ficha-datos dd { margin: 0; font-size: 13.5px; overflow-wrap: anywhere; }
.ficha-acciones {
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--border-softer);
}
.ficha-acciones .row { flex-wrap: wrap; gap: 8px; }
.ficha-acciones .btn-sm { flex: 1 1 auto; }

/* ---------- Impresión (cuenta de cobro del consultor) ---------- */
@media print {
  .sidebar, .topbar, .barra-movil, .no-print { display: none !important; }
  .main-area, .main { padding: 0 !important; margin: 0 !important; }
  body { background: #fff !important; }
  #invoice-print-area { box-shadow: none !important; margin: 0 !important; max-width: 100% !important; }
  /* Nómina preliminar — "Ver todos/Imprimir": una persona por hoja al
     imprimir, para que el desglose de cada quien no quede cortado a la
     mitad entre dos páginas. */
  .payroll-person-block { page-break-after: always; break-inside: avoid; }
  .payroll-person-block:last-child { page-break-after: auto; }
}


/* ====================================================================
   GRÁFICOS
   Lo mínimo que no cabe en línea: el contenedor relativo del tooltip, la
   rejilla y el estado de foco. Todo lo demás lo dibujan los componentes en
   SVG, con las variables de arriba.
   ==================================================================== */
.viz { position: relative; width: 100%; }
.viz svg { display: block; width: 100%; overflow: visible; }
.viz-tip {
  position: absolute; pointer-events: none; z-index: 6;
  background: var(--card-bg); color: var(--viz-tinta);
  border: 1px solid var(--border-soft); border-radius: 8px;
  box-shadow: 0 6px 20px rgba(20,20,60,.14);
  padding: 8px 10px; font-size: 12px; line-height: 1.5; white-space: nowrap;
  transform: translate(-50%, -112%);
}
.viz-tip b { font-variant-numeric: tabular-nums; }
.viz-eje { font-size: 10px; fill: var(--viz-tinta-suave); }
.viz-rejilla { stroke: var(--viz-rejilla); stroke-width: 1; }
.viz-marca:focus-visible { outline: 2px solid var(--viz-1); outline-offset: 2px; }
.viz-leyenda { display: flex; flex-wrap: wrap; gap: 4px 16px; font-size: 11.5px; color: var(--viz-tinta-suave); }
.viz-leyenda span.p { width: 9px; height: 9px; border-radius: 2px; display: inline-block; }
.viz-tabla { width: 100%; border-collapse: collapse; font-size: 12px; margin-top: 8px; }
.viz-tabla th { text-align: left; font-weight: 600; color: var(--viz-tinta-suave); padding: 4px 8px 4px 0; }
.viz-tabla td { padding: 4px 8px 4px 0; border-top: 1px solid var(--border-softer); font-variant-numeric: tabular-nums; }
.viz-vertabla { font-size: 11px; color: var(--muted); background: none; border: 0; cursor: pointer; padding: 0; text-decoration: underline; }

/* Un tablero es una rejilla de tarjetas, no una columna de widgets sueltos. */
/* La pista mínima era más ANCHA QUE LA PANTALLA: 330px de mínimo contra los
   ~326px útiles de un celular de 390px con sus márgenes. `auto-fit` no puede
   encoger por debajo del mínimo, así que armaba dos columnas igualmente y la
   segunda tarjeta quedaba cortada fuera del borde.

   `min(330px, 100%)` mantiene la intención en el escritorio —dos columnas
   cuando de verdad caben— y cede a una sola cuando no.

   Este arreglo ya lo había escrito una vez, doscientas líneas más arriba, y
   NO SERVÍA DE NADA: estas dos líneas lo volvían a declarar más abajo, con la
   misma especificidad, y en un empate gana la última. Por eso ahora hay una
   sola definición y no dos. Un arreglo que convive con lo que venía a
   arreglar es peor que no haberlo hecho: parece hecho. */
.tablero { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(230px, 100%), 1fr)); }
.tablero-2 { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(min(330px, 100%), 1fr)); }

@media (prefers-reduced-motion: reduce) {
  .viz * { transition: none !important; animation: none !important; }
}
