/* ============================================================================
   Portal Técnico · Sistema de diseño compartido (portal-ui.css)
   ----------------------------------------------------------------------------
   Fuente única de verdad para tokens y componentes base del portal.
   Se sirve en /portal-ui.css. Cada vista lo enlaza con:
     <link rel="stylesheet" href="/portal-ui.css">
   y define arriba SOLO lo propio de esa pantalla.

   Nombres canónicos (los de las views): --bg / --bg-2 / --panel / --panel-2 /
   --txt / --txt-dim / --txt-faint / --accent / --turno / --line / --radius.
   Al migrar, renombrar los locales (--gold→--turno, --navy→--bg, --panel2→
   --panel-2, --text→--txt, --bg2→--bg-2, etc.). Ver docs/PLAN-UNIFICACION-VISUAL.md
   ========================================================================== */

:root {
  /* Superficies */
  --bg: #0e141b;
  --bg-2: #141c26;
  --panel: #1a2430;
  --panel-2: #212e3d;

  /* Líneas / bordes */
  --line: #2b3a4d;
  --line-soft: #243140;
  --line-strong: #3c506a;            /* borde acentuado en hover (input/chip/card/opción) */

  /* Texto */
  --txt: #e6edf3;
  --txt-dim: #95a6b8;
  --txt-faint: #5e7185;

  /* Acento (turquesa) */
  --accent: #2dd4bf;
  --accent-deep: #14b8a6;
  --accent-ink: #06231f;             /* texto sobre relleno acento */
  --accent-glow: rgba(45, 212, 191, .14);
  --accent-soft: rgba(45, 212, 191, .09);  /* relleno tenue de estado "on" */

  /* Semánticos. Regla para no depender solo del color (daltonismo): estos
     tonos son válidos como RELLENO (badge/chip/borde) porque ya traen su
     propia tinta (--accent-ink/--ink-warm) o conviven con un ícono/texto que
     dice el estado (p.ej. ESTSOL en horarios.js ya manda "Pendiente"/
     "Aprobada"/etc, no solo el color). Si vas a agregar un indicador NUEVO
     que sea solo un color plano sin texto/ícono al lado (como era el punto
     de severidad en notificaciones), sumale una forma o un símbolo distinto,
     no otro tono más — ver .notif-item .punto más abajo. */
  --turno: #f0b429;                  /* dorado (turno / atención) */
  --urgente: #f97362;
  --danger: #f87171;
  --ok: #22c55e;
  --warn: #eab308;                   /* pendiente / advertencia */
  --pronto: var(--warn);             /* alias histórico (Mantenimiento) */
  --vencida: #f87171;
  --feriado: #ff8c42;                /* naranja (feriado en Horarios) */
  --ink-warm: #1a1205;               /* texto sobre rellenos cálidos */
  --bg-hi: #17222f;                  /* resalte del gradiente radial del fondo (ver body) */
  /* Acentos de identidad: NO son estados (nada "está" violeta). Sirven para
     distinguir formularios entre sí — ver .form-card en Formularios. */
  --viol: #a78bfa;
  --rose: #e07bab;

  /* Categóricos: SOLO para agrupar datos (ranking de marcas, series de un gráfico),
     nunca un estado. Evitar acá los tonos ya tomados por turno/semánticos (dorado,
     teal de acento, naranja feriado) — en Horarios y el dashboard turno y categoría
     conviven en la misma pantalla y no tienen que poder confundirse. */
  --cat-1: #38bdf8;
  --cat-2: #a78bfa;
  --cat-3: #f472b6;
  --cat-4: #34d399;
  --cat-5: #818cf8;
  --cat-6: #94a3b8;                  /* "Otras": siempre el gris, al final */

  /* Radios / sombras */
  --radius: 12px;
  --radius-sm: 8px;
  --radius-pill: 999px;
  --shadow: 0 12px 40px rgba(0, 0, 0, .45);

  /* Tipografía */
  --font: "Segoe UI", system-ui, -apple-system, Roboto, Arial, sans-serif;
  --mono: "Cascadia Code", "Consolas", "SF Mono", ui-monospace, monospace;
  --fs: 14px;
}

/* ============================================================================
   Tema claro
   ----------------------------------------------------------------------------
   Se activa con [data-theme="light"] en <html>. La persistencia (localStorage
   'theme.modo') y el botón del header viven en portal-secciones.js (precarga,
   antes del primer paint — mismo patrón que rail.visible/rail.mini) y
   portal-sesion.js (botón + alternar). login.html no carga esos scripts y
   replica la misma clave a mano, ver su <head>.

   Superficies/texto/líneas se remapean acá. Los semánticos (--accent/--danger/
   --turno/--ok/--warn/--urgente) NO cambian de valor: siguen sirviendo igual
   como RELLENO en los dos temas (badges, botones primary, chips), porque ya
   traen su propia tinta oscura encima. Donde esos mismos tonos se usan como
   TEXTO plano sobre el fondo de página (pensados para leerse sobre fondo
   oscuro) sí perdían contraste contra un fondo blanco — esos selectores
   puntuales se pisan más abajo con una versión más oscura/saturada de la
   misma familia de color, sin tocar la variable global.
   ========================================================================== */
