:root {
  color-scheme: light dark;
  --bg: #ffffff;
  --fg: #1a1a1a;
  --muted: #6b6b6b;
  --border: #dcdcdc;
  --card-bg: #fafafa;
  --accent: #4a6da8;
  --accent-contrast: #ffffff;
  --warn-bg: #fff4e5;
  --warn-fg: #7a4a00;
  --ok: #1f8a3c;
  --bad: #a85c52;
  /* 30/08/2026, pedido de Javi sobre el diagrama de Aprendizaje: "utiliza
     más colores (diferentes) para destacar contenidos" — un tinte suave
     y distinto para cada fase del diagrama (ver .learn-flow-band--tint-N
     en la sección del diagrama, más abajo). Tonos muy claros a propósito,
     para que el texto siga leyéndose sin esfuerzo encima. */
  --phase-tint-0: #f6f1e6;
  --phase-tint-1: #e9f1f9;
  --phase-tint-2: #f4edf8;
  --phase-tint-3: #edf6ec;
  --phase-tint-4: #fbebf1;
  --phase-tint-5: #eaf7f4;
  /* 30/08/2026, pedido de Javi: "es necesaria una opción para añadir
     carriles" — color de reserva para un 4º carril (los 3 primeros,
     Usuario/Ground/IA, reutilizan --muted/--ok/--accent, que ya existían).
     Ver LEARN_FLOW_LANE_COLORS en app.js. */
  --lane-color-3: #8a4fa8;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #14161a;
    --fg: #e9e9ea;
    --muted: #9a9ea6;
    --border: #2c2f36;
    --card-bg: #1c1f26;
    --accent: #8aa3d6;
    --accent-contrast: #0b0d10;
    --warn-bg: #2b230f;
    --warn-fg: #e2b464;
    --ok: #4fce76;
    --bad: #d98b82;
    --phase-tint-0: #262219;
    --phase-tint-1: #1a2129;
    --phase-tint-2: #221c27;
    --phase-tint-3: #1a2420;
    --phase-tint-4: #271a20;
    --phase-tint-5: #1a2624;
    --lane-color-3: #b28ad6;
  }
}

* {
  box-sizing: border-box;
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
  background: var(--bg);
  color: var(--fg);
}

/* Cabecera + menú fijos al hacer scroll (28/08/2026, pedido de Javi: que
   se queden siempre visibles, arriba o abajo del todo). Un solo
   contenedor "sticky" envolviendo topbar+tabs en vez de los dos por
   separado — así no hace falta calcular a mano la altura del uno para
   colocar el otro justo debajo. */
.app-header {
  position: sticky;
  top: 0;
  z-index: 10;
  background: var(--bg);
}

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.75rem 1.25rem;
  border-bottom: 1px solid var(--border);
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.brand {
  font-weight: 700;
  font-size: 1.1rem;
}

.badge {
  font-size: 0.8rem;
  padding: 0.15rem 0.6rem;
  border-radius: 999px;
  border: 1px solid var(--border);
}

.badge-ok {
  color: var(--ok);
  border-color: var(--ok);
}

.badge-bad {
  color: var(--bad);
  border-color: var(--bad);
}

.badge-unknown {
  color: var(--muted);
}

#active-universe-label {
  color: var(--muted);
  font-size: 0.9rem;
}

/* Banner arriba del todo en Aprendizaje: qué universo se verá afectado por
   ingesta/interpretar/reprocesar/embeddings — pedido de Javi, 27/08/2026,
   porque el label del topbar es pequeño y puede pasar desapercibido antes
   de una acción con coste o destructiva. */
.active-universe-banner {
  margin: 0.75rem 0;
  padding: 0.6rem 1rem;
  border-radius: 6px;
  font-size: 0.95rem;
  font-weight: 600;
  border: 1px solid var(--border);
}

.active-universe-banner--set {
  background: var(--card-bg);
  border-color: var(--accent);
  color: var(--accent);
}

.active-universe-banner--none {
  background: var(--warn-bg);
  border-color: var(--warn-fg);
  color: var(--warn-fg);
}

.tabs {
  display: flex;
  gap: 0.25rem;
  padding: 0.5rem 1.25rem 0;
  border-bottom: 1px solid var(--border);
}

.tab-button {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  padding: 0.6rem 0.9rem;
  font-size: 0.95rem;
  cursor: pointer;
}

.tab-button.active {
  color: var(--fg);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

/* Menú "Herramientas" (28/08/2026, pedido de Javi: Esquema y Editor
   agrupados aparte, a la derecha de una línea divisoria) — separa las
   pestañas del flujo normal (Inicio/Aprendizaje/Consulta) de las
   utilidades sueltas. */
.tabs-divider {
  width: 1px;
  align-self: stretch;
  margin: 0.4rem 0.25rem;
  background: var(--border);
}

.tabs-dropdown {
  position: relative;
  /* Empuja el menú al extremo derecho de la barra en vez de dejarlo
     pegado a "Consulta". */
  margin-left: auto;
}

.tabs-dropdown-toggle {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  padding: 0.6rem 0.9rem;
  font-size: 0.95rem;
  cursor: pointer;
}

/* Se marca como "activo" mientras Esquema o Editor sean la pestaña
   actual, aunque el propio botón no sea una pestaña — ver activateTab()
   en app.js. */
.tabs-dropdown-toggle.active {
  color: var(--fg);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

.tabs-dropdown-menu {
  position: absolute;
  top: 100%;
  right: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  min-width: 160px;
  margin-top: 0.25rem;
  padding: 0.35rem;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.12);
}

/* El "display: flex" de arriba tiene la misma prioridad que la regla del
   navegador para el atributo hidden (display: none) y va después en la
   hoja de estilos, así que la pisaba y el menú se veía siempre abierto
   (28/08/2026, "está siempre abierto aunque no se haya tocado"). Este
   selector es más específico (dos condiciones en vez de una) y gana
   siempre, toque el orden que toque. */
.tabs-dropdown-menu[hidden] {
  display: none;
}

.tabs-dropdown-menu .tab-button {
  text-align: left;
  border-bottom: none;
  border-radius: 5px;
  padding: 0.5rem 0.6rem;
}

.tabs-dropdown-menu .tab-button:hover {
  background: var(--bg);
}

.tabs-dropdown-menu .tab-button.active {
  color: var(--accent);
  font-weight: 600;
}

main {
  /* Antes 780px: en una pantalla ancha eso dejaba franjas enormes en
     blanco a los dos lados en TODAS las pestañas, no solo en Esquema
     (28/08/2026, "el problema de los grandes espacios en blanco... sucede
     en todas las páginas"). 1200px aprovecha mucho más el ancho real sin
     que un <input> suelto de un formulario se estire de forma absurda —
     eso se controla aparte, ver form input/select más abajo. */
  max-width: 1200px;
  margin: 0 auto;
  padding: 1.5rem 1.25rem 4rem;
}

/* Esquema es un diagrama, no texto para leer — le viene bien todavía más
   ancho que el resto de pestañas para que el lienzo tenga margen de
   sobra (28/08/2026, "la ampliación de la ventana... tiene un límite
   pequeño"). app.js añade esta clase a <main> solo mientras la pestaña
   activa es Esquema. */
main.is-wide-tab {
  max-width: 1700px;
}

.tab-panel {
  display: none;
}

.tab-panel.active {
  display: block;
}

h1 {
  font-size: 1.4rem;
  margin-bottom: 0.25rem;
}

/* 30/08/2026, pedido de Javi: "los títulos de todos los paneles más
   llamativos que el resto del panel" — antes un h2 apenas se distinguía
   del texto normal (mismo color, tamaño parecido). Ahora usa el color de
   acento y más peso/tamaño, para que destaque de un vistazo en cualquier
   tarjeta de la app. */
h2 {
  font-size: 1.18rem;
  font-weight: 700;
  color: var(--accent);
  letter-spacing: 0.01em;
  margin-top: 0;
}

/* Descripciones largas convertidas en tooltip del título (28/08/2026,
   pedido de Javi: "que esa descripción solo se vea al pasar el ratón por
   encima de la etiqueta" — antes eran un <p class="hint"> siempre
   visible debajo del título; ahora viven en su atributo title). El
   cursor de ayuda es la única pista de que ahí hay algo más que leer. */
h1[title],
h2[title],
h3[title],
.schema-detail-table[title] {
  cursor: help;
}

.hint {
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1.4;
  /* Con main más ancho (28/08/2026) un párrafo suelto estirado a todo el
     ancho de la tarjeta se hace incómodo de leer — 70 caracteres por
     línea es la referencia habitual para texto corrido. La tarjeta en sí
     sigue ocupando todo el ancho disponible; solo el texto se limita. */
  max-width: 70ch;
}

.warning {
  background: var(--warn-bg);
  color: var(--warn-fg);
  border-radius: 6px;
  padding: 0.6rem 0.8rem;
  font-size: 0.88rem;
}

.card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 1rem 1.1rem;
  margin: 1rem 0;
}

.notice {
  color: var(--muted);
}

