  /* ===================== ui.css — componentes comunes de las apps =====================
     ÚNICO archivo ui.css (antes había dos distintos con este mismo nombre: el del
     portal y el de la guía de estilo). Lo carga SIEMPRE el portal, una sola vez,
     justo después de base.css (CONTRATO-UI.md §A7.9); una página standalone tiene
     que enlazarlo ella misma, en ese mismo orden. Dos partes:

       1. Componentes genéricos con el color de la app (.ui-btn, .ui-badge, .ui-chip,
          .ui-card, .ui-kpi, formularios, .ui-seg, .ui-progress, .ui-alert, .ui-link).
       2. Guía de estilo SACAST — CONTRATO-UI.md Parte B (.ui-tabla, .ui-filtros,
          .ui-modal*, .ui-mrejilla/.ui-mcampo/.ui-mbloque, .ui-mtabs, .ui-empty, la
          impresión de tabla y de ficha, y el dashboard en mosaico .ui-mosaico/.ui-mtarjeta
          y sus piezas, §B7), con sus compañeros shared/js/ui.js y shared/js/ui-mosaico.js.

     Reglas comunes a las dos:
     - Todas las clases llevan prefijo `ui-` y los tokens propios `--ui-`: una app que
       no las use no nota que el archivo está cargado. Nada de aquí se aplica solo.
     - Nunca toca el chrome del portal (#col1, #col2, #topbar, #pageHead ni sus hijos):
       eso es de base.css.
     - Los tokens de color de la app (--app*, --brand*, --app-on) los pone base.css a
       partir del `color` del manifest; aquí solo se usan. Regla de uso (§A2.1):
       --brand/--app solo como RELLENO con --app-on encima; líneas, bordes de foco,
       iconos y texto de color van en --brand-ink/--app-ink.
     - De tabla hay UNA sola: `.ui-tabla` (Parte B, §B3). La antigua `.ui-table` del
       portal se retiró al unificar — no la usaba ninguna app.
     - Aquí dentro TODO selector empieza por `.ui-`: el chrome (tokens --app*, banda
       de cabecera, menú, avatar) está en base.css y la tarjeta del launcher en
       css/portal.css, que es donde se define. */

  /* ---------- Botones ---------- */
  .ui-btn{display:inline-flex;align-items:center;gap:8px;background:var(--app);color:var(--app-on);border:1px solid transparent;border-radius:11px;padding:10px 16px;font:inherit;font-size:13px;font-weight:600;cursor:pointer;transition:background .15s,border-color .15s,color .15s}
  .ui-btn:hover{background:var(--app-d)}
  .ui-btn:focus-visible{outline:none;box-shadow:0 0 0 3px var(--app-ring)}
  .ui-btn:disabled{opacity:.5;cursor:not-allowed}
  .ui-btn--soft{background:var(--app-soft);color:var(--app-ink)}
  .ui-btn--soft:hover{background:var(--app-soft-2)}
  .ui-btn--ghost{background:transparent;color:var(--ink);border-color:var(--line)}
  .ui-btn--ghost:hover{background:transparent;border-color:var(--app-ink);color:var(--app-ink)}
  .ui-btn--danger{background:var(--neg);color:#fff}
  .ui-btn--danger:hover{background:color-mix(in srgb, var(--neg) 82%, #000)}
  .ui-btn--sm{padding:6px 10px;font-size:12px;border-radius:9px}

  /* ---------- Badge / chip ---------- */
  .ui-badge{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:999px;font-size:11px;font-weight:700;background:var(--app-soft);color:var(--app-ink);white-space:nowrap}
  .ui-badge--solid{background:var(--app);color:var(--app-on)}
  .ui-chip{display:inline-flex;align-items:center;gap:6px;padding:5px 11px;border-radius:999px;border:1px solid var(--line);background:var(--panel);color:var(--ink);font-size:12px;cursor:pointer}
  .ui-chip:hover{border-color:var(--app-line)}
  .ui-chip.is-active{background:var(--app-soft);border-color:var(--app-line);color:var(--app-ink);font-weight:600}

  /* ---------- Tarjeta ---------- */
  .ui-card{background:var(--panel);border:1px solid var(--line);border-radius:14px;box-shadow:var(--shadow)}
  .ui-card--accent{border-top:3px solid var(--app)}
  .ui-card__head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:12px 16px;border-bottom:1px solid var(--line);font-weight:700}
  .ui-card__body{padding:14px 16px}

  /* ---------- KPI ---------- */
  .ui-kpi{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:12px 16px;box-shadow:var(--shadow-sm)}
  .ui-kpi__lbl{font-size:11.5px;color:var(--muted);font-weight:600}
  .ui-kpi__val{font-size:22px;font-weight:800;color:var(--app-ink);margin-top:2px}
  .ui-kpi__sub{font-size:11.5px;color:var(--muted);margin-top:2px}

  /* ---------- Formularios ---------- */
  .ui-input,.ui-select{width:100%;padding:8px 10px;border:1px solid var(--line);border-radius:9px;background:var(--panel);color:var(--ink);font:inherit;font-size:13px}
  .ui-input:focus,.ui-select:focus{outline:none;border-color:var(--app-line);box-shadow:0 0 0 3px var(--app-ring)}
  .ui-check{accent-color:var(--app)}

  /* ---------- Segmentado (pestañas internas de una vista) ---------- */
  .ui-seg{display:inline-flex;gap:2px;padding:3px;border-radius:11px;background:var(--bg);border:1px solid var(--line)}
  .ui-seg > button{border:none;background:transparent;color:var(--muted);font:inherit;font-size:12px;font-weight:600;padding:6px 12px;border-radius:8px;cursor:pointer}
  .ui-seg > button:hover{color:var(--ink)}
  .ui-seg > button.is-active{background:var(--panel);color:var(--app-ink);box-shadow:var(--shadow-sm)}

  /* ---------- Progreso ---------- */
  .ui-progress{height:8px;border-radius:999px;background:var(--app-soft);overflow:hidden}
  .ui-progress > span{display:block;height:100%;background:var(--app);border-radius:inherit}

  /* ---------- Aviso ---------- */
  .ui-alert{padding:10px 14px;border-radius:11px;background:var(--app-soft);border-left:3px solid var(--app);color:var(--ink);font-size:12.5px}

  /* ---------- Enlace ---------- */
  .ui-link{color:var(--app-ink);text-decoration:none;font-weight:600;cursor:pointer}
  .ui-link:hover{text-decoration:underline}

  /* ================================================================================
     Parte B — guía de estilo SACAST (CONTRATO-UI.md). Lo que una app pinta DENTRO
     de #vista y en sus ventanas modales. Su compañero es shared/js/ui.js.
     ================================================================================ */

/* ── Tokens propios (§B0) ─────────────────────────────────────────────────── */
/* --ui-hover usa el token de superficie del portal si existe (base.css: --surface-hover), y
   si no cae en su propio valor: así una copia junto a un base.css antiguo sigue funcionando. */
:root{
  --ui-line-dark: #c7ced5;                                  /* separador de apartado (fichas) */
  --ui-hover:     var(--surface-hover, rgba(30,42,54,.04)); /* fondo al pasar el ratón por filas y pestañas */
}
body.dark{
  --ui-line-dark: #3d4a58;
  --ui-hover:     var(--surface-hover, rgba(255,255,255,.04));
}

/* ── Tablas (§B3) ─────────────────────────────────────────────────────────── */
/* Contenedor: esquinas rectas a propósito (aspecto de documento, §B5), sin scroll interno. */
.ui-tabla-wrap{
  background:var(--panel); border:1px solid var(--line); border-radius:0;
  box-shadow:var(--shadow); overflow:hidden; margin-bottom:18px;
}
.ui-tabla{ width:100%; border-collapse:collapse; font-size:12.5px; }
/* Cabecera con el color de la app (relleno --brand, texto --app-on), como GMAO, y de doble
   alto en las tablas de página. */
.ui-tabla th{
  text-align:left; padding:18px 14px; border-bottom:1px solid var(--line);
  background:var(--brand); color:var(--app-on, #fff);
  font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.4px;
  white-space:nowrap; /* el título no se parte en dos líneas */
}
.ui-tabla td{ padding:9px 14px; border-bottom:1px solid var(--line); vertical-align:middle; }
/* Ninguna fila se parte en dos líneas: lo que no cabe acaba en puntos suspensivos (tope
   --ui-td-max por celda; ui.js pone el texto entero de título al pasar el ratón). */
.ui-tabla td{ white-space:nowrap; overflow:hidden; text-overflow:ellipsis; max-width:var(--ui-td-max, 320px); }
.ui-tabla tbody tr:last-child td{ border-bottom:none; }
/* Toda fila abre su ficha: fondo suave y cursor de mano. Sin cebreado. */
.ui-tabla tbody tr:hover td{ background:var(--ui-hover); cursor:pointer; }
/* Tabla cuyas filas NO abren ficha (p.ej. una rejilla de casillas o de valores editables):
   se mantiene el fondo al pasar el ratón, pero sin prometer un clic con la mano. */
.ui-tabla--estatica tbody tr:hover td{ cursor:default; }
/* Tabla en modo rejilla (§B3.3, UiRejilla): las celdas editables llevan su control a todo el
   hueco, sin borde propio, como una hoja de cálculo. Las filas no abren ficha. */
.ui-tabla--rejilla tbody tr:hover td{ background:none; cursor:default; }
.ui-tabla--rejilla td.ui-celda{ padding:0; }
.ui-tabla--rejilla .ui-celda-in{
  display:block; width:100%; min-width:60px; box-sizing:border-box;
  padding:9px 14px; margin:0; border:none; border-radius:0; background:transparent;
  font:inherit; color:var(--ink); outline:none; box-shadow:none;
}
.ui-tabla--rejilla td.num .ui-celda-in{ text-align:right; font-variant-numeric:tabular-nums; }
.ui-tabla--rejilla select.ui-celda-in{ cursor:pointer; }
.ui-tabla--rejilla .ui-celda-in:focus{ box-shadow:inset 0 0 0 2px var(--brand); background:var(--panel); }
/* Celda que no se puede editar en esta fila: se ve, pero apagada. */
.ui-tabla--rejilla td.ui-celda--fija{ background:var(--bg); color:var(--muted); }
/* Celda con un valor distinto del cargado, y celda que el guardado ha rechazado. */
.ui-tabla--rejilla td.ui-celda--cambiada{ background:var(--brand-soft); }
.ui-tabla--rejilla td.ui-celda--error{ box-shadow:inset 0 0 0 1px var(--neg); }
/* Fila nueva, todavía sin guardar: su nº va apagado. */
.ui-tabla--rejilla tr.ui-tabla-nueva td:first-child{ color:var(--muted); }
/* Fila de aviso dentro del <tbody> (Cargando…, Sin resultados, un error): no es un
   registro, así que ni se resalta ni parece pulsable. */
.ui-tabla tbody tr.ui-tabla-msg td{ color:var(--muted); background:none; cursor:default; padding:18px 14px; }

/* Totales */
.ui-tabla tfoot td{
  background:var(--bg); border-top:1px solid var(--line); font-size:12.5px;
  padding:9px 14px; vertical-align:middle;
}
.ui-tabla tfoot tr:hover td{ background:var(--bg); cursor:default; }

/* Columnas numéricas: a la derecha y con cifras tabulares (cabecera incluida). */
.ui-tabla .num{ text-align:right; font-variant-numeric:tabular-nums; }
/* Columnas de fecha: DD-MM-AAAA no se parte por el guion cuando la columna aprieta. */
.ui-tabla .fecha{ white-space:nowrap; }

/* Búsqueda por columna (UiColBuscar, ui.js): pulsar la cabecera la convierte en casilla. */
.ui-tabla:not([data-sin-buscar]) > thead th{ cursor:pointer; }
/* Margen negativo: la casilla ocupa casi lo mismo que el texto y la cabecera no cambia de alto. */
.ui-th-buscar{
  display:block; width:100%; min-width:60px; box-sizing:border-box; margin:-7px 0; padding:5px 8px;
  /* Translúcida sobre el color de la cabecera con el propio --app-on (blanco o tinta según la
     app), para que se vea igual con un color de app claro (§A2.1). */
  border:1px solid color-mix(in srgb, var(--app-on, #fff) 35%, transparent); border-radius:0; outline:none;
  background:color-mix(in srgb, var(--app-on, #fff) 15%, transparent); color:var(--app-on, #fff);
  font:inherit; font-size:11px; text-transform:none; letter-spacing:normal; font-weight:600;
}
.ui-th-buscar::placeholder{ color:var(--app-on, #fff); opacity:.6; text-transform:uppercase; font-size:10px; }
.ui-th-buscar:focus{ background:color-mix(in srgb, var(--app-on, #fff) 25%, transparent);
  border-color:color-mix(in srgb, var(--app-on, #fff) 70%, transparent); }
.ui-tabla th.num .ui-th-buscar{ text-align:right; }
.ui-tabla tbody tr.ui-colbuscar-oculto{ display:none; }

/* Orden por columna (UiColOrden, ui.js, §B3.2): icono a la derecha de cada título con texto.
   Pulsarlo ordena; el resto del título sigue abriendo la búsqueda. Sin orden, dos chevrons
   (arriba y abajo) apagados; con él, uno solo (arriba ascendente, abajo descendente) entero.
   Es un SVG usado como máscara y pintado con currentColor: toma el color del título.
   El hueco a la derecha es para el icono (la casilla de búsqueda no lo tapa). */
.ui-tabla:not([data-sin-ordenar]) > thead th{ position:relative; padding-right:30px; }
.ui-tabla:not([data-sin-ordenar]) > thead th:not(:empty)::after{
  --ui-orden-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 9l4-4 4 4M8 15l4 4 4-4'/%3E%3C/svg%3E");
  content:''; position:absolute; right:10px; top:50%; transform:translateY(-50%);
  width:14px; height:14px; background:currentColor; opacity:.55;
  -webkit-mask:var(--ui-orden-ico) center/contain no-repeat;
          mask:var(--ui-orden-ico) center/contain no-repeat;
}
.ui-tabla:not([data-sin-ordenar]) > thead th:not(:empty):hover::after{ opacity:1; }
.ui-tabla > thead th[aria-sort="ascending"]::after{
  opacity:1;
  --ui-orden-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 14l4-4 4 4'/%3E%3C/svg%3E");
}
.ui-tabla > thead th[aria-sort="descending"]::after{
  opacity:1;
  --ui-orden-ico:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 10l4 4 4-4'/%3E%3C/svg%3E");
}

/* Dentro de una ventana la tabla no es un listado principal: cabecera sin doble alto. */
.ui-modal-body .ui-tabla th{ padding:9px 14px; }
.ui-modal-body .ui-tabla:not([data-sin-ordenar]) > thead th{ padding-right:30px; }

/* Móvil: de la 4ª columna en adelante se ocultan. */
@media (max-width:640px){
  .ui-tabla th:nth-child(n+4),
  .ui-tabla td:nth-child(n+4){ display:none; }
}

/* ── Fila de filtros (§B2) ────────────────────────────────────────────────── */
/* Oculta por defecto ([hidden]); la abre y la cierra el icono de filtro de #pageActions.
   El margin-top negativo compensa el hueco bajo la banda de cabecera para que quede igual
   de pegada arriba que abajo. */
.ui-filtros{
  display:flex; flex-wrap:wrap; align-items:center; gap:8px 22px;
  background:var(--brand-soft); border:1px solid var(--line); border-radius:16px;
  padding:8px 18px; margin:-16px 0 16px;
}
.ui-filtros[hidden]{ display:none; }
.ui-filtros .ui-mcampo{ flex:1 1 170px; max-width:240px; min-width:0; }
/* Select más bajo que el de un formulario de ficha: aquí es un filtro rápido. */
.ui-filtros .ui-mcampo select{
  width:100%; padding:4px 10px; border:1px solid var(--line); border-radius:10px;
  background:var(--panel); color:var(--ink); font:inherit; font-size:14px;
}
.ui-filtros .ui-mcampo select:focus{ outline:none; border-color:var(--brand-ink); }
/* Campo de fecha nativo (tipo:'fecha'): misma altura que el select. */
.ui-filtros .ui-mcampo-fecha input[type=date]{
  width:100%; padding:3px 10px; border:1px solid var(--line); border-radius:10px;
  background:var(--panel); color:var(--ink); font:inherit; font-size:14px;
}
.ui-filtros .ui-mcampo-fecha input[type=date]:focus{ outline:none; border-color:var(--brand-ink); }
/* Multiselección (tipo:'multi'): botón con el aspecto del select que abre debajo una lista
   flotante de casillas, por encima del contenido como un popover (§B4.7). 5px arriba y abajo
   (y no los 4px del select) para que quede igual de alto que él: el select nativo suma 2px. */
.ui-filtros .ui-mcampo-multi{ position:relative; }
.ui-filtros .ui-multi-btn{
  display:flex; align-items:center; gap:6px; width:100%; padding:5px 10px;
  border:1px solid var(--line); border-radius:10px; background:var(--panel); color:var(--ink);
  font:inherit; font-size:14px; text-align:left; cursor:pointer;
}
.ui-filtros .ui-multi-btn:focus{ outline:none; border-color:var(--brand-ink); }
.ui-filtros .ui-multi-txt{ flex:1 1 auto; min-width:0; overflow:hidden; white-space:nowrap; text-overflow:ellipsis; }
.ui-filtros .ui-multi-btn svg{
  flex:none; width:16px; height:16px; fill:none; stroke:var(--muted); stroke-width:2;
  stroke-linecap:round; stroke-linejoin:round;
}
.ui-filtros .ui-multi-lista{
  position:absolute; z-index:600; top:calc(100% + 6px); left:0;
  min-width:100%; width:max-content; max-width:calc(100vw - 32px); box-sizing:border-box;
  display:flex; flex-direction:column; gap:4px; padding:6px;
  background:var(--bg); border:1px solid var(--line); border-radius:10px; box-shadow:var(--shadow);
}
.ui-filtros .ui-multi-lista[hidden]{ display:none; }
/* Si por la izquierda se saldría de la ventana, ui.js la alinea al borde derecho del campo. */
.ui-filtros .ui-multi-lista--dcha{ left:auto; right:0; }
/* Sin nowrap: con width:max-content va en una línea; solo parte si toca el max-width. */
.ui-filtros .ui-multi-op{
  display:flex; align-items:center; gap:8px; padding:4px 10px; border-radius:10px;
  font-size:14px; color:var(--ink); cursor:pointer;
}
.ui-filtros .ui-multi-op:hover{ background:var(--ui-hover); }
/* Sin el relleno y el borde de .ui-mcampo input: es una casilla, no una caja de texto. */
.ui-filtros .ui-multi-op input[type=checkbox]{ width:auto; margin:0; padding:0; border:0; flex:none; }
/* .ui-mcampo input:focus quita el contorno: con ↑/↓ hay que ver qué casilla tiene el foco. */
.ui-filtros .ui-multi-op input[type=checkbox]:focus-visible{ outline:2px solid var(--brand-ink); outline-offset:2px; }
.ui-filtros .ui-multi-op:focus-within{ background:var(--ui-hover); }
.ui-filtros [data-filtro-limpiar]{ margin-left:auto; }

/* ── Ventanas modales / fichas (§B4) ──────────────────────────────────────── */
/* Estructura (la monta UiModal, en <body>, una sola a la vez):
   .ui-modal-capa > .ui-modal[.ui-modal--ancho]
     > header.ui-modal-head (.ui-modal-head-l · .ui-modal-head-acc · .ui-modal-x)
     > .ui-modal-body > footer.ui-modal-pie (solo si hay botones)                   */
.ui-modal-capa{
  position:fixed; inset:0; z-index:500;
  background:rgba(16,24,34,.45);
  display:flex; align-items:flex-start; justify-content:center;
  padding:40px 20px; overflow-y:auto;
  animation:uiFundido .12s ease-out;
}
@keyframes uiFundido{ from{opacity:0} to{opacity:1} }
body.ui-modal-abierto{ overflow:hidden; }

.ui-modal{
  width:100%; max-width:980px; background:var(--panel); color:var(--ink);
  border-radius:0; box-shadow:0 12px 48px rgba(16,38,64,.28);
  overflow:hidden; animation:uiSubir .14s ease-out;
}
@keyframes uiSubir{ from{transform:translateY(8px)} to{transform:none} }
.ui-modal--ancho{ max-width:1200px; }
@media (prefers-reduced-motion:reduce){ .ui-modal-capa, .ui-modal{ animation:none; } }

/* Cabecera: título (crece) a la izquierda; iconos de acción y X juntos a la derecha. */
.ui-modal-head{
  display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  padding:20px 26px 16px; border-bottom:1.5px solid var(--ui-line-dark);
}
.ui-modal-head-l{ display:flex; flex-direction:column; gap:2px; flex:1 1 0; min-width:0; }
.ui-modal-titrow{ display:flex; align-items:center; gap:12px; flex-wrap:wrap; }
.ui-modal-head-acc{ display:flex; align-items:center; gap:8px; flex:0 0 auto; }
.ui-modal-eyebrow{
  margin:0; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.6px; color:var(--muted);
}
.ui-modal-tit{ margin:0; font-size:21px; font-weight:800; color:var(--ink); letter-spacing:-.01em; }

/* Iconos de cabecera: sin fondo ni borde salvo al pasar el ratón. El selector lleva
   .ui-modal-head-acc delante para pesar más que .btn.ghost(:hover) de base.css. */
.ui-modal-head-acc .btn.ui-modal-ico{
  width:42px; height:42px; padding:0; display:grid; place-items:center;
  background:transparent; border-color:transparent; box-shadow:none;
}
/* .btn delante para empatar con .btn.ghost svg de base.css (0,2,1) y ganarle por ir después:
   sin él los iconos salían en --ink, a 1.9 y la papelera sin rojo. */
.btn.ui-modal-ico svg{
  width:22px; height:22px; fill:none; stroke:var(--muted); stroke-width:1.7;
  stroke-linecap:round; stroke-linejoin:round;
}
.ui-modal-head-acc .btn.ui-modal-ico:hover:not([disabled]),
.ui-modal-head-acc .btn.ui-modal-ico:active:not([disabled]){ background:var(--panel); border-color:var(--line); }
.ui-modal-ico:hover:not([disabled]) svg{ stroke:var(--brand-ink); }
.ui-modal-ico[disabled]{ opacity:.4; cursor:default; }
/* Acción destructiva (borrar): en --neg desde el principio, no solo al pasar el ratón — es la
   misma señal que el botón de borrar del pie (§B4.6). */
.btn.ui-modal-ico--peligro svg{ stroke:var(--neg); }
.ui-modal-head-acc .btn.ui-modal-ico--peligro:hover:not([disabled]){ border-color:var(--neg); }
.ui-modal-ico--peligro:hover:not([disabled]) svg{ stroke:var(--neg); }

.ui-modal-x{
  width:38px; height:38px; border-radius:10px; background:var(--panel); border:1px solid var(--line);
  display:grid; place-items:center; cursor:pointer; padding:0;
}
.ui-modal-x svg{ width:16px; height:16px; fill:none; stroke:var(--muted); stroke-width:2; stroke-linecap:round; }
.ui-modal-x:hover{ border-color:var(--neg); }
.ui-modal-x:hover svg{ stroke:var(--neg); }

/* Flechas para pasar a la ficha anterior/siguiente de la tabla (§B4.9): fuera de la ventana, a
   12px de sus bordes (la posición horizontal la pone UiModal) y a media altura de la pantalla. */
.ui-modal-nav{
  position:fixed; top:50%; transform:translateY(-50%); z-index:1;
  width:44px; height:44px; border-radius:10px; background:var(--panel); border:1px solid var(--line);
  box-shadow:var(--shadow); display:grid; place-items:center; cursor:pointer; padding:0;
}
.ui-modal-nav svg{ width:22px; height:22px; fill:none; stroke:var(--muted); stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.ui-modal-nav:hover:not([disabled]){ border-color:var(--brand-ink); }
.ui-modal-nav:hover:not([disabled]) svg{ stroke:var(--brand-ink); }
.ui-modal-nav[disabled]{ opacity:.4; cursor:default; }

/* Cuerpo: mismo hueco (16px) arriba y abajo de cada separador. */
.ui-modal-body{ padding:16px 26px 16px; }

/* Pestañas de opciones: tira entre dos separadores a sangre (margen lateral negativo). */
.ui-mtabs{
  display:flex; gap:4px; flex-wrap:wrap; align-items:center;
  margin:16px -26px 0; padding:16px 26px;
  border-top:1.5px solid var(--ui-line-dark); border-bottom:1.5px solid var(--ui-line-dark);
}
.ui-modal-body > .ui-mtabs:first-child{ margin-top:-16px; border-top:none; }
.ui-mtab{
  display:inline-flex; align-items:center; padding:7px 14px; border-radius:9px;
  font:inherit; font-size:12.5px; font-weight:600; color:var(--muted);
  cursor:pointer; border:none; background:none;
}
.ui-mtab:hover{ background:var(--ui-hover); color:var(--ink); }
.ui-mtab.active{ background:var(--brand-soft); color:var(--brand-ink); }
/* Contador opcional detrás del texto de la pestaña (nº de líneas, de adjuntos…). */
.ui-mtab-n{ margin-left:6px; font-size:11px; font-weight:700; opacity:.7; font-variant-numeric:tabular-nums; }
/* Acción propia de la pestaña activa (p.ej. "Guardar cálculo", "Añadir línea"): icono pegado al
   borde derecho de la tira, mismo trazo que los iconos de cabecera (.ui-modal-ico) pero más
   pequeño para encajar en una tira más baja que la cabecera. */
.ui-mtabs-acc{ margin-left:auto; display:flex; align-items:center; gap:4px; }
.ui-mtabs-acc .btn.ui-modal-ico{
  width:32px; height:32px; padding:0; display:grid; place-items:center;
  background:transparent; border-color:transparent; box-shadow:none;
}
.ui-mtabs-acc .btn.ui-modal-ico:hover:not([disabled]),
.ui-mtabs-acc .btn.ui-modal-ico:active:not([disabled]){ background:var(--panel); border-color:var(--line); }
.ui-mtabcontent{ padding-top:16px; }
.ui-mtabcontent > .ui-mbloque:last-child{ margin-bottom:0; }

/* Pie: fondo --bg; el botón de borrar va a la derecha en --neg. */
.ui-modal-pie{
  display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:16px 26px 22px; border-top:1.5px solid var(--ui-line-dark); background:var(--bg);
}
.ui-modal-pie .btn.danger,
.ui-modal-pie .btn.borrar{ margin-left:auto; background:var(--neg); color:#fff; border-color:var(--neg); }
/* Un desplegable de acción en el pie (p.ej. "Cambiar estado…"): a la altura de los botones. */
.ui-modal-pie select{
  padding:7px 10px; border:1px solid var(--line); border-radius:10px;
  background:var(--panel); color:var(--ink); font:inherit; font-size:13px;
}
.ui-modal-pie select:focus{ outline:none; border-color:var(--brand-ink); }

/* Campos (§B4.5) */
.ui-mrejilla{ display:grid; grid-template-columns:repeat(3,1fr); gap:16px 22px; margin-bottom:18px; }
@media (max-width:760px){ .ui-mrejilla{ grid-template-columns:1fr; } }
.ui-mcampo{ display:flex; flex-direction:column; gap:6px; min-width:0; }
.ui-mcampo > label,
.ui-mbloque > label{
  font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.5px; color:var(--muted);
}
.ui-mcampo-val{ font-size:14px; color:var(--ink); padding:9px 0; min-height:20px; word-break:break-word; }
.ui-mcampo input, .ui-mcampo select, .ui-mcampo textarea,
.ui-mbloque input, .ui-mbloque select, .ui-mbloque textarea{
  width:100%; padding:11px 14px; border:1px solid color-mix(in srgb, var(--muted) 35%, var(--line));
  border-radius:10px; background:var(--panel); color:var(--ink); font:inherit; font-size:14px;
}
.ui-mcampo input:focus, .ui-mcampo select:focus, .ui-mcampo textarea:focus,
.ui-mbloque input:focus, .ui-mbloque select:focus, .ui-mbloque textarea:focus{
  outline:none; border-color:var(--brand-ink);
}
/* Los textos de ayuda dentro del campo (qué buscar, qué dato va) salen en cursiva (§B4.6). */
.ui-mcampo ::placeholder, .ui-mbloque ::placeholder, .ui-popover-campo ::placeholder{ color:var(--muted); font-style:italic; }
/* Campo obligatorio vacío al guardar (§B4.6, UiModal.faltanObligatorios): borde y aviso en --neg;
   se quita al escribir en él. */
.ui-mcampo--error input, .ui-mcampo--error select, .ui-mcampo--error textarea,
.ui-mcampo--error input:focus, .ui-mcampo--error select:focus, .ui-mcampo--error textarea:focus{ border-color:var(--neg); }
.ui-mcampo-error{ font-size:12px; color:var(--neg); }
/* Fuera de edición el campo se queda pintado como su control real, no como texto plano: solo
   cambia a gris y deja de poder tocarse (CONTRATO-UI §B4.5, ficha con lápiz). */
.ui-mcampo input:disabled, .ui-mcampo select:disabled, .ui-mcampo textarea:disabled,
.ui-mbloque input:disabled, .ui-mbloque select:disabled, .ui-mbloque textarea:disabled{
  background:color-mix(in srgb, var(--muted) 8%, var(--panel));
  border-color:color-mix(in srgb, var(--muted) 25%, var(--line));
  color:var(--muted); cursor:default;
}
.ui-mbloque{ display:flex; flex-direction:column; gap:6px; margin-bottom:18px; }
.ui-mbloque textarea{ min-height:88px; resize:vertical; }
.ui-modal-body > .ui-mbloque:last-child,
.ui-modal-body > .ui-mrejilla:last-child{ margin-bottom:0; }
/* Dos casillas de media anchura en una columna de la rejilla (§B4.6, UiModal.par). */
.ui-mpar{ display:grid; grid-template-columns:1fr 1fr; gap:22px; min-width:0; }
/* Campo que permite crear un registro nuevo (§B4.6, UiModal.conMas): un "+" dentro del campo, a la
   derecha, en vez de un botón de texto debajo. Mismo trazo que los iconos de la ficha, 32×32. */
.ui-mcampo-crear{ position:relative; }
.ui-mcampo-crear input{ padding-right:44px; }
.ui-mcampo-crear .btn.ui-mcampo-mas{
  position:absolute; right:6px; top:50%; transform:translateY(-50%);
  width:32px; height:32px; padding:0; display:grid; place-items:center;
  background:transparent; border-color:transparent; box-shadow:none;
}
.ui-mcampo-crear .btn.ui-mcampo-mas:hover:not([disabled]){ background:var(--ui-hover); }
.ui-mcampo-crear .btn.ui-mcampo-mas svg{ width:20px; height:20px; }
/* Apartado de campos (§B4.6, UiModal.seccion): agrupa rejillas/bloques bajo un título, con un
   separador de apartado a sangre encima y 16px de aire a cada lado (§B4.3). */
.ui-mseccion{ margin:0 -26px; padding:16px 26px 0; border-top:1.5px solid var(--ui-line-dark); }
.ui-mseccion + .ui-mseccion{ margin-top:16px; }
/* El primero no lleva línea: ya está la de la cabecera (no hay dobles líneas, aunque haya avisos encima). */
.ui-modal-body > .ui-mseccion:first-of-type{ padding-top:0; border-top:none; }
.ui-mseccion > :last-child{ margin-bottom:0; }
.ui-mseccion-tit{
  margin:0 0 14px; font-size:11px; font-weight:700; text-transform:uppercase;
  letter-spacing:.6px; color:var(--brand-ink);
}

/* Hueco de imagen de una ficha (UiModal.imagen) */
.ui-mimg{ display:flex; flex-direction:column; gap:8px; align-items:flex-start; }
.ui-mimg img, .ui-mimg-vacia{
  width:100%; max-width:260px; aspect-ratio:4 / 3; border-radius:12px; background:var(--bg);
}
.ui-mimg img{ object-fit:cover; border:1px solid var(--line); }
.ui-mimg-vacia{
  border:1px dashed var(--line); display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:4px; color:var(--muted);
}
.ui-mimg-vacia span{ font-size:26px; opacity:.55; }
.ui-mimg-vacia small{ font-size:11px; }
.ui-mimg-acc{ display:flex; gap:8px; }
/* Ficha con imagen (UiModal.conImagen): imagen a la izquierda, rejilla a 2 columnas al lado. */
.ui-mficha-img{ display:grid; grid-template-columns:260px 1fr; gap:24px; align-items:start; margin-bottom:18px; }
.ui-mficha-img .ui-mrejilla{ grid-template-columns:repeat(2,1fr); margin-bottom:0; }
.ui-modal-body > .ui-mficha-img:last-child{ margin-bottom:0; }
@media (max-width:760px){
  .ui-mficha-img, .ui-mficha-img .ui-mrejilla{ grid-template-columns:1fr; }
}

/* Aviso encima de los campos de una ficha (p.ej. "solo lectura por su estado"): sin él, el lápiz
   apagado no explica por qué. Con un botón dentro, el texto a la izquierda y el botón a la derecha. */
.ui-aviso-ficha{ margin:0 0 14px; }
.ui-aviso-ficha:has(> .btn){ display:flex; align-items:center; justify-content:space-between; gap:10px; flex-wrap:wrap; }

/* ── Pestañas comunes de una ficha (§B4.8) ────────────────────────────────── */
/* Comentarios: autor · fecha (y borrar a la derecha) encima del texto, con sus saltos de línea. */
.ui-hilo-item{ padding:10px 0; border-bottom:1px solid var(--line); }
.ui-hilo-item:last-child{ border-bottom:none; }
.ui-hilo-cab{ display:flex; align-items:center; justify-content:space-between; gap:10px; font-size:11.5px; color:var(--muted); margin-bottom:4px; }
.ui-hilo-txt{ font-size:14px; color:var(--ink); white-space:pre-wrap; }
/* Adjuntos: nombre (botón con aspecto de enlace que descarga), datos y borrar; la subida, debajo
   de un separador de apartado. */
.ui-adjunto{ padding:8px 0; border-bottom:1px solid var(--line); }
.ui-adjunto:last-child{ border-bottom:none; }
.ui-adjunto-nom{ flex:1; min-width:0; text-align:left; border:none; background:none; padding:0; cursor:pointer;
  font:inherit; font-size:13.5px; font-weight:600; color:var(--brand-ink); text-decoration:underline;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ui-adjunto-nom:hover{ color:var(--brand-d); }
.ui-adjunto-nuevo{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-top:16px;
  padding-top:16px; border-top:1.5px solid var(--ui-line-dark); }
.ui-adjunto-nuevo input[type=file]{ font-size:13px; color:var(--muted); max-width:100%; }

/* Login de una app abierta sola, fuera del portal (§B4.10, UiLogin) */
.ui-login{
  max-width:340px; margin:64px auto; padding:28px; box-sizing:border-box;
  background:var(--panel); border:1px solid var(--line); border-radius:16px; box-shadow:var(--shadow-sm);
  display:flex; flex-direction:column; gap:10px;
}
.ui-login-tit{ margin:0; font-size:21px; font-weight:800; color:var(--ink); letter-spacing:-.01em; }
.ui-login-nota{ margin:0 0 4px; font-size:13px; color:var(--muted); }
.ui-login .btn{ width:100%; justify-content:center; margin-top:6px; }
.ui-login .btn[disabled]{ opacity:.6; cursor:default; }
.ui-login-error{ min-height:18px; font-size:12.5px; color:var(--neg); }
@media (max-width:420px){ .ui-login{ margin:24px 16px; } }

/* Fotos de un alta (§B4.6, UiFotos): miniaturas antes de enviar, cada una con su × */
.ui-fotos-lista{ display:flex; flex-wrap:wrap; gap:8px; margin-top:10px; }
.ui-fotos-vacia{ font-size:12.5px; color:var(--muted); }
.ui-foto{ position:relative; width:80px; height:80px; }
.ui-foto img{ width:100%; height:100%; object-fit:cover; border-radius:10px; border:1px solid var(--line); display:block; }
.ui-foto-quitar{
  position:absolute; top:-6px; right:-6px; width:22px; height:22px; padding:0; border-radius:50%;
  display:grid; place-items:center; background:var(--panel); border:1px solid var(--line); cursor:pointer;
}
.ui-foto-quitar svg{ width:12px; height:12px; fill:none; stroke:var(--muted); stroke-width:2; stroke-linecap:round; }
.ui-foto-quitar:hover svg{ stroke:var(--neg); }

/* ── Popover (§B4.7, UiPopover) ───────────────────────────────────────────── */
/* Ficha flotante anclada bajo un botón, por encima de la ventana (z-index 500). */
.ui-popover{
  position:fixed; z-index:600; width:300px; max-width:calc(100vw - 16px); overflow:auto; box-sizing:border-box;
  display:flex; flex-direction:column; gap:10px; padding:12px;
  background:var(--bg); border:1px solid var(--line); border-radius:10px; box-shadow:var(--shadow);
}
/* Menú: una columna de botones a lo ancho, alineados a la izquierda. */
.ui-popover--menu{ width:240px; gap:6px; }
.ui-popover--menu > .btn{ width:100%; justify-content:flex-start; }
/* Formulario con una fila por registro (adjudicar líneas, enlazar varias ofertas…). */
.ui-popover--ancho{ width:min(520px, calc(100vw - 32px)); }
.ui-popover-tit{ font-weight:700; font-size:13px; color:var(--ink); }
/* Separador con rótulo dentro de un menú (p.ej. "Corregir (administrador)"). */
.ui-popover-sec{ margin-top:6px; padding-top:8px; border-top:1px solid var(--line);
  font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:.5px; color:var(--muted); }
.ui-popover-campo{ display:flex; flex-direction:column; gap:4px; font-size:11.5px; color:var(--muted); }
.ui-popover-campo select, .ui-popover-campo input, .ui-popover-campo textarea,
.ui-popover > textarea{ width:100%; box-sizing:border-box; }
.ui-popover-campo textarea{ min-height:64px; resize:vertical; }
.ui-popover > textarea{ min-height:80px; resize:vertical; }
.ui-popover-error{ margin:0; color:var(--neg); }
.ui-popover-pie{ display:flex; gap:8px; }

/* ── Estado vacío (§B6) ───────────────────────────────────────────────────── */
.ui-empty{
  text-align:center; padding:56px 16px; color:var(--muted);
  background:var(--panel); border:1px solid var(--line); border-radius:16px;
  box-shadow:var(--shadow-sm);
}
.ui-empty .ico{ font-size:40px; display:block; margin-bottom:14px; }

/* ── Línea de tiempo (§B6, UiLineaTiempo) ─────────────────────────────────── */
/* Círculo de 32px con icono de 16px, unidos por un raíl de 1.5px; título, quién · cuándo y el
   detalle. Sobre --brand el icono va en --app-on; sobre los colores de estado, en blanco. */
.ui-tl{ display:flex; flex-direction:column; }
.ui-tl-item{ position:relative; display:flex; gap:14px; padding-bottom:20px; }
.ui-tl-item:last-child{ padding-bottom:0; }
.ui-tl-item:not(:last-child)::before{
  content:""; position:absolute; left:15px; top:32px; bottom:-4px; width:1.5px; background:var(--line);
}
.ui-tl-marcador{ flex:0 0 auto; width:32px; height:32px; border-radius:50%; display:grid; place-items:center; z-index:1;
  background:var(--muted); color:#fff; }
.ui-tl-marcador svg{ width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:2.2; stroke-linecap:round; stroke-linejoin:round; }
.ui-tl-cuerpo{ flex:1; min-width:0; padding-top:3px; }
.ui-tl-tit{ margin:0; font-size:13px; font-weight:700; color:var(--ink); }
.ui-tl-meta{ margin:2px 0 0; font-size:11.5px; color:var(--muted); }
.ui-tl-detalle{ margin:5px 0 0; font-size:12px; color:var(--ink); opacity:.85; }
.ui-tl--gris { background:var(--muted); }
.ui-tl--marca{ background:var(--brand); color:var(--app-on, #fff); }
.ui-tl--azul { background:var(--blue); }
.ui-tl--verde{ background:var(--pos); }
.ui-tl--rojo { background:var(--neg); }
.ui-tl--ambar{ background:var(--warn); }

/* ── Impresión ────────────────────────────────────────────────────────────── */
/* Imprimir tabla (UiAcciones): solo el .ui-tabla-wrap marcado con .ui-print-tabla, sin
   el chrome de la app ni controles. Condicionado con :has() porque ui.css es global (lo
   carga el portal para todas las apps): sin tabla marcada, imprimir no cambia en nada. */
@media print{
  body:has(.ui-print-tabla) *{ visibility:hidden; }
  .ui-print-tabla, .ui-print-tabla *{ visibility:visible; }
  .ui-print-tabla{
    position:absolute; inset:auto; top:0; left:0; right:0;
    padding:0; background:#fff; box-shadow:none;
  }
  /* Con una ficha abierta se imprime solo la ficha, sin sus controles. */
  body:has(.ui-modal-capa) *{ visibility:hidden; }
  .ui-modal-capa, .ui-modal-capa *{ visibility:visible; }
  .ui-modal-capa{ position:absolute; inset:auto; top:0; left:0; right:0; padding:0; background:#fff; }
  .ui-modal{ max-width:100%; box-shadow:none; }
  .ui-modal-head-acc, .ui-modal-x, .ui-modal-pie, .ui-modal-nav{ display:none !important; }
}

/* ── Dashboard en mosaico configurable (§B7) ──────────────────────────────── */
/* Portado del dashboard de GMAO (gmao.css, .gmao-m*). Lo pinta UiMosaico (shared/js/ui-mosaico.js);
   cada app solo registra sus tarjetas. Rejilla de 12 columnas: el ancho lo decide la tarjeta
   (.ui-msp-N), nunca el usuario, así ninguna combinación deja huecos. Por debajo de 900px todas
   pasan a ancho completo. */
.ui-mosaico{ display:grid; grid-template-columns:repeat(12,minmax(0,1fr)); gap:14px; align-items:stretch; }
.ui-msp-3{ grid-column:span 3; } .ui-msp-4{ grid-column:span 4; } .ui-msp-5{ grid-column:span 5; }
.ui-msp-6{ grid-column:span 6; } .ui-msp-7{ grid-column:span 7; } .ui-msp-12{ grid-column:span 12; }

/* Barra de encima del mosaico: periodo (chips .ui-chip) a la izquierda, texto de ayuda a la derecha. */
.ui-mbarra-sup{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:12px; }
.ui-mbarra-sup-chips{ display:flex; gap:6px; flex-wrap:wrap; }
.ui-mbarra-sup-txt{ margin-left:auto; font-size:11.5px; color:var(--muted); }

/* Estilo "Grafito" (2026-10-16): tarjeta plana, borde fino, radio 8px y sin sombra; el título,
   en versalitas grises como la cabecera de una tabla. Las cifras, en negrita (800). */
.ui-mtarjeta{
  background:var(--panel); border:1px solid var(--line); border-radius:8px; box-shadow:none;
  padding:14px 16px; display:flex; flex-direction:column; min-width:0;
}
/* Sin marco (marco:false): para un grupo de sub-tarjetas sueltas. */
.ui-mtarjeta.desnuda{ background:none; border:0; box-shadow:none; padding:0; }
.ui-mtarjeta-h{ display:flex; align-items:center; gap:8px; min-height:22px; }
.ui-mtarjeta-tit{ flex:1; min-width:0; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.7px; color:var(--muted); }
.ui-mtarjeta-resumen{ font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; white-space:nowrap; }
.ui-mtarjeta-resumen b{ font-weight:800; color:var(--ink); }
.ui-mtarjeta-badge{ padding:2px 9px; border-radius:9px; background:var(--brand-soft); color:var(--brand-ink); font-size:11px; font-weight:800; }
.ui-mtarjeta-enl{ font-size:12px; font-weight:600; color:var(--brand-ink); text-decoration:none; cursor:pointer; }
.ui-mtarjeta-enl:hover{ text-decoration:underline; }
/* Botón de información en la esquina derecha de la cabecera (§B7.2, info): de dónde sale el cálculo. */
.ui-mtarjeta-info{
  flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center; width:22px; height:22px;
  padding:0; border:0; border-radius:50%; background:none; color:var(--muted); cursor:pointer;
}
.ui-mtarjeta-info svg{ width:16px; height:16px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; }
.ui-mtarjeta-info:hover, .ui-mtarjeta-info:focus-visible{ color:var(--brand-ink); background:var(--brand-soft); }
.ui-mosaico.editando .ui-mtarjeta-info{ display:none; }
.ui-popover--info p{ margin:0; font-size:12.5px; line-height:1.5; color:var(--ink); }
.ui-mtarjeta-cuerpo{ margin-top:12px; flex:1; min-width:0; }

/* Modo Personalizar (§B7.3) */
.ui-mtarjeta-asa, .ui-mtarjeta-ctrl{ display:none; }
.ui-mosaico.editando .ui-mtarjeta{ outline:1.5px dashed var(--ui-line-dark); outline-offset:4px; }
.ui-mosaico.editando .ui-mtarjeta-asa{
  display:inline-flex; align-items:center; justify-content:center;
  width:22px; height:22px; border-radius:6px; background:var(--bg); cursor:grab; flex:0 0 auto;
}
.ui-mosaico.editando .ui-mtarjeta-asa svg{ width:14px; height:14px; fill:none; stroke:var(--muted); stroke-width:2; stroke-linecap:round; }
.ui-mosaico.editando .ui-mtarjeta-ctrl{ display:inline-flex; gap:4px; flex:0 0 auto; }
/* Mientras se reordena, el contenido y el enlace de cabecera no responden al clic: un enlace que
   navega fuera no debe tirar un reordenado a medio hacer (se guarda solo al pulsar "Hecho"). */
.ui-mosaico.editando .ui-mtarjeta-cuerpo,
.ui-mosaico.editando .ui-mtarjeta-enl{ pointer-events:none; }
.ui-mtarjeta-ctrl button{
  width:24px; height:24px; border-radius:7px; border:1px solid var(--line); background:var(--panel);
  color:var(--muted); font:inherit; font-size:12px; line-height:1; cursor:pointer;
}
.ui-mtarjeta-ctrl button:hover{ color:var(--ink); border-color:var(--brand-ink); }
.ui-mtarjeta-ctrl button:disabled{ opacity:.4; cursor:default; }
.ui-mtarjeta-ctrl button.quitar{ color:var(--neg); font-size:15px; }
.ui-mcatalogo{
  display:flex; align-items:center; gap:8px; flex-wrap:wrap; padding:10px 14px; margin-bottom:12px;
  border:1.5px dashed var(--ui-line-dark); border-radius:14px;
}
.ui-mcatalogo-tit{ font-size:10px; font-weight:800; text-transform:uppercase; letter-spacing:.4px; color:var(--muted); }
.ui-mcatalogo-fin{ margin-left:auto; }

/* Piezas compartidas de las tarjetas (§B7.6) — todo por tokens, ni un hexadecimal. */
.ui-mbarra{ display:block; height:12px; border-radius:6px; background:color-mix(in srgb, var(--ink) 7%, transparent); overflow:hidden; }
.ui-mbarra-f{ display:block; height:100%; border-radius:6px; }

.ui-rosco{ display:block; width:100%; max-width:190px; height:auto; }
.ui-rosco-num{ font-size:38px; font-weight:800; fill:var(--ink); }
.ui-rosco-lbl{ font-size:12px; font-weight:600; fill:var(--muted); }
.ui-rosco-pie{ font-size:11px; fill:var(--muted); }

/* Tarjeta-botón de estado. Suelta, es una baldosa plana; dentro de .ui-mtira (abajo), una columna
   de un panel único. El color del estado llega en --c (style="--c:var(--pos)"). */
.ui-mestado{
  display:block; width:100%; text-align:left; box-sizing:border-box; padding:13px 15px; border-radius:8px;
  background:var(--panel); border:1px solid var(--line); box-shadow:none; cursor:pointer; font:inherit; color:inherit;
}
.ui-mestado[aria-pressed="true"]{ border-color:var(--brand-ink); outline:2px solid var(--brand-soft); }
.ui-mestado-cab{ display:flex; align-items:center; gap:8px; min-width:0; }
.ui-mestado-ico{
  width:30px; height:30px; border-radius:9px; flex:0 0 auto; display:inline-flex; align-items:center; justify-content:center;
  background:color-mix(in srgb, var(--c, var(--brand-ink)) 14%, transparent); color:var(--c, var(--brand-ink));
}
.ui-mestado-ico svg{ width:15px; height:15px; fill:none; stroke:currentColor; stroke-width:2; stroke-linecap:round; stroke-linejoin:round; }
.ui-mestado-lbl{ display:flex; align-items:center; gap:6px; min-width:0; font-size:10px; font-weight:800; text-transform:uppercase; letter-spacing:.4px; color:var(--muted); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
.ui-mestado-val{ display:block; margin-top:8px; font-size:26px; font-weight:800; line-height:1; color:var(--ink); font-variant-numeric:tabular-nums; }
.ui-mestado-cab + .ui-mestado-val{ margin-top:10px; }
.ui-mestado-sub{ display:block; margin-top:4px; font-size:11.5px; color:var(--muted); font-variant-numeric:tabular-nums; }
.ui-mestado-punto{ width:8px; height:8px; border-radius:50%; display:inline-block; flex:0 0 auto; }
.ui-mestado .ui-mchispa{ margin-top:8px; }

/* Tira de estados: un solo panel (la tarjeta, con marco) partido en columnas con separadores. El
   margen negativo + overflow esconde el separador de la primera columna de cada fila. */
.ui-mtira{ display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); row-gap:16px; margin:0 -16px; overflow:hidden; }
.ui-mtira > .ui-mestado{
  border:0; border-left:1px solid var(--line); margin-left:-1px; border-radius:0; outline:0;
  background:none; padding:2px 16px 4px;
}
.ui-mtira > button.ui-mestado:hover{ background:var(--ui-hover); }
div.ui-mestado{ cursor:default; }
.ui-mtira > .ui-mestado[aria-pressed="true"]{ background:var(--brand-soft); box-shadow:inset 0 -2px 0 var(--brand-ink); }
.ui-mtira > .ui-mestado[aria-pressed="true"] .ui-mestado-val{ color:var(--brand-ink); }

/* Minigráfica de tendencia (UiMosaico.chispa): línea gris fina, último punto en el color del dato. */
.ui-mchispa{ display:block; width:100%; height:26px; }
.ui-mchispa-l{ fill:none; stroke:var(--muted); stroke-width:1.2; stroke-linecap:round; stroke-linejoin:round; opacity:.7; }

.ui-mlista{ display:flex; flex-direction:column; gap:12px; }
.ui-mfila{ display:flex; align-items:center; gap:10px; text-decoration:none; color:inherit; min-width:0; }
.ui-mfila-nom{ font-size:12.5px; font-weight:600; color:var(--ink); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ui-mfila-val{ font-size:12.5px; font-weight:800; color:var(--ink); text-align:right; flex:0 0 auto; font-variant-numeric:tabular-nums; }
.ui-mfila-meta{ font-size:11.5px; color:var(--muted); text-align:right; flex:0 0 auto; font-variant-numeric:tabular-nums; }

/* Fila de aviso: filas planas separadas por un filete (antes, cajas con borde). Empieza por una
   fecha de calendario (.ui-mfecha) o una insignia (.ui-maviso-n); a la derecha, .ui-maviso-der. */
.ui-mlista:has(> .ui-maviso){ gap:0; }
.ui-maviso{ display:flex; align-items:center; gap:12px; padding:10px 4px; border:0; border-top:1px solid var(--line); border-radius:0; text-decoration:none; color:inherit; }
.ui-maviso:first-child{ border-top:0; padding-top:2px; }
button.ui-maviso{ width:100%; text-align:left; background:none; font:inherit; color:inherit; cursor:pointer; }
.ui-maviso:is(a, button):hover{ background:var(--ui-hover); }
.ui-maviso-n{ width:32px; height:32px; border-radius:10px; flex:0 0 auto; display:flex; align-items:center; justify-content:center; font-size:12px; font-weight:800; }
.ui-maviso-n.alto{ background:color-mix(in srgb, var(--neg) 14%, transparent); color:var(--neg); }
.ui-maviso-n.medio{ background:var(--bg); color:var(--warn); }
.ui-maviso-txt{ min-width:0; flex:1; }
.ui-maviso-der{ flex:0 0 auto; margin-left:auto; display:flex; flex-direction:column; align-items:flex-end; gap:3px; text-align:right; }
.ui-maviso-val{ font-size:12.5px; font-weight:800; color:var(--ink); font-variant-numeric:tabular-nums; white-space:nowrap; }
/* Fecha de calendario (UiMosaico.fechaBloque): mes y día sobre el tono suave de la app. */
.ui-mfecha{
  width:42px; height:42px; border-radius:10px; flex:0 0 auto; display:flex; flex-direction:column; align-items:center; justify-content:center;
  background:var(--brand-soft); color:var(--brand-ink); line-height:1;
}
.ui-mfecha em{ font-style:normal; font-size:9.5px; font-weight:800; letter-spacing:.5px; }
.ui-mfecha b{ font-size:16px; font-weight:700; margin-top:2px; font-variant-numeric:tabular-nums; }
/* Plazo (§B6): ámbar si es hoy, rojo si ya pasó; el resto, gris. */
.ui-mplazo{ display:inline-block; padding:2px 8px; border-radius:10px; font-size:10.5px; font-weight:700; letter-spacing:.3px; text-transform:uppercase; white-space:nowrap;
  background:color-mix(in srgb, var(--ink) 6%, transparent); color:var(--muted); }
.ui-mplazo.hoy{ background:color-mix(in srgb, var(--warn) 16%, transparent); color:color-mix(in srgb, var(--warn) 65%, var(--ink)); }
.ui-mplazo.vencido{ background:color-mix(in srgb, var(--neg) 14%, transparent); color:var(--neg); }
.ui-mmas{ display:block; margin-top:8px; font-size:11.5px; color:var(--muted); }

/* Unidad pequeña junto a una cifra de estado ("12.400 kg") y marca corta junto a la etiqueta ("en vivo"). */
.ui-mestado-val small{ margin-left:3px; font-size:14px; font-weight:600; color:var(--muted); }
.ui-mestado-marca{ margin-left:auto; display:inline-flex; align-items:center; gap:4px; font-size:10px; font-weight:600; color:var(--pos); white-space:nowrap; }
.ui-mestado-marca i{ width:6px; height:6px; border-radius:50%; background:var(--pos); }

/* Cifra destacada (UiMosaico.kpi): 40px/800, unidad pequeña gris, barra y puntos de reparto debajo. */
.ui-mkpi{ display:block; width:100%; text-align:left; padding:0; border:0; background:none; font:inherit; color:inherit; }
button.ui-mkpi{ cursor:pointer; }
.ui-mkpi-val{ display:block; font-size:40px; font-weight:800; line-height:1; color:var(--c, var(--ink)); font-variant-numeric:tabular-nums; }
.ui-mkpi-val small{ margin-left:4px; font-size:18px; font-weight:600; color:var(--muted); }
.ui-mkpi .ui-mbarra{ margin-top:12px; }
.ui-mkpi-sub{ display:block; margin-top:8px; font-size:12px; color:var(--muted); }
.ui-mkpi-puntos{ display:flex; flex-wrap:wrap; gap:6px 14px; margin-top:6px; font-size:12px; color:var(--ink); }
.ui-mkpi-puntos > span{ display:inline-flex; align-items:center; gap:5px; }
.ui-mkpi-puntos i{ width:8px; height:8px; border-radius:50%; background:var(--c); }

/* Lista de cifras (UiMosaico.cifras): etiqueta gris a la izquierda, cifra 20px/800 a la derecha. */
.ui-mcifras{ display:flex; flex-direction:column; min-width:0; }
.ui-mcifra{ display:flex; align-items:baseline; justify-content:space-between; gap:10px; padding:9px 0; border-bottom:1px solid var(--line); }
.ui-mcifra:first-child{ padding-top:0; }
.ui-mcifra:last-child{ border-bottom:0; padding-bottom:0; }
.ui-mcifra-lbl{ min-width:0; font-size:11.5px; color:var(--muted); display:flex; flex-wrap:wrap; align-items:center; column-gap:7px; }
.ui-mcifra-lbl small{ flex-basis:100%; font-size:10.5px; }
.ui-mcifra-der{ display:flex; flex-direction:column; align-items:flex-end; gap:2px; text-align:right; }
.ui-mcifra-val{ font-size:20px; font-weight:800; color:var(--ink); font-variant-numeric:tabular-nums; white-space:nowrap; }
.ui-mcifra-sub{ font-size:11.5px; color:var(--muted); font-variant-numeric:tabular-nums; }

/* Gráfico con su contenido al lado (UiMosaico.conGrafico). */
.ui-mgraf{ display:flex; align-items:center; gap:18px; flex-wrap:wrap; }
.ui-mgraf-g{ flex:0 0 auto; width:170px; max-width:100%; }
.ui-mgraf-c{ flex:1; min-width:160px; }

/* Fila nombre · dato con la barra debajo (UiMosaico.filaBarra). */
.ui-mfila-barra{ display:grid; grid-template-columns:minmax(0,1fr) auto; align-items:baseline; gap:6px 10px; }
.ui-mfila-barra .ui-mbarra{ grid-column:1 / -1; }
.ui-mfila-barra .ui-mfila-nom{ display:flex; align-items:center; gap:8px; min-width:0; }
.ui-mfila-barra .ui-mfila-nom > span:last-child{ overflow:hidden; text-overflow:ellipsis; }
.ui-mfila-der{ display:inline-flex; align-items:baseline; gap:8px; white-space:nowrap; }
.ui-mfila-meta b{ color:var(--ink); font-weight:800; }
.ui-mcuadro{ width:10px; height:10px; border-radius:3px; background:var(--c); flex:0 0 auto; }

/* Columnas por categoría (UiMosaico.columnas) y su leyenda. */
.ui-mcolumnas{ display:block; width:100%; height:auto; }
.ui-mcol-g{ stroke:var(--line); stroke-width:1; }
.ui-mcol-t{ fill:var(--muted); font-size:10px; font-variant-numeric:tabular-nums; }
.ui-mley{ display:flex; flex-wrap:wrap; gap:6px 18px; margin-bottom:8px; font-size:12px; color:var(--muted); font-variant-numeric:tabular-nums; }
.ui-mley > span{ display:inline-flex; align-items:center; gap:6px; }
.ui-mley i{ width:10px; height:10px; border-radius:3px; background:var(--c); }
.ui-mley b{ color:var(--ink); font-weight:800; }

/* Texto + selector de chips dentro de una tarjeta (UiMosaico.subcabecera / .selector). */
.ui-msub{ display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:12px; font-size:12px; color:var(--muted); }
.ui-mtira + .ui-msub, .ui-mlista + .ui-msub, .ui-mkpi + .ui-msub, .ui-mkpi + .ui-mley, .ui-mcifras + .ui-mley{ margin-top:18px; }
.ui-mselector{ display:inline-flex; gap:4px; margin-left:auto; }
.ui-mselector .ui-chip{ padding:2px 9px; font-size:11px; }
.ui-maviso-tit{ display:block; font-size:12.5px; font-weight:700; color:var(--ink); }
.ui-maviso-sub{ display:block; font-size:11.5px; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }

/* Estado vacío de UNA tarjeta: cada tarjeta redacta el suyo. */
.ui-mvacio{ padding:40px 18px; text-align:center; color:var(--muted); font-size:13px; }

@media (max-width:900px){
  .ui-mosaico{ grid-template-columns:repeat(1,minmax(0,1fr)); }
  .ui-msp-3, .ui-msp-4, .ui-msp-5, .ui-msp-6, .ui-msp-7, .ui-msp-12{ grid-column:span 1; }
}

/* ── Tablero kanban (UiTablero, §B6) ──────────────────────────────────────── */
/* Lo pinta UiTablero (shared/js/ui-tablero.js). Fondo de tarjeta: --panel, como .ui-mtarjeta. */
.ui-tablero{ display:grid; grid-template-columns:repeat(auto-fit,minmax(240px,1fr)); gap:16px; align-items:start; }
.ui-tablero-col{ background:var(--bg); border-radius:14px; padding:12px; min-height:120px; }
.ui-tablero-col.is-sobre{ outline:2px dashed var(--brand); outline-offset:-2px; }
.ui-tablero-cab{ display:flex; justify-content:space-between; align-items:center; font-size:13px; font-weight:700; color:var(--ink); margin:0 4px 10px; }
.ui-tablero-n{ font-size:11.5px; font-weight:600; color:var(--muted); }
.ui-tablero-lista{ display:flex; flex-direction:column; gap:10px; }
.ui-tablero-tarjeta{ position:relative; background:var(--panel); border-radius:12px; box-shadow:var(--shadow-sm); padding:10px 34px 10px 12px; cursor:grab; transition:transform .12s, box-shadow .12s; }
.ui-tablero-tarjeta:hover{ transform:translateY(-1px); }
.ui-tablero-tarjeta.is-arrastrando{ opacity:.5; }
.ui-tablero-mover{ position:absolute; top:6px; right:6px; }
.ui-tablero-vacia{ font-size:12px; color:var(--muted); text-align:center; margin:12px 0; }
/* soloLectura: no se arrastra ni lleva «⋯»; la tarjeta solo se abre. */
.ui-tablero--lectura .ui-tablero-tarjeta{ cursor:pointer; padding-right:12px; }
/* enFila: todas las columnas en una fila (se lee el flujo de izquierda a derecha); si no caben, desplaza. */
.ui-tablero--fila{ grid-template-columns:none; grid-auto-flow:column; grid-auto-columns:minmax(220px,1fr); overflow-x:auto; padding-bottom:6px; }