[data-theme="light"] {
  --bg: #f3f5f7;
  --bg-2: #ffffff;
  --bg-hi: #ffffff;
  --panel: #ffffff;
  --panel-2: #eef1f4;

  --line: #d7dee5;
  --line-soft: #e6ebf0;
  --line-strong: #b9c4cf;

  --txt: #1b2430;
  --txt-dim: #55606c;
  --txt-faint: #7c8794;

  --accent-glow: rgba(15, 118, 110, .16);
  --accent-soft: rgba(15, 118, 110, .10);

  --shadow: 0 12px 30px rgba(30, 41, 59, .12);
}
[data-theme="light"] .ok,
[data-theme="light"] .rail-grp.on,
[data-theme="light"] .tab.on,
[data-theme="light"] .btn.acc { color: #0f766e; }
[data-theme="light"] .tab.on,
[data-theme="light"] .btn.acc { border-color: #0f766e; }
[data-theme="light"] .err,
[data-theme="light"] .btn.danger { color: #b91c1c; }
[data-theme="light"] .btn.danger { border-color: #b91c1c; }
[data-theme="light"] .warn,
[data-theme="light"] header .mark b { color: #b45309; }

/* --- reset mínimo --- */
* { box-sizing: border-box; }
html, body { margin: 0; }
body {
  background: radial-gradient(1200px 600px at 50% -10%, var(--bg-hi), var(--bg));
  color: var(--txt);
  font-family: var(--font);
  font-size: var(--fs);
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}
button, input, select, textarea { font-family: inherit; }

/* ============================================================================
   Layout
   ========================================================================== */
.wrap { max-width: 1250px; margin: 0 auto; padding: 22px 24px 60px; }
.spacer { flex: 1; }
.hide { display: none !important; }

h1 { font-size: 19px; font-weight: 700; margin: 0 0 2px; }
.lead { color: var(--txt-dim); font-size: 12.5px; margin: 0 0 16px; }

/* ============================================================================
   Header / navbar
   ----------------------------------------------------------------------------
   FORMATO igual en todas las vistas; los BOTONES varían por jerarquía
   (Inicio siempre → "/"; Volver solo en páginas nivel-2 → su submenú).
   ========================================================================== */
header {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 24px; border-bottom: 1px solid var(--line);
}
header .mark { font-size: 17px; font-weight: 700; letter-spacing: -.02em; }
header .mark b { color: var(--turno); }
header .sub {
  font-size: 11.5px; color: var(--txt-faint);
  text-transform: uppercase; letter-spacing: .06em;
}
header .ver { font-size: 11.5px; color: var(--txt-faint); }
/* La identidad ahora vive en el chip .nav-ident (abajo), no en el texto suelto .who.
   .who queda en el DOM como ancla oculta: ~18 vistas todavía lo setean y varios
   inyectores lo usan de referencia; ocultarlo evita tocar todas esas vistas. */
header .who, .portal-nav .who { display: none; }
/* Los botones de navegación del header (anchors) quedan alineados con ícono */
header .btn { text-decoration: none; display: inline-flex; align-items: center; gap: 7px; }

/* ----------------------------------------------------------------------------
   Cluster de acciones del navbar — MISMO orden visual en toda vista.
   Los botones los inyectan scripts distintos (portal-sesion, -notificaciones,
   -reportar, -calendario-widget) y a veces async, así que el orden de inserción
   no es fiable. Se fija con flex `order` acá (header y .portal-nav son flex).
   ------------------------------------------------------------------------- */
header .reportar-btn,   .portal-nav .reportar-btn    { order: 10; }
header .notif-wrap,     .portal-nav .notif-wrap      { order: 20; }
header .cal-widget-wrap,.portal-nav .cal-widget-wrap { order: 30; }
header .tema-btn,       .portal-nav .tema-btn        { order: 40; }
header .nav-ident,      .portal-nav .nav-ident       { order: 50; }

/* Chip de identidad + menú de cuenta (lo arma portal-sesion.js). Reemplaza al botón
   "Salir" suelto: nombre + rol en una pastilla que abre "Mi cuenta / Salir". */
.nav-ident { position: relative; }
.nav-ident .nav-ident-btn {
  display: inline-flex; align-items: center; gap: 9px;
  border: 1px solid var(--line); background: var(--panel); color: var(--txt);
  padding: 4px 10px 4px 5px; border-radius: var(--radius-pill);
  font-family: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer;
  transition: background .12s, border-color .12s;
}
.nav-ident .nav-ident-btn:hover { background: var(--panel-2); border-color: var(--line2, var(--line)); }
.nav-ident .ini {
  display: inline-flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 50%; flex: 0 0 auto;
  background: var(--accent); color: var(--accent-ink);
  font-size: 11.5px; font-weight: 700;
}
.nav-ident .nom { display: flex; flex-direction: column; line-height: 1.15; text-align: left; }
.nav-ident .nom b { font-weight: 600; font-size: 12.5px; }
.nav-ident .nom small { color: var(--txt-faint); font-size: 10.5px; font-weight: 500; }
.nav-ident .caret { color: var(--txt-faint); transition: transform .15s; }
.nav-ident.open .caret { transform: rotate(180deg); }
.nav-menu {
  position: absolute; top: calc(100% + 8px); right: 0; z-index: 60;
  min-width: 190px; padding: 6px; display: none;
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 12px 32px rgba(0,0,0,.35);
}
.nav-ident.open .nav-menu { display: block; }
.nav-menu a, .nav-menu button {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 8px 10px; border: 0; background: none; border-radius: var(--radius-sm);
  color: var(--txt); font-family: inherit; font-size: 12.5px; font-weight: 500;
  text-align: left; text-decoration: none; cursor: pointer;
}
.nav-menu a:hover, .nav-menu button:hover { background: var(--panel-2); }
.nav-menu .sep { height: 1px; background: var(--line); margin: 5px 4px; }
.nav-menu .salir { color: var(--danger); }
/* Mobile: la pastilla se reduce al círculo (nombre/rol ocupan demasiado). */
@media (max-width: 640px) {
  .nav-ident .nom { display: none; }
  .nav-ident .nav-ident-btn { padding: 4px; }
}

/* ============================================================================
   Botones
   ----------------------------------------------------------------------------
   .btn            secundario (default)
   .btn.primary    acción primaria (relleno acento)
   .btn.acc        acento en outline (énfasis suave)
   .btn.danger     destructivo
   .btn.mini/.big  tamaños
   ========================================================================== */
.btn {
  border: 1px solid var(--line); background: var(--panel); color: var(--txt);
  padding: 7px 12px; border-radius: var(--radius-sm);
  font-size: 12.5px; font-weight: 600; cursor: pointer; font-family: inherit;
  display: inline-flex; align-items: center; gap: 7px;
  text-decoration: none; user-select: none;
  transition: background .12s, border-color .12s, color .12s;
}
.btn:hover { background: var(--panel-2); }
.btn:disabled { opacity: .5; cursor: not-allowed; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn.primary:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
.btn.acc { border-color: var(--accent); color: var(--accent); background: var(--panel); }
.btn.acc:hover { background: var(--panel-2); }
.btn.danger { border-color: var(--danger); color: var(--danger); }
.btn.mini { padding: 3px 8px; font-size: 11px; }
.btn.big { padding: 11px 18px; font-size: 14px; }

/* ============================================================================
   Paneles / avisos
   ========================================================================== */
.panel {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 16px; margin-bottom: 16px;
}
.panel > b { font-size: 14px; }
.panel h2 { font-size: 13.5px; font-weight: 700; margin: 0 0 12px; }
/* Título de panel + link de acción a la derecha ("Ver X →") — dashboard de Inicio
   y secciones de página completa que linkean a otra vista (ej. "La sala" en /equipo). */
.dash-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 12px; }
.dash-head b { font-size: 13px; }
.dash-head a { margin-left: auto; font-size: 11.5px; color: var(--accent); text-decoration: none; }
.dash-head a:hover { text-decoration: underline; }
.aviso {
  font-size: 12px; color: var(--txt-dim); background: var(--bg-2);
  border: 1px solid var(--line); border-radius: var(--radius-sm);
  padding: 9px 12px; margin: 8px 0;
}
.ok { color: var(--accent); }
.err { color: var(--danger); }
.warn { color: var(--turno); }

/* ============================================================================
   Tarjetas KPI y barras de ranking — compartidas por Métricas operativas y
   Panorama (antes solo en metricas-operativas.css).
   ========================================================================== */
.kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 12px; margin-bottom: 16px; }
.kpi {
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 14px 16px;
}
.kpi .k-label { font-size: 11px; color: var(--txt-faint); text-transform: uppercase; letter-spacing: .05em; }
.kpi .k-val { font-size: 26px; font-weight: 700; margin: 4px 0 2px; letter-spacing: -.02em; }
.kpi .k-val.acc { color: var(--accent); }
.kpi .k-val.danger { color: var(--danger); }
.kpi .k-val.warn { color: var(--turno); }
.kpi .k-sub { font-size: 11.5px; color: var(--txt-dim); }
/* Cabecera de página/dashboard: título + <span class="spacer"> (flex:1) + acciones/estado
   a la derecha. Vive acá (source of truth) — la comparten datos-contables, equipo, métricas,
   monitoreo, movimientos y horarios. Antes se copiaba en cada CSS y monitoreo la olvidó
   (el botón quedaba pegado a los tabs). */
.top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 16px; }
.top h1 { margin: 0; }
/* Bajada pegada al h1 en la cabecera de dashboard (.top): sin el margen inferior
   de una .lead de página, que ahí abriría demasiado. */
.top .lead { margin: 2px 0 0; }

.fila-bar { display: grid; grid-template-columns: minmax(110px, 1fr) 2fr auto; gap: 10px; align-items: center; margin-bottom: 8px; font-size: 12.5px; }
.fila-bar .nom { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.fila-bar .track { background: var(--panel-2); border-radius: 4px; height: 8px; position: relative; }
.fila-bar .marca { position: absolute; top: -2px; bottom: -2px; width: 2px; background: var(--urgente); opacity: .85; border-radius: 1px; }
.fila-bar .fill { background: var(--accent); height: 100%; border-radius: 4px; }
.fila-bar .fill.gold { background: var(--turno); }
.fila-bar .num { color: var(--txt-dim); font-size: 12px; white-space: nowrap; }
.fila-bar .num b { color: var(--txt); }

.estado { font-size: 11.5px; color: var(--txt-faint); margin-top: 4px; }
.vacio { color: var(--txt-faint); font-size: 12.5px; padding: 8px 0; }
.chip-reinc { display: inline-block; font-size: 10.5px; font-weight: 600; padding: 1px 8px; border-radius: var(--radius-pill); border: 1px solid var(--danger); color: var(--danger); white-space: nowrap; }
.nota-h2 { font-weight: 400; color: var(--txt-faint); font-size: 11px; }
/* Leyenda de gráfico (svgTendencia): metricas-operativas.html, datos-contables.html, equipo.html. */
.leg { margin-bottom: 8px; }
.leg span { display: inline-flex; align-items: center; margin-right: 16px; font-size: 11.5px; color: var(--txt-dim); }
.leg i { width: 10px; height: 10px; border-radius: 3px; display: inline-block; margin-right: 6px; }
/* Swatch rayado del tramo "Fuera de servicio" (svgBarrasApiladas) — mismo patrón que el
   <pattern> SVG, reproducido en CSS porque acá no hay <svg> al que referenciarlo. */
.leg i.hatch-fs { background: repeating-linear-gradient(45deg, var(--warn), var(--warn) 2px, var(--panel) 2px, var(--panel) 4px); }

/* Distribución por marca (dona) + top modelos (barras) — panel "Maestro de sala"
   de Inicio y sección "La sala" de /equipo (pintarDistribucionMarca en portal-charts.js). */
.maestro-lay { display: flex; align-items: center; gap: 16px; }
.maestro-donut { position: relative; flex: 0 0 auto; width: 84px; height: 84px; }
.maestro-donut svg { display: block; width: 100%; height: 100%; }
.maestro-total { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; pointer-events: none; }
.maestro-total span { font-size: 18px; font-weight: 700; line-height: 1; }
.maestro-total small { font-size: 9px; color: var(--txt-faint); margin-top: 1px; }
.maestro-leg { display: flex; flex-direction: column; gap: 6px; min-width: 0; flex: 1; }
.maestro-leg span { display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--txt-dim); min-width: 0; }
.maestro-leg span b { width: 8px; height: 8px; border-radius: 2px; flex: 0 0 auto; }
.maestro-leg span em { font-style: normal; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.maestro-leg span small { color: var(--txt-faint); flex: 0 0 auto; }
.maestro-bars { margin-top: 14px; display: flex; flex-direction: column; gap: 7px; }
.maestro-bars .mb-row { display: flex; align-items: center; gap: 8px; }
.maestro-bars .mb-k { font-size: 11px; color: var(--txt-dim); flex: 0 0 108px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.maestro-bars .mb-track { flex: 1; min-width: 0; height: 10px; background: var(--panel-2); border-radius: 3px; overflow: hidden; }
.maestro-bars .mb-fill { display: block; height: 100%; background: var(--accent); border-radius: 3px 0 0 3px; }
.maestro-bars .mb-v { font-size: 10.5px; color: var(--txt-faint); flex: 0 0 20px; text-align: right; }

/* ============================================================================
   Tabs (navegación interna de una vista)
   ========================================================================== */
.tabs { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.tab {
  border: 1px solid var(--line); background: var(--panel); color: var(--txt-dim);
  padding: 7px 14px; border-radius: var(--radius-sm);
  font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.tab.on { color: var(--accent); border-color: var(--accent); }
a.tab { text-decoration: none; }
/* Etiquetas de solapa con versión corta para móvil (ver #slotTabs en el @media):
   por defecto se muestra la larga; la corta aparece solo en pantalla angosta. */
.tab .lbl-mini { display: none; }

/* ============================================================================
   Formularios
   ========================================================================== */
label.f {
  display: block; font-size: 11px; color: var(--txt-dim);
  margin: 10px 0 3px; text-transform: uppercase; letter-spacing: .04em;
}
input:not([type=checkbox]):not([type=radio]), select, textarea {
  background: var(--bg-2); border: 1px solid var(--line); color: var(--txt);
  border-radius: var(--radius-sm); padding: 8px 10px; font-size: 13px;
  width: 100%; transition: border-color .15s ease, box-shadow .15s ease;
}
input:hover:not(:disabled), select:hover:not(:disabled), textarea:hover:not(:disabled) { border-color: var(--line-strong); }
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow);
}
input::placeholder, textarea::placeholder { color: var(--txt-faint); }
input:disabled, select:disabled, textarea:disabled { opacity: .5; }
input[type=date] { color-scheme: dark; }
select {
  appearance: none; -webkit-appearance: none; padding-right: 28px; cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%2395a6b8' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'><path d='M2.5 4.5 6 8l3.5-3.5'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center;
}
/* fila de campos parejos */
.filtros { display: flex; flex-wrap: wrap; gap: 10px; align-items: end; }
.filtros .campo { display: flex; flex-direction: column; gap: 4px; flex: 0 1 150px; }
.filtros .campo label.f { margin: 0; }
/* fila de campos en grid auto-responsive */
.fila { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
/* patrón edit-mode: inputs disabled lucen como texto plano */
input:not([type=checkbox]):not([type=radio]):disabled { border-color: transparent; background: transparent; }

/* ============================================================================
   Formulario en tarjeta (.form-card)
   ----------------------------------------------------------------------------
   Es la "ventana" de edición (el .modal de Horarios) abierta y apoyada en la
   página: cabecera con ícono + título + bajada, cuerpo con los campos y pie con
   las acciones. La idea es que dar de alta y editar se vean igual, en vez de un
   modal cuidado y un formulario suelto de campos apilados.

     <div class="form-card gold">
       <div class="fc-head">
         <span class="fc-ico">🔁</span>
         <div class="fc-t"><b>Título</b><small>De qué se trata</small></div>
       </div>
       <div class="fc-body"><div class="campos">…</div></div>
       <div class="fc-foot"><span class="msg"></span><button class="btn primary">Guardar</button></div>
     </div>

   El COLOR sale de --fc / --fc-soft / --fc-glow, que define la propia tarjeta:
   la barra superior, el ícono, el foco de los campos y los controles marcados
   lo toman de ahí. Así cada formulario se identifica por color sin repetir
   reglas. Variantes: (sin clase)=acento, .gold .viol .rose .fer .danger.
   ========================================================================== */
/* La banda de color va en el borde de la TARJETA, no en el de la cabecera: con
   overflow:hidden un borde del hijo tirado hacia arriba se recorta y queda de
   1px. Acá son 2px siempre. */
.form-card {
  --fc: var(--accent); --fc-soft: var(--accent-soft); --fc-glow: var(--accent-glow);
  background: var(--panel); border: 1px solid var(--line); border-top: 2px solid var(--fc);
  border-radius: var(--radius); margin-bottom: 16px; overflow: hidden;
}
/* El tono lo definen las clases .t-* — están al final del archivo, después de
   .modal y .lanzador: misma especificidad, gana el último. */

/* el degradé bajo la banda: es lo que hace que la tarjeta se lea como una
   ventana con identidad y no como un panel más */
.fc-head {
  display: flex; align-items: center; gap: 12px; padding: 14px 18px;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, var(--fc-soft), rgba(0, 0, 0, 0));
}
.fc-ico {
  flex: 0 0 auto; width: 34px; height: 34px; border-radius: 10px;
  display: grid; place-items: center; font-size: 17px; line-height: 1;
  background: var(--fc-soft); box-shadow: inset 0 0 0 1px var(--fc-glow);
}
.fc-t { min-width: 0; }
.fc-t b { display: block; font-size: 14px; font-weight: 700; }
.fc-t small { display: block; font-size: 11.5px; color: var(--txt-dim); margin-top: 1px; }
/* acción secundaria en la cabecera (un ✎, un contador) */
.fc-head .fc-acc { margin-left: auto; display: flex; align-items: center; gap: 8px; }

.fc-body { padding: 16px 18px; }
.fc-body > .lead:last-child, .fc-body > p:last-child { margin-bottom: 0; }
.fc-foot {
  display: flex; align-items: center; gap: 10px; justify-content: flex-end;
  padding: 12px 18px; border-top: 1px solid var(--line); background: var(--bg-2);
}
.fc-foot .msg { margin-right: auto; font-size: 12.5px; min-width: 0; }
/* dentro de la tarjeta el foco y los controles marcados usan SU color */
.form-card input:focus, .form-card select:focus, .form-card textarea:focus {
  border-color: var(--fc); box-shadow: 0 0 0 3px var(--fc-glow);
}
.form-card input[type=checkbox], .form-card input[type=radio] { accent-color: var(--fc); }

/* --- grilla de campos ---------------------------------------------------- */
.campos {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 200px), 1fr));
  gap: 14px 12px; align-items: end;
}
.campos .campo { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.campos .campo label.f { margin: 0; }
.campos .campo .hint { font-size: 11.5px; color: var(--txt-faint); }
.campo.ancho { grid-column: span 2; }
.campo.full { grid-column: 1 / -1; }
/* separador con rótulo, para agrupar campos dentro de un mismo cuerpo */
.campos-sep {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 10px;
  font-size: 10.5px; font-weight: 700; color: var(--txt-faint);
  text-transform: uppercase; letter-spacing: .05em; margin-top: 2px;
}
.campos-sep::after { content: ''; flex: 1; height: 1px; background: var(--line); }

/* --- opciones en tarjeta (radio / checkbox) ------------------------------
   Mismo lenguaje que .seg pero con el control real adentro, así el JS que lee
   `input[name=x]:checked` sigue funcionando igual. */
.opciones { display: flex; flex-wrap: wrap; gap: 10px; }
.opt-card {
  flex: 1 1 220px; display: flex; align-items: flex-start; gap: 10px; cursor: pointer;
  border: 1px solid var(--line); background: var(--bg-2); border-radius: 10px;
  padding: 10px 12px; font-size: 13px; font-weight: 600; color: var(--txt-dim);
  transition: border-color .15s ease, color .15s ease, background .15s ease, box-shadow .15s ease;
}
.opt-card:hover { border-color: var(--line-strong); color: var(--txt); }
.opt-card input { flex: 0 0 auto; margin: 2px 0 0; }
.opt-card small { display: block; font-weight: 600; font-size: 10.5px; color: var(--txt-faint); margin-top: 2px; }
.opt-card:has(input:checked) {
  color: var(--txt); border-color: var(--fc, var(--accent));
  background: var(--fc-soft, var(--accent-soft)); box-shadow: 0 0 0 1px var(--fc, var(--accent)) inset;
}

/* --- interruptor ---------------------------------------------------------
   El checkbox real queda oculto (no `display:none`: tiene que seguir siendo
   enfocable y llegarle el click desde el <label>). */
.switch { position: relative; display: inline-flex; align-items: center; gap: 10px; cursor: pointer; font-size: 13px; user-select: none; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch .track {
  flex: 0 0 auto; position: relative; width: 38px; height: 22px; border-radius: var(--radius-pill);
  background: var(--panel-2); border: 1px solid var(--line);
  transition: background .15s ease, border-color .15s ease;
}
.switch .track::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--txt-faint);
  transition: transform .15s ease, background .15s ease;
}
.switch input:checked + .track { background: var(--fc-soft, var(--accent-soft)); border-color: var(--fc, var(--accent)); }
.switch input:checked + .track::after { transform: translateX(16px); background: var(--fc, var(--accent)); }
.switch input:focus-visible + .track { box-shadow: 0 0 0 3px var(--fc-glow, var(--accent-glow)); }
.switch .sw-t small { display: block; font-size: 11px; color: var(--txt-faint); }

/* --- zona de archivo (drag & drop) ---------------------------------------
   El <input type=file> nativo es el control más feo del portal. Queda adentro,
   estirado e invisible: sigue siendo él el que abre el diálogo y el que el JS
   lee con .files[0], pero lo que se ve es la zona. */
.dropzone {
  position: relative; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 7px; text-align: center; padding: 26px 18px;
  border: 1.5px dashed var(--line); border-radius: var(--radius);
  background: var(--bg-2); cursor: pointer;
  transition: border-color .15s ease, background .15s ease;
}
.dropzone:hover { border-color: var(--fc, var(--accent)); background: var(--panel-2); }
/* el input real es invisible: el foco de teclado tiene que verse en la zona */
.dropzone:focus-within { border-color: var(--fc, var(--accent)); box-shadow: 0 0 0 3px var(--fc-glow, var(--accent-glow)); }
.dropzone.over { border-style: solid; border-color: var(--fc, var(--accent)); background: var(--fc-soft, var(--accent-soft)); }
/* el verde de "archivo cargado" manda también con el mouse encima: si no, pasar
   por arriba lo devuelve al color de la zona vacía y parece que se perdió */
.dropzone.cargado, .dropzone.cargado:hover { border-style: solid; border-color: var(--ok); background: rgba(34, 197, 94, .06); }
.dropzone input[type=file] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; padding: 0; border: 0; background: none;
}
.dropzone .dz-ico { font-size: 26px; line-height: 1; }
.dropzone .dz-t { font-size: 13.5px; font-weight: 600; }
.dropzone .dz-t u { color: var(--fc, var(--accent)); text-underline-offset: 2px; }
.dropzone .dz-h { font-size: 11.5px; color: var(--txt-faint); }
.dropzone.cargado .dz-ico { color: var(--ok); }

/* ============================================================================
   Ventana (.modal) y lanzadores (.lanzador)
   ----------------------------------------------------------------------------
   Los formularios NO van sueltos en la página: cada alta o edición es un botón
   que abre su ventana. La página queda con lo que se lee (tablas, calendarios)
   y el trabajo de cargar pasa por acá.

     <button class="lanzador t-gold" onclick="abrirModal('mod-x')">
       <span class="lz-ico">🔁</span>
       <span class="lz-t"><b>Título</b><small>Qué hace</small></span>
       <span class="lz-go">+</span>
     </button>

     <div class="modal-bg" id="mod-x"><div class="modal ancho">
       <div class="mhead">…</div><div class="mbody">…</div><div class="mfoot">…</div>
     </div></div>

   Comparten --fc con .form-card: el lanzador y la ventana que abre llevan el
   mismo tono y se leen como la misma cosa.
   ========================================================================== */
.lanzadores {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
  gap: 12px; margin-bottom: 16px;
}
/* si están todos ocultos por permisos, la grilla no deja su margen colgando */
.lanzadores:not(:has(> :not(.hide))) { display: none; }
.lanzador {
  --fc: var(--accent); --fc-soft: var(--accent-soft); --fc-glow: var(--accent-glow);
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--fc);
  border-radius: var(--radius); padding: 13px 15px; cursor: pointer;
  font-family: inherit; color: var(--txt);
  transition: background .15s ease, border-color .15s ease, transform .08s ease;
}
.lanzador:hover { background: var(--panel-2); border-color: var(--fc); border-left-color: var(--fc); }
.lanzador:active { transform: translateY(1px); }
.lanzador:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--fc-glow); }
.lanzador .lz-ico {
  flex: 0 0 auto; width: 38px; height: 38px; border-radius: 11px;
  display: grid; place-items: center; font-size: 18px; line-height: 1;
  background: var(--fc-soft); box-shadow: inset 0 0 0 1px var(--fc-glow);
}
.lanzador .lz-t { flex: 1; min-width: 0; }
.lanzador .lz-t b { display: block; font-size: 13.5px; font-weight: 700; }
.lanzador .lz-t small { display: block; font-size: 11.5px; color: var(--txt-dim); margin-top: 2px; }
.lanzador .lz-go {
  flex: 0 0 auto; color: var(--fc); font-size: 17px; font-weight: 700; opacity: .55;
  transition: opacity .15s ease, transform .15s ease;
}
.lanzador:hover .lz-go { opacity: 1; transform: translateX(2px); }