form label {
  display: block;
  font-size: 0.85rem;
  color: var(--muted);
  margin: 0.5rem 0 0.25rem;
}

form input,
form select,
form textarea {
  width: 100%;
  /* Con main más ancho (28/08/2026) un campo de una sola línea ocupando
     el 100% de una tarjeta ancha queda absurdamente largo — un textarea
     sí aprovecha bien el ancho extra, así que este tope no le aplica
     (ver "form textarea" más abajo, que lo anula). */
  max-width: 480px;
  padding: 0.45rem 0.55rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: 0.9rem;
  font-family: inherit;
  /* Altura de línea en unidad absoluta (no un multiplicador del propio
     font-size) para que los botones puedan tener letra más pequeña y aun
     así medir exactamente lo mismo de alto que esta caja (27/08/2026). */
  line-height: 1.25rem;
}

form textarea {
  resize: vertical;
  /* A diferencia de un input de una línea, un textarea SÍ aprovecha bien
     el ancho extra de main (28/08/2026) — anula el tope de arriba. */
  max-width: none;
}

.inline-form {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: flex-end;
}

.inline-form label {
  flex: 1 1 130px;
  margin: 0;
}

button {
  margin-top: 0.75rem;
  background: var(--accent);
  color: var(--accent-contrast);
  border: 1px solid transparent;
  border-radius: 6px;
  /* Compacto: alto ajustado (poco relleno vertical) y ancho ajustado al
     propio texto (poco relleno horizontal, sin width ni flex-grow en
     ningún sitio) — pedido de Javi, 27/08/2026. */
  padding: 0.28rem 0.65rem;
  font-size: 0.85rem;
  font-family: inherit;
  line-height: 1.1rem;
  cursor: pointer;
  width: auto;
}

button:hover {
  opacity: 0.9;
}

button:disabled {
  opacity: 0.5;
  cursor: default;
}

.message {
  font-size: 0.85rem;
  margin-top: 0.5rem;
}

.list .universe-row,
.list .source-root-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
}

/* Sin línea de separación entre fuentes (25/08/2026) — la lista de fuentes
   se ve más limpia sin ella, sobre todo ahora que el meta-dato de abajo
   solo aparece al abrir cada una. */
.list .source-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0;
}

.universe-row .universe-name {
  font-weight: 600;
}

.universe-row-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  align-items: center;
  gap: 0.4rem;
  flex-shrink: 0;
}

.back-link {
  background: none;
  border: none;
  color: var(--accent);
  padding: 0;
  margin: 0 0 0.75rem;
  font-size: 0.9rem;
  cursor: pointer;
}

.back-link:hover {
  text-decoration: underline;
}

.universe-row-actions button,
.source-root-row button,
.source-row button {
  margin-top: 0;
}

button.danger {
  background: var(--bad);
}

.root-path,
.source-path {
  font-weight: 600;
  word-break: break-all;
}

/* 30/08/2026, pedido de Javi: "los nombres de las fuentes irán precedidas
   de dos checkbox" — "seleccionada" (a mano, decide qué fuentes entran en
   depurar/interpretar/embeddings/tratamiento completo; icono 🧹 porque
   depurar = limpiar) y "tratada" (de solo lectura: se marca sola en cuanto
   la fuente ya tiene versión depurada; icono 💧 porque ya se ha lavado — a
   juego con la 🧹 de "seleccionada"). Javi pidió icono en vez de dejarlos en
   blanco, "si es significativo" — de ahí la elección ligada al propio
   significado de cada checkbox, no decorativa.
   Ampliado el mismo día tras preguntar "¿aparecerán otros checks?": dos
   checkboxes de solo lectura más, mismo patrón — "interpretada" (🧠, ya se
   extrajeron sus objetos de conocimiento) y "vectorizada" (🔎, ya tiene
   embeddings, buscable "por significado" en Consulta).
   30/08/2026, segunda vuelta: el icono original de "tratada" era ✅, pero
   Javi señaló que un check verde JUNTO a una casilla real confundía — parecía
   que la casilla ya estaba marcada aunque no lo estuviera. Cambiado a 🧼
   (un icono que no se parece a un estado de "marcado") para que la única
   marca de check visible sea la casilla de verdad.
   31/08/2026, tercera vuelta: 🧼 tampoco funcionó — Javi no lo distinguía
   bien (se ve como una mancha verde borrosa en muchas fuentes/tamaños).
   Se le ofrecieron varias opciones (🧽/⭐/💧/🟢/📦) y eligió 💧 — nítida a
   cualquier tamaño, sigue la idea de "limpieza" a juego con 🧹, y no se
   parece a ningún estado de "marcado". */
.source-row-checks {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-right: 0.4rem;
  vertical-align: middle;
}

.source-check-label {
  display: inline-flex;
  align-items: center;
  gap: 0.15rem;
  cursor: pointer;
}

.source-check-icon {
  font-size: 0.85rem;
  line-height: 1;
}

.source-row-checks input[type='checkbox'] {
  accent-color: var(--ok);
  cursor: pointer;
}

.source-check-tratada,
.source-check-interpretada,
.source-check-vectorizada {
  cursor: help;
}

.source-check-seleccionada:disabled {
  cursor: not-allowed;
}

.source-check-label:has(.source-check-tratada),
.source-check-label:has(.source-check-interpretada),
.source-check-label:has(.source-check-vectorizada) {
  cursor: help;
}

.source-check-label:has(.source-check-seleccionada:disabled) {
  cursor: not-allowed;
}

/* 30/08/2026, pedido de Javi: "se ve recargado" — con dos checkboxes
   nuevos delante del nombre, "Quitar" pasa de botón con texto a icono
   pequeño y discreto (mismo sitio, menos ancho). */
.icon-btn {
  padding: 0.25rem 0.5rem;
  font-size: 0.95rem;
  line-height: 1;
  background: none;
  border: 1px solid var(--border);
  color: var(--fg);
}

.icon-btn.danger {
  background: none;
  color: var(--bad);
  border-color: var(--bad);
}

.icon-btn.danger:hover {
  background: var(--bad);
  color: var(--accent-contrast);
}

.root-status {
  color: var(--muted);
  font-size: 0.8rem;
  display: block;
}

/* El meta-dato (estado, fragmentos, origen) solo se ve al abrir la fuente
   (25/08/2026) — en la lista solo se muestra el nombre. */
.source-meta {
  color: var(--muted);
  font-size: 0.8rem;
  display: none;
}

.source-row.is-open .source-meta {
  display: block;
}

.root-status.muted {
  font-style: italic;
}

/* Las filas de fuentes pueden crecer mucho al abrir el detalle — que el
   botón "Quitar" se quede arriba en vez de centrarse con toda la fila. */
.list .source-row {
  align-items: flex-start;
}

.source-row-main {
  min-width: 0;
  flex: 1 1 auto;
}

.source-row-summary {
  cursor: pointer;
}

.source-row-summary:hover .source-path {
  text-decoration: underline;
}

.source-detail {
  margin-top: 0.5rem;
}

.source-detail p {
  margin: 0 0 0.4rem;
}

.source-content {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 320px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.6rem 0.75rem;
  margin: 0;
}

.source-detail-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 1rem;
  margin-bottom: 0.5rem;
  font-size: 0.82rem;
}

.source-turns {
  max-height: 420px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.source-turn {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.5rem 0.65rem;
  background: var(--bg);
}

.source-turn-noise {
  opacity: 0.6;
  border-style: dashed;
}

.source-turn-meta {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.72rem;
  color: var(--muted);
  margin-bottom: 0.3rem;
}

.source-turn-content {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
  white-space: pre-wrap;
  word-break: break-word;
  margin: 0;
  max-height: 200px;
  overflow-y: auto;
}

/* --- Explorador de ficheros ad-hoc -------------------------------------- */

.explorer-panel {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem;
  margin-top: 0.75rem;
  background: var(--bg);
}

.explorer-toolbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
}

.explorer-toolbar button {
  margin-top: 0;
}

