/* ============================================================
   Vercel dark — sistema de diseño (fuente única de verdad)
   Referencia visual: TEMPLATE_VERCEL/. Consumido por index.html,
   login.html y perfiles.html.
   ============================================================ */

:root {
  color-scheme: light;

  /* ---- Paleta: papel, tinta, grafito y azul de plano técnico ---- */
  --paper:      oklch(0.965 0.014 92);
  --paper-deep: oklch(0.932 0.018 92);
  --ink:        oklch(0.26 0.012 60);
  --graphite:   oklch(0.55 0.008 60);
  --blueprint:  oklch(0.48 0.09 250);

  /* ---- Superficies ---- */
  --bg:        var(--paper);
  --bg-2:      var(--paper-deep);
  --surface:   oklch(0.985 0.01 92);
  --surface-2: var(--paper-deep);
  --border:        oklch(0.26 0.012 60 / 18%);
  --border-strong: oklch(0.26 0.012 60 / 30%);

  /* ---- Texto ---- */
  --text:      var(--ink);
  --text-soft: oklch(0.26 0.012 60 / .85);
  --text-mut:  var(--graphite);

  /* ---- Acento de marca (personalizable por el usuario) ---- */
  --accent:    var(--blueprint);
  /* Sobre papel el acento hace falta oscurecerlo para que sea legible como
     TEXTO (un cian claro guardado en localStorage.accentColor puede quedar
     casi invisible sobre --paper si no se oscurece acá). */
  --accent-txt: color-mix(in srgb, var(--accent) 55%, #000);

  /* ---- Acentos semánticos (FIJOS, no personalizables) ---- */
  --success:   #059669;   /* hecho / éxito */
  --warning:   #ea580c;   /* advertencia / en curso */
  --question:  #db2777;   /* preguntas */
  --decision:  #7c3aed;   /* decisiones / personas */
  --danger:    #dc2626;   /* error */
  --nota:      #52525b;   /* al pasar / ya lo dijo — el gris deliberado */
  --tarea:     #a16207;   /* tarea, traer para la próxima */
  --eyebrow:   var(--accent-txt);   /* etiquetas eyebrow */

  /* ---- Fondo de tarjeta ----
     Sólido en papel: no hay glow de fondo que dejar ver, y la transparencia
     solo lo ensucia (ver el bloque del tema oscuro, donde sí es translúcido). */
  --card-bg:       var(--surface);
  --card-bg-hover: var(--surface-2);

  /* ---- Lienzo de Apuntes (canvas/nota a pantalla completa) ---- */
  --lienzo:       var(--paper);
  --lienzo-punto: oklch(0.48 0.09 250 / .13);

  /* El diseño es de esquinas casi rectas — pero solo en botones/inputs/tabs
     (--radius/--radius-sm). Las superficies grandes (tarjetas, modales,
     paneles flotantes) redondean más (2026-08-22, pedido del usuario: la app
     se sentía "demasiado cuadrada"). El radio NO es una propiedad del tema:
     los dos (papel/oscuro) comparten esta escala — ver el comentario en el
     bloque `[data-tema="oscuro"]` más abajo. */
  --radius-sm:   2px;
  --radius:      4px;
  --radius-card: 12px;
  --radius-lg:   16px;
}

/* Papel milimetrado: base de toda la interfaz sobre el tema papel */
.paper-grid {
  background-color: var(--paper);
  background-image:
    linear-gradient(to right, oklch(0.48 0.09 250 / 0.07) 0.5px, transparent 0.5px),
    linear-gradient(to bottom, oklch(0.48 0.09 250 / 0.07) 0.5px, transparent 0.5px),
    linear-gradient(to right, oklch(0.48 0.09 250 / 0.13) 0.5px, transparent 0.5px),
    linear-gradient(to bottom, oklch(0.48 0.09 250 / 0.13) 0.5px, transparent 0.5px);
  background-size: 12px 12px, 12px 12px, 60px 60px, 60px 60px;
}

/* 🔴 Corrección 2026-08-22: los 10 PNG de la sala traen alfa real (los PNG-8
   de paleta indexada igual codifican transparencia por `tRNS`) — no "vienen
   con fondo blanco". Lo que hace `multiply` es volver invisibles los
   RELLENOS internos, que son blanco casi opaco, dejando pasar el papel; el
   fondo del lienzo ya era transparente antes de tocar nada. Sobre fondo
   oscuro hay que invertir la línea (negra→blanca) y cambiar a `screen`, que
   es lo que elimina el negro — ver el bloque oscuro. Detalle completo del
   formato en `ESPECIFICACIÓN.md` (raíz del repo). */
.sprite {
  mix-blend-mode: multiply;
}

.label-tech {
  font-family: var(--font-mono, "IBM Plex Mono"), ui-monospace, monospace;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  font-size: 0.6875rem;
}

/* ============================================================
   Modo oscuro — capa de compatibilidad
   ------------------------------------------------------------
   Reinterpreta los tokens y, contra especificidad, las ~430 utilidades
   Tailwind con blanco fijo (`text-white/40`, `bg-white/5`, `border-white/10`…)
   que hay repartidas por index.html y app.js. Esas utilidades NO se tocan:
   `:root:not([data-tema="oscuro"]) .text-white` no aplica acá — este bloque
   son los TOKENS del oscuro; la capa de compatibilidad de las utilidades
   quedó invertida más abajo, aplicando por defecto (sin atributo = papel).
   ============================================================ */
:root[data-tema="oscuro"] {
  color-scheme: dark;

  /* ---- Superficies ---- */
  --bg:        #0b0c0e;   /* fondo base */
  --bg-2:      #101114;   /* fondo alterno / paneles */
  --surface:   #16181c;   /* tarjetas */
  --surface-2: #1e2126;   /* hover de tarjeta */
  --border:        rgba(255,255,255,0.08);
  --border-strong: rgba(255,255,255,0.16);

  /* ---- Texto ---- */
  --text:      #fafafa;   /* títulos y números grandes */
  --text-soft: #d4d6da;   /* texto de cuerpo */
  --text-mut:  #8f939b;   /* subtítulos, secundario */

  --accent-txt: var(--accent);

  /* ---- Acentos semánticos (FIJOS, no personalizables) ---- */
  --success:   #34d399;   /* hecho / éxito */
  --warning:   #fb923c;   /* advertencia / en curso */
  --question:  #f472b6;   /* preguntas */
  --decision:  #a78bfa;   /* decisiones / personas */
  --danger:    #f87171;   /* error */
  --nota:      #94a3b8;   /* al pasar / ya lo dijo — el gris deliberado */
  --tarea:     #facc15;   /* tarea, traer para la próxima */
  --eyebrow:   #22d3ee;   /* etiquetas eyebrow */

  /* ---- Fondo de tarjeta ----
     Translúcido en oscuro: deja ver el glow de fondo, que es lo que le da
     profundidad. En papel se vuelve sólido (ver el bloque de arriba): no hay
     glow detrás y la transparencia solo lo ensucia. */
  --card-bg:       color-mix(in srgb, var(--surface) 72%, transparent);
  --card-bg-hover: color-mix(in srgb, var(--surface-2) 85%, transparent);

  /* ---- Lienzo de Apuntes (canvas/nota a pantalla completa) ---- */
  --lienzo:       #0a0b10;
  --lienzo-punto: rgba(255,255,255,.06);

  /* 🔴 Fix 2026-08-22: este bloque redefinía los 4 radios (10/14/18/22, la
     escala vieja de "Vercel dark") mientras el papel usaba 2/4/12/16 — cambiar
     de tema también cambiaba cuán redonda era la app, sin que fuera intención
     de nadie. El radio no es una propiedad del tema: los dos heredan de
     `:root` (arriba). Si hace falta un radio distinto en oscuro alguna vez,
     que sea una decisión explícita y no un arrastre de la escala vieja. */
}

/* Un dibujo de línea negra sobre blanco, invertido, da línea blanca sobre
   negro; `screen` elimina ese negro y deja solo el trazo. Sin esto los
   sprites desaparecen sobre fondo oscuro (multiply contra #0b0c0e = negro). */
:root[data-tema="oscuro"] .sprite {
  mix-blend-mode: screen;
  filter: invert(1);
}

/* Glow de fondo: solo tiene sentido con superficies translúcidas (oscuro).
   Sobre papel es el glassmorphism que el brief prohíbe. */
#glowFondo { display: none; }
:root[data-tema="oscuro"] #glowFondo { display: block; }

/* ---- Texto: .text-white/NN → el token de texto a la misma opacidad ---- */
:root:not([data-tema="oscuro"]) .text-white { color: var(--text); }
:root:not([data-tema="oscuro"]) .text-white\/90 { color: color-mix(in srgb, var(--text) 90%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/85 { color: color-mix(in srgb, var(--text) 85%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/80 { color: color-mix(in srgb, var(--text) 80%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/75 { color: color-mix(in srgb, var(--text) 75%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/70 { color: color-mix(in srgb, var(--text) 70%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/60 { color: color-mix(in srgb, var(--text) 60%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/55 { color: color-mix(in srgb, var(--text) 55%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/50 { color: color-mix(in srgb, var(--text) 50%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/45 { color: color-mix(in srgb, var(--text) 45%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/40 { color: color-mix(in srgb, var(--text) 40%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/35 { color: color-mix(in srgb, var(--text) 35%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/30 { color: color-mix(in srgb, var(--text) 30%, transparent); }
:root:not([data-tema="oscuro"]) .text-white\/25 { color: color-mix(in srgb, var(--text) 25%, transparent); }

/* ---- Fondo: un tinte NEGRO pesa más que uno blanco, se baja a propósito ---- */
:root:not([data-tema="oscuro"]) .bg-white { background-color: var(--surface); }
:root:not([data-tema="oscuro"]) .bg-white\/20 { background-color: color-mix(in srgb, var(--text) 8%, transparent); }
:root:not([data-tema="oscuro"]) .bg-white\/10 { background-color: color-mix(in srgb, var(--text) 5%, transparent); }
:root:not([data-tema="oscuro"]) .bg-white\/5  { background-color: color-mix(in srgb, var(--text) 3%, transparent); }

/* ---- Borde ---- */
:root:not([data-tema="oscuro"]) .border-white\/30 { border-color: color-mix(in srgb, var(--text) 22%, transparent); }
:root:not([data-tema="oscuro"]) .border-white\/15 { border-color: color-mix(in srgb, var(--text) 12%, transparent); }
:root:not([data-tema="oscuro"]) .border-white\/10 { border-color: color-mix(in srgb, var(--text) 9%, transparent); }
:root:not([data-tema="oscuro"]) .border-white\/5  { border-color: color-mix(in srgb, var(--text) 6%, transparent); }

/* ---- Divisor ---- */
:root:not([data-tema="oscuro"]) .divide-white\/5 > * + * { border-color: color-mix(in srgb, var(--text) 6%, transparent); }

/* ---- Variantes con estado: la clase lleva el pseudo, la regla también ---- */
:root:not([data-tema="oscuro"]) .hover\:bg-white\/5:hover { background-color: color-mix(in srgb, var(--text) 3%, transparent); }
:root:not([data-tema="oscuro"]) .hover\:bg-white\/10:hover { background-color: color-mix(in srgb, var(--text) 5%, transparent); }
:root:not([data-tema="oscuro"]) .hover\:bg-white\/20:hover { background-color: color-mix(in srgb, var(--text) 8%, transparent); }
:root:not([data-tema="oscuro"]) .focus\:bg-white\/5:focus { background-color: color-mix(in srgb, var(--text) 3%, transparent); }
:root:not([data-tema="oscuro"]) .hover\:text-white:hover { color: var(--text); }
:root:not([data-tema="oscuro"]) .hover\:text-white\/90:hover { color: color-mix(in srgb, var(--text) 90%, transparent); }
:root:not([data-tema="oscuro"]) .hover\:text-white\/80:hover { color: color-mix(in srgb, var(--text) 80%, transparent); }
:root:not([data-tema="oscuro"]) .hover\:text-white\/70:hover { color: color-mix(in srgb, var(--text) 70%, transparent); }
:root:not([data-tema="oscuro"]) .hover\:border-white\/30:hover { border-color: color-mix(in srgb, var(--text) 22%, transparent); }
:root:not([data-tema="oscuro"]) .group:hover .group-hover\:text-white\/60 { color: color-mix(in srgb, var(--text) 60%, transparent); }

/* ---------- Base ---------- */
body { background: var(--bg); color: var(--text); }
/* Scrollbar invisible en toda la app (2026-08-22, a pedido del usuario) — el
   scroll sigue funcionando (rueda, trackpad, teclado, arrastre táctil), solo
   se oculta el widget visual. `*` + `::-webkit-scrollbar` cubre TODOS los
   `overflow-y: auto` del árbol (.db-side-nav, .db-main, .db-drawer-lista,
   .db-nav-flyout, modales, el lienzo de Apuntes…) sin tener que acordarse de
   agregarlo a cada uno nuevo que se cree. 🔴 Regla dura: no reintroducir un
   scrollbar visible (ni thin ni estilizado) en ningún selector nuevo — si en
   algún momento hace falta uno puntual, la excepción va acá, explicada, no
   como una regla suelta en otro lado del archivo. */
* { scrollbar-width: none; }
::-webkit-scrollbar { display: none; }

/* ---------- Eyebrow ----------
   F3: pasa a ser el `.label-tech` del rediseño (mono, uppercase, tracking
   .14em, 11px, grafito) — la clase `.db-eyebrow` se queda (no se toca HTML),
   solo cambia lo que dibuja. */
.db-eyebrow {
  font-family: var(--font-mono, "IBM Plex Mono"), ui-monospace, monospace;
  font-size: .6875rem; font-weight: 500; letter-spacing: .14em;
  text-transform: uppercase; color: var(--text-mut);
}

/* ---------- Tarjetas ----------
   F3: borde .5px, sin sombra ni `backdrop-filter` — el brief pide plano
   técnico, no glassmorphism. */
.db-card {
  background: var(--card-bg);
  border: .5px solid var(--border);
  border-radius: var(--radius-card);
  transition: background .18s ease, border-color .18s ease;
}
.db-card-hover { cursor: pointer; }
.db-card-hover:hover {
  background: var(--card-bg-hover);
  border-color: var(--blueprint);
}

/* ---- Tarjeta grande seleccionable de un carrusel de pasos (PLAN_CARRUSEL_100).
   <button type="button" class="db-choice-card" aria-pressed="false">
     <span class="db-choice-card-icono"><svg>...</svg></span>
     <span class="db-choice-card-titulo">Título</span>
     <span class="db-choice-card-subtitulo">Subtítulo</span>
   </button>
   Seleccionada: agregar/sacar la clase `.seleccionada` (no toca `disabled`). */
.db-choice-card {
  display: flex; flex-direction: column; gap: 16px;
  width: 100%; text-align: left;
  padding: 24px; border-radius: var(--radius-card);
  background: var(--card-bg);
  border: .5px solid var(--border);
  cursor: pointer;
  transition: border-color .18s ease, background .18s ease;
}
.db-choice-card:hover { border-color: color-mix(in srgb, var(--accent) 40%, transparent); }
.db-choice-card.seleccionada {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 5%, transparent);
  box-shadow: inset 0 0 0 1px var(--accent);
}
.db-choice-card-icono {
  display: flex; align-items: center; justify-content: center;
  height: 48px; width: 48px; border-radius: var(--radius);
  background: rgba(255,255,255,0.06); color: var(--accent-txt);
  transition: background .18s ease, color .18s ease;
}
.db-choice-card.seleccionada .db-choice-card-icono { background: var(--accent); color: #06222a; }
.db-choice-card-icono svg { height: 1.5rem; width: 1.5rem; }
.db-choice-card-titulo { display: block; font-size: 1.35rem; font-weight: 700; line-height: 1.15; color: var(--text); }
.db-choice-card-subtitulo { display: block; margin-top: 6px; font-size: .85rem; line-height: 1.5; color: var(--text-mut); }

/* ---- Variante de .db-choice-card para los modos de Flashcards ----
   Suma una línea de "tagline" en mayúsculas entre el título y la
   descripción (ej. Agresivo/Normal/Rápidas). Mismo HTML que
   .db-choice-card + un <span class="db-mode-card-tagline"> extra. */
.db-mode-card-tagline {
  display: block; margin-top: 4px;
  font-size: .68rem; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--accent-txt);
}
.db-mode-card .db-choice-card-subtitulo { margin-top: 8px; }

/* ---- Barra de progreso del carrusel ----
   La pinta pintarProgresoAsistente(). Cada paso es un <button role="tab">
   con .db-wizard-bar (la barrita) + .db-wizard-label (el texto). Estados:
     .activo        → paso actual: barra ancha (32px) acentuada, label en --text
     .completado    → paso ya recorrido: barra ancha (32px) atenuada
     (sin clase)    → paso futuro no visitado: barra angosta (16px)
     .deshabilitado → no alcanzable todavía: disabled + cursor not-allowed */
.db-wizard-progress { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.db-wizard-step { display: flex; align-items: center; gap: 8px; background: none; border: none; padding: 0; cursor: pointer; }
.db-wizard-step.deshabilitado { cursor: not-allowed; }
.db-wizard-bar {
  display: block; height: 6px; width: 16px; border-radius: 999px;
  background: var(--border);
  transition: width .3s ease, background-color .3s ease;
}
.db-wizard-step:hover:not(.deshabilitado):not(.activo) .db-wizard-bar {
  background: color-mix(in srgb, var(--text-mut) 60%, transparent);
}
.db-wizard-step.activo .db-wizard-bar { width: 32px; background: var(--accent); }
.db-wizard-step.completado .db-wizard-bar { width: 32px; background: color-mix(in srgb, var(--accent) 50%, transparent); }
.db-wizard-label { font-size: .78rem; font-weight: 600; color: var(--text-mut); transition: color .18s ease; }
.db-wizard-step.activo .db-wizard-label { color: var(--text); }

/* ---- Navegador de carpetas (breadcrumb + lista con divisores) ---- */
.db-folder-browser {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}
.db-folder-breadcrumb {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  font-size: .82rem;
}
.db-folder-breadcrumb button { color: var(--text-mut); transition: color .15s ease; }
.db-folder-breadcrumb button:hover { color: var(--text-soft); }
.db-folder-breadcrumb .actual { color: var(--text); font-weight: 600; }
.db-folder-breadcrumb .sep { color: var(--text-mut); opacity: .5; }
.db-folder-list { max-height: 360px; overflow-y: auto; }
.db-folder-item {
  display: flex; align-items: center; gap: 12px;
  width: 100%; padding: 12px 16px; text-align: left;
  border-bottom: 1px solid var(--border);
  transition: background .15s ease;
  cursor: pointer;
}
.db-folder-item:last-child { border-bottom: none; }
.db-folder-item:hover { background: var(--card-bg-hover); }
.db-folder-item.seleccionado { background: color-mix(in srgb, var(--accent) 8%, transparent); }

/* ---- Transición horizontal entre pasos del carrusel (slide 48px + fade,
   0.32s) ----
   🔴 Nombrado `.db-wizard-panel`, NO `.db-paso` — esa clase ya existe más abajo
   (el indicador "Paso 3 de 8" del tutor EN VIVO) y es una cosa completamente
   distinta; reusar el nombre habría pisado esa regla.
   `transicionarPaso()` en app.js: agrega la clase de "sale", espera 320ms,
   reemplaza el contenido, agrega la de "entra" con `transition: none` +
   reflow (para no animar desde la posición vieja) y la saca en el frame
   siguiente para que sí anime hacia el centro. */
.db-wizard-panel {
  transition: transform .32s cubic-bezier(.22,1,.36,1), opacity .32s cubic-bezier(.22,1,.36,1);
}
.db-wizard-panel-entra-adelante { transform: translateX(48px); opacity: 0; }
.db-wizard-panel-sale-adelante  { transform: translateX(-48px); opacity: 0; }
.db-wizard-panel-entra-atras    { transform: translateX(-48px); opacity: 0; }
.db-wizard-panel-sale-atras     { transform: translateX(48px); opacity: 0; }

/* ---------- Badge de ícono ---------- */
.db-badge {
  height: 44px; width: 44px; border-radius: var(--radius);
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
}
.db-badge-sm { height: 32px; width: 32px; border-radius: var(--radius-sm); }

/* ---------- Pill de estado ---------- */
.db-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 4px 11px; border-radius: 999px; font-size: 0.72rem; font-weight: 500;
  border: .5px solid var(--border); background: var(--bg-2);
}
.db-dot { height: 7px; width: 7px; border-radius: 999px; flex-shrink: 0; }

/* ---------- Botones ---------- */
.db-btn {
  border-radius: var(--radius); font-weight: 600; font-size: 0.875rem;
  padding: 9px 18px; transition: background .15s ease, border-color .15s ease, filter .15s ease; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
}
.db-btn:disabled { opacity: .45; pointer-events: none; }
/* F3: plano — fondo `--text`/`--bg` en vez de `--accent` (recto, sin sombra ni
   `hover:scale`; es el mismo par que el FAB de la sala, F2.4). */
.db-btn-primary { background: var(--text); color: var(--bg); }
.db-btn-primary:hover { filter: brightness(1.12); }
.db-btn-ghost { background: color-mix(in srgb, var(--bg-2) 40%, transparent); border: .5px solid var(--border); color: var(--text-soft); }
.db-btn-ghost:hover { background: var(--bg-2); }
/* Botón de icono puro: cuadrado, sin etiqueta. Lo usan las acciones de la barra
   del resultado (copiar, exportar), donde el texto competía con las pestañas.
   Al no tener label, el `title`/`aria-label` no es decorativo: es el nombre
   accesible del botón. */
.db-btn-icono { padding: 0; width: 36px; height: 36px; gap: 0; }
.db-btn-icono svg { height: 1rem; width: 1rem; }

/* ---------- Tutor JARVIS: la escena (PLAN_TUTOR_GEMINI_100 §5) ----------
   Esfera reactiva a la voz + imagen del resumen en recuadro chico + botón
   grande de hablar. Sin glassmorphism, mismos tokens que el resto (§9 de
   CLAUDE.md): la esfera es un gradiente con --accent, no un blur. */

.db-tutor-escena { padding: 1rem 0; }

.db-tutor-imagen {
  width: 168px; max-width: 60%; aspect-ratio: 4 / 3;
  border-radius: var(--radius-card); border: .5px solid var(--border);
  overflow: hidden; background: var(--surface);
  opacity: 0; transform: scale(.94);
  transition: opacity .35s ease, transform .35s ease;
}
.db-tutor-imagen.visible { opacity: 1; transform: scale(1); }
.db-tutor-imagen img { width: 100%; height: 100%; object-fit: cover; display: block; }

.db-tutor-esfera {
  --nivel-voz: 0;
  height: 128px; width: 128px; border-radius: 50%; flex-shrink: 0;
  position: relative; display: flex; align-items: center; justify-content: center;
  background: radial-gradient(circle at 35% 30%,
    color-mix(in srgb, var(--accent) 55%, var(--surface)) 0%,
    color-mix(in srgb, var(--accent) 22%, var(--surface)) 55%,
    var(--surface) 100%);
  border: .5px solid var(--border-strong);
  box-shadow: 0 0 0 color-mix(in srgb, var(--accent) 35%, transparent);
  transition: box-shadow .12s ease-out, transform .12s ease-out;
  transform: scale(calc(1 + var(--nivel-voz) * 0.16));
}
.db-tutor-esfera.hablando {
  box-shadow: 0 0 28px color-mix(in srgb, var(--accent) 35%, transparent);
}
/* F7 (2026-08-28) — dos estados más allá de "hablando": ESCUCHANDO (mic
   grabando, push-to-talk) con el mismo tinte que `.db-tutor-hablar.grabando`
   para que la esfera y el botón lean como la misma acción; PENSANDO (turno
   en vuelo, sin audio todavía) con un pulso más suave — nunca se solapan
   entre sí ni con `.hablando` (ver `iniciarLoopEsferaTutor`). */
.db-tutor-esfera.escuchando {
  box-shadow: 0 0 24px color-mix(in srgb, var(--danger) 45%, transparent);
  animation: pulso 1.5s ease-in-out infinite;
}
.db-tutor-esfera.pensando { animation: pulso 1.1s ease-in-out infinite; }
.db-tutor-esfera-nucleo {
  height: 46%; width: 46%; border-radius: 50%;
  background: color-mix(in srgb, var(--accent) 75%, white 10%);
  filter: blur(6px); opacity: .8;
}
@media (prefers-reduced-motion: reduce) {
  .db-tutor-esfera { transition: opacity .2s ease; transform: none; }
  .db-tutor-esfera.hablando { opacity: .85; box-shadow: none; }
  .db-tutor-esfera.escuchando, .db-tutor-esfera.pensando { animation: none; opacity: .85; box-shadow: none; }
}

/* T.4 — pantalla de preparación: pulso lento y decorativo (no reactivo a
   WebAudio, no hay audio real sonando todavía). Reusa `@keyframes pulso`
   ya definido para `.db-tutor-hablar.grabando`, más lento. */
.db-tutor-esfera-pulso { animation: pulso 2.4s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
  .db-tutor-esfera-pulso { animation: none; opacity: .85; }
}

.db-tutor-subtitulo {
  min-height: 2.6em; max-width: 34ch; margin: 0; padding: 0 1rem;
  text-align: center; font-size: .84rem; line-height: 1.4;
  color: var(--text-mut);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Botón grande de hablar del tutor — reemplaza al viejo .db-mic circular. */
.db-tutor-hablar {
  display: inline-flex; align-items: center; gap: .5rem;
  padding: .75rem 1.5rem; border-radius: 999px;
  background: var(--text); color: var(--bg); border: none;
  font-size: .84rem; font-weight: 500; cursor: pointer;
  transition: opacity .15s ease, transform .1s ease;
}
.db-tutor-hablar:hover { opacity: .88; }
.db-tutor-hablar:active { transform: scale(.97); }
.db-tutor-hablar svg { height: 1.2rem; width: 1.2rem; flex-shrink: 0; }
.db-tutor-hablar.grabando {
  background: var(--danger); color: #fff;
  animation: pulso 1.5s ease-in-out infinite;
}
.db-tutor-hablar:disabled { opacity: .4; cursor: not-allowed; animation: none; }

@keyframes pulso {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}

/* ---------- Número grande ---------- */
.db-num {
  font-family: 'IBM Plex Sans', system-ui, sans-serif; font-weight: 800;
  color: var(--text); letter-spacing: -0.02em;
}

/* ---------- Inputs ---------- */
.db-input {
  width: 100%; background: var(--surface);
  border: .5px solid var(--border); color: var(--text);
  border-radius: var(--radius); padding: 12px; transition: border-color .15s, box-shadow .15s;
}
/* Variante compacta: controles que van dentro de una fila de 36px, como el
   selector de velocidad del reproductor. */
.db-input-sm { padding: 4px 8px; border-radius: var(--radius-sm); font-size: .74rem; }
.db-input::placeholder { color: var(--text-mut); }
.db-input:focus { outline: none; border-color: var(--blueprint); box-shadow: 0 0 0 3px color-mix(in srgb, var(--blueprint) 18%, transparent); }

/* ---------- Caja de error ---------- */
.db-error {
  background: color-mix(in srgb, var(--danger) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--danger) 25%, transparent);
  color: var(--danger);
}

/* ---------- Caja de confirmación (gemela de .db-error) ---------- */
.db-ok {
  background: color-mix(in srgb, var(--success) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--success) 25%, transparent);
  color: var(--success);
}

/* ---------- Aviso discreto (G1.5, PLAN_EDITOR_DIRECTO) ----------
   Para avisos que pueden aparecer seguido (el round-trip del editor de
   Hoja) — invariante 4: una caja roja permanente entrena a ignorar los
   rojos, así que esto NO es .db-error. Texto en el gris de siempre, con
   el acento de --warning solo en el borde/fondo, apenas insinuado. */
.db-aviso-suave {
  background: color-mix(in srgb, var(--warning) 8%, transparent);
  border: 1px solid color-mix(in srgb, var(--warning) 20%, transparent);
  color: var(--text-mut);
}
.db-aviso-suave summary { color: var(--warning); }

/* ---------- Panel de modal ---------- */
.db-modal-panel {
  background: var(--card-bg);
  border: .5px solid var(--border);
  border-radius: var(--radius-lg);
}

/* ============================================================
   Herramientas flotantes — FAB + grid de lanzamiento + ventanas
   arrastrables estilo NotebookLM (Resumen/Chat/Complementar).
   `--tool-accent` es una custom property que trae cada tarjeta/panel
   inline desde el HTML (`style="--tool-accent: var(--question)"`);
   acá solo se define el fallback (`var(--accent)`) y el color-mix.
   ============================================================ */
/* F3: plano — mismo par ink/paper que `.db-fab` de la sala, sin sombra de
   glow ni `hover:scale`. */
.db-tool-fab {
  position: fixed; bottom: 24px; right: 24px; z-index: 40;
  height: 56px; width: 56px; border-radius: 999px; border: none;
  display: flex; align-items: center; justify-content: center;
  background: var(--text); color: var(--bg); cursor: pointer;
  box-shadow: 3px 3px 0 0 var(--border-strong);
  transition: filter .15s ease;
}
.db-tool-fab svg { height: 24px; width: 24px; color: var(--bg); }
.db-tool-fab:hover { filter: brightness(1.12); }

.db-tool-grid {
  position: fixed; bottom: 96px; right: 24px; z-index: 40;
  width: 340px; max-width: calc(100vw - 32px);
  background: var(--surface);
  border: .5px solid var(--border);
  border-radius: var(--radius-card);
  overflow: hidden;
}
.db-tool-grid-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: .75rem 1rem; border-bottom: 1px solid var(--border);
}
.db-tool-grid-body {
  display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; padding: .75rem;
}

.db-tool-card {
  display: flex; flex-direction: column; gap: .5rem;
  padding: .75rem; border-radius: var(--radius);
  border: .5px solid var(--border);
  background: color-mix(in oklab, var(--tool-accent, var(--accent)) 12%, var(--surface));
  cursor: pointer; text-align: left;
  transition: border-color .15s ease, background .15s ease;
}
.db-tool-card:hover {
  background: color-mix(in oklab, var(--tool-accent, var(--accent)) 18%, var(--surface));
  border-color: color-mix(in srgb, var(--tool-accent, var(--accent)) 40%, var(--border));
}
.db-tool-card.activa {
  background: color-mix(in oklab, var(--tool-accent, var(--accent)) 22%, var(--surface));
  border-color: var(--tool-accent, var(--accent));
}
.db-tool-card-icon {
  width: 2rem; height: 2rem; border-radius: var(--radius-sm); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in oklab, var(--tool-accent, var(--accent)) 30%, transparent);
}
.db-tool-card-icon svg { height: 1.05rem; width: 1.05rem; color: var(--tool-accent, var(--accent)); }

/* La ventana flotante individual. `top`/`left` los pone JS inline por cada
   una (es lo que la hace arrastrable); acá solo el tamaño y el cromo. */
.db-tool-panel {
  position: fixed; z-index: 50;
  width: 460px; max-width: calc(100vw - 32px);
  height: 520px; max-height: calc(100vh - 32px);
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--surface);
  border: .5px solid var(--border);
  border-radius: var(--radius-card);
}
/* F5.1 (PLAN_RESUMEN_COMPLEMENTOS_100, 2026-08-25): el panel de Resumen es
   más grande que el resto (las 3 tarjetas de complementos, F5.2, no entran
   cómodas en 460×520). Chat queda con el tamaño general de arriba: es una
   ventana con menos contenido. `max-width`/`max-height` no se repiten — ya
   vienen de `.db-tool-panel` general. */
.db-tool-panel[data-herramienta="resumen"] {
  width: 640px;
  height: 660px;
}
/* PLAN_COMPLEMENTAR_CARRUSEL_100 (2026-08-27): el carrusel de destino
   (Tipo/Carpeta/Documento, reusa .db-choice-card de Apuntes) tampoco entra
   cómodo en 460×520 — mismo motivo que Resumen arriba. */
.db-tool-panel[data-herramienta="complementar"] {
  width: 600px;
  height: 640px;
}
.db-tool-panel-header {
  display: flex; align-items: center; gap: .75rem; flex-shrink: 0;
  padding: .75rem 1rem; border-bottom: 1px solid var(--border);
  cursor: grab; touch-action: none;
}
.db-tool-panel-header:active { cursor: grabbing; }
.db-tool-panel-icon {
  width: 2.25rem; height: 2.25rem; border-radius: var(--radius-sm); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in oklab, var(--tool-accent, var(--accent)) 30%, transparent);
}
.db-tool-panel-icon svg { height: 1.15rem; width: 1.15rem; color: var(--tool-accent, var(--accent)); }
/* Reemplaza, para el contenido de cada herramienta, al viejo padding de
   panel de pestaña a ancho completo: acá la ventana es angosta, así que
   nada de `max-width`/`mx-auto`, solo un padding chico. */
.db-tool-panel-body { flex: 1 1 auto; overflow-y: auto; padding: 1rem; }

@media (max-width: 720px) {
  .db-tool-fab { bottom: 16px; right: 16px; }
  .db-tool-grid { bottom: 88px; right: 16px; }
}

/* ============================================================
   Shell de la aplicación — sidebar + barra de contexto + contenido
   ------------------------------------------------------------
   🔴 El shell es el dueño del alto, y de eso depende todo lo demás.
   La pantalla de resultado tiene paneles internos `flex-1
   overflow-y-auto` (transcripción, resumen, chat, complementar) que
   solo funcionan si el padre tiene un alto acotado. Antes eso lo daba
   un `fixed inset-0` que tapaba la app entera — de ahí que el dock
   «desapareciera». Ahora lo da la celda del grid: la pantalla de
   resultado comparte celda con `.db-main`, así que la tapa sin ser
   `fixed`, con el alto acotado y el sidebar visible al lado.

   Reemplaza al dock flotante (`.db-dock`), que estaba `fixed` y
   centrado con `translateY(-50%)`: eso lo hacía incapaz de crecer —
   pasado el alto de la ventana se recortaba arriba y abajo sin scroll
   posible. El sidebar de ahora está en el flujo y su zona de materias
   scrollea sola.
   ============================================================ */
.db-shell {
  /* 236px → 56px (2026-08-22, F1 del rediseño Sandbox Room): el rail de
     iconos deja de ser un modo colapsable y pasa a ser el sidebar permanente
     en desktop. Las materias, que vivían acá adentro, son ahora un flyout
     (F1.2) que no ocupa columna del grid. */
  --side-w: 56px;
  /* 44px → 56px (F1.3): la topbar suma el saludo/fecha de Inicio y las 3
     pastillas de stats, que antes vivían en la tarjeta hero. */
  --topbar-h: 56px;
  height: 100%;
  display: grid;
  grid-template-columns: var(--side-w) minmax(0, 1fr);
  grid-template-rows: auto minmax(0, 1fr);
  overflow: hidden;
  transition: grid-template-columns .18s ease;
}

/* Rail oculto a las 3h sin interacción (2026-08-22, puramente estético — ver
   `actualizarVisibilidadRail` en app.js). Colapsa --side-w a 0 en vez de dejar
   un hueco vacío, reusando la transición de grid-template-columns de arriba.
   Solo aplica en el grid de escritorio: en <1024px --side-w ni se usa (el
   drawer mobile tiene su propio mecanismo, más abajo). */
.db-shell.rail-inactivo { --side-w: 0px; }
.db-shell.rail-inactivo .db-side { opacity: 0; pointer-events: none; }

/* Sidebar: ocupa las dos filas, a la izquierda de todo. */
.db-side {
  grid-column: 1; grid-row: 1 / 3;
  display: flex; flex-direction: column; min-height: 0;
  background: var(--bg-2);
  border-right: 1px solid var(--border);
  z-index: 20;
  transition: opacity .3s ease;
}
/* La zona que scrollea cuando la navegación crece (las materias). */
.db-side-nav {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; gap: 4px;
  padding: 10px 8px;
}
/* El bloque de navegación se centra verticalmente en desktop (F0, 2026-08-23):
   `.db-nav-spacer-top` (antes de Inicio) y `.db-nav-spacer` (antes de
   Configuración) son dos `flex:1 1 auto` iguales — el grupo del medio queda
   centrado y Configuración se queda pinneada al fondo del rail. */
.db-side-pie {
  flex-shrink: 0; padding: 8px; border-top: 1px solid var(--border);
  display: flex; flex-direction: column; gap: 4px;
}
/* Separador fino, para menús y para agrupar dentro del sidebar. */
.db-divider { height: 1px; background: var(--border); margin: 4px 6px; flex-shrink: 0; }

/* Wrapper de la fila 1 del grid (F1.2, 2026-08-23): antes `.db-topbar` era el
   grid item directo; ahora lo es este `<div>`, para poder sumar el hero de
   Inicio como una segunda franja SIN agregar una fila al grid de `.db-shell`
   (invariante duro — ver ui-shell.md). Border/fondo/z-index que antes tenía
   `.db-topbar` se mudan acá, porque ahora delimitan el conjunto (barra +
   hero), no solo la barra de 56px. */
.db-topbar-wrap {
  grid-column: 2; grid-row: 1;
  display: flex; flex-direction: column; min-width: 0;
  border-bottom: 1px solid var(--border);
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  z-index: 10;
}

/* Barra de contexto: fila fija de 56px dentro de `.db-topbar-wrap`. Siempre
   visible, también con una grabación abierta — es lo que dice dónde estás. */
.db-topbar {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 14px; min-height: var(--topbar-h); flex-shrink: 0;
}

/* Hero de Inicio (F1.2): segunda franja, solo visible en Inicio sin
   resultado abierto (togglea `pintarContexto()`). Los dos cuadros son
   decorativos, absolutos y detrás del saludo (z-index:0 contra el z-index:1
   del `<h1>`) — mismo patrón `color-mix(in srgb, var(--accent) N%,
   transparent)` que ya usa `iconoFormato()` en app.js. */
.db-hero-inicio {
  position: relative; overflow: hidden;
  display: flex; align-items: center;
  height: 110px; padding: 0 24px;
}
.db-hero-inicio.hidden { display: none; }
.db-hero-saludo {
  position: relative; z-index: 1;
  font-size: 1.7rem; font-weight: 600; color: var(--text);
}
.db-hero-cuadro {
  position: absolute; pointer-events: none; z-index: 0;
  width: 104px; height: 104px; border-radius: var(--radius-card);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: .5px solid color-mix(in srgb, var(--accent) 30%, transparent);
}
.db-hero-cuadro-a { top: -24px; left: 48px; transform: rotate(-6deg); }
.db-hero-cuadro-b { bottom: -34px; right: 64px; transform: rotate(8deg); }
/* Pantallas de poca altura (~768px, riesgo #2 del plan): recortar el hero
   antes que tocar el grid. */
@media (max-height: 780px) {
  .db-hero-inicio { height: 76px; }
  .db-hero-saludo { font-size: 1.3rem; }
  .db-hero-cuadro { width: 72px; height: 72px; }
}

/* Contenido: el scroll de las 5 vistas. Las vistas conservan su
   `max-w-* mx-auto`, así que el scroll vive acá y la barra queda pegada
   al borde del área de contenido, no en el medio de la pantalla. */
.db-main { grid-column: 2; grid-row: 2; min-width: 0; overflow-y: auto; position: relative; }

/* Pantalla de resultado: misma celda que `.db-main`.
   🔴 `position` + `z-index` explícitos, y no basta el orden del DOM: `.db-main`
   es `position: relative`, y un elemento posicionado se pinta SIEMPRE encima de
   uno estático aunque vaya antes en el árbol. Sin esto, la pantalla de materia
   se dibujaba sobre la transcripción.
   `.hidden` de Tailwind es una sola clase y theme.css se carga después, así que
   hace falta el `.hidden` calificado para que gane. */
.db-resultado {
  grid-column: 2; grid-row: 2; min-width: 0; min-height: 0;
  display: flex; flex-direction: column; overflow: hidden;
  background: var(--bg);
  position: relative; z-index: 5;
}
.db-resultado.hidden { display: none; }

/* ---- Micro-etiqueta monoespaciada ----
   El subtítulo chico de EJ1 («COMMAND LAYER») y el metadato de EJ2 («PDF ·
   2.4 MB»). Stack del sistema: no se agrega ninguna fuente. `db-mono` es la
   etiqueta (va en mayúsculas); `db-meta` es el metadato de una fila, que
   lleva nombres propios y por eso NO se pasa a mayúsculas. */
.db-mono {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: .62rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--text-mut); line-height: 1.5;
}
.db-meta {
  font-family: 'IBM Plex Mono', ui-monospace, monospace;
  font-size: .7rem; letter-spacing: .01em;
  color: var(--text-mut);
  font-variant-numeric: tabular-nums;
}

/* Chip de ancla: la marca de tiempo de un bloque de audio (renderizarBloques)
   y el ancla de un tramo de documento (renderizarTramosDocumento) son el
   mismo componente, para que audio y documento se lean igual. */
.db-chip-ancla {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--accent-txt);
}
.db-chip-ancla:hover { opacity: .8; }

/* ---- Marca del sidebar ---- */
.db-side-brand {
  flex-shrink: 0; display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; min-height: var(--topbar-h);
  border-bottom: 1px solid var(--border);
}
.db-side-logo {
  height: 32px; width: 32px; border-radius: 10px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center; color: #06222a;
  background: linear-gradient(140deg, var(--accent) 0%, var(--decision) 100%);
}
.db-side-brand-txt { min-width: 0; flex: 1; overflow: hidden; }
.db-side-brand-nombre {
  font-family: 'IBM Plex Sans', system-ui, sans-serif; font-weight: 800; font-size: .88rem;
  letter-spacing: -0.02em;
  color: var(--text); line-height: 1.2; white-space: nowrap;
}
/* ---- Item de navegación: icono + etiqueta + cola ----
   El color activo/inactivo es CSS puro (ya no se togglean utilidades de
   Tailwind desde JS): el markup no trae ninguna clase de color, así que no
   hay nada con lo que pelear en la cascada. */
.db-nav-item {
  display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border-radius: 11px; flex-shrink: 0;
  color: var(--text-mut); font-size: .855rem; font-weight: 600;
  text-align: left; cursor: pointer;
  transition: background .15s ease, color .15s ease;
}
.db-nav-item:hover { background: rgba(255,255,255,.05); color: var(--text); }
.db-nav-item.nav-active {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent-txt);
  box-shadow: inset 3px 0 0 var(--accent), 0 0 20px -6px color-mix(in srgb, var(--accent) 40%, transparent);
  outline: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  outline-offset: -1px;
}
.db-nav-item > svg { height: 18px; width: 18px; flex-shrink: 0; }
.db-nav-txt { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.db-nav-avatar {
  height: 28px; width: 28px; border-radius: 9px; flex-shrink: 0;
  object-fit: contain; background: var(--surface);
}
.db-nav-lista { display: flex; flex-direction: column; gap: 2px; }

/* Header de sección («MATERIAS»), al estilo de los eyebrow */
.db-nav-sec {
  flex-shrink: 0; margin: 16px 10px 6px;
  font-size: .62rem; font-weight: 700; letter-spacing: .12em;
  text-transform: uppercase; color: var(--text-mut);
}

/* Punto de color de la materia — mismo orden que `paletaMaterias` en app.js */
.db-nav-punto { height: 8px; width: 8px; border-radius: 3px; flex-shrink: 0; margin-left: 5px; }

/* Cola del item: pendientes de repaso (ámbar) o cantidad de clases (tenue) */
.db-nav-pend {
  flex-shrink: 0; font-size: .65rem; font-weight: 700; padding: 1px 6px; border-radius: 999px;
  background: color-mix(in srgb, var(--warning) 18%, transparent); color: var(--warning);
}
.db-nav-conteo { flex-shrink: 0; font-size: .68rem; font-weight: 600; color: var(--text-mut); opacity: .7; }
.db-nav-vacio { color: var(--text-mut); font-size: .74rem; padding: 2px 10px; opacity: .8; }

/* ---- Rail permanente: solo iconos, con tooltip nativo (`title`) ----
   Solo en pantalla grande: en teléfono el sidebar es un cajón que se corre, y
   ahí las etiquetas tienen que estar (por eso todo esto va detrás del media
   query, nunca suelto). */
@media (min-width: 1024px) {
  .db-side .db-nav-txt,
  .db-side .db-nav-pend,
  .db-side .db-nav-conteo,
  .db-side .db-nav-vacio,
  .db-side .db-nav-sec,
  .db-side-brand-txt { display: none; }
  .db-side .db-nav-item,
  .db-side-brand { justify-content: center; }
  .db-side .db-nav-item { padding: 9px 0; gap: 0; }
  .db-side .db-nav-punto { margin-left: 0; height: 10px; width: 10px; }
  /* Centrado vertical del grupo de navegación (F0, 2026-08-23) */
  .db-nav-spacer-top { flex: 1 1 auto; }
}

/* Empuja Configuración al fondo del bloque de navegación (F1.1): con
   `.db-side-nav` en flex-column, este `<div>` sin contenido se lleva todo el
   espacio libre y el resto de los items queda apoyado arriba y abajo. */
.db-nav-spacer { flex: 1 1 auto; }

/* ---- Flyout de materias (F1.2) ----
   `#navMaterias` no vive más adentro de `.db-side-nav`: cuelga a la derecha
   del rail, mostrado con hover/foco del grupo (`renderMateriasSidebar()`
   sigue sin saber que esto existe — solo cambió dónde vive su contenedor).
   🔴 `position: fixed`, NO `absolute`: `.db-side-nav` tiene `overflow-y:auto`,
   y por la regla de CSS Overflow (`overflow-x` sin declarar al lado de un
   `overflow-y` no-visible se computa también a `auto`, nunca `visible`) un
   panel `absolute` quedaba recortado en el borde derecho del rail — invisible
   del todo. `fixed` lo saca de ese contexto de scroll; la posición la calcula
   `abrirFlyoutMaterias()` en JS a partir de `getBoundingClientRect()`. */
.db-nav-flyout-grupo { position: relative; flex-shrink: 0; }
.db-nav-flyout {
  position: fixed;
  min-width: 220px; max-height: min(70vh, 480px); overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-card); box-shadow: 0 12px 32px -8px rgba(0,0,0,.35);
  padding: 10px 8px; z-index: 30;
  opacity: 0; visibility: hidden; transform: translateX(-4px);
  transition: opacity .15s ease, transform .15s ease, visibility .15s;
}
.db-nav-flyout--abierta {
  opacity: 1; visibility: visible; transform: translateX(0);
}
/* Dentro del flyout los textos van siempre, aunque el rail esté compacto. */
.db-nav-flyout .db-nav-sec,
.db-nav-flyout .db-nav-txt,
.db-nav-flyout .db-nav-pend,
.db-nav-flyout .db-nav-conteo,
.db-nav-flyout .db-nav-vacio { display: block; }
.db-nav-flyout .db-nav-item { justify-content: flex-start; padding: 8px 10px; gap: 10px; }
.db-nav-flyout .db-nav-punto { margin-left: 5px; height: 8px; width: 8px; }
/* En teléfono el flyout no tiene sentido (el rail ya es un cajón ancho con
   etiquetas): las materias vuelven a mostrarse inline, como un item más. */
@media (max-width: 1023px) {
  .db-nav-flyout {
    position: static; opacity: 1; visibility: visible; transform: none;
    box-shadow: none; border: none; background: transparent; padding: 0;
    max-height: none; overflow: visible;
  }
}

.db-side-logo-txt {
  font-family: var(--font-mono, "IBM Plex Mono"), ui-monospace, monospace;
  font-size: .68rem; font-weight: 700; letter-spacing: .1em;
}

/* ---- Teléfono y tablet: el sidebar es un cajón sobre el contenido ----
   El grid pasa a una sola columna y el sidebar sale del flujo. Ojo: la barra
   de contexto y el contenido declaran `grid-column: 2`, así que hay que
   devolverlos a la 1 o el grid inventa una segunda columna vacía. */
.db-topbar-menu {
  height: 34px; width: 34px; border-radius: 10px; flex-shrink: 0;
  display: none; align-items: center; justify-content: center;
  color: var(--text-mut); cursor: pointer;
  transition: background .15s, color .15s;
}
.db-topbar-menu:hover { background: rgba(255,255,255,.06); color: var(--text); }

.db-side-backdrop {
  position: fixed; inset: 0; z-index: 39;
  background: rgba(0,0,0,.55);
  opacity: 0; pointer-events: none; transition: opacity .2s ease;
}

@media (max-width: 1023px) {
  .db-shell { grid-template-columns: minmax(0, 1fr); }
  .db-topbar-wrap, .db-main, .db-resultado { grid-column: 1; }
  .db-topbar-menu { display: flex; }

  .db-side {
    position: fixed; left: 0; top: 0; bottom: 0; width: 264px;
    z-index: 40; transform: translateX(-100%);
    transition: transform .22s ease;
    box-shadow: 0 0 40px rgba(0,0,0,.5);
  }
  .db-shell.side-abierto .db-side { transform: translateX(0); }
  .db-shell.side-abierto .db-side-backdrop { opacity: 1; pointer-events: auto; }
}

/* Migaja de la barra de contexto. Es referencia, no título: por eso cede el
   ancho (`min-width: 0` + ellipsis en la hoja) cuando comparte la fila con las
   pestañas de una clase abierta, que no se encogen. */
.db-crumb {
  display: flex; align-items: center; gap: 6px; min-width: 0; flex: 1 1 auto;
  font-size: .76rem; color: var(--text-mut);
}

/* Acciones de la clase abierta, dentro de la barra de contexto. */
.db-topbar-acciones { display: flex; align-items: center; gap: 6px; flex-shrink: 0; }
/* `.hidden` de Tailwind es una sola clase y theme.css se carga después: sin
   esto, el `display: flex` de arriba ganaría y las acciones se verían en las
   otras 4 vistas. Mismo motivo que en `.db-resultado.hidden`. */
.db-topbar-acciones.hidden { display: none; }
/* Compactas solo acá: los otros `.db-tabs` (Estudio, Configuración) siguen
   con su tamaño, que en un panel ancho está bien. */
.db-topbar .db-tabs { padding: 2px; border-radius: 10px; }
.db-topbar .db-tabs > button { padding: 4px 10px; font-size: .74rem; border-radius: 8px; }
.db-topbar .db-btn-icono { width: 30px; height: 30px; }
.db-topbar .db-btn-icono svg { height: .9rem; width: .9rem; }

/* En pantallas angostas la migaja de 3 niveles y 4 pestañas no coexisten. Se
   cae el nivel raíz («Transcripciones»), nunca el del medio: ese es la materia,
   y desde una clase abierta es el camino de vuelta ahora que no hay botón
   «Volver». `:has(> :nth-child(5))` = hay 3 niveles (3 textos + 2 separadores),
   así que con 2 no se oculta nada. */
@media (max-width: 720px) {
  .db-crumb:has(> :nth-child(5)) > :nth-child(-n+2) { display: none; }
  .db-topbar .db-tabs > button { padding: 4px 8px; font-size: .7rem; }
  /* Volumen y velocidad salen de la fila: en teléfono el volumen son los
     botones del aparato, y la barra la necesita el seek. */
  .db-player-extra { display: none; }
}
.db-crumb-sep { color: var(--text-mut); opacity: .5; flex-shrink: 0; }
.db-crumb-hoja { color: var(--text); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Los niveles que no son la hoja navegan hacia atrás: la migaja es la forma de
   volver, y funciona igual dentro de una grabación abierta. */
.db-crumb-ir {
  color: var(--text-mut); flex-shrink: 0; cursor: pointer;
  border-radius: 7px; padding: 2px 6px; margin: -2px -6px;
  transition: background .15s, color .15s;
}
.db-crumb-ir:hover { background: rgba(255,255,255,.06); color: var(--text); }

/* ---- Topbar: Inicio (fecha + saludo) vs migaja, y las 3 pastillas (F1.3) ----
   `pintarContexto()` alterna `#topbarInicio`/`#contextoRuta`: en la raíz no
   hay nada que navegar hacia atrás, así que no tiene sentido una migaja de un
   solo nivel. */
.db-topbar-inicio { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.db-topbar-inicio.hidden { display: none; }
.db-topbar-right { display: flex; align-items: center; gap: 8px; margin-left: auto; flex-shrink: 0; }
.db-topbar-stats { display: flex; align-items: center; gap: 6px; }
.db-topbar-stats.hidden { display: none; }
.db-pill-stat {
  display: flex; align-items: baseline; gap: 6px;
  border: .5px solid var(--border); background: var(--bg);
  padding: 4px 8px; border-radius: 3px;
}
.db-pill-stat .label-tech { color: var(--text-mut); }
@media (max-width: 720px) {
  /* Racha y Precisión se leen del panel de Estudio; con la migaja/pestañas de
     una clase abierta compitiendo por ancho, alcanza con la más urgente. */
  .db-pill-stat:nth-child(1), .db-pill-stat:nth-child(3) { display: none; }
}

/* ---- Sala isométrica (F2, Inicio) ----
   Full-bleed dentro de `.db-main` (que gana `overflow:hidden` vía la clase
   `.vista-sala` — F2.3): no comparte scroll con las demás vistas, tiene su
   propia cámara con tilt/parallax. */
.db-main.vista-sala { overflow: hidden; }
.db-sala { height: 100%; position: relative; display: flex; flex-direction: column; }
.db-sala-escena { position: relative; flex: 1; min-height: 0; perspective: 1600px; }

/* Estado de carga (2026-08-22): hasta que los 5 sprites `idle` de ZONAS_SALA
   terminan de decodificar (`renderSala()` en app.js agrega `.db-sala-lista` a
   #salaEscena recién ahí), se ve solo el piso + este spinner — nada de las
   zonas ni sus `alt` de texto grande mientras cargan. */
#salaZonas { opacity: 0; transition: opacity .3s ease; }
.db-sala-lista #salaZonas { opacity: 1; }
.db-sala-spinner {
  display: none;
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 28px; height: 28px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--blueprint);
  animation: girar-spinner-sala .7s linear infinite;
}
#salaEscena:not(.db-sala-lista) .db-sala-spinner { display: block; }
@keyframes girar-spinner-sala { to { transform: translate(-50%, -50%) rotate(360deg); } }
.db-sala-titulo {
  position: absolute; z-index: 40; color: var(--text-mut); pointer-events: none;
  top: 16px; left: 20px;
}
/* F1.3 (2026-08-23): "sala de estudio ·" queda chico y sutil; el nombre real
   del grupo (`salaTituloGrupo`, pintado por `cargarSesion()`) hereda el
   tamaño normal de `.label-tech`. */
.db-sala-titulo-sub { font-size: .68rem; opacity: .6; }

/* Pie de la sala (2026-08-22): "Seguí donde ibas" o, si no hay nada que
   retomar, un ticker de consejos. Hijo de `.db-sala`, no de `#salaEscena`:
   así sobrevive en <768px, donde la escena se oculta. */
.db-sala-pie {
  position: absolute; bottom: 16px; left: 20px; z-index: 40;
  pointer-events: none; max-width: calc(100% - 40px);
}
.db-sala-pie-titulo { color: var(--text-mut); margin-bottom: 8px; }
.db-sala-pie-chips { display: flex; flex-wrap: wrap; gap: 8px; pointer-events: auto; }
.db-chip-retomar {
  display: inline-flex; align-items: center; gap: 6px; max-width: 220px;
  padding: 6px 12px; border-radius: var(--radius); font-size: .78rem;
  border: .5px solid var(--border); background: var(--surface); color: var(--text-soft);
  transition: background .15s ease, border-color .15s ease;
}
.db-chip-retomar:hover { background: var(--surface-2); border-color: var(--border-strong); }
/* F2 (2026-08-23): el ícono de ámbito (iconoFormato/iconoNota) no se achica
   ni trunca — solo el texto, que es lo que puede ser largo. iconoFormato()
   trae su propio tamaño de badge (32px, pensado para tarjetas de historial);
   acá se reusa la misma función/markup pero achicado a la escala de un chip
   chico — no es iconografía nueva, es el mismo `.db-badge` con otro tamaño. */
.db-chip-retomar > :first-child { flex-shrink: 0; }
.db-chip-retomar .db-badge-sm { width: 20px; height: 20px; border-radius: 3px; }
.db-chip-retomar-texto { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.db-sala-tip {
  color: var(--text-mut); opacity: .75;
  transition: opacity .3s ease;
}
.db-sala-tip.cambiando { opacity: 0; }
@media (max-width: 767px) {
  .db-sala-pie {
    position: static; order: -1; max-width: none;
    padding: 16px 16px 0;
  }
}
.db-sala-viewport {
  position: absolute; inset: 0; display: flex; align-items: center;
  justify-content: center; padding: 24px;
}
.db-sala-camara {
  /* 1180px -> 1062px (-10%, a pedido del usuario 2026-08-22: la escena entera
     "no se alcanza a ver bien cuando doy click"). El esqueleto compensa con
     --zona-escala más abajo, no se achica con el resto.
     1062px -> 950px (-10% de nuevo, 2026-08-22 tanda 2 de sprites): floor/
     shelf-wide/shelf-tall siguen en 1370x784, no en los 2740x1568 recomendados
     por ESPECIFICACIÓN.md, así que en pantallas 2x el navegador los agranda y
     se ven borrosos. Achicar la escena no soluciona la falta de resolución
     real, solo la disimula un poco mientras no hay archivos a mayor tamaño. */
  width: 100%; max-width: 950px; aspect-ratio: 1370 / 784;
  position: relative; transform-style: preserve-3d; will-change: transform;
}
.db-sala-inner {
  position: absolute; inset: 0;
  transition: transform .55s cubic-bezier(.16,1,.3,1), opacity .55s cubic-bezier(.16,1,.3,1);
}
/* 1.75x -> 1.45x: el acercamiento se leía como que la escena se iba de
   pantalla en vez de una cámara entrando. El fundido a .4 se conserva. */
.db-sala-inner.zoom-activa { transform: scale(1.45); opacity: .4; }
.db-sala-floor { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; }
#salaZonas { position: absolute; inset: 0; }

/* --zona-escala: compensación por zona (hoy solo el tutor, ver ZONAS_SALA en
   app.js) para que el 10% de arriba no le pegue a un sprite que ya es chico.
   El origen 50% 95% es el mismo que .db-zona-imgs, así que crece desde los
   pies sin despegarse del piso; el hotspot y la etiqueta son hijos de
   .db-zona, así que escalan junto con el sprite sin desalinearse. */
.db-zona {
  position: absolute; pointer-events: none;
  transform: scale(var(--zona-escala, 1)); transform-origin: 50% 95%;
  transition: opacity .3s cubic-bezier(.16,1,.3,1);
}
/* Al enfocar una zona (hover/foco), las demás se atenúan — pedido del
   2026-08-24. `actualizarEnfoqueSala()` en app.js marca #salaZonas cada vez
   que cambia `.db-zona--activa`, agrupada o no. */
#salaZonas.db-zonas-enfoque .db-zona:not(.db-zona--activa) { opacity: .4; }
.db-zona-imgs {
  position: absolute; inset: 0; transform-origin: 50% 95%;
  transition: transform .35s cubic-bezier(.16,1,.3,1);
}
.db-zona--activa .db-zona-imgs { transform: scale(1.02); }
.db-zona-imgs.idle-loop { animation: salaIdle 4s ease-in-out infinite; }
@keyframes salaIdle { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.01); } }
.db-zona-img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain;
  pointer-events: none; user-select: none;
  transition: opacity .3s cubic-bezier(.16,1,.3,1);
}
.db-zona-img-active { opacity: 0; }
/* El fade idle→active solo aplica si hay una segunda imagen (`--conactiva`):
   la pizarra no tiene `activeSrc` a propósito y su única imagen no se apaga. */
.db-zona--conactiva.db-zona--activa .db-zona-img-idle { opacity: 0; }
.db-zona--conactiva.db-zona--activa .db-zona-img-active { opacity: 1; }

.db-zona-hotspot {
  position: absolute; pointer-events: auto; cursor: pointer;
  border-radius: 2px; border: .5px dashed transparent; background: transparent;
  transition: border-color .3s ease; outline: none;
}
.db-zona--activa .db-zona-hotspot,
.db-zona-hotspot:focus-visible {
  border-color: color-mix(in srgb, var(--blueprint) 60%, transparent);
}
/* Contrarresta --zona-escala (2026-08-24): .db-zona-etiqueta es hija de
   .db-zona, que se escala entera (hoy solo el tutor, 1.45x) para que el
   hotspot siga alineado con el sprite agrandado. Sin este contra-escalado el
   texto crecía junto con el sprite y label/hint/privacidad quedaban casi del
   mismo tamaño — "todo se ve título". El origen 0% 100% (esquina inferior
   izquierda) es el punto de anclaje real de la etiqueta (bottom:100%; left:0
   respecto del hotspot), así que contra-escalar desde ahí no la desplaza. */
.db-zona-etiqueta {
  position: absolute; bottom: 100%; left: 0; margin-bottom: 10px;
  display: flex; flex-direction: column; gap: 2px; white-space: normal; max-width: 230px;
  opacity: 0; transform: translateY(6px) scale(calc(1 / var(--zona-escala, 1))); pointer-events: none;
  transform-origin: 0% 100%;
  transition: opacity .25s cubic-bezier(.16,1,.3,1), transform .25s cubic-bezier(.16,1,.3,1);
}
.db-zona--activa .db-zona-etiqueta,
.db-zona-hotspot:focus-visible .db-zona-etiqueta { opacity: 1; transform: translateY(0) scale(calc(1 / var(--zona-escala, 1))); }
/* Halo con el color de fondo: el tutor cae encima de la estantería alta en el
   dibujo (misma zona de la escena, z-index más alto) y sin esto el trazo del
   mueble se comía la etiqueta. No es parte del diseño original de v0 — ahí no
   hacía falta, sus fondos detrás de cada label eran zonas despejadas. */
.db-zona-etiqueta-label {
  /* --blueprint fijo -> --accent-txt (2026-08-24, pedido del compañero): el
     color de la etiqueta pasa a ser el acento que el usuario ya eligió en
     Configuración (mismo token que el eyebrow del hero), no un azul fijo. */
  color: var(--accent-txt);
  font-size: .85rem;
  font-weight: 700;
  text-shadow: 0 0 4px var(--bg), 0 0 4px var(--bg), 0 0 8px var(--bg);
}
.db-zona-etiqueta-hint {
  font-family: var(--font-mono, "IBM Plex Mono"), monospace; font-size: 12px; color: var(--text-mut);
  text-shadow: 0 0 4px var(--bg), 0 0 4px var(--bg), 0 0 8px var(--bg);
}
.db-zona-etiqueta-privacidad {
  font-family: var(--font-mono, "IBM Plex Mono"), monospace; font-size: 9px;
  text-transform: uppercase; letter-spacing: .08em; color: var(--text-mut); opacity: .75;
  text-shadow: 0 0 4px var(--bg), 0 0 4px var(--bg), 0 0 8px var(--bg);
}
.db-zona-etiqueta-regla { margin-top: 2px; height: .5px; width: 56px; background: color-mix(in srgb, var(--blueprint) 50%, transparent); }

@media (prefers-reduced-motion: reduce) {
  .db-sala-camara { transform: none !important; }
  .db-zona-imgs.idle-loop { animation: none; }
}

/* ---- Mobile <768px: lista vertical en vez de la escena (F2.7) ---- */
.db-sala-lista-mobile { display: none; flex-direction: column; gap: 10px; padding: 16px; overflow-y: auto; list-style: none; }
.db-sala-lista-item-btn {
  display: flex; width: 100%; align-items: center; gap: 12px;
  border: .5px solid var(--border); background: var(--surface);
  padding: 12px; text-align: left; border-radius: var(--radius);
}
.db-sala-lista-img { width: 56px; height: 56px; object-fit: contain; flex-shrink: 0; }
@media (max-width: 767px) {
  .db-sala-escena { display: none; }
  .db-sala-lista-mobile { display: flex; }
}

/* ---- FAB de subida (F2.4, tarjeta rectangular desde F3 2026-08-23) ----
   Círculo -> rectángulo: mismo fondo ink/paper y mismo box-shadow (sigue
   sintiéndose de la misma familia visual que antes), pero con una forma
   claramente distinta a `.db-tool-fab` (que se queda circular, con sparkles,
   en la vista de Resultado) — eso es lo que resuelve la confusión, no un
   cambio de posición. */
.db-fab {
  position: absolute; right: 20px; bottom: 20px; z-index: 40;
  height: 58px; width: 190px; padding: 10px 14px; border-radius: var(--radius-card);
  background: var(--text); color: var(--bg);
  display: flex; align-items: center; gap: 8px; text-align: left;
  box-shadow: 3px 3px 0 0 var(--border-strong);
  transition: transform .15s ease;
}
.db-fab:hover { transform: translateY(-2px); }
.db-fab:active { transform: translateY(0); }
.db-fab-icono { height: 1.1rem; width: 1.1rem; flex-shrink: 0; }
.db-fab-texto { font-size: .76rem; font-weight: 600; line-height: 1.15; }
@media (max-width: 420px) {
  /* Teléfonos angostos: el rectángulo de 190px + 20px de margen no entra
     siempre — se achica antes que recortarse. */
  .db-fab { width: 150px; height: 52px; }
  .db-fab-texto { font-size: .7rem; }
}

/* ---- Drawer «Últimas grabaciones» (F2.5) ----
   `position: fixed`, fuera del grid — no agrega columna, así que se abre
   igual desde cualquier vista. */
.db-drawer-pestaña {
  position: fixed; right: 0; top: 50%; transform: translateY(-50%);
  z-index: 35; writing-mode: vertical-rl;
  display: flex; align-items: center; gap: 8px;
  border: .5px solid var(--border); border-right: none;
  background: var(--bg-2); color: var(--text-mut);
  padding: 14px 8px; border-radius: 8px 0 0 8px;
  transition: background .15s, color .15s;
}
.db-drawer-pestaña:hover { background: var(--surface-2); color: var(--text); }
@media (max-width: 767px) { .db-drawer-pestaña { display: none; } }
.db-drawer-fondo {
  position: fixed; inset: 0; z-index: 44; background: rgba(0,0,0,.45);
}
.db-drawer-fondo.hidden { display: none; }
.db-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 45;
  width: 100%; max-width: 380px;
  background: var(--bg); border-left: 1px solid var(--border-strong);
  display: flex; flex-direction: column;
  box-shadow: -12px 0 32px -12px rgba(0,0,0,.4);
}
.db-drawer.hidden { display: none; }
.db-drawer-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 14px 16px; border-bottom: 1px solid var(--border); flex-shrink: 0;
}
.db-drawer-cerrar { color: var(--text-mut); cursor: pointer; }
.db-drawer-cerrar:hover { color: var(--text); }
.db-drawer-lista { flex: 1; min-height: 0; overflow-y: auto; list-style: none; }
.db-drawer-fila {
  display: flex; width: 100%; align-items: center; gap: 10px;
  padding: 10px 16px; text-align: left; border-bottom: 1px solid var(--border);
  transition: background .15s;
}
.db-drawer-fila:hover:not(:disabled) { background: rgba(255,255,255,.04); }
.db-drawer-fila:disabled { cursor: default; opacity: .55; }
.db-drawer-fila-txt { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 2px; }
.db-drawer-fila-nombre { font-size: .82rem; color: var(--text-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.db-drawer-vacio { padding: 16px; font-size: .82rem; color: var(--text-mut); }

/* Drawer «Conversación» del tutor (F6, 2026-08-28) — mismo componente que
   arriba, nested dentro de #tutorSesion. Desplazado más abajo que la
   pestaña de "Últimas grabaciones" (top: 50%): las dos pueden estar
   visibles a la vez en una sesión de tutor y no deben superponerse. */
.db-drawer-pestaña-tutor { top: 72%; }

/* ---------- Estados activos (toggle desde JS) ---------- */
.nav-active {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent-txt);
  box-shadow: inset 3px 0 0 var(--accent), 0 0 20px -6px color-mix(in srgb, var(--accent) 40%, transparent);
  outline: 1px solid color-mix(in srgb, var(--accent) 28%, transparent);
  outline-offset: -1px;
}
.tier-active { border-color: var(--accent) !important; background: color-mix(in srgb, var(--accent) 10%, transparent); }
/* `.speed-active` se retiró con los 5 botones de velocidad (2026-07-30): ahora
   es un <select>, y el estado activo lo pinta el navegador. */
.dropzone-active { border-color: var(--accent) !important; background: color-mix(in srgb, var(--accent) 6%, transparent); }

/* ---------- Bloques de transcripción ---------- */
.bloque { border-left: 2px solid transparent; transition: background-color .15s, border-color .15s; }
.bloque-activo {
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  border-left-color: var(--accent);
  border-radius: var(--radius-sm);
}

/* ---- Pestañas Transcripción / Resumen ---- */
.db-tabs {
  display: inline-flex;
  gap: 2px;
  padding: 3px;
  background: var(--bg-2);
  border: .5px solid var(--border);
  border-radius: var(--radius);
}
.db-tabs > button {
  padding: 5px 12px;
  border-radius: var(--radius-sm);
  font-size: .8rem;
  font-weight: 600;
  color: var(--text-mut);
  transition: background-color .15s, color .15s;
}
.db-tabs > button:hover { color: var(--text-soft); }
.db-tabs > button.tab-activa {
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent-txt);
  outline: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  outline-offset: -1px;
}

.opcion-contenido.contenido-activo {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent-txt);
}
.opcion-contenido:disabled { opacity: .35; pointer-events: none; }

/* ---- Render del resumen en markdown ---- */
.db-prose { color: var(--text-soft); line-height: 1.7; font-size: .95rem; }
.db-prose h1, .db-prose h2, .db-prose h3 {
  font-family: 'IBM Plex Sans', system-ui, sans-serif;
  color: var(--text);
  font-weight: 700;
  line-height: 1.3;
}
.db-prose h1 { font-size: 1.35rem; margin: 1.4em 0 .5em; }
.db-prose h2 { font-size: 1.15rem; margin: 1.3em 0 .5em; }
.db-prose h3 { font-size: 1rem; margin: 1.2em 0 .4em; color: var(--accent-txt); }
.db-prose h1:first-child, .db-prose h2:first-child, .db-prose h3:first-child { margin-top: 0; }
.db-prose p { margin: .7em 0; }
.db-prose strong { color: var(--text); font-weight: 700; }
.db-prose em { color: var(--text-mut); }
.db-prose ul, .db-prose ol { margin: .7em 0; padding-left: 1.3em; }
.db-prose ul { list-style: disc; }
.db-prose ol { list-style: decimal; }
.db-prose li { margin: .3em 0; }
.db-prose li::marker { color: var(--accent-txt); }
.db-prose code {
  background: rgba(255,255,255,0.07);
  padding: .1em .35em;
  border-radius: 6px;
  font-size: .88em;
}
.db-prose hr { border: none; border-top: 1px solid var(--border); margin: 1.4em 0; }
.db-prose a { color: var(--accent-txt); text-decoration: underline; text-underline-offset: 2px; }

/* Preview de Complementar: lo faltante se muestra en rojo (así se escribirá en la nota). */
/* Tarjetas del preview de Complementar: el borde izquierdo es del color con el
   que va a quedar el bloque en el canvas, para que se reconozca de un vistazo
   antes de escribir nada. Los tres tonos son los mismos que usa el visor. */
.db-prop-completar { border-left: 3px solid var(--danger); background: color-mix(in srgb, var(--danger) 6%, transparent); border-radius: var(--radius-card); }
.db-prop-matiz     { border-left: 3px solid var(--text-mut); background: color-mix(in srgb, var(--text-mut) 6%, transparent); border-radius: var(--radius-card); }
.db-prop-extra     { border-left: 3px solid var(--accent); background: color-mix(in srgb, var(--accent) 6%, transparent); border-radius: var(--radius-card); }
.db-prop-matiz .db-prose { font-size: .85em; color: var(--text-mut); }

/* ---------- Burbujas de chat ---------- */
.db-burbuja { max-width: 85%; padding: 10px 16px; font-size: .875rem; border-radius: var(--radius-card); }
.db-burbuja-yo {
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  border-bottom-right-radius: 6px;
  color: var(--text);
}
.db-burbuja-ia { border-bottom-left-radius: 6px; }

.db-complemento-preview {
  border-left: 3px solid var(--danger);
  background: color-mix(in srgb, var(--danger) 7%, var(--surface));
}
.db-complemento-preview,
.db-complemento-preview strong,
.db-complemento-preview em,
.db-complemento-preview li::marker { color: var(--danger); }

/* ============================================================
   Visor de apuntes — árbol, notas Obsidian y canvas
   ============================================================ */

/* ---- Árbol / lista de notas ---- */
.db-arbol-carpeta > summary {
  list-style: none; cursor: pointer; padding: 4px 6px; border-radius: 8px;
  color: var(--text-mut); font-weight: 600; font-size: .8rem;
  display: flex; align-items: center; gap: 6px;
}
.db-arbol-carpeta > summary::-webkit-details-marker { display: none; }
.db-arbol-carpeta > summary:hover { background: rgba(255,255,255,.05); color: var(--text-soft); }
.db-arbol-carpeta > summary::before { content: "▸"; font-size: .7rem; transition: transform .15s; }
.db-arbol-carpeta[open] > summary::before { transform: rotate(90deg); }
.db-arbol-hijos { margin-left: 10px; padding-left: 8px; border-left: 1px solid var(--border); }
/* El cajón del dock (F4) muestra el árbol en 2 columnas: que una carpeta no
   se parta a la mitad entre columnas. */
.db-arbol-carpeta { break-inside: avoid-column; }

.db-nota-item {
  display: flex; align-items: center; gap: 6px; width: 100%; text-align: left;
  padding: 4px 6px; border-radius: 8px; color: var(--text-soft); font-size: .82rem;
  transition: background .15s, color .15s;
}
.db-nota-item:hover { background: rgba(255,255,255,.05); color: var(--text); }
.db-nota-item.activa { background: color-mix(in srgb, var(--accent) 14%, transparent); color: var(--accent-txt); }
.db-nota-item .db-nota-nombre { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- F6.1/F6.2: renombrar/borrar sobre una fila del árbol ---- */
.db-nota-fila { display: flex; align-items: center; gap: 2px; }
.db-nota-fila .db-nota-item { width: auto; flex: 1 1 auto; min-width: 0; }
.db-arbol-carpeta-nombre {
  flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.db-nota-acciones { display: flex; align-items: center; gap: 2px; flex-shrink: 0; }
.db-nota-accion {
  display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border-radius: 6px; flex-shrink: 0;
  color: var(--text-mut); opacity: 0; transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.db-nota-accion svg { width: 12px; height: 12px; }
.db-nota-fila:hover .db-nota-accion,
.db-arbol-carpeta > summary:hover .db-nota-accion { opacity: 1; }
.db-nota-accion:hover { background: rgba(255,255,255,.08); color: var(--text); }
/* 🔴 Sin esto, renombrar y borrar son INALCANZABLES en teléfono y tablet: el
   único disparador de `opacity: 1` era `:hover`, que en un dispositivo táctil
   no ocurre nunca. Se ven siempre (más tenues) donde no hay hover, y el foco
   de teclado también los revela — el árbol se navega con Tab. */
@media (hover: none) {
  .db-nota-accion { opacity: .6; }
}
.db-nota-accion:focus-visible { opacity: 1; }
.db-nota-accion.db-nota-accion-borrar:hover { color: var(--danger); }

/* ---- Contenido de una nota ---- */
.db-nota-img { max-width: 100%; border-radius: 12px; border: 1px solid var(--border); margin: .8em 0; }
.db-wikilink { color: var(--accent-txt); border-bottom: 1px dotted currentColor; cursor: pointer; }
.db-wikilink-roto { color: var(--text-mut); border-bottom: 1px dotted currentColor; cursor: help; }
.db-embed-nota {
  display: inline-flex; align-items: center; gap: 6px; margin: .4em 0;
  padding: 6px 12px; border-radius: 10px; border: 1px solid var(--border);
  background: rgba(255,255,255,.03); color: var(--text-soft); font-size: .85rem; cursor: pointer;
}
.db-embed-nota:hover { background: rgba(255,255,255,.07); }
/* Lo que Complementar escribió en rojo se ve rojo también acá. */
.db-rojo, .db-rojo * { color: var(--danger); }

/* ---- Callouts de Obsidian ---- */
.db-callout {
  --cl: var(--accent);
  border-left: 3px solid var(--cl); border-radius: 10px;
  background: color-mix(in srgb, var(--cl) 8%, transparent);
  padding: 10px 14px; margin: 1em 0;
}
.db-callout-titulo { font-weight: 700; color: var(--cl); margin: 0 0 .2em; font-size: .9rem; }
.db-callout > p:last-child { margin-bottom: 0; }
.callout-tip, .callout-success, .callout-check, .callout-done { --cl: var(--success); }
.callout-warning, .callout-caution, .callout-attention { --cl: var(--warning); }
.callout-danger, .callout-error, .callout-bug, .callout-failure { --cl: var(--danger); }
.callout-question, .callout-help, .callout-faq { --cl: var(--question); }
.callout-abstract, .callout-summary, .callout-example { --cl: var(--decision); }
/* Los cuatro del resumen de clase (2026-08-05). `quote` pasó de violeta a gris
   a propósito: es el recuadro de «esto ya lo había dicho», y tiene que leerse
   como secundario — si compite en color con «posible parcial», no sirve. */
.callout-quote, .callout-cite, .callout-note, .callout-repaso { --cl: var(--nota); }
.callout-info, .callout-clinica, .callout-clinico { --cl: var(--accent); }
.callout-todo, .callout-tarea { --cl: var(--tarea); }

/* ---- Esquemas (bloque ```esquema de un resumen) ----
   Mapa conceptual de izquierda a derecha, el mismo sentido de lectura que un
   canvas de Obsidian y que el PDF exportado. De arriba hacia abajo no entra:
   un nivel con seis hojas de texto médico en español necesita más ancho del
   que hay, y acá además el panel puede ser angosto. */
.db-esquema {
  display: flex; overflow-x: auto;
  padding: 14px 4px; margin: 1em 0;
  background: rgba(255,255,255,.02);
  border: 1px solid var(--border); border-radius: 12px;
}
.db-esq-rama { display: flex; align-items: center; gap: 14px; }
.db-esq-hijos { display: flex; flex-direction: column; gap: 8px; position: relative; }
/* La llave que agrupa a los hijos de un nodo. */
.db-esq-hijos::before {
  content: ""; position: absolute; left: -7px; top: 12px; bottom: 12px;
  border-left: 1px solid var(--border-strong);
}
.db-esq-rama > .db-esq-hijos > .db-esq-rama > .db-esq-nodo { position: relative; }
.db-esq-rama > .db-esq-hijos > .db-esq-rama > .db-esq-nodo::before {
  content: ""; position: absolute; left: -14px; top: 50%;
  width: 14px; border-top: 1px solid var(--border-strong);
}
.db-esq-nodo {
  --cl: var(--accent);
  flex-shrink: 0; max-width: 210px;
  padding: 7px 12px; border-radius: 9px; text-align: center;
  font-size: .8rem; line-height: 1.3; color: var(--cl);
  border: 1px solid color-mix(in srgb, var(--cl) 45%, transparent);
  background: color-mix(in srgb, var(--cl) 10%, transparent);
}
.db-esq-n0 { font-weight: 700; }
.db-esq-n1 { --cl: var(--decision); }
.db-esq-n2 { --cl: var(--success); }

/* ---- Bloque de código dentro de un resumen ---- */
.db-codigo {
  overflow-x: auto; margin: 1em 0; padding: 10px 12px;
  background: var(--bg-2); border: 1px solid var(--border); border-radius: 10px;
  font-size: .8rem; color: var(--text-soft);
}

/* ---- Tablas dentro de una nota / resumen ---- */
.db-tabla-scroll { overflow-x: auto; margin: 1em 0; }
.db-prose table { border-collapse: collapse; width: 100%; font-size: .88rem; }
.db-prose th, .db-prose td { border: 1px solid var(--border); padding: 6px 10px; text-align: left; }
.db-prose th { background: rgba(255,255,255,.04); color: var(--text); font-weight: 700; }

/* ---- Canvas ----
   Cámara propia (F4): `overflow:hidden` en vez de scroll nativo — el pan y el
   zoom mueven `camara.x/y/s` en JS, que se aplica como transform sobre
   `#canvasLienzo`. La grilla de puntos es PROPIA de este viewport (no la
   estática y fija de `.db-apuntes-lienzo`, detrás): se mueve con la cámara
   (`pintarTransformCanvas` en app.js actualiza `background-position`/`-size`
   en cada gesto), y con `background-color` opaco tapa la de atrás. */
.db-canvas-viewport {
  position: absolute; inset: 0; overflow: hidden;
  cursor: grab;
  background-color: var(--lienzo);
  background-image: radial-gradient(var(--lienzo-punto) 1px, transparent 1px);
  background-size: 26px 26px;
  background-position: 0 0;
}
.db-canvas-viewport.arrastrando { cursor: grabbing; }
/* Lienzo de tamaño 0×0 en el origen del viewport: los nodos que cuelgan de
   él con `position:absolute` se posicionan en coordenadas ABSOLUTAS, sin
   restar ningún bounding-box. */
.db-canvas-lienzo { position: relative; transform-origin: 0 0; }
/* Cartel de "canvas vacío" (F4.7): overlay del VIEWPORT, no del lienzo — se
   queda centrado en pantalla sin importar dónde esté la cámara. */
.db-canvas-vacio-aviso {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  padding: 10px 16px; border-radius: var(--radius-card);
  background: color-mix(in srgb, var(--surface) 78%, transparent);
  border: 1px solid var(--border);
  color: var(--text-mut); font-size: .85rem; white-space: nowrap;
  pointer-events: none;
}
.db-canvas-nodo {
  position: absolute; overflow: auto; border-radius: 10px; padding: 10px 12px;
  background: var(--surface); border: 1px solid var(--border-strong);
  font-size: 13px; line-height: 1.5;
}
.db-canvas-nodo.c1 { border-color: var(--danger);   background: color-mix(in srgb, var(--danger) 10%, var(--surface)); }
.db-canvas-nodo.c2 { border-color: var(--warning);  background: color-mix(in srgb, var(--warning) 10%, var(--surface)); }
.db-canvas-nodo.c3 { border-color: #e0de71;         background: color-mix(in srgb, #e0de71 10%, var(--surface)); }
.db-canvas-nodo.c4 { border-color: var(--success);  background: color-mix(in srgb, var(--success) 10%, var(--surface)); }
.db-canvas-nodo.c5 { border-color: var(--accent);   background: color-mix(in srgb, var(--accent) 10%, var(--surface)); }
.db-canvas-nodo.c6 { border-color: var(--decision); background: color-mix(in srgb, var(--decision) 10%, var(--surface)); }
.db-canvas-nodo .db-prose { font-size: 13px; }
.db-canvas-nodo .db-prose h1 { font-size: 1.1rem; }
.db-canvas-nodo .db-prose h2 { font-size: 1rem; }
.db-canvas-nodo .db-prose h3 { font-size: .92rem; }

/* ---- Editor de tableros (E.1, PLAN_APUNTES_100 · F5: editable siempre) ---- */
.db-canvas-nodo.editable { cursor: move; user-select: none; }
.db-canvas-nodo.editable:hover { box-shadow: 0 0 0 2px var(--accent); }
.db-canvas-nodo.editable.moviendo { box-shadow: 0 0 0 2px var(--accent); opacity: .85; z-index: 5; }
/* F5.4: selección — un anillo doble, distinto del simple del hover, para que
   se note la diferencia entre "el mouse está encima" y "está seleccionado". */
.db-canvas-nodo.editable.seleccionado {
  box-shadow: 0 0 0 2px var(--accent), 0 0 0 5px color-mix(in srgb, var(--accent) 35%, transparent);
}
.db-canvas-resize {
  position: absolute; right: -6px; bottom: -6px; width: 14px; height: 14px;
  border-radius: 4px; background: var(--accent); cursor: nwse-resize;
  border: 2px solid var(--bg); z-index: 6;
}
.db-tablero-editar-bar {
  position: absolute; top: 16px; right: 16px; z-index: 16; max-width: min(70vw, 420px);
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; border-radius: var(--radius-card);
  background: var(--card-bg); border: 1px solid var(--border);
}
/* F5.5: la barra arranca oculta hasta el primer cambio pendiente. Regla
   obligatoria (ver ui-shell.md): `.hidden` de Tailwind pierde contra el
   `display:flex` de arriba si no se combina la especificidad acá. */
.db-tablero-editar-bar.hidden { display: none; }

/* ---- E.2: borrar nodo + editar texto inline ---- */
.db-canvas-borrar {
  position: absolute; top: -8px; right: -8px; width: 20px; height: 20px; z-index: 6;
  display: flex; align-items: center; justify-content: center;
  border-radius: 999px; background: var(--danger); color: #fff;
  border: 2px solid var(--bg); font-size: 13px; line-height: 1; cursor: pointer;
  opacity: 0; transition: opacity .12s ease;
}
.db-canvas-nodo.editable:hover .db-canvas-borrar { opacity: 1; }

/* ---- E.3: conectores de flecha + flechas clickeables para borrar ---- */
.db-canvas-conector {
  position: absolute; width: 12px; height: 12px; border-radius: 999px;
  background: var(--accent); border: 2px solid var(--bg); z-index: 6;
  cursor: crosshair; opacity: 0; transition: opacity .12s ease;
}
.db-canvas-nodo.editable:hover .db-canvas-conector { opacity: 1; }
.db-canvas-conector.lado-top { top: -6px; left: 50%; transform: translateX(-50%); }
.db-canvas-conector.lado-bottom { bottom: -6px; left: 50%; transform: translateX(-50%); }
.db-canvas-conector.lado-left { left: -6px; top: 50%; transform: translateY(-50%); }
.db-canvas-conector.lado-right { right: -6px; top: 50%; transform: translateY(-50%); }
.db-canvas-arista-tmp { stroke: var(--accent); stroke-width: 2; stroke-dasharray: 5 4; }
.db-canvas-edges.editable-aristas path[data-id] { pointer-events: stroke; cursor: pointer; }
.db-canvas-edges.editable-aristas path[data-id]:hover { stroke: var(--danger) !important; stroke-width: 3; }
.db-canvas-texto-editor {
  position: absolute; inset: 0; width: 100%; height: 100%; resize: none;
  padding: 10px 12px; border-radius: 10px; border: none;
  background: var(--surface); color: var(--text); font: inherit; font-size: 13px;
  outline: 2px solid var(--accent);
}
.db-canvas-nodo img { max-width: 100%; border-radius: 6px; }
.db-canvas-grupo {
  position: absolute; border: 1px solid var(--border-strong); border-radius: 14px;
  background: rgba(255,255,255,.02);
}
.db-canvas-grupo-label {
  position: absolute; top: -24px; left: 4px; font-size: 12px; font-weight: 700;
  color: var(--text-mut); white-space: nowrap;
}
.db-canvas-archivo, .db-canvas-link {
  display: flex; flex-direction: column; gap: 4px; justify-content: center;
  text-decoration: none; color: var(--text-soft); height: 100%;
}
.db-canvas-archivo:hover, .db-canvas-link:hover { color: var(--accent-txt); }
.db-canvas-edges { position: absolute; inset: 0; overflow: visible; pointer-events: none; }
.db-canvas-vacio { color: var(--text-mut); font-size: .9rem; padding: 24px; }

/* ============================================================
   Panel de estudio (PLAN 2.0) — anillo, heatmap, barras, ritmo
   Reglas: marcas finas, punta redondeada de 4px apoyada en la base,
   ejes hairline sólidos, 2px de aire entre marcas, texto siempre con
   tokens de texto (el color lo lleva la marca, no la etiqueta).
   ============================================================ */

/* ---- Barra de progreso (sesión / metros de los tiles) ---- */
.db-progress, .db-meter {
  height: 6px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}
.db-meter { height: 5px; }
.db-progress-fill, .db-meter-fill {
  height: 100%; border-radius: 999px; background: var(--accent);
  transition: width .35s ease;
}
.db-meter-fill.meter-warning { background: var(--warning); }
.db-meter-fill.meter-danger  { background: var(--danger); }

/* Indicador del paso del guion del tutor ("Paso 3 de 8") */
.db-paso {
  display: flex; align-items: center; gap: 12px; padding: 8px 12px;
  border-radius: var(--radius); background: rgba(255,255,255,0.03);
  border: 1px solid var(--border); font-size: .75rem; color: var(--text-soft);
}
.db-paso-numero { font-weight: 700; color: var(--text); font-family: 'IBM Plex Mono', ui-monospace; }
.db-paso-barra { display: flex; gap: 2px; flex: 1; }
.db-paso-punto { height: 3px; width: 3px; border-radius: 50%; background: rgba(255,255,255,0.2); flex-shrink: 0; }
.db-paso-punto.completado { background: var(--accent); }

/* ---- Tarjeta de estadística ---- */
.db-tile {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius-card); padding: 16px;
}
.db-tile-label { font-size: .72rem; color: var(--text-mut); }
.db-tile-valor {
  font-family: 'IBM Plex Mono', ui-monospace, monospace; font-weight: 700; font-size: 1.6rem;
  color: var(--text); letter-spacing: -.02em; line-height: 1.15; margin-top: 2px;
}
.db-tile-pie { font-size: .68rem; color: var(--text-mut); margin-top: 6px; }

/* ---- Heatmap de actividad (secuencial: un solo tono, tenue → intenso) ---- */
.db-heat { display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, 11px); gap: 3px; }
.db-heat-cell { width: 11px; height: 11px; border-radius: 3px; background: rgba(255,255,255,.05); }
.db-heat-cell[data-n="1"] { background: color-mix(in srgb, var(--accent) 25%, transparent); }
.db-heat-cell[data-n="2"] { background: color-mix(in srgb, var(--accent) 45%, transparent); }
.db-heat-cell[data-n="3"] { background: color-mix(in srgb, var(--accent) 70%, transparent); }
.db-heat-cell[data-n="4"] { background: var(--accent); }
.db-heat-cell.hoy { outline: 1px solid var(--border-strong); outline-offset: 1px; }

/* ---- Columnas de carga futura ---- */
.db-cols { display: flex; align-items: flex-end; gap: 4px; height: 96px;
           border-bottom: 1px solid var(--border); padding-bottom: 0; }
.db-col-wrap { flex: 1 1 0; display: flex; flex-direction: column; justify-content: flex-end;
               align-items: center; height: 100%; max-width: 24px; margin: 0 auto; }
.db-col { width: 100%; border-radius: 4px 4px 0 0; background: var(--accent); min-height: 2px; }
.db-col.vacia { background: rgba(255,255,255,.07); }

/* ---- Barra horizontal (una serie: mismo color en todas las filas) ---- */
.db-hbar { height: 6px; border-radius: 4px; background: rgba(255,255,255,.06); overflow: hidden; }
.db-hbar > span { display: block; height: 100%; border-radius: 4px; background: var(--accent); }

/* ---- Puntos de estado (siempre acompañados de texto) ---- */
.db-estado { height: 8px; width: 8px; border-radius: 999px; flex-shrink: 0; display: inline-block; }
.db-estado.verde { background: var(--success); }
.db-estado.ambar { background: var(--warning); }
.db-estado.rojo  { background: var(--danger); }

/* ---- Tarjetas de ritmo (selección) ---- */
.db-ritmo {
  text-align: left; padding: 14px; border-radius: 14px;
  border: 1px solid var(--border); background: rgba(255,255,255,.03);
  transition: border-color .15s, background .15s;
}
.db-ritmo:hover { background: rgba(255,255,255,.06); }
.db-ritmo.ritmo-activo {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.db-ritmo.ritmo-activo .db-ritmo-nombre { color: var(--accent-txt); }
.db-ritmo-nombre { font-weight: 700; color: var(--text); font-size: .95rem; }

/* ---- Botones de calificación con su intervalo ---- */
.db-btn-cal {
  border-radius: 999px; font-weight: 600; font-size: .8rem; padding: 8px 10px; cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  background: color-mix(in srgb, var(--cal) 18%, transparent); color: var(--cal);
  transition: filter .15s, transform .1s;
}
.db-btn-cal:hover { filter: brightness(1.15); }
.db-btn-cal:active { transform: scale(.96); }
.db-cal-int { font-size: .65rem; font-weight: 500; opacity: .75; }

/* ---- Chip de atajo de teclado ---- */
.db-tecla {
  font-size: .62rem; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  padding: 2px 6px; border-radius: 6px; background: rgba(0,0,0,.18); opacity: .7;
}

/* Repaso — opciones de quiz */
.quiz-opcion {
  display: block;
  width: 100%;
  text-align: left;
  padding: .7rem .9rem;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--surface);
  color: var(--text-soft);
  font-size: .95rem;
  transition: background .15s, border-color .15s;
}
.quiz-opcion:hover:not(:disabled) { background: rgba(255,255,255,.05); }
.quiz-opcion:disabled { cursor: default; }
.quiz-opcion.correcta {
  border-color: var(--success);
  background: color-mix(in srgb, var(--success) 15%, transparent);
  color: var(--success);
}
.quiz-opcion.incorrecta {
  border-color: var(--danger);
  background: color-mix(in srgb, var(--danger) 15%, transparent);
  color: var(--danger);
}

/* ============================================================
   Apuntes: lienzo a pantalla completa + dock inferior (F4, PLAN_REDISENO)
   El explorador dejó de ser un sidebar fijo: el lienzo (nota o canvas) ocupa
   toda la vista y un dock flotante abajo despliega un cajón con el árbol.
   Todo `absolute` dentro de `.db-apuntes` (no `fixed`): así respeta el
   sidebar de 236px del shell y desaparece solo al cambiar de vista.
   ============================================================ */
.db-apuntes { height: 100%; position: relative; overflow: hidden; }

/* Lienzo: fondo obsidian + grilla de puntos + dos glows ambientales. Detrás
   de todo (canvas o nota), siempre visible. */
.db-apuntes-lienzo {
  position: absolute; inset: 0; overflow: hidden;
  background-color: var(--lienzo);
  background-image: radial-gradient(var(--lienzo-punto) 1px, transparent 1px);
  background-size: 26px 26px;
}
.db-apuntes-lienzo::before, .db-apuntes-lienzo::after {
  content: ""; position: absolute; pointer-events: none; border-radius: 999px; filter: blur(90px);
}
.db-apuntes-lienzo::before { top: -12%; left: -10%; width: 42%; height: 42%; background: color-mix(in srgb, var(--accent) 22%, transparent); }
.db-apuntes-lienzo::after { top: 8%; right: -12%; width: 46%; height: 46%; background: color-mix(in srgb, var(--decision) 20%, transparent); }

/* ---- F1.1 (2026-08-23): topbar de Apuntes fusionado con el del shell ----
   La franja propia (`.db-apuntes-topbar`, `position:absolute` encima del
   lienzo) se retiró entera: `#apuntesCabecera` y `#apuntesZoom` ahora viven
   como hermanos dentro de `.db-topbar`/`.db-topbar-right` del shell
   (`index.html`), y `#apuntesCuerpo` ya no necesita dejarles alto libre —
   vuelve a ocupar el 100% de su celda del grid, igual que los paneles de
   Documentos/Estudio. Las clases de abajo se conservan porque siguen
   describiendo cómo se ven esos dos bloques dentro de una barra flex. */

/* Migaja (ruta + nombre + tipo), en una línea dentro de la barra del shell.
   Cede el ancho (mismo criterio que la migaja de resultado, ui-shell.md):
   `min-width:0` es lo que le permite truncar en vez de desbordar cuando el
   zoom/acciones de la derecha necesitan su lugar. */
.db-apuntes-crumb {
  flex: 1 1 auto; min-width: 0; display: flex; align-items: center; gap: 8px;
  overflow: hidden;
}

/* Zoom/acciones del documento (Editar, Renombrar, Borrar, Guardar…), fijas
   a la derecha — ya no flotan, son un hijo más del flex de la barra. */
.db-apuntes-zoom {
  flex-shrink: 0; display: flex; align-items: center; gap: 4px;
}
.db-apuntes-zoom-btn {
  min-width: 30px; height: 30px; padding: 0 8px; border-radius: 999px;
  color: var(--text-soft); font-size: .78rem; font-weight: 600; text-align: center;
  transition: background .15s, color .15s;
}
.db-apuntes-zoom-btn:hover { background: rgba(255,255,255,.08); color: var(--text); }
.db-apuntes-zoom-pct { font-variant-numeric: tabular-nums; min-width: 42px; }

/* Dock flotante, abajo centrado. */
.db-apuntes-dock {
  position: absolute; bottom: 16px; left: 50%; transform: translateX(-50%); z-index: 20;
  display: flex; align-items: center; gap: 8px; max-width: calc(100% - 32px);
  padding: 6px; border-radius: 999px;
  background: rgba(16,17,20,.8);
  border: 1px solid var(--border); box-shadow: 0 18px 40px -12px rgba(0,0,0,.7);
}
.db-apuntes-dock-buscar {
  display: flex; align-items: center; gap: 8px; padding: 8px 14px; border-radius: 999px;
  background: rgba(255,255,255,.05); border: none; color: var(--text-mut);
  font-size: .82rem; cursor: pointer; transition: background .15s, color .15s;
}
.db-apuntes-dock-buscar:hover { background: rgba(255,255,255,.09); color: var(--text-soft); }
.db-apuntes-dock-buscar svg { height: .95rem; width: .95rem; flex-shrink: 0; }

/* Dock angosto (D.1 sumó el switch Cuaderno/Tableros + Crear, y ya venía
   apretado): en pantallas chicas se esconden las etiquetas de texto y quedan
   solo los íconos — mismo criterio que .db-player-extra en ui-shell.md. */
@media (max-width: 560px) {
  .db-apuntes-dock-buscar span, .db-apuntes-dock-explorar span { display: none; }
  #apuntesRamaSwitch button { padding-left: 8px; padding-right: 8px; font-size: .7rem; }
}
.db-apuntes-dock-activa {
  display: none; align-items: center; padding: 6px 12px; border-radius: 999px;
  font-size: .74rem; font-weight: 600; color: var(--accent-txt);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  max-width: 220px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
@media (min-width: 640px) {
  .db-apuntes-dock-activa:not(.hidden) { display: flex; }
}
.db-apuntes-dock-explorar {
  display: inline-flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: 999px;
  background: var(--accent); color: #06222a; font-size: .78rem; font-weight: 700;
  border: none; cursor: pointer; transition: filter .15s, transform .15s;
  box-shadow: 0 10px 26px -8px color-mix(in srgb, var(--accent) 45%, transparent);
}
.db-apuntes-dock-explorar:hover { filter: brightness(1.08); transform: scale(1.02); }

/* Backdrop + cajón: ocultos por defecto, `.vault-abierto` en `#vistaApuntes`
   los despliega. El cajón cubre el dock al subir (mismo z-index más alto). */
.db-apuntes-backdrop {
  position: absolute; inset: 0; z-index: 25;
  background: rgba(0,0,0,.55); opacity: 0; pointer-events: none;
  transition: opacity .28s ease;
}
.vault-abierto .db-apuntes-backdrop { opacity: 1; pointer-events: auto; }

.db-apuntes-drawer {
  position: absolute; bottom: 0; left: 0; right: 0; z-index: 30;
  max-height: 56vh; display: flex; flex-direction: column;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--border); border-bottom: none;
  box-shadow: 0 -18px 50px -12px rgba(0,0,0,.7);
  transform: translateY(110%);
  transition: transform .28s cubic-bezier(.22,1,.36,1);
}
.vault-abierto .db-apuntes-drawer { transform: translateY(0); }

.db-apuntes-drawer-head {
  flex-shrink: 0; display: flex; align-items: center; gap: 10px;
  padding: 16px 20px; border-bottom: 1px solid var(--border);
}
.db-apuntes-drawer-body { flex: 1; min-height: 0; overflow-y: auto; padding: 16px 20px 20px; }
@media (min-width: 640px) {
  .db-apuntes-drawer-body { columns: 2; column-gap: 24px; }
}

/* Lectura de una nota .md: columna centrada, con aire arriba para la migaja
   y abajo para el dock. */
.db-apuntes-lectura { position: absolute; inset: 0; overflow-y: auto; padding: 96px 24px 120px; }
.db-apuntes-lectura > * { max-width: 46rem; margin-left: auto; margin-right: auto; }

/* Elector Cuaderno/Pizarra (D.1, PLAN_APUNTES_100): dos .db-choice-card lado a
   lado. Más ancho que el 46rem por defecto de .db-apuntes-lectura — dos
   tarjetas grandes necesitan más aire que una columna de lectura. */
.db-apuntes-elector { max-width: 52rem !important; display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-top: 10vh; }
@media (max-width: 640px) { .db-apuntes-elector { grid-template-columns: 1fr; } }

/* ---- F-nav: recorrido de 3 pasos de Apuntes (Tipo → Carpeta → Documento),
   montado con el motor de carrusel genérico (crearAsistentePasos) dentro de
   #apuntesCuerpo. Mismo scroll+padding que .db-apuntes-lectura, pero con más
   ancho para la grilla de tarjetas — mismo criterio que .db-apuntes-elector
   arriba, declarado después para ganar el empate de especificidad contra
   `.db-apuntes-lectura > *`. */
.db-apuntes-wizard > * { max-width: 60rem; }
.db-apuntes-wizard-grid { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }

/* Los selectores del modal Crear se ocultan cuando el "+" de un paso del
   recorrido ya fija tipo y/o carpeta (abrirModalCrear con esos argumentos).
   `.db-tabs` es display:inline-flex — mismo empate contra `.hidden` de
   Tailwind que documenta `.db-topbar-acciones.hidden` más arriba. */
#tipoCrearApunte.hidden { display: none; }

/* C.1: el dock de Apuntes (display:flex) contra el mismo empate. */
.db-apuntes-dock.hidden { display: none; }
/* .db-wizard-progress también es display:flex — mismo empate, acotado a
   #progresoApuntes porque #progresoFlashcards/#progresoTutorCarrusel nunca
   se ocultan con esta clase. */
#progresoApuntes.hidden { display: none; }

/* ---- G0 (PLAN_EDITOR_DIRECTO): el editor de una Hoja usa toda la página ----
   `activarEdicionHoja` ya NO pinta dentro de `.db-apuntes-lectura` (esa
   clase sigue siendo de LECTURA — la usa `renderNotaMd`, con sus 46rem
   correctos ahí). Contenedor propio, hermano de lo que pintaría
   `#canvasViewport`, mismo contrato absolute+flex que el resto de
   #apuntesCuerpo (que ya deja debajo de la barra de G0.2). */
.db-hoja-editor { position: absolute; inset: 0; display: flex; flex-direction: column; }
.db-hoja-editor > .db-error { flex-shrink: 0; margin: 12px 16px 0; }
.db-hoja-editor #errorEditarHoja { flex-shrink: 0; margin: 0 16px 10px; }
.db-hoja-pagina-scroll {
  flex: 1 1 auto; min-height: 0; overflow: hidden;
  display: flex; justify-content: center;
}
/* Por defecto el bloque (toolbar + contenido) ocupa el ancho completo, A
   SANGRE (§1 del plan, fila "ancho del editor"): es la prioridad que fija
   el plan cuando toolbar-ancho y texto-angosto no pueden convivir en Toast
   UI (no se puede sacar el toolbar de su raíz DOM — ver
   .claude/rules/editor-hojas.md). ">70% del área de escritura" es un umbral
   verificado en navegador y hasta el padding cuenta: con 16px de aire a
   cada lado el área medía 66,3% en vez de >70%. `.centrado` la angosta
   para quien prefiera una línea de lectura corta (botón "Centrar texto" en
   la barra, localStorage) — ahí sí vale el aire alrededor, por eso el
   padding vuelve con `:has()`. */
.db-hoja-pagina-scroll:has(.centrado) { padding: 16px; }
.db-hoja-pagina { width: 100%; height: 100%; }
.db-hoja-pagina.centrado { max-width: 50rem; margin: 0 auto; }

/* ==========================================================================
   Toast UI Editor (F3, PLAN_VAULT_EDITOR): excepción CONSCIENTE a la regla
   "usar las clases db-*" del §9 de CLAUDE.md — acá se están skineando las
   clases fijas de un widget de terceros vendorizado (§0 de este plan), no
   inventando UI propia. Bloque único, no repartido por el archivo.

   `toastui-editor.min.css` se carga DESPUÉS de este archivo (index.html),
   así que sin `!important` sus reglas ganan por orden de cascada a igual
   especificidad — de ahí que casi todo acá lo lleve. El tema (claro/oscuro)
   no se resuelve acá: ya viene resuelto en los tokens `--surface`/`--text`/
   etc., que cambian solos con `data-tema="oscuro"`; la clase
   `.toastui-editor-dark` (agregada desde JS) solo trae la hoja de estilos
   vendorizada que evita blanco duro en los popups internos que Toast UI no
   expone por variable CSS.
   ========================================================================== */
.toastui-editor-defaultUI {
  border: 1px solid var(--border) !important;
  border-radius: var(--radius) !important;
  background: var(--surface) !important;
  font-family: 'IBM Plex Sans', system-ui, sans-serif !important;
}
.toastui-editor-toolbar,
.toastui-editor-defaultUI-toolbar {
  background: var(--bg-2) !important;
  border-bottom: 1px solid var(--border) !important;
}
.toastui-editor-toolbar-icons {
  background-color: transparent !important;
  color: var(--text-soft) !important;
}
.toastui-editor-toolbar-icons:hover { background-color: var(--surface-2) !important; }
.toastui-editor-toolbar-divider { background-color: var(--border) !important; }
.toastui-editor-md-container,
.toastui-editor-ww-container {
  background: var(--surface) !important;
}
.toastui-editor-contents,
.toastui-editor-contents p,
.toastui-editor-contents li,
.ProseMirror {
  color: var(--text-soft) !important;
  font-family: 'IBM Plex Sans', system-ui, sans-serif !important;
}
.toastui-editor-contents h1, .toastui-editor-contents h2, .toastui-editor-contents h3,
.toastui-editor-contents h4, .toastui-editor-contents h5, .toastui-editor-contents h6 {
  color: var(--text) !important;
}
.toastui-editor-md-preview { background: var(--surface) !important; }
.toastui-editor-contents code,
.toastui-editor-md-code,
.toastui-editor-contents pre {
  font-family: 'IBM Plex Mono', ui-monospace, monospace !important;
  background: var(--bg-2) !important;
  color: var(--text-soft) !important;
}
.toastui-editor-md-splitter { background: var(--border) !important; }
.toastui-editor-mode-switch { background: var(--bg-2) !important; border-top: 1px solid var(--border) !important; }
.toastui-editor-mode-switch .tab-item { color: var(--text-mut) !important; }
.toastui-editor-mode-switch .tab-item.active { color: var(--text) !important; background: var(--surface) !important; }
.toastui-editor-popup {
  background: var(--surface) !important;
  border: 1px solid var(--border) !important;
  color: var(--text-soft) !important;
}
.toastui-editor-popup input {
  background: var(--bg-2) !important;
  border: 1px solid var(--border) !important;
  color: var(--text) !important;
}
.toastui-editor-table, .toastui-editor-table-cell {
  border-color: var(--border) !important;
}
.toastui-editor-ok-button { background-color: var(--accent) !important; }
/* db-wikilink ya existe (visor de lectura, `renderNotaVault`) — el widget
   de F3.3 reusa exactamente la misma clase, sin duplicar estilo acá. */