.modal-bg {
  position: fixed; inset: 0; z-index: 50; display: none;
  align-items: flex-start; justify-content: center; padding: 40px 16px;
  background: rgba(6, 10, 14, .66); overflow-y: auto;
}
.modal-bg.show { display: flex; }
/* El rail empuja el contenido con padding-left en body (ver html.has-rail más abajo);
   el fondo del modal es position:fixed, así que ese padding no lo alcanza y termina
   centrando sobre el viewport COMPLETO — el modal queda corrido hacia la izquierda.
   Con uno angosto casi no se nota (sobra margen de los dos lados); en uno ancho como
   .modal.xl.mov de Movimientos (1160px) se come casi todo el margen izquierdo y el
   rail tapa un pedazo. En móvil el rail es cajón, no empuja (ver su propio @media),
   por eso esto solo aplica de escritorio en adelante. */
@media (min-width: 861px) {
  html.has-rail .modal-bg { left: var(--rail-w, 208px); }
}
/* Columna con tope de alto: la cabecera y el pie quedan fijos y lo que scrollea
   es el cuerpo. Con overflow en el fondo, un formulario largo se llevaba los
   botones de Guardar fuera de la pantalla. */
.modal {
  --fc: var(--accent); --fc-soft: var(--accent-soft); --fc-glow: var(--accent-glow);
  display: flex; flex-direction: column; max-height: calc(100vh - 80px);
  width: 100%; max-width: 480px;
  background: var(--panel); border: 1px solid var(--line); border-top: 2px solid var(--fc);
  border-radius: 14px; box-shadow: var(--shadow); overflow: hidden;
}
.modal.ancho { max-width: 640px; }
.modal.xl { max-width: 900px; }
/* El modal "Detalle de máquina" (portal-charts.js) es compartido pero se monta en
   vistas que redefinen .modal/.chico localmente (p.ej. relevo-tecnico.css), así que su
   ancho cambiaba según dónde se abría. Lo fijamos acá scopeado por su id: gana por
   especificidad de id sobre cualquier .modal.chico local. Ancho ÚNICO para las 4
   solapas (ya no se alterna .ancho por tab): el contenido de cada solapa se adapta a
   este ancho, no al revés. El colapso a 100% en móvil se resuelve en el @media de abajo. */