.explorer-path {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.explorer-entries {
  max-height: 240px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
}

.explorer-entry {
  padding: 0.4rem 0.6rem;
  font-size: 0.85rem;
  cursor: pointer;
}

.explorer-entry:hover {
  background: var(--card-bg);
}

.explorer-entry.file-other {
  color: var(--muted);
  cursor: default;
}

.explorer-entry.file-other:hover {
  background: none;
}

.explorer-entry.file-ok {
  color: var(--accent);
}

.explorer-empty {
  padding: 0.5rem 0.6rem;
  color: var(--muted);
  font-size: 0.85rem;
}

.explorer-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

.explorer-actions button {
  margin-top: 0;
}

/* --- Capturado al vuelo (28/08/2026) ------------------------------------ */

.capture-thread {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  min-height: 2.5rem;
  max-height: 420px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.6rem 0.75rem;
  margin: 0.6rem 0;
}

.capture-thread:empty::before {
  content: 'Escribe algo abajo para empezar.';
  color: var(--muted);
  font-size: 0.85rem;
}

.capture-message {
  max-width: 80%;
  padding: 0.45rem 0.65rem;
  border-radius: 8px;
  font-size: 0.9rem;
  line-height: 1.4;
}

.capture-message-text {
  white-space: pre-wrap;
}

.capture-message--user {
  align-self: flex-end;
  background: var(--accent);
  color: var(--accent-contrast);
}

.capture-message--assistant {
  align-self: flex-start;
  background: var(--card-bg);
  border: 1px solid var(--border);
}

.capture-citations {
  margin-top: 0.35rem;
  font-size: 0.78rem;
  opacity: 0.85;
}

.capture-citations a {
  color: inherit;
}

/* Fuera de .inline-form a propósito (28/08/2026, pedido de Javi: "la caja
   para escribir, el micrófono y el botón de enviar en la línea siguiente
   para que la caja se pueda hacer más ancha") — como label de un form es
   un bloque normal (display:block, ver "form label" más arriba), esto ya
   ocupa toda la línea por sí solo, sin depender de ningún flex-basis. */
.capture-input-row {
  display: flex;
  gap: 0.4rem;
  align-items: flex-start;
}

.capture-input-row textarea {
  flex: 1 1 auto;
}

.capture-input-row button {
  flex: 0 0 auto;
  margin-top: 0;
  align-self: stretch;
}

.capture-mic-btn:disabled {
  opacity: 0.35;
}

.capture-mic-btn.capture-mic-active {
  background: var(--bad);
  color: var(--accent-contrast);
}

.capture-voice-toggle {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  flex: 1 1 220px;
}

.capture-voice-toggle input {
  width: auto;
}

#capture-new-conversation {
  background: var(--ok);
}

/* --- Chat del grafo (Consulta, 30/08/2026) — reutiliza .capture-thread/
   .capture-message/.capture-input-row tal cual, mismo look que "Capturado
   al vuelo". Solo el botón de nueva conversación necesita su propio color,
   igual que #capture-new-conversation de arriba. ------------------------- */

#graph-chat-clear {
  background: var(--ok);
}

/* --- Criterios de construcción de Conocimiento (30/08/2026) ------------- */
/* Las cajas viven sueltas dentro de .card (no dentro de <form>), así que no
   heredan el ancho/padding de "form textarea" de más arriba — se repite
   aquí lo mínimo para que se vean igual de bien. */

.criteria-textarea {
  display: block;
  width: 100%;
  max-width: none;
  padding: 0.45rem 0.55rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: 0.9rem;
  font-family: inherit;
  line-height: 1.35;
  resize: vertical;
  margin: 0.4rem 0;
}

.criteria-card .criteria-reset {
  background: var(--card-bg);
}

/* --- Ventana de progreso en vivo (Aprendizaje) -------------------------- */

.progress-log {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.78rem;
  max-height: 240px;
  overflow-y: auto;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.5rem 0.65rem;
  margin: 0.6rem 0 0.5rem;
}

.progress-line {
  white-space: pre-wrap;
  padding: 0.08rem 0;
}

.progress-line.progress-ok {
  color: var(--ok);
}

.progress-line.progress-error {
  color: var(--bad);
}

.progress-line.warning-line {
  color: var(--warn-fg);
}

.result {
  margin-top: 0.75rem;
  font-size: 0.85rem;
}

.result table {
  border-collapse: collapse;
  width: 100%;
}

.result td {
  padding: 0.15rem 0.5rem 0.15rem 0;
  vertical-align: top;
}

.result td:first-child {
  color: var(--muted);
  white-space: nowrap;
}

.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 0.75rem;
}

.stat {
  text-align: center;
}

.stat .stat-value {
  font-size: 1.4rem;
  font-weight: 700;
}

.stat .stat-label {
  color: var(--muted);
  font-size: 0.78rem;
}

.object-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem 0.9rem;
  margin-bottom: 0.75rem;
  background: var(--card-bg);
}

.object-card .object-meta {
  display: flex;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.35rem;
  font-size: 0.75rem;
  color: var(--muted);
}

.object-type-badge {
  border: 1px solid var(--accent);
  color: var(--accent);
  border-radius: 999px;
  padding: 0.05rem 0.55rem;
  font-size: 0.72rem;
}

.object-card h3 {
  margin: 0 0 0.35rem;
  font-size: 1rem;
}

.object-card p {
  margin: 0;
  font-size: 0.9rem;
  line-height: 1.45;
}

pre.json-output {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem;
  overflow-x: auto;
  font-size: 0.8rem;
}

/* --- Catálogo de tipos y sugerencias (Consulta) ---------------------- */

/* 31/08/2026, pedido de Javi ("aprovecha mejor el espacio de la
   pantalla"): las dos tarjetas de catálogo (tipos de conocimiento / tipos
   de relación) van en fila en vez de apiladas a todo el ancho — mismo
   patrón que .aprendizaje-top-row. En pantallas estrechas se apilan solas
   (flex-wrap). */
.consulta-catalog-row {
  display: flex;
  gap: 1.25rem;
  align-items: flex-start;
  flex-wrap: wrap;
}

.consulta-catalog-row > .card {
  flex: 1 1 320px;
  min-width: 280px;
}

/* 31/08/2026, pedido de Javi ("haz los botones más pequeños y
   discretos"): en vez del azul de acento sólido que llevan por defecto
   (pensado para el botón principal de un formulario), en Consulta pasan a
   un estilo secundario — borde fino, fondo neutro, más pequeños — para no
   competir visualmente con el contenido (catálogos, grafo, resultados de
   búsqueda). Los de "Quitar" (.danger) se quedan como estaban: siguen
   necesitando destacar por ser una acción destructiva. */
#tab-consulta button:not(.danger) {
  padding: 0.2rem 0.55rem;
  font-size: 0.78rem;
  background: var(--card-bg);
  color: var(--fg);
  border: 1px solid var(--border);
}

#tab-consulta button:not(.danger):hover {
  background: var(--bg);
  opacity: 1;
}

#tab-consulta button.danger {
  padding: 0.2rem 0.55rem;
  font-size: 0.78rem;
}

/* Los campos "Nombre del tipo nuevo" / "Nombre del tipo de relación
   nuevo" usan el estilo genérico de "form input" (0.45rem de padding +
   1.25rem de line-height, pensado para el resto de la app) — aquí al
   lado de un botón ya achicado se veían más altos que él. Mismo ajuste
   que .type-row-label-input: igualar a la altura real del botón. */
#type-create-form input,
#relation-type-create-form input {
  padding-top: 0.2rem;
  padding-bottom: 0.2rem;
  line-height: 1.1rem;
}

/* 31/08/2026: quitado el <h3>Catálogo</h3> que encabezaba cada lista (era
   ruido — el <h2> de la tarjeta ya dice de qué catálogo se trata) y
   apretado el espaciado entre líneas, para que la lista de tipos
   aproveche mejor el hueco que deja el título de menos. */
.type-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.25rem 0;
}

/* El botón por defecto lleva margin-top: 0.75rem (para separarse de lo
   que venga encima en un formulario normal) — dentro de .type-row eso
   descuadra el "align-items: center" de la fila: el margen asimétrico
   (solo arriba) hace que el centrado se calcule sobre una caja más alta
   por arriba, y el botón queda visualmente más bajo que la cajita de
   texto de al lado. Quitado aquí, igual que ya se hacía en
   .universe-row-actions/.source-root-row/.source-row (31/08/2026, visto
   por Javi). */
.type-row button {
  margin-top: 0;
}

.type-color-dot {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  flex-shrink: 0;
  display: inline-block;
}

.type-row-key {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.72rem;
  color: var(--muted);
  min-width: 7rem;
}

.type-row-label-input {
  flex: 1 1 auto;
  min-width: 0;
  /* Vive fuera de un <form>, así que no le llega el estilo de
     "form input" — se repite aquí a mano para que se vea igual (misma
     caja que el resto de campos de texto de la app).
     31/08/2026, pedido de Javi ("iguala la altura de las cajas de datos
     siendo los botones la referencia de altura correcta"): padding
     vertical y line-height ajustados para medir EXACTAMENTE lo mismo de
     alto que "#tab-consulta button:not(.danger)" (0.2rem + 1.1rem +
     0.2rem + el mismo borde de 1px en ambos). */
  padding: 0.2rem 0.55rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: 0.9rem;
  font-family: inherit;
  line-height: 1.1rem;
}

#type-suggestions-card {
  background: var(--warn-bg);
  color: var(--warn-fg);
  border-radius: 8px;
  padding: 0.75rem;
  margin-bottom: 1rem;
}

#type-suggestions-card h3 {
  margin-top: 0;
}

.type-suggestion-row {
  border-top: 1px solid rgba(0, 0, 0, 0.1);
  padding-top: 0.5rem;
  margin-top: 0.5rem;
}

.type-suggestion-row:first-of-type {
  border-top: none;
  padding-top: 0;
  margin-top: 0;
}

.type-suggestion-row p {
  margin: 0 0 0.4rem;
}

/* --- Grafo de conocimiento (Consulta) ------------------------------- */

.graph-controls {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-bottom: 0.75rem;
}

.graph-controls-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  align-items: flex-end;
}

.graph-checkbox-label {
  flex-direction: row !important;
  align-items: center;
  gap: 0.4rem;
}

.graph-type-filter {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.9rem;
  padding: 0.6rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card-bg);
}

.graph-type-filter label {
  flex-direction: row !important;
  align-items: center;
  gap: 0.35rem;
  font-size: 0.82rem;
  font-weight: normal;
}

.graph-star-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.5rem 0.75rem;
  margin-bottom: 0.6rem;
  border-radius: 8px;
  border: 1px solid var(--accent);
  background: var(--card-bg);
  font-size: 0.85rem;
}

.graph-star-banner button {
  margin-top: 0;
  flex-shrink: 0;
}

#graph-wrap {
  position: relative;
}

#graph-container {
  width: 100%;
  height: 560px;
  border: 1px solid var(--border);
  border-radius: 8px;
  /* Fondo oscuro fijo (no depende del tema claro/oscuro de la app): la
     propia escena 3D pinta este color de fondo vía JS (backgroundColor de
     3d-force-graph) — se fija también aquí para que no haya un parpadeo
     claro antes de que WebGL termine de inicializar. */
  background: #0b1220;
}

.graph-camera-controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-top: 0.6rem;
}

.graph-pan-buttons {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

.graph-pan-row {
  display: flex;
  gap: 0.25rem;
}

/* Con #tab-consulta delante para ganarle en especificidad a la regla
   "#tab-consulta button:not(.danger)" de más abajo (botones pequeños y
   discretos) — si no, esas flechas perderían su ancho fijo/centrado. */
#tab-consulta .graph-pan-buttons button {
  width: 2.1rem;
  padding: 0.28rem 0;
  text-align: center;
}

.graph-node-info {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  width: 300px;
  max-height: calc(100% - 1.5rem);
  overflow-y: auto;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 0.75rem;
  font-size: 0.85rem;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
}

.graph-node-info h4 {
  margin: 0 0 0.4rem;
  font-size: 0.95rem;
}

.graph-node-info dt {
  color: var(--muted);
  font-size: 0.72rem;
  margin-top: 0.4rem;
}

.graph-node-info dd {
  margin: 0;
  white-space: pre-wrap;
}

.graph-star-actions {
  margin-top: 0.6rem;
}

.graph-star-actions button {
  margin-top: 0;
  margin-right: 0.4rem;
}

.d-status-select {
  padding: 0.3rem 0.4rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: 0.82rem;
  font-family: inherit;
  line-height: 1.15rem;
}

.d-status-feedback {
  margin-left: 0.4rem;
  font-size: 0.72rem;
  color: var(--muted);
}

.graph-node-info .provenance-entry {
  padding: 0.3rem 0;
  font-size: 0.78rem;
  white-space: normal;
}

.graph-node-info .hint {
  font-size: 0.72rem;
  margin-top: 0.6rem;
}

.graph-node-info button {
  margin-top: 0.6rem;
}

/* Panel explicativo de Aprendizaje (26/08/2026): a la izquierda, vertical,
   redimensionable con el tirador nativo del navegador (resize: both) para
   hacerlo mas o menos alto/ancho segun haga falta. */
.aprendizaje-layout {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

/* Panel horizontal (26/08/2026): antes era una barra lateral estrecha y
   alta, que desaprovechaba el ancho de pantalla. Ahora va en una fila junto
   al gráfico del pipeline — reparte los tres pasos en columnas, mas corto,
   mas equilibrado. Sigue siendo redimensionable (el tirador de la esquina)
   por si Javi quiere verlo mas alto o mas ancho. */
.aprendizaje-top-row {
  display: flex;
  gap: 1.25rem;
  align-items: stretch;
  flex-wrap: wrap;
}

.explainer-panel {
  flex: 2 1 380px;
  min-width: 260px;
  height: 220px;
  min-height: 120px;
  max-height: 70vh;
  overflow: auto;
  resize: both;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem 1.25rem;
  box-sizing: border-box;
}

.explainer-panel h2 {
  margin: 0 0 0.75rem;
  font-size: 1rem;
}

.explainer-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 0.5rem 1.5rem;
}

.explainer-grid h3 {
  margin: 0 0 0.25rem;
  font-size: 0.9rem;
}

.explainer-grid p {
  margin: 0;
  font-size: 0.85rem;
  color: var(--muted);
}

/* Gráfico pequeño del pipeline (26/08/2026): Fuentes - Lotes (fragmentos
   interpretados) - Fragmentos - Vectores, a la derecha del panel
   explicativo. Cada etapa con "pendiente" real conocido (todas menos
   Fragmentos, que es el total de referencia) lleva su barra de progreso. */
.pipeline-panel {
  flex: 1 1 220px;
  min-width: 200px;
  height: 220px;
  min-height: 120px;
  max-height: 70vh;
  overflow: auto;
  resize: vertical;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 1rem 1.25rem;
  box-sizing: border-box;
}

.pipeline-panel h2 {
  margin: 0 0 0.75rem;
  font-size: 1rem;
}

.pipeline-stages {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
}

.pipeline-label {
  font-size: 0.78rem;
  color: var(--muted);
}

.pipeline-value {
  font-size: 0.95rem;
  font-weight: 600;
}

.pipeline-pending {
  font-weight: 400;
  color: var(--muted);
  font-size: 0.8rem;
}

.pipeline-bar {
  height: 6px;
  border-radius: 999px;
  background: var(--border);
  overflow: hidden;
  margin-top: 0.2rem;
}

.pipeline-bar-fill {
  height: 100%;
  background: var(--accent);
}

.pipeline-cost {
  font-size: 0.72rem;
  font-style: italic;
  color: var(--muted);
  margin-top: 0.15rem;
}

.aprendizaje-main {
  min-width: 0;
}

.card h2 .badge {
  margin-left: 0.5rem;
  vertical-align: middle;
  font-weight: 400;
}

.cost-estimate {
  font-size: 0.85rem;
  color: var(--muted);
  margin: 0.35rem 0 0.75rem;
}

/* --- Esquema: mapa de tablas (28/08/2026) -------------------------------
   Tres columnas: lista de tablas a la izquierda, lienzo arrastrable en el
   centro, detalle de la tabla seleccionada a la derecha. Solo consulta —
   nada aquí escribe en la base. */

.schema-layout {
  display: flex;
  gap: 1rem;
  align-items: flex-start;
}

.schema-sidebar {
  flex: 0 0 220px;
}

.schema-sidebar-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  margin-bottom: 0.6rem;
}

.schema-sidebar-actions button {
  margin-top: 0;
  font-size: 0.78rem;
  padding: 0.3rem 0.55rem;
}

.schema-entity-list {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  max-height: 560px;
  overflow-y: auto;
  padding-right: 0.25rem;
}

/* Cabecera de cada dominio (28/08/2026, pedido de Javi: "tres dominios...
   universos y fuentes, Aprendizaje y Consultas"). Solo texto, sin
   checkbox — agrupa visualmente las tablas de abajo, no es seleccionable
   por sí misma. */
.schema-domain-heading {
  margin: 0.4rem 0 0.15rem;
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  padding-bottom: 0.2rem;
}

.schema-domain-heading:first-child {
  margin-top: 0;
}

.schema-entity-item {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.85rem;
  cursor: pointer;
}

.schema-entity-item input {
  margin: 0;
}

.schema-entity-item span:last-of-type {
  flex: 1 1 auto;
}

/* "+rel" por tabla (28/08/2026, pedido de Javi: antes era un único botón
   global "+ Relacionadas" que expandía TODO lo marcado a la vez — ahora
   cada tabla tiene el suyo, para añadir solo las relacionadas con ESA. */
.schema-entity-related-btn {
  flex-shrink: 0;
  margin-top: 0;
  font-size: 0.68rem;
  padding: 0.15rem 0.4rem;
  cursor: pointer;
}

.schema-canvas-col {
  flex: 1 1 auto;
  min-width: 420px;
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
}

.schema-canvas-wrap {
  /* flex: none a propósito (28/08/2026): con "flex: 1 1 auto" el propio
     flexbox se peleaba con el tirador de redimensionar en el eje
     horizontal (el ancho es el eje principal de la fila y flex-grow lo
     recalculaba por encima de lo que arrastrabas) — el vertical sí
     funcionaba porque la altura no es el eje que controla flex-grow aquí.
     Sin flex-grow/shrink, "resize" manda en los dos sentidos sin que nadie
     se lo pise. */
  flex: none;
  position: relative;
  /* Antes 900px fijos: con .schema-canvas-col ya ensanchado (ver
     main.is-wide-tab) eso dejaba un hueco enorme sin usar a la derecha
     (28/08/2026, "quedan áreas muy grandes vacías"). 100% lo hace
     arrancar ocupando todo el ancho disponible de la columna; el tirador
     de "resize" lo sigue pudiendo encoger hasta min-width. */
  width: 100%;
  height: 560px;
  min-width: 420px;
  min-height: 320px;
  /* Tope al ensanchar (28/08/2026, pedido de Javi): al ser "flex: none" el
     lienzo no participa en el reparto de flexbox, así que sin este límite
     el tirador de "resize" lo dejaba crecer por encima del ancho real que
     le queda a .schema-canvas-col y se comía el panel de "Detalle" de al
     lado. 100% aquí es 100% del contenedor (.schema-canvas-col), que sí
     respeta el sitio fijo reservado para el panel — así que el arrastre
     simplemente deja de moverse justo en ese borde, sin taparlo nunca. */
  max-width: 100%;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: auto;
  background: var(--card-bg);
  resize: both;
}