#slotDetalleBg .modal { max-width: 480px; }
.modal .mhead {
  flex: 0 0 auto; display: flex; align-items: center; gap: 12px; padding: 14px 20px;
  border-bottom: 1px solid var(--line);
  background: linear-gradient(180deg, var(--fc-soft), rgba(0, 0, 0, 0));
}
.modal .mhead h2 { margin: 0; font-size: 15px; font-weight: 700; }
.modal .mhead .mh-sub { display: block; font-size: 11.5px; font-weight: 400; color: var(--txt-dim); margin-top: 1px; }
.modal .mhead .x {
  margin-left: auto; align-self: flex-start; background: transparent; border: none;
  color: var(--txt-dim); font-size: 22px; line-height: 1; cursor: pointer; padding: 0 2px;
}
.modal .mhead .x:hover { color: var(--txt); }
.modal .mbody { flex: 1 1 auto; overflow-y: auto; padding: 18px 20px; background: var(--bg-2); }
.modal .mbody > .lead:first-child { margin-top: 0; }
.modal .mfoot {
  flex: 0 0 auto; display: flex; align-items: center; gap: 10px; justify-content: flex-end;
  padding: 12px 20px; border-top: 1px solid var(--line); background: var(--panel);
}
.modal .mfoot .msg { margin-right: auto; font-size: 12.5px; min-width: 0; }
.modal input:focus, .modal select:focus, .modal textarea:focus {
  border-color: var(--fc); box-shadow: 0 0 0 3px var(--fc-glow);
}
.modal input[type=checkbox], .modal input[type=radio] { accent-color: var(--fc); }

/* ============================================================================
   Tonos de identidad
   ----------------------------------------------------------------------------
   Lo lee cualquier componente que use --fc: .form-card, .lanzador, .modal.
   Sin clase, acento. Este bloque tiene que quedar DESPUÉS de esos componentes:
   todos son un selector de clase, así que a igual especificidad gana el último
   — si se sube antes, cada componente se repinta con su propio --fc y todo
   vuelve a verse turquesa.
   ========================================================================== */
.t-gold { --fc: var(--turno);   --fc-soft: rgba(240, 180, 41, .10); --fc-glow: rgba(240, 180, 41, .17); }
.t-viol { --fc: var(--viol);    --fc-soft: rgba(167, 139, 250, .11); --fc-glow: rgba(167, 139, 250, .18); }
.t-rose { --fc: var(--rose);    --fc-soft: rgba(224, 123, 171, .11); --fc-glow: rgba(224, 123, 171, .18); }
.t-fer  { --fc: var(--feriado); --fc-soft: rgba(255, 140, 66, .10);  --fc-glow: rgba(255, 140, 66, .17); }
.t-dngr { --fc: var(--danger);  --fc-soft: rgba(248, 113, 113, .10); --fc-glow: rgba(248, 113, 113, .17); }

/* ============================================================================
   Chips / tags
   ========================================================================== */
.chip {
  border: 1px solid var(--line); background: var(--bg-2); color: var(--txt-dim);
  border-radius: var(--radius-pill); padding: 8px 14px; font-size: 13px;
  font-weight: 600; cursor: pointer; font-family: inherit; user-select: none;
  transition: border-color .12s, color .12s, background .12s;
}
.chip:hover { border-color: var(--line-strong); color: var(--txt); }
.chip.on { color: var(--txt); background: rgba(45, 212, 191, .12); border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent) inset; }
/* Selector compacto de rango (7/15/30 d) dentro de un .dash-head: menu.html
   "Máquinas más conflictivas" y equipo.html "Máquinas sin movimiento". */
.conf-right { margin-left: auto; display: flex; align-items: center; gap: 10px; }
.conf-right a { margin-left: 0; }
.conf-seg { display: inline-flex; gap: 4px; }
.conf-seg .chip { padding: 2px 9px; font-size: 10.5px; }
.tag {
  display: inline-block; font-size: 10.5px; border-radius: 5px;
  padding: 1px 7px; border: 1px solid var(--line); color: var(--txt-dim);
}

/* ============================================================================
   Popovers
   ----------------------------------------------------------------------------
   Panel flotante anclado a un botón (filtros del relevo, la campanita). El
   patrón nació duplicado dentro de relevo-tecnico.html; acá queda la versión
   genérica. Se abre agregando .show — sin JS de posicionamiento.
   El wrapper DEBE ser .pop-wrap: el popover se ancla a él, no a la página.
   ========================================================================== */
.pop-wrap { position: relative; }
.pop {
  display: none; position: absolute; right: 0; top: calc(100% + 6px);
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow); z-index: 40;
}
.pop.show { display: block; }
.pop-list { max-height: 260px; overflow-y: auto; }
.pop-foot { display: flex; gap: 8px; justify-content: flex-end; margin-top: 10px; }
.pop-hint { font-size: 11px; color: var(--txt-faint); }
.pop-sec {
  font-size: 10px; font-weight: 700; color: var(--txt-faint);
  text-transform: uppercase; letter-spacing: .04em;
}

/* ============================================================================
   Rango de fechas dual (Desde/Hasta)
   ----------------------------------------------------------------------------
   Reemplazo visual de un par de <input type="date"> "Desde"/"Hasta": los
   inputs nativos siguen en el DOM (ocultos) para que el resto del código siga
   leyendo/escribiendo su .value sin cambios — portal-rango-fechas.js solo los
   pilotea (les agrega .hide, la misma utilidad !important de siempre — así
   no depende de a qué se le gane en especificidad en el CSS de cada vista).
   Markup: envolver el par de .campo existentes en
   <div class="rango-fechas pop-wrap"> y listo, el script hace el resto.
   Reusa .pop-wrap/.pop/.show (ver Popovers arriba).
   ========================================================================== */
.filtros .rango-fechas { flex: 0 1 auto; }
.rf-trigger {
  display: flex; align-items: center; gap: 8px; white-space: nowrap;
  border: 1px solid var(--line); background: var(--panel); color: var(--txt);
  padding: 7px 12px; border-radius: var(--radius-sm); font-size: 12.5px;
  font-weight: 600; cursor: pointer; font-family: inherit;
}
.rf-trigger:hover { border-color: var(--line-strong); }
.rf-trigger svg { flex: 0 0 auto; color: var(--txt-faint); }
.rf-trigger .rf-lbl { color: var(--txt-dim); font-weight: 400; }
.rf-trigger .rf-dias { color: var(--accent); font-weight: 600; }
.rango-fechas.rf-activo .rf-trigger { border-color: var(--accent); }
.rf-pop { left: 0; right: auto; padding: 14px; width: 520px; max-width: calc(100vw - 40px); }
/* Cuando el disparador está a la derecha de su fila (p. ej. los KPIs de Equipo,
   donde un spacer lo empuja al borde), anclado a la izquierda el popover se
   saldría por el borde derecho de la ventana. portal-rango-fechas.js mide al
   abrir y agrega esta clase para anclarlo al borde derecho del disparador. */
.rf-pop.rf-pop-derecha { left: auto; right: 0; }
.rf-meses { display: flex; gap: 18px; }
.rf-mes { flex: 1; min-width: 0; }
.rf-mes h5 {
  display: flex; align-items: center; justify-content: space-between;
  margin: 0 0 10px; font-size: 12.5px; font-weight: 600; color: var(--accent);
}
.rf-mes h5 button {
  background: none; border: none; padding: 2px; color: var(--txt-faint);
  cursor: pointer; display: flex;
}
.rf-mes h5 button:hover { color: var(--accent); }
.rf-mes h5 span.rf-nav-vacio { width: 20px; }
.rf-dow { display: grid; grid-template-columns: repeat(7, 1fr); font-size: 10px; color: var(--txt-faint); margin-bottom: 4px; }
.rf-dow span { text-align: center; }
.rf-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.rf-day {
  text-align: center; font-size: 12px; padding: 6px 0; border-radius: 6px;
  cursor: pointer; color: var(--txt); background: none; border: none; font-family: inherit;
}
.rf-day.mute { color: var(--txt-faint); }
.rf-day.rf-in-range { background: var(--accent-soft); border-radius: 0; }
.rf-day.rf-edge { background: var(--accent); color: var(--accent-ink); font-weight: 600; border-radius: var(--radius-pill); }
.rf-day:not(.rf-edge):hover { outline: 1px solid var(--accent); }
.rf-foot { display: flex; justify-content: space-between; align-items: center; margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }
.rf-hoy { font-size: 12px; font-weight: 600; color: var(--accent); background: none; border: none; cursor: pointer; padding: 4px; }
.rf-btns { display: flex; gap: 8px; }
@media (max-width: 640px) {
  .rf-pop { width: calc(100vw - 40px); }
  .rf-meses { flex-direction: column; gap: 14px; }
}