.schema-pan-buttons {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
}

.schema-pan-row {
  display: flex;
  gap: 0.25rem;
}

.schema-pan-buttons button {
  width: 2.1rem;
  margin-top: 0;
  padding: 0.28rem 0;
  text-align: center;
}

#schema-canvas {
  position: relative;
  width: 1600px;
  height: 1100px;
  transform-origin: 0 0;
}

#schema-canvas-svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 1600px;
  height: 1100px;
  pointer-events: none;
  transform-origin: 0 0;
}

.schema-entity-card {
  position: absolute;
  width: 210px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
  cursor: grab;
  user-select: none;
  font-size: 0.78rem;
  /* display:flex (28/08/2026, tirador de redimensionar): con altura
     automática esto se comporta igual que antes (cabecera + columnas
     apiladas, alto = suma de ambas). Solo cambia algo cuando Javi fija un
     alto a mano con el tirador — entonces "overflow: hidden" es lo que
     hace que la tarjeta no crezca por su cuenta más allá de ese alto. */
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

.schema-entity-card.is-dragging,
.schema-entity-card.is-resizing {
  z-index: 5;
}

.schema-entity-card.is-dragging {
  cursor: grabbing;
}

.schema-entity-card.is-selected {
  outline: 2px solid var(--accent);
}

/* Tirador de la esquina inferior derecha (28/08/2026, pedido de Javi:
   "cada caja de entidad que se pueda modificar la altura y la anchura
   con un tirador"). Rayitas diagonales dibujadas con gradiente, igual de
   discreto en claro y oscuro sin depender de una imagen. */
.schema-entity-card-resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 14px;
  height: 14px;
  cursor: nwse-resize;
  background:
    linear-gradient(135deg, transparent 0 40%, var(--muted) 40% 55%, transparent 55% 100%),
    linear-gradient(135deg, transparent 0 65%, var(--muted) 65% 80%, transparent 80% 100%);
}

.schema-entity-card-header {
  padding: 0.4rem 0.55rem;
  border-radius: 8px 8px 0 0;
  font-weight: 600;
  color: #fff;
}

.schema-entity-card-columns {
  padding: 0.35rem 0.55rem;
  max-height: 150px;
  overflow-y: auto;
  /* flex:1 + min-height:0 (28/08/2026, tirador de redimensionar): cuando
     la tarjeta tiene un alto fijado a mano (más que la cabecera + 150px),
     esto es lo que deja que las columnas ocupen el hueco de más en vez de
     quedarse en 150px con espacio en blanco debajo. Sin min-height:0 un
     hijo flex nunca se encoge por debajo de su contenido, y el scroll no
     llegaría a activarse cuando el alto fijado es MENOR que el contenido. */
  flex: 1 1 auto;
  min-height: 0;
}

.schema-entity-card-column {
  display: flex;
  justify-content: space-between;
  gap: 0.4rem;
  padding: 0.1rem 0;
  color: var(--muted);
  white-space: nowrap;
}

.schema-entity-card-column.is-key {
  color: var(--fg);
  font-weight: 600;
}

.schema-entity-card-column-icon {
  flex-shrink: 0;
}

/* Tabla "fantasma" (28/08/2026, pedido de Javi: "ver los valores de las
   listas cerradas aunque no se puedan editar"): borde discontinuo y
   cabecera gris en vez del color por tabla, para que se note a simple
   vista que no es una tabla real de la base de datos. */
.schema-entity-card.is-ghost {
  border-style: dashed;
  border-color: var(--muted);
}

.schema-entity-card.is-ghost .schema-entity-card-header {
  background: var(--muted);
  font-style: italic;
}

#schema-canvas-svg line {
  stroke: var(--accent);
  stroke-width: 1.5;
  opacity: 0.6;
}

/* Indicadores padre/hijo en cada línea de relación (28/08/2026, pedido de
   Javi). Antes eran <marker> de SVG colocados en los extremos exactos de
   la línea — que son el CENTRO de cada tarjeta, tapado por su fondo
   opaco, así que nunca se veían. Ahora son un <circle> (padre) y un
   <polygon> (hija, con forma de flecha) que se calculan y colocan a mano
   justo donde la línea sale del borde de cada tarjeta — ver
   drawSchemaRelationLines en app.js — con su propia opacidad, sin heredar
   la de la línea (que la dejaría demasiado tenue).*/
.schema-rel-parent-marker,
.schema-rel-child-marker {
  fill: var(--accent);
}

/* Diagrama del proceso de Aprendizaje (28/08/2026, pedido de Javi, versión
   2 tras ver un ejemplo de referencia — "más gráfico, más visual"): un
   diagrama tipo carril/swimlane con posiciones ABSOLUTAS y fijas en px
   (no calculadas por JS, es un lienzo estático — todas las coordenadas de
   abajo, en el HTML y en el SVG, están pensadas para encajar entre sí a
   mano). Dos carriles verticales: "Tú decides" (config) a la izquierda y
   "Ground ejecuta" (acciones reales) a la derecha, con seis franjas de
   fase apiladas (0, 1, 1b, 2, 2b, 3), círculos de inicio/fin, y flechas
   discontinuas con "puertos" (los puntitos en el borde de cada caja,
   igual que el ejemplo) conectando todo. El SVG de líneas va DEBAJO de
   las cajas HTML — por eso cada trazo para unos 12px antes de tocar la
   caja de destino (si no, la caja opaca tapa la punta de flecha, el mismo
   bug que ya se arregló una vez en el diagrama de Esquema); los puntos de
   "puerto" sí se dibujan justo en el borde de la caja a propósito, para
   que la mitad quede tapada y la mitad visible, como en el ejemplo.
   Verde (--ok) sigue marcando las ACCIONES — las únicas cajas de este
   diagrama que hacen algo de verdad. */
.learn-flow-wrap {
  /* 29/08/2026, pedido de Javi: zoom con la rueda del ratón y tamaño de
     cada tarea a mano — el lienzo ya no tiene un tamaño fijo (900×1382),
     lo calcula JS cada vez (ver computeLearnFlowLayout en app.js), así que
     aquí solo hace falta un contenedor con scroll en los dos sentidos y un
     tope de alto para que no se coma la página entera.
     "position: relative" (29/08/2026, arreglo: "las flechas de unión
     están desplazadas") es IMPRESCINDIBLE — sin él, #learn-flow-svg
     (position:absolute) no tiene ningún ancestro con posición propia
     entre medias y acaba colocándose respecto a la página entera, muy
     lejos del diagrama. #learn-flow (el div con las cajas) no tenía este
     problema porque él mismo ya era "position:relative". */
  position: relative;
  overflow: auto;
  max-height: 640px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card-bg);
}

.learn-flow {
  position: relative;
  font-size: 0.78rem;
  transform-origin: 0 0;
}

/* Leyenda fija (29/08/2026, pedido de Javi: "¿qué significan las
   etiquetas con líneas de puntos?") — antes solo se explicaba al pasar el
   ratón por encima de cada píldora; ahora queda a la vista todo el rato,
   sin tener que descubrirlo tarea a tarea. */
.learn-flow-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin: 0.4rem 0 0.6rem;
  font-size: 0.76rem;
  color: var(--muted);
}

.learn-flow-legend-item {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.learn-flow-legend-swatch {
  width: 15px;
  height: 15px;
  border-radius: 4px;
  flex-shrink: 0;
  display: inline-block;
  box-sizing: border-box;
}

.learn-flow-legend-swatch--accion {
  border: 2px solid var(--ok);
}

.learn-flow-legend-swatch--tu {
  border: 1px solid var(--border);
}

.learn-flow-legend-swatch--optional {
  border: 2px dashed var(--accent);
}

/* Barra de descripción (29/08/2026, pedido de Javi: "todas las tareas
   deben tener una pequeña descripción al pasar el ratón por encima, no
   solo un símbolo de interrogación") — antes la única pista era el
   cursor de ayuda (?) y el tooltip nativo del navegador, que tarda en
   aparecer y algunos navegadores lo recortan. Ahora cada elemento
   describible lleva también un atributo "data-desc" (ver renderLearnFlow
   en app.js) y un solo listener delegado (setupLearnFlowDescBar) lo
   vuelca aquí al instante, sin esperas. El title nativo se deja además,
   como refuerzo de accesibilidad. */
.learn-flow-desc-bar {
  margin-top: 0.6rem;
  padding: 0.55rem 0.8rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card-bg);
  font-size: 0.8rem;
  color: var(--fg);
  min-height: 1.3em;
}