/* Contador sobre un elemento (pendientes, no leídas). Generaliza el .pend del menú. */
.badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px;
  border-radius: var(--radius-pill); background: var(--warn); color: var(--ink-warm);
  font-size: 10.5px; font-weight: 700; line-height: 1;
}

/* ============================================================================
   Campanita de notificaciones (public/portal-notificaciones.js)
   ----------------------------------------------------------------------------
   Se inyecta por JS en el header de todas las vistas. El markup no está en
   ningún HTML: el navbar está duplicado en 10 archivos.
   ========================================================================== */
.notif-btn { position: relative; padding: 7px 9px; }   /* relative: ancla el contador */
.notif-btn svg { display: block; }
/* Botón de tema claro/oscuro, inyectado por portal-sesion.js junto a la campanita. */
.tema-btn { padding: 7px 9px; }
.tema-btn svg { display: block; }
/* Botón "Reportar" (public/portal-reportar.js), inyectado en el header junto a la
   campanita. Buzón de feedback: cualquiera logueado deja un bug/idea/mejora. */
.reportar-btn { position: relative; padding: 7px 9px; }   /* relative: ancla el contador */
.reportar-btn svg { display: block; }
/* Reportes sin triage (llega en /api/ping.reportesNuevos, SOLO a gestores): el botón se
   resalta y muestra el contador, para que el aviso no se pierda entre la operativa de la
   campanita. Se apaga solo cuando el gestor atiende los reportes ('nuevo' → otro estado). */
.reportar-btn.tiene-nuevos { color: var(--warn); border-color: var(--warn); }
.reportar-btn .badge {
  position: absolute; top: -5px; right: -5px;
  border: 2px solid var(--bg);   /* recorta el contador contra el fondo del header */
}
.notif-btn .badge {
  position: absolute; top: -5px; right: -5px;
  /* El borde recorta el contador contra el fondo del header: sin esto se pega
     al ícono y se lee como parte del dibujo. */
  border: 2px solid var(--bg);
}
/* Severidad: define el color del contador y del punto de cada ítem. Es la
   jerarquía visual del feed — accionable y urgente no pueden leerse igual.
   El punto (.notif-item .punto) no tiene texto al lado, así que además del
   color cambia de FORMA — círculo/cuadrado/triángulo — para no depender
   solo del color (daltonismo, ver nota en :root de este archivo). */
.sev-info    { background: var(--txt-faint); }
.sev-accion  { background: var(--warn); }
.sev-urgente { background: var(--urgente); }
.notif-item .punto.sev-accion  { border-radius: 3px; }
.notif-item .punto.sev-urgente { border-radius: 0; clip-path: polygon(50% 0%, 0% 100%, 100% 100%); }

/* ============================================================================
   Rail lateral (public/portal-rail.js)
   ----------------------------------------------------------------------------
   Barra de navegación vertical, inyectada por JS en cualquier vista que enlace
   portal-rail.js. No hay markup en ningún HTML: el árbol lo arma el script y lo
   filtra por /api/me (herramientas + capacidades). Reemplaza a los botones de
   navegación del header; el header queda solo con marca, campanita, usuario y
   Salir.

   Montaje sin tocar el layout de cada vista: el rail es position:fixed y el
   corrimiento del contenido lo hace html.has-rail body (padding-left). Así una vista
   se suma al rail con SOLO enlazar el script, sin envolver su HTML en columnas.
   ========================================================================== */
/* La clase va en <html>, no en <body>: portal-secciones.js la anticipa desde
   localStorage ANTES de que <body> exista (evita el salto de layout que se veía
   en cada navegación, mientras portal-rail.js espera el fetch a /api/me). */
html.has-rail body { padding-left: var(--rail-w, 208px); transition: padding-left .16s ease; }
html.has-rail.rail-mini body { --rail-w: 56px; }

.rail {
  position: fixed; top: 0; left: 0; bottom: 0; width: var(--rail-w, 208px);
  background: var(--bg-2); border-right: 1px solid var(--line);
  display: flex; flex-direction: column; z-index: 50;
  transition: width .16s ease, transform .16s ease;
}
/* Cabecera del rail: solo el control de colapsar (la marca vive en el header
   de la página, a la derecha del rail). En mini queda centrado. */
.rail-top { display: flex; align-items: center; padding: 11px 10px; min-height: 51px; }
.rail-mini .rail-top { justify-content: center; }
.rail-collapse {
  display: inline-flex; align-items: center; justify-content: center;
  width: 34px; height: 34px; padding: 0; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--txt-dim); cursor: pointer;
}
.rail-collapse:hover { background: var(--panel); color: var(--txt); }
.rail-collapse svg { display: block; }

.rail-nav { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 4px 8px 14px; }
.rail-nav::-webkit-scrollbar { width: 8px; }
.rail-nav::-webkit-scrollbar-thumb { background: var(--line); border-radius: 8px; }

/* Fila de grupo (o link suelto): ícono + etiqueta + chevron. <a> o <button>. */
.rail-grp {
  display: flex; align-items: center; gap: 11px; width: 100%;
  padding: 9px 10px; margin-top: 1px; border: 0; border-radius: var(--radius-sm);
  background: transparent; color: var(--txt-dim); font: inherit; font-size: 13px;
  text-align: left; text-decoration: none; cursor: pointer; transition: background .12s, color .12s;
}
.rail-grp:hover { background: var(--panel); color: var(--txt); }
.rail-grp.on { color: var(--accent); background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }
.rail-grp.soon { color: var(--txt-faint); }
.rail-grp .ic { flex: 0 0 auto; width: 20px; height: 20px; }
.rail-grp .ic svg { display: block; width: 20px; height: 20px; }
.rail-grp .tx { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-grp .chev { flex: 0 0 auto; color: var(--txt-faint); transition: transform .16s ease; }
.rail-grp[aria-expanded="true"] .chev { transform: rotate(180deg); }
.rail-grp .soon-tag {
  flex: 0 0 auto; font-size: 9px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--txt-faint); border: 1px solid var(--line); border-radius: 4px; padding: 0 4px;
}

/* Sub-lista de una herramienta. Se pliega con [hidden] (una sola abierta). */
.rail-sub { margin: 1px 0 3px 20px; padding-left: 10px; border-left: 1px solid var(--line-soft); }
.rail-it {
  display: flex; align-items: center; gap: 8px;
  padding: 6px 9px; margin-top: 1px; border-radius: var(--radius-sm);
  color: var(--txt-dim); font-size: 12px; text-decoration: none; transition: background .12s, color .12s;
}
.rail-it:hover { background: var(--panel); color: var(--txt); }
.rail-it.on { background: var(--panel-2); color: var(--txt); }
.rail-it .tx { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-it .badge { flex: 0 0 auto; height: 16px; min-width: 16px; font-size: 9.5px; }

/* Punto de "algo por resolver" por herramienta (portal-rail.js). Badge con el conteo en
   el rail ancho; en mini se colapsa a un punto sobre el ícono. Usa el color de aviso del
   portal (--warn); se distingue de la campanita por su ubicación (sobre la fila de la
   herramienta). Es una cola de trabajo accionable, no un aviso que se lee. */
.rail-grp .rail-badge {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; border-radius: var(--radius-pill);
  background: var(--warn); color: var(--ink-warm); font-size: 10.5px; font-weight: 700; line-height: 1;
  /* contorno sutil: anillo del color del rail para despegarlo del fondo + halo cálido
     tenue para que el número resalte sin gritar. --warn no cambia entre temas. */
  box-shadow: 0 0 0 1.5px var(--bg-2), 0 0 7px 1px rgba(234, 179, 8, .45);
}
/* Mismo badge en la solapa puntual (sub-item) que tiene el trabajo, más chico para la
   sub-lista, así al abrir el rail se ve EN CUÁL sección hay que entrar. */
.rail-it .rail-badge {
  flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
  height: 16px; min-width: 16px; padding: 0 4px; border-radius: var(--radius-pill);
  background: var(--warn); color: var(--ink-warm); font-size: 9.5px; font-weight: 700; line-height: 1;
  box-shadow: 0 0 0 1.5px var(--bg-2), 0 0 6px rgba(234, 179, 8, .4);
}
.rail-mini .rail-grp .rail-badge { display: none; }
.rail-mini .rail-grp.rail-con-pend .ic { position: relative; }
.rail-mini .rail-grp.rail-con-pend .ic::after {
  content: ""; position: absolute; top: -3px; right: -3px;
  width: 9px; height: 9px; border-radius: 50%; background: var(--warn);
  box-shadow: 0 0 0 2px var(--bg-2), 0 0 6px rgba(234, 179, 8, .55);
}

/* Modo mini (colapsado a íconos): se ocultan etiquetas, chevrons y sub-listas. */
.rail-mini .rail-grp { justify-content: center; padding: 9px 0; }
.rail-mini .rail-grp .tx,
.rail-mini .rail-grp .chev,
.rail-mini .rail-grp .soon-tag,
.rail-mini .rail-sub { display: none; }

/* Pie del rail: Salir. Fuera de .rail-nav (que scrollea) para quedar siempre visible,
   sin importar cuántos grupos haya arriba — ver portal-rail.js. */
.rail-foot { flex: 0 0 auto; padding: 6px 8px; border-top: 1px solid var(--line); }

/* Modo degradado (SlotAdmin caído): aviso arriba del rail + marca en las herramientas
   cuyas acciones activas quedaron fuera de servicio. Ver docs/ACCESO-DEGRADADO.md. */
.rail-aviso {
  display: flex; gap: 8px; align-items: flex-start;
  margin: 2px 2px 8px; padding: 8px 10px;
  background: rgba(234, 179, 8, .13);                 /* --warn suave */
  border: 1px solid rgba(234, 179, 8, .42);
  border-radius: var(--radius-sm);
  color: var(--txt-dim); font-size: 11px; line-height: 1.35;
}
.rail-aviso-ic { flex: 0 0 auto; color: var(--warn); }
.rail-aviso-ic svg { display: block; width: 16px; height: 16px; }
.rail-aviso-tx b { display: block; color: var(--txt); font-size: 12px; margin-bottom: 2px; }
.rail-degradada { opacity: .72; }
.rail-tag {
  flex: 0 0 auto; font-size: 9px; text-transform: uppercase; letter-spacing: .04em;
  color: var(--warn); border: 1px solid rgba(234, 179, 8, .42);
  border-radius: 4px; padding: 0 4px; white-space: nowrap;
}
/* En mini se ocultan los textos: el aviso queda como ícono y las tags desaparecen. */
.rail-mini .rail-aviso-tx, .rail-mini .rail-tag { display: none; }
.rail-mini .rail-aviso { justify-content: center; padding: 8px 0; }

/* Flyout: en mini, clickear un grupo con hijos no navega ni expande en la fila
   (no hay lugar) — abre este mismo .rail-sub como panel flotante al lado del
   ícono (portal-rail.js calcula top/left con getBoundingClientRect). Sin esto,
   un grupo cuyos hijos son páginas sueltas (p. ej. "Herramientas") no tenía forma
   de ir directo a una en mini, solo al hub de tarjetas. Selector con .rail-mini
   + dos clases para ganarle a la regla de arriba sin depender del orden. */
.rail-mini .rail-sub.rail-flyout {
  display: block; position: fixed; margin: 0; padding: 6px; border-left: 0;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); min-width: 190px; z-index: 55;
}
.rail-mini .rail-sub.rail-flyout .rail-it { font-size: 13px; }

/* Sub-menú de un grupo en escritorio expandido: SIEMPRE se abre como tarjeta flotante
   sobre el rail (no acordeón en línea que empuje la lista). portal-rail.js calcula
   left/width con getBoundingClientRect y ancla top (crece hacia abajo desde la fila) o,
   si no entra abajo —grupo pegado al pie, p. ej. Admin—, bottom (crece hacia arriba).
   max-height + scroll propio es la red de seguridad si ni así entra entero. En móvil
   (cajón) y en el flyout de mini mandan otras reglas; acá manda el escritorio expandido. */
.rail-sub-flota {
  position: fixed; margin: 0; padding: 6px; border-left: 0;
  background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius-sm);
  box-shadow: var(--shadow); z-index: 55; max-height: calc(100vh - 90px); overflow-y: auto;
}

/* Botón hamburguesa: solo en móvil, inyectado al inicio del header. Abre el
   cajón. En escritorio el rail está siempre visible y sobra. */
.rail-burger {
  display: none; align-items: center; justify-content: center;
  width: 36px; height: 36px; padding: 0; border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--panel); color: var(--txt); cursor: pointer;
}
.rail-burger svg { display: block; }
.rail-bd {
  display: none; position: fixed; inset: 0; background: rgba(0, 0, 0, .5); z-index: 49;
}

@media (max-width: 860px) {
  /* El rail fijo se lleva demasiado ancho en pantallas chicas: pasa a cajón. */
  html.has-rail body { padding-left: 0; }
  .rail { transform: translateX(-100%); width: 232px; }
  .rail.open { transform: translateX(0); box-shadow: var(--shadow); }
  .rail.open + .rail-bd, .rail-bd.show { display: block; }
  .rail-burger { display: inline-flex; }
  /* En cajón nunca se muestra en mini: se abre completo o no se abre. */
  .rail-mini .rail-grp { justify-content: flex-start; padding: 9px 10px; }
  .rail-mini .rail-grp .tx, .rail-mini .rail-grp .chev, .rail-mini .rail-sub { display: flex; }
  .rail-mini .rail-grp .chev { display: inline-flex; }
  /* En el cajón el rail va completo: se muestra el badge con conteo, no el punto de mini. */
  .rail-mini .rail-grp .rail-badge { display: inline-flex; }
  .rail-mini .rail-grp.rail-con-pend .ic::after { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  html.has-rail body, .rail, .rail-grp .chev { transition: none; }
}

/* El display va en el estado ABIERTO, no acá: si .notif-pop fijara display:flex suelto,
   ganaría sobre el `.pop { display:none }` de arriba (misma especificidad, viene después)
   y el popover quedaría siempre expandido. La columna la necesita el layout sticky. */
.notif-pop { width: 360px; max-width: calc(100vw - 24px); }
.notif-pop.show { display: flex; flex-direction: column; }
.notif-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 13px; border-bottom: 1px solid var(--line-soft);
}
.notif-head b { font-size: 13px; }

/* Solapas de la campanita: una sola campana, filtro por grupo adentro (operativa vs
   equipo). Segmented control chico; cada solapa lleva su punto de no-leídas para que el
   volumen operativo no sepulte lo de equipo. */
.notif-tabs {
  display: flex; flex-wrap: wrap; gap: 4px; padding: 7px 10px;
  border-bottom: 1px solid var(--line-soft);
}
.notif-tab {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px; border: 0; border-radius: 999px;
  background: none; color: var(--txt-dim);
  font: inherit; font-size: 12px; font-weight: 600; cursor: pointer;
}
.notif-tab:hover { background: var(--panel-2); }
.notif-tab.activo { background: var(--accent-soft); color: var(--accent); }
.notif-tab-dot {
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700; line-height: 1;
  background: var(--accent); color: #fff;
}
.notif-tab-dot.sev-accion  { background: var(--warn); color: var(--ink-warm); }
.notif-tab-dot.sev-urgente { background: var(--urgente); color: #fff; }

/* Ícono de categoría por ítem: al lado del punto de severidad, no lo reemplaza. */
.notif-cat { font-size: 13px; line-height: 1; margin-top: 3px; }

.notif-list { max-height: min(60vh, 420px); overflow-y: auto; }

.notif-item {
  display: grid; grid-template-columns: 8px auto 1fr; gap: 10px; align-items: start;
  width: 100%; text-align: left; padding: 10px 13px;
  background: none; border: 0; border-bottom: 1px solid var(--line-soft);
  border-left: 2px solid transparent;   /* reservado: lo pinta .no-leida, así el texto no se corre */
  color: var(--txt); font: inherit; cursor: pointer;
}
.notif-item:hover { background: var(--panel-2); }
.notif-item:last-child { border-bottom: 0; }
.notif-item.no-leida { border-left-color: var(--accent); background: var(--accent-soft); }
.notif-item.no-leida:hover { background: var(--panel-2); }
.notif-item .punto { width: 8px; height: 8px; border-radius: 50%; margin-top: 5px; }
.notif-item .t { font-size: 12.5px; font-weight: 600; }
.notif-item .m { font-size: 11.5px; color: var(--txt-dim); margin-top: 2px; }
.notif-item .cuando { font-size: 10.5px; color: var(--txt-faint); margin-top: 3px; }

/* Separador de día. Sticky: al scrollear una lista larga siempre se sabe de cuándo
   es lo que se está mirando. */
.notif-dia {
  position: sticky; top: 0; z-index: 1;
  padding: 7px 13px; background: var(--bg-2);
  border-bottom: 1px solid var(--line-soft);
}
.notif-vacio { padding: 26px 16px; text-align: center; font-size: 12.5px; }
.notif-cargando { padding: 14px 13px; display: grid; gap: 12px; }
.notif-cargando i {
  display: block; height: 9px; border-radius: 5px; background: var(--panel-2);
  animation: notif-late 1.1s ease-in-out infinite;
}
.notif-cargando i:nth-child(2) { width: 75%; }
.notif-cargando i:nth-child(3) { width: 55%; }
@keyframes notif-late { 50% { opacity: .45; } }
@media (prefers-reduced-motion: reduce) { .notif-cargando i { animation: none; } }

/* ---- Calendario: mini-popover del header (junto a la campanita) ----
   El calendario dejó de ser sección del rail y pasó a utilidad global: el botón lo
   inyecta portal-calendario-widget.js y este popover muestra los próximos eventos de
   un vistazo, con link a la vista completa. Reusa .pop-wrap/.pop/.show (arriba). */
.cal-btn svg { display: block; }
.cal-pop { width: 320px; max-width: calc(100vw - 24px); }
.cal-pop.show { display: flex; flex-direction: column; }
.cal-pop-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 11px 13px; border-bottom: 1px solid var(--line-soft);
}
.cal-pop-head b { font-size: 13px; }
.cal-pop-list { max-height: min(60vh, 400px); overflow-y: auto; padding: 4px 0; }
.cal-pop-it {
  display: grid; grid-template-columns: 38px 1fr; gap: 11px; align-items: center;
  width: 100%; text-align: left; padding: 9px 13px;
  background: none; border: 0; border-left: 3px solid var(--accent);
  color: var(--txt); font: inherit; cursor: pointer;
}
.cal-pop-it:hover { background: var(--panel-2); }
.cal-pop-it.sala { border-left-color: var(--turno, #e3a44b); }
.cal-pop-when { text-align: center; line-height: 1.1; font-variant-numeric: tabular-nums; }
.cal-pop-when .d { display: block; font-size: 17px; font-weight: 750; }
.cal-pop-when .m {
  font-size: 9px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--txt-faint); font-weight: 600;
}
.cal-pop-t { font-size: 12.5px; font-weight: 600; }
.cal-pop-sub { font-size: 11px; color: var(--txt-dim); margin-top: 1px; }
.cal-pop-imp { font-size: 10.5px; color: var(--accent); font-weight: 600; margin-top: 2px; }
.cal-pop-vacio { padding: 24px 16px; text-align: center; color: var(--txt-faint); font-size: 12.5px; }

/* ============================================================================
   Tablas de lectura (.lista)
   ----------------------------------------------------------------------------
   Componente único para las tablas de lectura del portal (admin, mantenimiento,
   horarios, datos-contables, metricas). Antes cada vista repetía casi el mismo
   bloque `.lista`/`table` con micro-diferencias de padding y tamaño. Acá vive
   la base; las vistas solo agregan sus modificadores propios (.yes/.no en admin,
   sticky de columna en #p-tabla, etc.).

   NO aplica a las grillas densas especializadas: maestro (`table` a secas, con
   min-width y zebra propios) y horarios `.equipo` (columnas/headers sticky y
   semántica de turnos). Esas siguen con su hoja de vista.

   Muchas `.lista` ponen el `<th>` en el primer `<tr>` sin `<thead>` (se arman
   en JS), por eso los selectores van sobre `.lista th`/`td`, no `thead th`. */
table.lista { border-collapse: collapse; width: 100%; font-size: 12.5px; }

/* Contenedor redondeado: solo cuando la .lista va dentro de un .scroll (las
   listas de gestión). border-radius + overflow del .scroll recortan el header
   a las esquinas. Los dashboards (datos-contables/metricas) envuelven en
   `<div style="overflow-x:auto">` a propósito y quedan sin marco, ya viven
   dentro de un .panel. */
.scroll:has(> table.lista) {
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--panel);
}