.learn-flow-procbar {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 700;
  cursor: help;
}

.learn-flow-lane-bg {
  /* 30/08/2026, pedido de Javi: "los bordes de los carriles no están
     claros" — antes este borde de 1px casi no se notaba, y encima el
     fondo opaco (var(--card-bg)) que llevaba aquí tapaba por completo el
     tinte de color de cada fase (ver .learn-flow-band--tint-N) allí donde
     coincidían — por eso ahora este elemento va SIN relleno propio (solo
     borde) y se pinta DESPUÉS de las bandas de fase en renderLearnFlow()
     (app.js), así su borde queda siempre por encima, bien visible, en
     vez de escondido debajo. El borde grueso de color a la izquierda ya
     no es una clase distinta por carril (30/08/2026: con "es necesaria
     una opción para añadir carriles" el número de carriles ya no es fijo)
     — lo pone `renderLearnFlow()` como estilo en línea, a juego con el
     color de la cabecera de ESE carril (ver LEARN_FLOW_LANE_COLORS en
     app.js), sea cual sea. Aquí solo queda un color de reserva por si
     algo fallara al ponerlo en línea. */
  position: absolute;
  border: 1px solid var(--border);
  border-left: 3px solid var(--border);
  border-radius: 8px;
  background: none;
}

.learn-flow-lane-header {
  /* Igual que arriba: el color de fondo (a juego con el borde del
     carril) lo pone renderLearnFlow() en línea, no una clase por carril
     — un carril nuevo no necesita CSS propio para tener color. */
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 6px;
  font-weight: 700;
  color: var(--accent-contrast);
  cursor: help;
}

.learn-flow-band {
  /* 30/08/2026, arreglo: Javi preguntó "¿qué significan las etiquetas con
     líneas de puntos?" porque TODAS las bandas de fase llevaban borde
     discontinuo (solo cambiaba el color) — la leyenda decía "discontinuo
     = opcional" pero eso no era verdad en el dibujo. Ahora el discontinuo
     es EXCLUSIVO de las fases opcionales (ver --optional más abajo); una
     fase normal lleva un borde fino y sólido, de simple agrupación. */
  position: absolute;
  left: 14px;
  width: 862px;
  border: 1px solid var(--border);
  border-radius: 10px;
  pointer-events: none;
}

.learn-flow-band--optional {
  border: 2px dashed var(--accent);
}

/* Tinte de fondo por fase (30/08/2026, pedido de Javi: "utiliza más
   colores (diferentes) para destacar contenidos") — rota entre 6 colores
   suaves (ver LEARN_FLOW_PHASE_TINT_COUNT en app.js), uno distinto por
   fase, para que se distinga de un vistazo dónde acaba una y empieza la
   siguiente. El borde de discontinuo/sólido (optional o no) manda igual
   encima de esto. */
.learn-flow-band--tint-0 { background: var(--phase-tint-0); }
.learn-flow-band--tint-1 { background: var(--phase-tint-1); }
.learn-flow-band--tint-2 { background: var(--phase-tint-2); }
.learn-flow-band--tint-3 { background: var(--phase-tint-3); }
.learn-flow-band--tint-4 { background: var(--phase-tint-4); }
.learn-flow-band--tint-5 { background: var(--phase-tint-5); }

.learn-flow-pill {
  position: absolute;
  display: inline-block;
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  background: var(--accent);
  color: var(--accent-contrast);
  font-weight: 700;
  font-size: 0.74rem;
  cursor: help;
  z-index: 1;
}

.learn-flow-pill--optional {
  background: transparent;
  border: 2px dashed var(--accent);
  color: var(--accent);
}

.learn-flow-svg {
  position: absolute;
  top: 0;
  left: 0;
  /* Ancho/alto ya no van aquí (29/08/2026): renderLearnFlow() en app.js
     los pone en línea, iguales a los del lienzo de al lado, cada vez que
     recalcula el diagrama. pointer-events:none para que el tirador de
     redimensionar de una tarea (que vive por encima, en el HTML) no quede
     nunca tapado por esta capa de líneas.
     z-index (30/08/2026, arreglo: "las líneas de flujo no se muestran
     completas") — el SVG es hermano de #learn-flow (el div con las
     cajas), colocado ANTES que él en el HTML, así que sin z-index propio
     quedaba por debajo de TODO lo de #learn-flow: el fondo opaco de cada
     carril (.learn-flow-lane-bg) y de cada banda de fase (.learn-flow-band)
     tapaba el trazo entero, y solo se veía el trocito que caía en el
     hueco vacío entre carriles. z-index:1 lo sube por encima de esos
     fondos (que no tienen z-index propio, valen 0) pero lo deja empatado
     con las cajas/círculos/píldoras (que también valen 1) — en un empate,
     gana quien va después en el HTML, o sea las cajas, así que el trocito
     de puerto que toca el borde de una caja se sigue viendo medio tapado
     a propósito, como estaba pensado. */
  z-index: 1;
  pointer-events: none;
  transform-origin: 0 0;
}

.learn-flow-conn {
  fill: none;
  stroke: var(--muted);
  stroke-width: 2;
  stroke-dasharray: 5, 4;
}

/* Punta de flecha (30/08/2026, arreglo: "las líneas de flujo deben tocar
   la línea perimetral del objeto de entrada y de salida"). Ya no es un
   <marker> de SVG con la línea acortada antes de llegar — es un triángulo
   calculado a mano en drawLearnFlowConnections (app.js): la PUNTA toca
   exactamente el borde de la caja de destino (igual que la propia línea)
   y es la BASE del triángulo la que queda un poco por detrás, hacia el
   hueco abierto — así el triángulo entero cae fuera de la caja (nunca
   tapado por su fondo opaco) sin que la línea deje de tocar el borde. */
.learn-flow-arrowhead {
  fill: var(--muted);
}

/* Solo en el extremo de SALIDA (30/08/2026): el de llegada ya lo marca la
   punta de flecha, que toca ese mismo borde — un segundo punto ahí solo
   añadía ruido visual sin aportar nada nuevo. */
.learn-flow-dot {
  fill: var(--fg);
}

.learn-flow-event {
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--card-bg);
  font-size: 0.68rem;
  line-height: 1.1;
  text-align: center;
  cursor: help;
  z-index: 1;
}

.learn-flow-event--start {
  border: 2px solid var(--accent);
  color: var(--accent);
}

.learn-flow-event--end {
  border: 2px solid var(--bad);
  color: var(--bad);
}

.learn-flow-box {
  position: absolute;
  box-sizing: border-box;
  border-radius: 8px;
  padding: 0.4rem 0.6rem;
  background: var(--bg);
  cursor: help;
  overflow: hidden;
  z-index: 1;
}

.learn-flow-box--tu {
  border: 1px solid var(--border);
}

.learn-flow-box--accion {
  border: 2px solid var(--ok);
}

.learn-flow-box.is-resizing {
  z-index: 5;
}

/* Tirador de la esquina inferior derecha de cada tarea (29/08/2026,
   pedido de Javi: "todas las tareas puedan modificar su tamaño") — mismo
   dibujo a rayas que el de las tarjetas de Esquema, para que se reconozca
   de un vistazo que hace lo mismo. */
.learn-flow-box-resize-handle {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 14px;
  height: 14px;
  cursor: nwse-resize;
  z-index: 3;
  background:
    linear-gradient(135deg, transparent 0 40%, var(--muted) 40% 55%, transparent 55% 100%),
    linear-gradient(135deg, transparent 0 65%, var(--muted) 65% 80%, transparent 80% 100%);
}

.learn-flow-box-title {
  font-weight: 700;
  margin-bottom: 0.25rem;
}

.learn-flow-box--accion .learn-flow-box-title::before {
  content: '✓ ';
  color: var(--ok);
}

.learn-flow-box-list {
  margin: 0;
  padding-left: 1rem;
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.35;
}

/* Modal "diagrama de acciones" al hacer doble clic en una tarea
   (29/08/2026, pedido de Javi: "que se abra un diagrama de acciones con
   lo que se ejecuta realmente en esa tarea"). Reutiliza la misma chapa de
   modal que Esquema (.schema-modal-overlay/.schema-modal en el HTML,
   línea de flujo vertical con círculos numerados y flechas propia de
   aquí). */
.learn-flow-action-modal {
  width: 560px;
}

.learn-flow-action-steps {
  display: flex;
  flex-direction: column;
  margin-top: 0.75rem;
}

.learn-flow-action-step {
  display: flex;
  align-items: flex-start;
  gap: 0.65rem;
}

.learn-flow-action-step-marker {
  flex-shrink: 0;
  width: 1.7rem;
  height: 1.7rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--accent-contrast);
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.82rem;
}

.learn-flow-action-step-text {
  padding-top: 0.22rem;
  line-height: 1.4;
}

.learn-flow-action-step-connector {
  width: 1.7rem;
  text-align: center;
  color: var(--muted);
  line-height: 1.1;
}