.lista th {
  position: sticky; top: 0; z-index: 1;
  background: var(--bg-2); color: var(--txt-dim);
  font-weight: 600; font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em;
  text-align: left; padding: 10px 13px; border-bottom: 1px solid var(--line);
  white-space: nowrap; cursor: default;
}
.lista td {
  padding: 9px 13px; border-bottom: 1px solid var(--line-soft);
  vertical-align: middle; line-height: 1.45;
}
.lista tr:last-child td { border-bottom: none; }

/* Hover: tiñe la fila. Solo el td se tiñe, así el header (que trae th) queda
   quieto. Las filas clicables suman barra de acento a la izquierda. */
.lista tr:hover td { background: var(--bg-2); }
.lista tr.click { cursor: pointer; }
.lista tr.click:hover td:first-child { box-shadow: inset 3px 0 0 var(--accent); }

.lista th.num { text-align: right; }
.lista td.num { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
.lista td.dim { color: var(--txt-dim); }
.lista td.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.lista td.center { text-align: center; }
.lista tr.anulada { opacity: .4; text-decoration: line-through; }

/* Pill de estado: forma (punto) + color, no solo color — cumple la regla
   anti-daltonismo (ver nota de los semánticos arriba). Variantes semánticas;
   los alias mapean los nombres históricos de cada vista. */
.est-pill {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11px; font-weight: 600; line-height: 1.4;
  padding: 2px 9px 2px 8px; border-radius: var(--radius-pill, 999px); white-space: nowrap;
}
.est-pill::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.est-pill.ok { color: var(--ok); background: rgba(34, 197, 94, .12); }
.est-pill.warn { color: var(--warn); background: rgba(234, 179, 8, .12); }
.est-pill.danger { color: var(--danger); background: rgba(248, 113, 113, .12); }
.est-pill.neutral { color: var(--txt-faint); background: rgba(148, 166, 184, .1); }

/* Nombre "sin ficha en Colaboradores" (unificación de nombres, 2026-08-25): se
   muestra el login crudo atenuado + ⚠ para detectar quién del PERSONAL falta cargar.
   Los externos/tercerizados NO llevan esta marca (el backend nunca setea sinFicha en
   ellos). Lo pinta window.fmtNombre() de portal-secciones.js. */
.nombre-sinficha { color: var(--txt-dim); }
.nombre-sinficha .nombre-warn { color: var(--warn); font-size: .9em; }

/* ============================================================================
   Listas de acción — celda de botones por fila (.acciones)
   ----------------------------------------------------------------------------
   Contraparte de las tablas de lectura: las mismas .lista pero con acciones por
   fila (planos, admin, mantenimiento, horarios). Antes cada vista repetía el
   `style="text-align:right;white-space:nowrap"` en la celda y amontonaba
   .btn.mini con caja, separados por espacios sueltos. Acá viven el contenedor
   (gap y alineado consistentes) y la jerarquía.

   Idea: en una .lista los botones quedan QUIETOS (sin caja) mientras leés la
   fila, y arman el marco al pasar el mouse — así mandan los datos, no la
   botonera. Cada variante conserva su color, para que la jerarquía se lea
   siempre sin necesidad de hover: primaria .acc (acento), destructiva .danger
   (rojo), el resto texto normal. En touch (sin hover) los botones quedan con su
   caja de siempre, bien visibles. */
.acciones { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
/* OJO: un <td> con display:flex deja de generar caja de celda real — el
   navegador lo envuelve en una celda anónima que sí estira a la fila, pero la
   caja flex (la que pintamos) queda del alto de su contenido nomás. Con una
   fila más alta que una línea (nombre largo que envuelve, como en Planos) la
   botonera queda chica y pegada arriba. height:100% NO lo arregla (confirmado
   a mano: la celda no hereda una altura resoluble a través de la anónima).
   Por eso acá se pisa a table-cell de vuelta y el alineado/espaciado se
   resuelve con text-align + margin, no con justify-content/gap. */
.lista td.acciones {
  display: table-cell;
  text-align: right;
  white-space: nowrap;
}
.lista td.acciones .btn + .btn { margin-left: 6px; }

@media (hover: hover) {
  /* Quieto: sin caja, para que la fila respire. El borde sigue ocupando su 1px
     (transparente), así aparecer/desaparecer no mueve el layout. */
  .lista td.acciones .btn { background: transparent; border-color: transparent; }
  /* Los secundarios (sin color propio) se atenúan en reposo para no competir con
     los datos, y recuperan tinta plena cuando te acercás. Los .acc/.danger/.primary
     conservan su color siempre: así la jerarquía se lee sin necesidad de hover. */
  .lista td.acciones .btn:not(.acc):not(.danger):not(.primary) { color: var(--txt-dim); }
  .lista tr:hover td.acciones .btn:not(.acc):not(.danger):not(.primary),
  .lista tr:focus-within td.acciones .btn:not(.acc):not(.danger):not(.primary) { color: var(--txt); }
  /* Al pasar por la fila —o cuando estás editándola (un input/select con foco,
     patrón ✎ → Guardar/✕)— se insinúa el marco de toda la botonera. */
  .lista tr:hover td.acciones .btn,
  .lista tr:focus-within td.acciones .btn { border-color: var(--line-soft); }
  /* Al ir a tocar un botón puntual, se arma del todo con su color. */
  .lista td.acciones .btn:hover { background: var(--panel-2); border-color: var(--line); }
  .lista td.acciones .btn.acc:hover { border-color: var(--accent); }
  .lista td.acciones .btn.danger:hover { border-color: var(--danger); }
}

/* ============================================================================
   Utilidades
   ========================================================================== */
.mono { font-family: var(--mono); }
/* overscroll-behavior-x: sin esto el swipe horizontal sobre una tabla
   dispara el "volver atrás" del navegador en móvil */
.scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; overscroll-behavior-x: contain; }
.faint { color: var(--txt-faint); }
.mt { margin-top: 10px; }
.self-end { align-self: end; }   /* campo alineado al piso de una .fila */
.w-auto { width: auto; }         /* select/input que no debe estirarse */
/* .hide vive en Layout; .ok/.err/.warn en Paneles y avisos */

/* ============================================================================
   Mobile
   ----------------------------------------------------------------------------
   Primera media query del portal. Nace por Horarios (ahí el celular es el
   dispositivo principal, reemplaza la hoja de papel de la oficina), pero el
   chrome es compartido, así que aplica a todas las vistas.
   NO se oculta header .sub: relevo-tecnico y proximamente no tienen <h1> y
   se quedarían sin título; en menu y login el .sub dice algo distinto.
   ========================================================================== */
@media (max-width: 640px) {
  .wrap { padding: 16px 14px 48px; }
  h1 { font-size: 17px; }
  header { padding: 12px 14px; gap: 10px; flex-wrap: wrap; }
  header .who { max-width: 45vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .panel { padding: 12px; }

  /* --- formulario en tarjeta ---
     Menos aire en cabecera/cuerpo/pie, y las acciones del pie a ancho completo
     con target cómodo. `.ancho` pasa a fila entera: con una sola columna, un
     `span 2` crearía una segunda columna implícita y desbordaría. */
  .fc-head { padding: 12px 14px; gap: 10px; }
  .fc-body { padding: 14px; }
  .fc-foot { padding: 11px 14px; flex-wrap: wrap; }
  .fc-foot .msg { flex-basis: 100%; margin: 0 0 4px; }
  .fc-foot .btn { flex: 1 1 45%; min-height: 44px; justify-content: center; }
  .campo.ancho { grid-column: 1 / -1; }
  .opt-card { flex-basis: 100%; }
  .dropzone { padding: 22px 14px; }

  /* --- ventana y lanzadores ---
     La ventana ocupa la pantalla de punta a punta y se pega arriba; el pie no
     scrollea con el cuerpo, así que Guardar siempre está donde el pulgar. */
  .modal-bg { padding: 10px; }
  .modal, .modal.ancho, .modal.xl { max-width: 100%; max-height: calc(100vh - 20px); }
  /* La regla scopeada por id de arriba (más específica) forzaría 480px en móvil;
     acá la neutralizamos para que el Detalle también ocupe la pantalla. */
  #slotDetalleBg .modal { max-width: 100%; }
  .modal .mhead, .modal .mbody, .modal .mfoot { padding-left: 14px; padding-right: 14px; }
  .modal .mfoot { flex-wrap: wrap; }
  .modal .mfoot .msg { flex-basis: 100%; margin: 0 0 4px; }
  .modal .mfoot .btn { flex: 1 1 45%; min-height: 44px; justify-content: center; }
  .lanzador { padding: 12px 13px; }

  /* Las pestañas no se apilan: una sola fila que se arrastra con el dedo.
     Horarios tiene 9 y en 375px ocuparían cuatro renglones. */
  .tabs {
    flex-wrap: nowrap; overflow-x: auto; padding-bottom: 2px;
    scroll-snap-type: x proximity; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  .tabs::-webkit-scrollbar { display: none; }
  .tab { flex: 0 0 auto; scroll-snap-align: start; }

  /* El Detalle de máquina tiene solo 4 solapas: en vez de arrastrarse (y quedar
     "sangradas" contra el borde derecho, como si estuvieran cortadas) se reparten
     a lo ancho de la fila en partes iguales — una sola línea, sin scroll. */
  #slotTabs { gap: 6px; }
  #slotTabs .tab {
    flex: 1 1 0; min-width: 0; text-align: center; white-space: nowrap;
    padding: 8px 4px;
  }
  /* Etiquetas largas → corta en móvil: "Configuración"→"Config", "Mantenimiento"→"Mant." */
  #slotTabs .tab .lbl-full { display: none; }
  #slotTabs .tab .lbl-mini { display: inline; }

  /* El header hace flex-wrap acá arriba, así que un popover de 360px anclado al
     botón se sale de la pantalla (y si la campanita cayó al segundo renglón,
     encima aparece a mitad de página). Pasa a ocupar el ancho, fijo bajo el header. */
  .notif-pop {
    position: fixed; left: 8px; right: 8px; top: 58px;
    width: auto; max-width: none;
  }
  .notif-list { max-height: 65vh; }
}

/* -- Componentes compartidos (hoisted en Fase 2b desde los CSS de página; ver
   docs/REDUCCION-EMBEBIDO.md). Eran reglas IDENTICAS duplicadas en varias vistas;
   movidas acá sin cambios. Selectores únicos: no colisionan con nada previo. ---- */

/* Breadcrumb (herramientas, maestro, métricas, optimización, próximamente) */
.crumb { font-size: 12px; color: var(--txt-faint); margin: 0 0 10px; }
.crumb a { color: var(--txt-dim); text-decoration: none; }
.crumb a:hover { color: var(--accent); }

/* Grilla de tarjetas-herramienta (herramientas, optimización) */
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
.tool { display: block; text-decoration: none; color: inherit; background: var(--bg-2); border: 1px solid var(--line); border-radius: var(--radius); padding: 18px; transition: .12s; }
.tool:hover { border-color: var(--accent); transform: translateY(-2px); }
.tool.soon { opacity: .5; pointer-events: none; }
.tool .t { font-size: 15px; font-weight: 700; margin-bottom: 4px; }
.tool .d { font-size: 12.5px; color: var(--txt-dim); }
.tool .tag { display: inline-block; margin-top: 10px; font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--accent); border: 1px solid var(--accent); border-radius: 5px; padding: 1px 6px; }
.tool.soon .tag { color: var(--txt-faint); border-color: var(--txt-faint); }

/* Modal "Detalle de máquina" -- filas del detalle (menu, relevo). Las clases de la
   ventana (.modal-bg/.modal/.mhead/.tabs) ya son globales más arriba en este archivo. */
.maq-num.link { cursor: pointer; color: var(--accent); text-decoration: none; }
.maq-num.link:hover { text-decoration: underline; }
.mhint { font-size: 12.5px; color: var(--txt-dim); margin-bottom: 12px; }
.slot-cab { font-size: 14px; margin-bottom: 14px; }
.slot-grupo { margin-bottom: 14px; }
.slot-grupo h4 { margin: 0 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--txt-dim); }
.slot-campo { display: flex; justify-content: space-between; gap: 10px; padding: 4px 0; border-bottom: 1px solid var(--line-soft); font-size: 13px; }
.slot-campo .lbl { color: var(--txt-dim); }
.slot-campo .val { font-weight: 600; text-align: right; }

/* Tab "Historial" del modal "Detalle de máquina" -- timeline calcado del "Historial de
   slot" de SlotAdmin (notas/tareas/fuera de servicio de /api/maestro/historial/:slotId,
   normalizado por public/portal-charts.js). Un solo carril (no alternado como el
   original): más robusto en el ancho acotado del modal y en mobile. --tipo-color lo fija
   cada item inline según su tipo (nota=--warn, tarea=--cat-2, etc.) */
.historial-list { position: relative; padding-left: 30px; }
.historial-list::before {
  content: ''; position: absolute; left: 13px; top: 4px; bottom: 4px; width: 2px; background: var(--line-soft);
}
.historial-it { position: relative; margin-bottom: 14px; }
.historial-it:last-child { margin-bottom: 0; }
.historial-ico {
  position: absolute; left: -30px; top: 0; width: 27px; height: 27px; border-radius: 50%;
  display: grid; place-items: center; font-size: 12.5px; line-height: 1;
  background: var(--bg-2); box-shadow: 0 0 0 2px var(--tipo-color, var(--line-soft));
}
.historial-card {
  background: var(--panel); border: 1px solid var(--line); border-top: 3px solid var(--tipo-color, var(--accent));
  border-radius: 10px; padding: 10px 14px;
}
.historial-cab { display: flex; align-items: baseline; gap: 8px; }
.historial-cab .num { color: var(--accent); font-weight: 700; font-size: 12.5px; }
.historial-cab .fecha { margin-left: auto; font-size: 11px; color: var(--txt-faint); white-space: nowrap; }
.historial-sitio { font-size: 11.5px; color: var(--txt-dim); margin-top: 2px; }
.historial-desc { font-size: 13px; margin: 6px 0; }
.historial-ref { font-size: 11.5px; color: var(--txt-faint); font-style: italic; margin: -2px 0 6px; }
.historial-foot { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.historial-estado { font-size: 10.5px; font-weight: 600; padding: 2px 8px; border-radius: var(--radius-pill); white-space: nowrap; }
.historial-estado.aprobado { color: var(--ok); border: 1px solid var(--ok); background: rgba(34, 197, 94, .1); }
.historial-estado.requiere { color: var(--warn); border: 1px solid var(--warn); background: rgba(234, 179, 8, .1); }
.historial-estado.rechazada { color: var(--danger); border: 1px solid var(--danger); background: rgba(248, 113, 113, .1); }

/* Tab "Mantenimiento" del modal "Detalle de máquina" -- registros PROPIOS del Portal
   (no SlotAdmin): estado de vencimiento por categoría (tarjetas con medidor) + timeline
   de últimos registros (reusa las clases .historial-* del tab Historial, coloreadas por
   categoría). Alimentado por /mantenimiento/api/ficha/:num desde portal-charts.js. */
.mant-sec { margin-bottom: 18px; }
.mant-sec:last-child { margin-bottom: 0; }
.mant-sec h4 { margin: 0 0 8px; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; color: var(--txt-dim); }
.mant-est { font-size: 10.5px; font-weight: 600; padding: 2px 8px; border-radius: var(--radius-pill); white-space: nowrap; }
.mant-est.ok { color: var(--ok); border: 1px solid var(--ok); background: rgba(34, 197, 94, .1); }
.mant-est.pronto { color: var(--warn); border: 1px solid var(--warn); background: rgba(234, 179, 8, .1); }
.mant-est.vencida { color: var(--danger); border: 1px solid var(--danger); background: rgba(248, 113, 113, .1); }
.mant-est.nunca { color: var(--txt-faint); border: 1px solid var(--line); }
/* Estado por categoría: grilla de tarjetas. Punto = color de la categoría; borde
   izquierdo + medidor = color del estado de vencimiento (día/cadaDías). */
/* Una columna: el modal es angosto (480px) e igual en las 4 solapas, así que dos cards
   por fila apretaban el nombre contra el pill de estado y lo desbordaban. minmax(240px)
   mantiene una sola columna a este ancho y solo abriría a dos si hubiera mucho más aire. */
.mant-cats { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
.mant-cat { border: 1px solid var(--line); border-left: 3px solid var(--est-color, var(--line)); border-radius: 10px; padding: 10px 12px; background: var(--panel); }
.mant-cat-top { display: flex; align-items: center; gap: 8px; }
.mant-cat-top .dot { width: 9px; height: 9px; border-radius: 50%; background: var(--cat-color, var(--accent)); flex: none; }
.mant-cat-top .nom { font-weight: 600; font-size: 13px; }
.mant-cat-top .mant-est { margin-left: auto; }
.mant-cat-meter { height: 5px; border-radius: 3px; background: var(--line-soft); margin: 9px 0 6px; overflow: hidden; }
.mant-cat-meter > span { display: block; height: 100%; border-radius: 3px; background: var(--est-color, var(--accent)); }
.mant-cat-sub { font-size: 11px; color: var(--txt-faint); }

/* ── Sitios: pestañas seleccionables (Maestro, Movimientos) ─────────────────
   Fila horizontal de sitios de la sala. El activo (con catálogo) resaltado; los
   demás inertes con badge "próx." hasta integrarse desde la API. Fuente única:
   la usan varias vistas, no copiar a los CSS de vista. */
.sitios-tabs { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 14px; }
.sitios-tabs .lbl { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--txt-faint); margin-right: 2px; }
.sitio {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 12px; border-radius: var(--radius-pill);
  font: inherit; font-size: 12.5px; color: var(--txt-dim);
  background: var(--panel); border: 1px solid var(--line); cursor: pointer;
}
.sitio .pin { flex: 0 0 auto; color: var(--txt-faint); }
.sitio.activo { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.sitio.activo .pin { color: var(--accent); }
.sitio.inerte { color: var(--txt-faint); cursor: default; opacity: .8; }
.sitio.inerte::after {
  content: "próx."; font-size: 9px; letter-spacing: .05em; text-transform: uppercase;
  padding: 1px 5px; border-radius: var(--radius-pill); background: var(--bg-2); color: var(--txt-faint);
}

/* ============================================================================
   Reportes — piezas COMPARTIDAS (docs/REPORTES.md)
   ----------------------------------------------------------------------------
   El modal "Reportar" (public/portal-reportar.js) se inyecta en TODAS las vistas,
   así que su estilo tiene que vivir acá (portal-ui.css es lo único que cargan
   todas). El tablero (public/reportes.css) reusa estas mismas clases — pills de
   estado/prioridad, chips de tipo — para no duplicar y evitar drift.
   ========================================================================== */
/* Chips de elección (tipo del reporte). Botón toggle, uno activo. */
.rep-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.rep-chip {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-radius: var(--radius-pill); cursor: pointer;
  font: inherit; font-size: 13px; color: var(--txt-dim);
  background: var(--panel); border: 1px solid var(--line);
}
.rep-chip:hover { border-color: var(--accent); }
.rep-chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 600; }
/* Pill de estado / prioridad. El color lo pone cada modificador vía --c. */
.rep-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 1px 8px; border-radius: var(--radius-pill);
  font-size: 11px; font-weight: 700; line-height: 1.7; letter-spacing: .02em;
  color: var(--c, var(--txt-dim)); border: 1px solid var(--c, var(--line));
  background: color-mix(in srgb, var(--c, var(--txt-faint)) 12%, transparent);
}
.rep-pill.st-nuevo        { --c: var(--txt-faint); }
.rep-pill.st-planificado  { --c: var(--accent); }
.rep-pill.st-en-curso     { --c: var(--warn); }
.rep-pill.st-hecho        { --c: var(--ok); }
.rep-pill.st-descartado   { --c: var(--txt-faint); opacity: .75; }
.rep-pill.pr-alta         { --c: var(--danger); }
.rep-pill.pr-media        { --c: var(--warn); }
.rep-pill.pr-baja         { --c: var(--txt-dim); }
/* Botón de voto 👍 "me sirve" (compartido modal + tablero). */
.rep-voto {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: var(--radius-pill); cursor: pointer;
  font: inherit; font-size: 12px; color: var(--txt-dim);
  background: var(--panel); border: 1px solid var(--line);
}
.rep-voto:hover { border-color: var(--accent); }
.rep-voto.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }
/* "Mis reportes" dentro del modal del top bar. */
.rep-mis { margin-top: 6px; display: flex; flex-direction: column; gap: 8px; }
.rep-mi {
  display: flex; align-items: center; gap: 8px; justify-content: space-between;
  padding: 8px 10px; border-radius: var(--radius); background: var(--bg-2); border: 1px solid var(--line-soft);
}
.rep-mi .tt { font-size: 13px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rep-mi .meta { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