/* Edición del diagrama (30/08/2026, pedido de Javi: "permite editar el
   diagrama, mover situación, añadir o eliminar tareas, líneas de flujo y
   eventos de inicio y fin, y editar textos"). El diagrama sigue siendo de
   ajuste automático (fases/carriles se recalculan solos, ver
   computeLearnFlowLayout) — "mover" una tarea aquí es cambiarla de fase o
   de carril desde el formulario, o subirla/bajarla dentro de su columna
   con ▲/▼, no arrastrarla a un punto libre; así no se pierde el reajuste
   automático que Javi ya pidió y le gustó. Todo se guarda en
   localStorage del navegador (state.learnFlow.doc) — no toca la base de
   datos ni el servidor, es solo la vista de este diagrama. */
.learn-flow-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.4rem 0 0.6rem;
}

.learn-flow-toolbar button {
  margin-top: 0;
  font-size: 0.78rem;
  padding: 0.3rem 0.7rem;
}

.learn-flow-toolbar button.is-active {
  background: var(--accent);
  color: var(--accent-contrast);
  border-color: var(--accent);
}

/* Mini barra ✏️/▲/▼/🗑️ en la esquina superior derecha de cada tarea en
   modo edición — la esquina inferior derecha ya la ocupa el tirador de
   redimensionar, por eso esta va arriba. */
.learn-flow-node-toolbar {
  position: absolute;
  top: 2px;
  right: 2px;
  display: flex;
  gap: 2px;
  z-index: 3;
}

.learn-flow-node-toolbar button {
  width: 20px;
  height: 20px;
  padding: 0;
  margin: 0;
  font-size: 0.65rem;
  line-height: 1;
  border-radius: 4px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  cursor: pointer;
}

.learn-flow-node-toolbar button:hover {
  background: var(--accent);
  color: var(--accent-contrast);
}

.learn-flow-phase-add {
  position: absolute;
  width: 24px;
  height: 24px;
  padding: 0;
  margin: 0;
  border-radius: 50%;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--fg);
  cursor: pointer;
  font-size: 0.85rem;
  line-height: 1;
  z-index: 2;
}

.learn-flow-phase-add:hover {
  background: var(--accent);
  color: var(--accent-contrast);
}

.learn-flow-new-phase-btn {
  position: absolute;
  border: 1px dashed var(--border);
  border-radius: 8px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.78rem;
  margin: 0;
}

.learn-flow-new-phase-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Modo "nueva conexión" activo: el cursor avisa de que el próximo clic en
   una tarea la usa como origen o destino, en vez de no hacer nada. */
.learn-flow.is-connect-mode .learn-flow-box,
.learn-flow.is-connect-mode .learn-flow-event {
  cursor: crosshair;
}

.learn-flow-box.is-connect-source,
.learn-flow-event.is-connect-source {
  outline: 3px solid var(--accent);
  outline-offset: 1px;
}

/* Botón de borrar una conexión, en el punto medio de su línea (solo en
   modo edición). pointer-events:auto reactiva los clics en este grupo
   aunque el SVG entero los tenga desactivados (ver .learn-flow-svg). */
.learn-flow-edge-delete {
  pointer-events: auto;
  cursor: pointer;
}

.learn-flow-edge-delete-bg {
  fill: var(--bad);
  opacity: 0.85;
}

.learn-flow-edge-delete-x {
  fill: #fff;
  font-size: 9px;
  font-weight: 700;
  text-anchor: middle;
  dominant-baseline: central;
}

/* Formulario de edición (tarea/fase/conexión), dentro del modal genérico
   reutilizado de Esquema (.schema-modal). */
.learn-flow-edit-modal {
  width: 620px;
}

.learn-flow-form-row {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-top: 0.75rem;
}

.learn-flow-form-row label {
  font-weight: 600;
  font-size: 0.8rem;
}

.learn-flow-form-row input[type='text'],
.learn-flow-form-row textarea,
.learn-flow-form-row select {
  width: 100%;
  box-sizing: border-box;
}

.learn-flow-form-row textarea {
  min-height: 3.5rem;
  resize: vertical;
}

.learn-flow-form-inline {
  display: flex;
  gap: 0.75rem;
}

.learn-flow-form-inline > .learn-flow-form-row {
  flex: 1;
  margin-top: 0.75rem;
}

.learn-flow-step-row {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  margin-top: 0.4rem;
}

.learn-flow-step-row input {
  flex: 1;
}

.learn-flow-step-remove {
  margin-top: 0;
  flex-shrink: 0;
  padding: 0.2rem 0.5rem;
}

.learn-flow-form-actions {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.25rem;
}

.learn-flow-form-actions-right {
  display: flex;
  gap: 0.5rem;
}

.learn-flow-form-actions button {
  margin-top: 0;
}

/* Etiqueta "X a Y" en el punto medio de cada línea (28/08/2026, pedido de
   Javi). El "stroke" del mismo color que el fondo del lienzo, dibujado
   ANTES que el relleno (paint-order), hace de halo — así el texto se lee
   igual de bien encima de una línea o cruzando otra tarjeta, sin tapar
   nada por completo como haría un <rect> de fondo. */
.schema-rel-cardinality {
  font-size: 10px;
  font-family: inherit;
  fill: var(--fg);
  stroke: var(--card-bg);
  stroke-width: 3px;
  paint-order: stroke fill;
  text-anchor: middle;
  dominant-baseline: middle;
}

/* Antes era la tercera columna de .schema-layout, a la derecha del
   lienzo; Javi pidió que apareciera debajo de las flechas de mover en su
   lugar (28/08/2026) — ahora vive dentro de .schema-canvas-col, así que
   ya no necesita un ancho de columna fijo (flex: 0 0 260px), solo separarse
   visualmente de las flechas y no crecer sin límite si la tabla tiene
   muchas columnas. */
.schema-detail-panel {
  border-top: 1px solid var(--border);
  padding-top: 0.75rem;
  max-height: 320px;
  overflow-y: auto;
}

.schema-detail-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.schema-detail-header h2 {
  margin: 0;
  font-size: 1rem;
}

.schema-detail-header-actions {
  display: flex;
  align-items: center;
  gap: 0.4rem;
}

.schema-detail-header button {
  margin-top: 0;
  font-size: 0.72rem;
  padding: 0.25rem 0.5rem;
  flex-shrink: 0;
}

.schema-detail-table {
  color: var(--accent);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
}

.schema-detail-columns {
  margin-top: 0.6rem;
  border-top: 1px solid var(--border);
  padding-top: 0.5rem;
}

.schema-detail-column {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.82rem;
  padding: 0.2rem 0;
  border-bottom: 1px solid var(--border);
}

.schema-detail-column-type {
  color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.75rem;
}

/* Modo "Editar nombres" del panel de Detalle (28/08/2026, pedido de
   Javi): el nombre de la tabla y el de cada columna se convierten en
   estos inputs en vez de texto suelto. Mismo tamaño de letra que el
   texto que sustituyen, para que el cambio de modo no salte visualmente. */
.schema-detail-table-input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.3rem 0.4rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--accent);
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.85rem;
}

.schema-detail-column-label-input {
  flex: 1 1 auto;
  min-width: 0;
  box-sizing: border-box;
  padding: 0.15rem 0.35rem;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--bg);
  color: var(--fg);
  font-size: 0.82rem;
  font-family: inherit;
}

.schema-detail-edit-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

/* Sin esta línea, el atributo hidden no hace nada: una hoja de estilos
   propia (esta) siempre gana a la regla por defecto del navegador
   [hidden]{display:none}, así que si aquí se fija "display" sin más, el
   modal se ve siempre — vacío, tapando la página entera — desde que carga,
   y el botón de cerrar tampoco sirve (bug real, visto por Javi 27/08/2026). */
.schema-modal-overlay[hidden] {
  display: none;
}

.schema-modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 20;
  padding: 1.5rem;
}

.schema-modal {
  position: relative;
  background: var(--bg);
  border-radius: 10px;
  border: 1px solid var(--border);
  max-width: 90vw;
  max-height: 85vh;
  width: 900px;
  padding: 1.25rem 1.5rem;
  overflow-y: auto;
}

/* Tiras invisibles en los cuatro bordes (28/08/2026, pedido de Javi):
   arrastrarlas cambia el tamaño del panel en cualquier dirección. La
   primera vez que se arrastra una, el JS pasa el panel de "centrado por
   flexbox" a posición fija con su ancho/alto exactos — a partir de ahí el
   tamaño y la posición quedan en tus manos. */
.schema-modal-resize-handle {
  position: absolute;
  z-index: 5;
}

.schema-modal-resize-handle.edge-top,
.schema-modal-resize-handle.edge-bottom {
  left: 10px;
  right: 10px;
  height: 8px;
  cursor: ns-resize;
}

.schema-modal-resize-handle.edge-top {
  top: -4px;
}

.schema-modal-resize-handle.edge-bottom {
  bottom: -4px;
}

.schema-modal-resize-handle.edge-left,
.schema-modal-resize-handle.edge-right {
  top: 10px;
  bottom: 10px;
  width: 8px;
  cursor: ew-resize;
}

.schema-modal-resize-handle.edge-left {
  left: -4px;
}

.schema-modal-resize-handle.edge-right {
  right: -4px;
}

.schema-modal-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

.schema-modal-toolbar .hint {
  margin: 0;
}

.schema-modal-toolbar button {
  margin-top: 0;
  font-size: 0.78rem;
  padding: 0.3rem 0.6rem;
  flex-shrink: 0;
}

.schema-modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.schema-modal-header h2 {
  margin: 0;
}

.schema-modal-close {
  margin-top: 0;
  background: none;
  color: var(--muted);
  border: none;
  font-size: 1.1rem;
  padding: 0.2rem 0.5rem;
}

/* Altura de las filas y anchura de las columnas, ambas arrastrables
   (28/08/2026, pedido de Javi). La altura es UNA sola tira arrastrable
   para toda la tabla (--schema-row-pad, en px) porque lo que pidió fue
   "la altura", en singular — no el alto de cada fila por separado. La
   anchura, en cambio, la pidió "de cada columna", así que cada `<th>`
   lleva su propio tirador a la derecha; para que eso funcione, la tabla
   pasa a table-layout:fixed con un <colgroup> que fija el ancho inicial
   de cada columna en JS (renderSchemaModalTable). */
.schema-modal-row-height-handle {
  height: 10px;
  margin-top: 0.5rem;
  border-radius: 4px;
  background: var(--card-bg);
  border: 1px dashed var(--border);
  cursor: ns-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.6rem;
  letter-spacing: 0.02em;
  color: var(--muted);
  user-select: none;
}

.schema-modal-body {
  overflow-x: auto;
  margin-top: 0.4rem;
}

.schema-modal-body table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.78rem;
  table-layout: fixed;
}

.schema-modal-body th,
.schema-modal-body td {
  border: 1px solid var(--border);
  padding: calc(0.3rem + var(--schema-row-pad, 0px)) 0.5rem;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  position: relative;
}

/* En cuanto la fila tiene algo de aire extra, aprovecharlo: el texto
   envuelve en vez de cortarse con "…" (si no, el espacio ganado con la
   altura no serviría para ver más contenido). */
.schema-modal-body.is-tall th,
.schema-modal-body.is-tall td {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  word-break: break-word;
}

.schema-modal-body th {
  background: var(--card-bg);
  position: sticky;
  top: 0;
}

.schema-col-resize-handle {
  position: absolute;
  top: 0;
  right: -3px;
  width: 6px;
  height: 100%;
  cursor: col-resize;
  z-index: 2;
}

.schema-col-resize-handle:hover,
.schema-col-resize-handle.is-resizing {
  background: var(--accent);
  opacity: 0.5;
}

/* Editor de index.html (28/08/2026, pedido de Javi: "botón de guardar +
   revisar sintaxis"; sub-pestañas Textos/HTML añadidas el mismo día tras
   ver el HTML crudo con &quot; por todas partes). */
.editor-subtabs {
  display: flex;
  gap: 0.25rem;
  margin-bottom: 0.75rem;
  border-bottom: 1px solid var(--border);
}

.editor-subtab-button {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--muted);
  padding: 0.45rem 0.7rem;
  font-size: 0.88rem;
  cursor: pointer;
}

.editor-subtab-button.active {
  color: var(--fg);
  border-bottom-color: var(--accent);
  font-weight: 600;
}

/* Una tarjeta por texto editable — misma base visual que .card para no
   inventar un lenguaje visual nuevo solo para esto. */
.editor-descriptions-list {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.editor-description-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0.8rem 1rem;
}

.editor-description-card > label {
  display: block;
  font-size: 0.85rem;
  font-weight: 600;
  margin-bottom: 0.4rem;
}

.editor-description-card textarea {
  width: 100%;
  min-height: 4.5rem;
  box-sizing: border-box;
  padding: 0.5rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: 0.88rem;
  font-family: inherit;
  line-height: 1.4;
  resize: vertical;
}

/* Se marca en cuanto Javi toca una tarjeta, para que quede claro qué
   textos se van a guardar con "Guardar todo" (28/08/2026). */
.editor-description-card.is-dirty {
  border-color: var(--accent);
}

/* Etiquetas editables de tabla/columna en "Etiquetas y notas" (28/08/2026,
   pedido de Javi: "poder modificar también las etiquetas de las columnas
   y campos"). El input de etiqueta va antes que la nota, para que quede
   claro que es lo primero que se lee; visualmente más compacto que un
   textarea porque es solo una línea. */
.editor-label-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  padding: 0.4rem 0.55rem;
  margin-bottom: 0.4rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--fg);
  font-size: 0.88rem;
  font-weight: 600;
  font-family: inherit;
}

/* Cabecera de cada tabla dentro de "Etiquetas y notas" — antes era un
   <h3> de solo texto (schema-domain-heading); ahora es un input para
   poder editar el nombre de la tabla igual que el de cada columna, pero
   conserva el aspecto de cabecera (letra más grande, sin caja visible
   hasta que se toca). */
.editor-table-label-input {
  display: block;
  width: 100%;
  box-sizing: border-box;
  margin: 0.6rem 0 0.3rem;
  padding: 0.2rem 0.3rem;
  border: 1px solid transparent;
  border-radius: 6px;
  background: none;
  color: var(--fg);
  font-size: 0.95rem;
  font-weight: 700;
  font-family: inherit;
}

.editor-table-label-input:hover,
.editor-table-label-input:focus {
  border-color: var(--border);
  background: var(--card-bg);
  outline: none;
}

.editor-table-label-input:first-child {
  margin-top: 0;
}

/* Nombre técnico real de la tabla, junto a su etiqueta editable — para
   que Javi siempre sepa a qué tabla física corresponde aunque le haya
   puesto un nombre distinto. */
.editor-table-key-note {
  font-size: 0.7rem;
  color: var(--muted);
  font-weight: 400;
  margin-left: 0.4rem;
}

.editor-toolbar {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  margin-bottom: 0.6rem;
}

.editor-toolbar button {
  margin-top: 0;
}

.editor-status {
  font-size: 0.85rem;
  color: var(--muted);
}

.editor-status.is-ok {
  color: var(--ok);
}

.editor-status.is-error {
  color: var(--bad);
}

.editor-issues {
  background: var(--warn-bg);
  color: var(--warn-fg);
  border-radius: 6px;
  padding: 0.6rem 0.8rem;
  margin-bottom: 0.6rem;
  font-size: 0.85rem;
}

.editor-issues p {
  margin: 0 0 0.3rem;
  font-weight: 600;
}

.editor-issues ul {
  margin: 0;
  padding-left: 1.2rem;
}

.editor-textarea {
  width: 100%;
  min-height: 60vh;
  box-sizing: border-box;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.8rem;
  line-height: 1.45;
  padding: 0.75rem;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card-bg);
  color: var(--fg);
  resize: vertical;
  tab-size: 2;
}

/* Botón "Subir" (28/08/2026, pedido de Javi: fijo, visible siempre haya
   scroll o no — a diferencia de otros sitios donde solo aparece al
   bajar). Flotante en la esquina, por encima de todo lo demás. */
.scroll-top-button {
  position: fixed;
  right: 1.5rem;
  bottom: 1.5rem;
  z-index: 20;
  padding: 0.55rem 0.9rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--fg);
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
}

.scroll-top-button:hover {
  background: var(--bg);
}

/* --- Síntesis (31/08/2026) ------------------------------------------------
   Historial de versiones (lista de botones, un clic abre la vista previa)
   + tarjeta de vista previa con índice y descargas. Reutiliza .card,
   .hint, .warning, .result ya existentes — solo lo específico de esta
   pestaña vive aquí. */

.sintesis-version-row {
  padding: 0.3rem 0;
  border-bottom: 1px solid var(--border);
}

.sintesis-version-row:last-child {
  border-bottom: none;
}

.sintesis-version-open {
  width: 100%;
  text-align: left;
  background: none;
  color: var(--fg);
  border: none;
  margin-top: 0;
  padding: 0.3rem 0.1rem;
  font-size: 0.88rem;
}

.sintesis-version-open:hover {
  color: var(--accent);
  opacity: 1;
}

.sintesis-preview-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
}

.sintesis-preview-actions {
  display: flex;
  gap: 0.5rem;
}

.sintesis-download-link {
  font-size: 0.8rem;
  padding: 0.28rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--fg);
  text-decoration: none;
}

.sintesis-download-link:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.sintesis-preview-body {
  max-width: 70ch;
  margin-top: 0.75rem;
}

.sintesis-preview-body h3 {
  font-size: 0.95rem;
  margin-bottom: 0.4rem;
}

.sintesis-index {
  list-style: disc;
  padding-left: 1.25rem;
  font-size: 0.85rem;
  color: var(--muted);
  margin-bottom: 1rem;
}

.sintesis-preview-body p {
  font-size: 0.92rem;
  line-height: 1.5;
  white-space: pre-wrap;
}
