* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* `hidden` tiene que ganar SIEMPRE. El `display:none` que el navegador da a [hidden] lo pisa
   cualquier `display:flex/grid` de una clase (más específico), y eso ya nos había mordido
   cinco veces —cada una parcheada aparte, cada una descubierta en producción: la última, un
   botón "Eliminar" visible al subrayar algo que aún no existía—. Una sola regla, arriba, en
   vez de tener que acordarse en cada componente nuevo. */
[hidden] { display: none !important; }

html, body {
  height: 100%;
  overflow: hidden;
  /* Evita que el swipe horizontal de dos dedos (trackpad) dispare atrás/adelante del
     navegador. Al hacer pinch-zoom en el PDF, el componente horizontal del gesto se
     interpretaba como swipe-atrás y te sacaba del libro a la biblioteca. El botón atrás y
     el logo→biblioteca siguen funcionando; solo se desactiva la navegación por GESTO. */
  overscroll-behavior-x: none;
  font-family: var(--font-ui);
  font-size: var(--font-size-base);
  background: var(--surface-0);
  color: var(--text);
  transition: background var(--transition-normal), color var(--transition-normal);
}

/* ============ SIDEBAR ============ */
.sidebar {
  position: fixed;
  top: 0;
  left: 0;
  width: var(--sidebar-width);
  height: 100%;
  background: var(--surface-1);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  z-index: 100;
  transform: translateX(-100%);
  /* Los paneles que ENTRAN en pantalla usan la curva de salida suave (--transition-panel):
     arrancan rápido y frenan largo. Con la curva simétrica anterior el drawer parecía
     arrastrarse. El lector (.reader-main) usa la MISMA curva y duración, si no el empuje
     del contenido y el panel van desacompasados. */
  transition: transform var(--transition-panel);
}

.sidebar.open {
  transform: translateX(0);
}

.sidebar-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Misma altura que la cabecera del lector y la del agente (borde inferior alineado). */
  height: var(--header-height);
  flex-shrink: 0;
  box-sizing: border-box;
  padding: 0 16px;
  border-bottom: 1px solid var(--border);
}

.sidebar-header h2 {
  font-size: 16px;
  font-weight: 600;
}

.sidebar-header-actions {
  display: flex;
  align-items: center;
  gap: 2px;
}

/* El engranaje es un modo del panel, no una acción suelta: cuando los ajustes de lectura
   están a la vista se tiñe de acento, igual que la pestaña activa. */
#reading-settings.active, #reading-settings.active:hover { color: var(--accent); }

/* Tabs (v2: más limpias) */
.sidebar-tabs {
  display: flex;
  /* Sin wrap: con wrap las 4 pestañas se partían en dos filas irregulares y el punto de
     corte cambiaba con el idioma (la maquetación dependía del locale). */
  flex-wrap: nowrap;
  /* Red de seguridad para el sidebar estrecho (drawer móvil: min(86%, 360px), que en un
     teléfono de 320px son 275). Cuando las 4 etiquetas no caben, la barra se desliza en
     vez de dejar que se aplasten unas contra otras. Sin barra de scroll visible: sobre
     4 pestañas cantaría más que el propio desbordamiento. */
  overflow-x: auto;
  scrollbar-width: none;
  border-bottom: 1px solid var(--border-soft);
}

.sidebar-tabs::-webkit-scrollbar { display: none; }

.tab-btn {
  /* Crece para repartirse el sobrante, pero NUNCA encoge por debajo de su texto
     (shrink 0): con shrink las etiquetas se metían unas dentro de otras al estrechar
     —"ContenidoBuscar" pegados— en vez de desbordar y dejar deslizar la barra. */
  flex: 1 0 auto;
  white-space: nowrap;
  padding: 12px 6px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-soft);
  font-size: 12.5px;
  font-weight: 500;
  cursor: pointer;
  transition: color var(--transition-fast), border-color var(--transition-fast);
}

.tab-btn:hover {
  color: var(--text);
}

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

.tab-content {
  display: none;
  flex: 1;
  overflow-y: auto;
  padding: 12px;
}

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

.sidebar-footer {
  flex: none;
  border-top: 1px solid var(--border);
  padding: 8px;
}
.sidebar-footer-btn {
  display: flex; align-items: center; gap: 9px; width: 100%;
  padding: 9px 12px; border: none; border-radius: var(--r-sm); background: none;
  color: var(--text-soft); font-size: 14px; text-align: left; cursor: pointer;
  transition: background var(--transition-fast), color var(--transition-fast);
}
.sidebar-footer-btn:hover { background: var(--surface-3); color: var(--text); }

/* ============ BUTTONS ============ */
.icon-btn {
  background: none;
  border: none;
  color: var(--text-soft);
  font-size: 18px;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: var(--r-sm);
  transition: background var(--transition-fast), color var(--transition-fast);
}

.icon-btn:hover {
  background: var(--surface-3);
  color: var(--text);
}

.icon-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* Botón-marca (logo → volver a la biblioteca): teñido de acento como identidad,
   igual que el logo coloreado de Play Books entre iconos neutros. Mantiene el
   emerald también en hover (solo cambia el fondo). */
.icon-btn.brand-btn, .icon-btn.brand-btn:hover { color: var(--accent); }
/* El botón de marca lleva el imagotipo completo (tile + página + cinta), no un
   glyph de línea: no hereda currentColor, sus colores vienen de --logo-*. */
.icon-btn.brand-btn .brand-mark { display: block; }

.primary-btn {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  padding: 12px 24px;
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
  transition: background var(--transition-fast);
}

.primary-btn:hover {
  background: var(--accent-hover);
}

/* ============ FILTROS DE PANTALLA (brillo / luz nocturna, tipo Play Books) ============ */
/* Overlays a pantalla completa que NO capturan eventos. Brillo = capa negra que atenúa;
   luz nocturna = capa ámbar en `multiply` que entibia y recorta la luz azul. La web no
   puede tocar el brillo/temperatura reales del dispositivo, así que se emula en pantalla. */
.screen-filter {
  position: fixed;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  z-index: 9000;
  transition: opacity 0.15s var(--ease);
}
#screen-dim { background: #000; z-index: 9001; }
#night-warm { background: #ff8a2b; mix-blend-mode: multiply; }

/* Controles deslizantes con icono + valor (brillo, luz nocturna). */
.slider-control { display: flex; align-items: center; gap: 10px; }
.slider-control input[type="range"] { flex: 1; min-width: 0; }
.slider-ico { color: var(--text-faint); flex: 0 0 auto; display: inline-flex; }
.slider-value { min-width: 42px; text-align: right; font-size: 14px; color: var(--text-soft); }

/* ============ EMPTY STATES ============ */
.empty-state {
  color: var(--text-faint);
  text-align: center;
  padding: 24px;
  font-size: 14px;
}

/* ============ TOC (v3: jerarquía por grupos, estilo Notion) ============
   Antes era una lista plana donde la única señal de jerarquía era la sangría: parecía un
   listado de ficheros. Ahora hay tres niveles de lectura — cabecera de grupo (entrada con
   subentradas), capítulo y subcapítulo — y AIRE entre grupos, que es lo que deja escanear
   el índice de un vistazo. La estructura del DOM no cambia (todo siguen siendo <a>): el
   marcado de la sección actual, la atenuación por relevancia y los tests siguen igual. */
.toc-list a {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 9px 12px;
  color: var(--text);
  text-decoration: none;
  border-radius: var(--r-sm);
  font-size: 14px;
  line-height: 1.45;
  transition: background var(--transition-fast), color var(--transition-fast);
}

.toc-list a:hover {
  background: var(--surface-3);
}

/* Entrada que ABRE grupo (tiene subentradas): hace de título de sección. */
.toc-list a.has-sub {
  font-weight: 600;
  letter-spacing: -0.01em;
}

/* Aire entre grupos: el primer capítulo de nivel 1 después de un bloque de subentradas
   abre grupo nuevo. Es la separación que hace legible un índice largo. */
.toc-list a.subitem + a:not(.subitem) { margin-top: var(--s-4); }

.toc-list a.subitem {
  padding-left: 22px;
  font-size: 13px;
  color: var(--text-soft);
}

/* Punto guía en vez de sangría a secas: marca el nivel sin dibujar una línea vertical
   (las barras laterales están proscritas, ver DESIGN.md §1.6). */
.toc-list a.subitem .toc-label::before {
  content: '';
  display: inline-block;
  width: 4px; height: 4px;
  margin-right: 9px;
  vertical-align: middle;
  border-radius: 50%;
  background: var(--text-faint);
  opacity: 0.65;
}
.toc-list a.subitem.current .toc-label::before { background: currentColor; opacity: 1; }

/* Entrada escalonada del índice al abrir el panel (F4). Solo las primeras entradas: más
   allá el retardo acumulado se percibe como lentitud, no como cuidado. El resto aparece
   ya colocado, que es lo correcto —el ojo está arriba—. */
.sidebar.open .toc-list a { animation: toc-in 0.22s var(--ease-out-soft) both; }
@keyframes toc-in { from { opacity: 0; transform: translateX(-6px); } to { opacity: 1; transform: none; } }
.sidebar.open .toc-list a:nth-child(1)  { animation-delay: 0ms; }
.sidebar.open .toc-list a:nth-child(2)  { animation-delay: 14ms; }
.sidebar.open .toc-list a:nth-child(3)  { animation-delay: 28ms; }
.sidebar.open .toc-list a:nth-child(4)  { animation-delay: 42ms; }
.sidebar.open .toc-list a:nth-child(5)  { animation-delay: 56ms; }
.sidebar.open .toc-list a:nth-child(6)  { animation-delay: 70ms; }
.sidebar.open .toc-list a:nth-child(7)  { animation-delay: 84ms; }
.sidebar.open .toc-list a:nth-child(8)  { animation-delay: 98ms; }
.sidebar.open .toc-list a:nth-child(n+9) { animation-delay: 112ms; }

/* Sección que se está leyendo: fondo suave y texto de acento, sin barra lateral. */
.toc-list a.current {
  background: var(--accent-soft);
  color: var(--accent-hover);
  font-weight: 600;
}
.toc-list a.current .toc-page { color: var(--accent-hover); }

/* Etiqueta flexible + número de página anclado a la derecha, como un índice impreso.
   El número no se encoge ni se parte aunque el título sea larguísimo. */
.toc-label { flex: 1; min-width: 0; }
.toc-page {
  flex: none;
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  color: var(--text-faint);
}

/* ============ BÚSQUEDA (v2) ============ */
.search-box { padding: 12px; }
.search-input {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-2);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  transition: border-color var(--transition-fast);
}
.search-input:focus {
  outline: none;
  border-color: var(--accent);
}
.search-count { color: var(--text-faint); font-size: 12px; padding: 8px 12px; }
.search-results { display: flex; flex-direction: column; }
.search-hit {
  display: block;
  width: 100%;
  text-align: left;
  padding: 10px 12px;
  border: none;
  border-bottom: 1px solid var(--border-soft);
  background: none;
  color: var(--text);
  cursor: pointer;
  font: inherit;
  transition: background var(--transition-fast);
}
.search-hit:hover { background: var(--surface-3); }
.search-hit-ctx { display: block; font-size: 13px; line-height: 1.5; }
.search-hit-ctx mark { background: var(--accent); color: var(--on-accent); border-radius: 3px; padding: 1px 3px; }
.search-hit-meta { display: block; margin-top: 4px; font-size: 11px; color: var(--text-faint); }

/* ============ BOOKMARKS (v2) ============ */
.bookmark-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background var(--transition-fast);
}

.bookmark-item:hover {
  background: var(--surface-3);
}

.bookmark-item .bookmark-info {
  flex: 1;
  min-width: 0;
}

.bookmark-item .bookmark-title {
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bookmark-item .bookmark-chapter {
  font-size: 11px;
  color: var(--text-faint);
}

.bookmark-item .bookmark-page {
  font-size: 11px;
  color: var(--text-soft);
  margin-top: 2px;
}

.bookmark-item .bookmark-delete {
  background: none;
  border: none;
  color: var(--text-faint);
  cursor: pointer;
  padding: 4px;
  font-size: 14px;
}

.bookmark-item .bookmark-delete:hover {
  color: #e53935;
}

/* ============ HIGHLIGHTS LIST (v2) ============ */
.highlights-header {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 12px;
}

.export-btn {
  background: var(--accent);
  color: var(--on-accent);
  border: none;
  padding: 8px 14px;
  border-radius: var(--r-sm);
  font-size: 12px;
  font-weight: 500;
  cursor: pointer;
  transition: background var(--transition-fast);
}

.export-btn:hover:not(:disabled) {
  background: var(--accent-hover);
}

.export-btn:disabled {
  opacity: 0.4;
  cursor: default;
}

/* El color del subrayado se lleva en el propio fondo de la ficha (--hl-color lo pone
   renderHighlights), como una marca de rotulador: dice de qué color es sin recurrir a
   una barra lateral. */
.highlight-item {
  padding: 10px 12px;
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--hl-color, var(--surface-2)) 16%, transparent);
  margin-bottom: 10px;
  cursor: pointer;
  transition: background var(--transition-fast);
}

.highlight-item:hover {
  background: color-mix(in srgb, var(--hl-color, var(--surface-2)) 30%, transparent);
}

.highlight-item .highlight-text {
  font-size: 13px;
  font-style: italic;
  line-height: 1.5;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.highlight-item .highlight-meta {
  font-size: 11px;
  color: var(--text-faint);
  margin-top: 6px;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Capítulo y fecha a la izquierda, acciones a la derecha. El empuje lo da el primer BOTÓN,
   no el span: así la fila no se descoloca cuando un subrayado no trae fecha (los importados
   de un backup antiguo). El capítulo cede espacio antes que la fecha —un título largo se
   recorta, pero el "cuándo" se lee entero—. */
.highlight-item .highlight-meta > span:first-child {
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.highlight-item .highlight-when { flex: none; }
/* Separador solo cuando hay capítulo delante (si no, el punto quedaría huérfano). */
.highlight-item .highlight-meta > span + .highlight-when::before { content: '·'; margin-right: 6px; }
.highlight-item .highlight-share { margin-left: auto; }

.highlight-item .highlight-share,
.highlight-item .highlight-delete {
  background: none;
  border: none;
  color: var(--text-faint);
  cursor: pointer;
  font-size: 12px;
  display: inline-flex;
  align-items: center;
}

.highlight-item .highlight-share:hover { color: var(--accent); }
.highlight-item .highlight-delete:hover {
  color: #e53935;
}

/* ============ SETTINGS (v2) ============ */
.settings-group {
  margin-bottom: 24px;
}

/* Grupos ocultos por formato (data-format + updateFormatScopedUI). Explícito para que
   añadir un `display` a .settings-group no resucite el grupo oculto. */

.settings-group label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--text-soft);
  margin-bottom: 10px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.theme-selector {
  display: flex;
  gap: 10px;
}

/* Control segmentado Páginas/Scroll (modo de lectura). */
.reading-mode-selector, .pdf-fit-selector { display: flex; gap: 10px; }
.reading-mode-btn, .pdf-fit-btn {
  flex: 1;
  padding: 10px 12px;
  border: 2px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-0);
  color: var(--text);
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: border-color var(--transition-fast), background var(--transition-fast), color var(--transition-fast);
}
.reading-mode-btn:hover, .pdf-fit-btn:hover { border-color: var(--accent); }
.reading-mode-btn.active,
.pdf-fit-btn.active { border-color: var(--accent); background: var(--accent); color: var(--on-accent); }

.theme-btn {
  width: 44px;
  height: 44px;
  border: 2px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-0);
  font-size: 20px;
  cursor: pointer;
  transition: border-color var(--transition-fast), transform var(--transition-fast);
}

.theme-btn:hover {
  transform: scale(1.05);
}

.theme-btn.active {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}

.font-size-control {
  display: flex;
  align-items: center;
  gap: 12px;
}

.font-size-control span {
  min-width: 44px;
  text-align: center;
  font-size: 14px;
  font-weight: 500;
}

select {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-0);
  color: var(--text);
  font-size: 14px;
  transition: border-color var(--transition-fast);
}
select:focus {
  outline: none;
  border-color: var(--accent);
}

.column-width-control,
.line-height-control {
  display: flex;
  align-items: center;
  gap: 12px;
}

input[type="range"] {
  flex: 1;
  accent-color: var(--accent);
}

.column-width-control span,
.line-height-control span {
  min-width: 48px;
  text-align: center;
  font-size: 13px;
  color: var(--text-soft);
}

/* ============ READER MAIN ============ */
.reader-main {
  display: flex;
  flex-direction: column;
  height: 100%;
  margin-left: 0;
  /* `margin` y no `margin-left`: el panel del agente empuja por la DERECHA y sin esto
     ese lado saltaba de golpe mientras el panel se deslizaba. */
  transition: margin var(--transition-panel);
}

/* ============ READER HEADER ============ */
.reader-header {
  height: var(--header-height);
  display: flex;
  align-items: center;
  padding: 0 12px;
  gap: 12px;
  border-bottom: 1px solid var(--border);
  background: var(--surface-1);
  flex-shrink: 0;
}

/* Título del libro: centrado en la cabecera (estilo Apple Books/Kindle), con
   presencia tipográfica y truncado elegante. Al ir en position:absolute no
   "empuja" el layout, así que las dos islas de iconos quedan balanceadas a los
   lados y el hueco central se llena con el título en vez de aire. */
.reader-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  /* Ancho limitado a lo que queda libre entre las dos islas de iconos, para que el
     título centrado NUNCA se solape con ellas (crítico en móvil). */
  max-width: min(calc(100% - 260px), 460px);
  text-align: center;
  font-size: 15px;
  font-weight: 600;
  color: var(--text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;   /* no interfiere con los clics de los iconos */
}

.header-actions {
  display: flex;
  gap: 4px;
  margin-left: auto;      /* empuja las acciones a la derecha (el título ya no ocupa flujo) */
}

/* ============ READER VIEWPORT ============ */
.reader-viewport {
  flex: 1;
  overflow: hidden;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-0);
}
/* Con un libro abierto, el fondo del viewport iguala al de la página (--page-bg, fijado
   por epub-reader según el tema). Así, al arrastrar para pasar página, el hueco que se
   revela no muestra una franja de otro color. */
body.reading .reader-viewport { background: var(--page-bg, var(--surface-0)); }

/* Salvo cuando la página se dibuja como HOJA: entonces el viewport es el "escritorio"
   sobre el que reposa (el mismo color, un punto más oscuro). La condición la decide
   `sizeContainer()` en js/epub-reader.js poniendo `.has-desk` cuando hay margen lateral
   de sobra; con poco margen el gris no se lee como escritorio sino como una franja rara
   pegada al borde. Ver --desk en themes.css. */
body.reading .reader-viewport:has(> .epub-container.has-desk) { background: var(--desk); }
/* Modo scroll: el contenido crece en vertical y se desplaza; alineamos arriba y dejamos
   que epub.js gestione el scroll interno (no centramos ni ocultamos el desborde). */
body.scroll-mode .reader-viewport { align-items: stretch; justify-content: flex-start; }

/* ============ READER FOOTER (v2) ============ */
.reader-footer {
  height: var(--footer-height);
  display: flex;
  align-items: center;
  padding: 0 20px;
  gap: 20px;
  border-top: 1px solid var(--border-soft);
  background: var(--surface-1);
  flex-shrink: 0;
}

.nav-btn {
  background: none;
  border: none;
  color: var(--text);
  font-size: 18px;
  cursor: pointer;
  padding: 8px 12px;
  border-radius: var(--r-sm);
  transition: background var(--transition-fast);
}

.nav-btn:hover {
  background: var(--surface-3);
}

/* Pie del lector: info (página · % · tiempo restante) sobre una barra clicable. */
.progress-wrap {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.progress-info {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-4);
  font-size: 12px;
  line-height: 1;
  color: var(--text-faint);
}
.progress-info > span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* Capítulo actual: la información primaria del pie (lo que un lector quiere saber es
   dónde está, no qué número de localización lleva). Lo rellena markCurrentToc() en
   app.js reusando la sección que ya calcula para marcar el índice. Sin índice queda
   vacío y el bloque de cifras se va solo a la izquierda. */
.progress-chapter {
  flex: 1;
  min-width: 0;
  color: var(--text-soft);
  font-weight: 500;
}

/* Cifras: página · % · tiempo restante, en un solo bloque atenuado y con cifras de
   ancho fijo para que no bailen al avanzar. El separador es CSS (no texto) para no
   meter cadenas nuevas al i18n, y salta las piezas vacías. */
.progress-meta {
  flex: none;
  display: inline-flex;
  align-items: baseline;
  font-variant-numeric: tabular-nums;
}
.progress-meta > span:not(:empty) + span:not(:empty)::before {
  content: '·';
  margin: 0 6px;
  color: var(--text-faint);
  opacity: 0.7;
}

.progress-container {
  height: 3px;
  background: var(--surface-3);
  border-radius: var(--r-pill);
  position: relative;
  cursor: pointer;
}
/* Zona de pulsación más alta que los 6px visibles: es más fácil acertar el salto
   (el clic sobre el pseudo-elemento se atribuye al contenedor). */
.progress-container::before {
  content: '';
  position: absolute;
  left: 0; right: 0; top: -8px; bottom: -8px;
}

.progress-bar {
  height: 100%;
  background: var(--accent);
  border-radius: var(--r-pill);
  width: 0%;
  transition: width var(--transition-fast);
  pointer-events: none;
}

/* Arrastre estilo Play Books. `touch-action: none` es lo que impide que el gesto
   horizontal lo robe el scroll de la página en móvil. */
.progress-container { touch-action: none; }
.progress-container:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* Pulgar: colgado del extremo de la barra, así sigue su anchura sin que JS tenga que
   posicionarlo. Solo aparece al enfocar o arrastrar, para no ensuciar la barra fina. */
.progress-bar { position: relative; }
.progress-bar::after {
  content: '';
  position: absolute;
  top: 50%; right: -6px;
  width: 12px; height: 12px;
  margin-top: -6px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
  transform: scale(0.6);
  transition: opacity var(--transition-fast), transform var(--transition-fast);
}
.progress-container.dragging .progress-bar::after,
.progress-container:focus-visible .progress-bar::after { opacity: 1; transform: scale(1); }
/* Durante el arrastre la anchura sigue al dedo: la transición la haría ir a rastras. */
.progress-container.dragging .progress-bar { transition: none; }

.progress-bubble {
  position: absolute;
  bottom: 18px;
  transform: translateX(-50%);
  display: none;
  flex-direction: column;
  gap: 2px;
  max-width: min(240px, 70vw);
  padding: 6px 10px;
  border-radius: var(--r-sm);
  background: var(--surface-1);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-md, 0 4px 16px rgba(0, 0, 0, 0.18));
  font-size: 12px;
  line-height: 1.3;
  color: var(--text);
  pointer-events: none;
  white-space: nowrap;
  z-index: 5;
}
.progress-bubble.visible { display: flex; }
.progress-bubble strong { font-weight: 600; }
.progress-bubble span {
  color: var(--text-soft);
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ============ ZOOM DE IMÁGENES (lightbox) ============ */
.img-zoom {
  position: fixed; inset: 0; z-index: 400;
  display: none; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.92);
  touch-action: none; overflow: hidden; overscroll-behavior: contain;
}
.img-zoom-img {
  max-width: 100%; max-height: 100%;
  transform-origin: center center; will-change: transform;
  user-select: none; -webkit-user-select: none; -webkit-user-drag: none;
}
.img-zoom-close {
  position: absolute; z-index: 1;
  top: calc(12px + env(safe-area-inset-top)); right: calc(12px + env(safe-area-inset-right));
  width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%; cursor: pointer;
  background: rgba(0, 0, 0, 0.5); color: #fff;
}
.img-zoom-close:hover { background: rgba(0, 0, 0, 0.75); }

/* ============ EPUB CONTAINER ============ */
.epub-container {
  width: 100%;
  height: 100%;
  position: relative;
  overflow: hidden;
}

/* La página como objeto (F1). Solo en pantallas anchas: es donde `sizeContainer()`
   (js/epub-reader.js) limita el contenedor al "ancho de columna" y sobra escritorio a los
   lados. Por debajo de 1000px la página va a sangre y una tarjeta solo robaría línea de
   lectura. El umbral debe seguir al `vw > 1000` de sizeContainer: si cambia allí, cambia aquí.
   El fondo va en el CONTENEDOR (no en el viewport) porque el swipe lo traslada: la hoja se
   mueve entera y lo que asoma detrás es el escritorio, como al pasar página de verdad.
   Ojo: NADA aquí puede cambiar el tamaño del área de lectura. Un inset vertical (padding en
   el viewport) parece inofensivo y repagina el EPUB entero: la página que anuncia la burbuja
   de la barra deja de ser la página a la que se salta (progress-scrub.spec) y, de paso, el
   PDF se re-ajusta. Por eso el respiro es solo lateral —el que sizeContainer ya deja— y aquí
   únicamente se pinta: fondo, radio y sombra. */
@media (min-width: 1001px) {
  body.reading:not(.scroll-mode) .epub-container.has-desk {
    background: var(--page-bg, var(--surface-0));
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-2);
  }
  /* En inmersivo la hoja ocupa todo: sin cromo alrededor, el marco sobra. */
  body.reading.immersive:not(.scroll-mode) .epub-container { border-radius: 0; box-shadow: none; }
}


/* Entrada de capítulo (F4). La dispara fadeChapterIn() en js/epub-reader.js al
   renderizarse una sección nueva; ahí están las exclusiones (giro de pantalla, swipe). */
.epub-container.chapter-in { animation: chapter-in var(--dur-chapter) var(--ease-out-soft); }
@keyframes chapter-in { from { opacity: 0.35; } to { opacity: 1; } }

/* En PAGINADO la vista debe llenar el viewport (altura fija). En SCROLL debe conservar
   su altura de contenido (la que epub.js pone inline, p. ej. 1229px) para que el
   contenedor de epub.js (overflow:auto) tenga algo que desplazar; por eso gateamos estas
   alturas fijas a :not(.scroll-mode). */
body:not(.scroll-mode) .epub-container > div {
  height: 100% !important;
}

.epub-container iframe {
  border: none !important;
}

body:not(.scroll-mode) .epub-container .epub-view {
  overflow: hidden !important;
  height: 100% !important;
}

/* ============ PDF CONTAINER (zoom fluido) ============ */
/* El contenedor es el área de SCROLL (paneo nativo, 1 dedo). Dentro, #pdf-zoom-layer
   agrupa las páginas y es lo que escalamos EN VIVO durante el pinch (2 dedos, por JS).
   El zoom "horneado" vive en el layout: cada .pdf-page es una caja de tamaño fit·zoom y
   su .pdf-scaler escala el contenido — así el scroll nativo abarca el contenido ampliado
   y la selección de texto sigue funcionando. */
.pdf-container {
  width: 100%;
  height: 100%;
  overflow: auto;
  position: relative;
  padding: 20px;
  background: var(--surface-3);
  touch-action: pan-x pan-y;      /* 1 dedo = scroll/selección nativos; pinch lo gestiona el JS */
  overscroll-behavior: contain;   /* al panear a zoom>1, el rebote en el borde no navega ni mueve el body */
  -webkit-overflow-scrolling: touch;
}

#pdf-zoom-layer {
  display: flex;
  flex-direction: column;
  align-items: safe center;       /* centra horizontalmente */
  justify-content: safe center;   /* centra verticalmente si la página cabe (paginado) */
  min-height: 100%;
}
/* El hueco entre páginas escala CON el zoom. Durante el pinch se escala el layer entero
   (gaps incluidos); si al hornear el gap se quedara fijo, el contenido de arriba mediría
   distinto que en el preview y la vista pegaba un salto al soltar los dedos. */
.pdf-container.pdf-scroll #pdf-zoom-layer {
  justify-content: flex-start;
  gap: calc(12px * var(--pdf-zoom, 1));
}

/* Doble página (revistas): las dos hojas en fila, con el mismo hueco que escala
   con el zoom que usa el modo continuo entre páginas. `align-items: flex-start`
   porque dos hojas de distinto alto —pasa en revistas escaneadas— deben quedar
   alineadas por arriba, no centradas cada una por su cuenta. */
.pdf-spread {
  display: flex;
  flex-direction: row;
  align-items: flex-start;
  gap: calc(12px * var(--pdf-zoom, 1));
}

.pdf-page {
  position: relative;
  flex: none;
  /* La caja es la VENTANA de recorte (ADR-033): el .pdf-scaler de dentro sigue midiendo la
     página entera y desplazado, así que lo que cae fuera hay que cortarlo aquí. Sin recorte
     el scaler mide exactamente la caja y este overflow no corta nada. La sombra es del
     propio elemento, no un descendiente: `hidden` no se la come. */
  overflow: hidden;
  box-shadow: 0 2px 8px var(--shadow);
  /* Grupo de mezcla acotado a la página: el tinte de papel y los subrayados (ambos con
     mix-blend-mode) se mezclan con el papel y entre sí, nunca con el fondo de la app que
     hay detrás ni con la sombra de la caja. */
  isolation: isolate;
}

/* ---- Papel del PDF: tinte y modo noche (ADR-026) --------------------------
   Una sola palanca: `data-pdf-paper` en <html>, que resuelve settings.js. TODO el pintado
   es CSS → el cambio es instantáneo, no vuelve a pdf.js y no re-rasteriza nada (respeta
   ADR-019/025: el zoom sigue siendo compositor puro).

   Son DOS mecanismos distintos bajo el mismo control, y no es un capricho:
   - Tintes claros → capa en `multiply`. Multiplicar solo puede OSCURECER, que es justo lo
     que hace falta: papel blanco × crema = crema, y la tinta (ya negra) no se mueve.
   - Noche → `invert`. Un tinte NO sirve: multiplicar por negro deja la página entera en
     negro, tinta incluida. Por eso «Noche» no es un color más de la paleta.

   Lo que el agente de visión captura (capturePageImage / captureRegionImage) lee el canvas
   directamente, así que sigue viendo la página REAL, sin teñir ni invertir. Es la ventaja
   de hacer esto en CSS y no al rasterizar. */
:root { --pdf-paper-tint: transparent; }
[data-pdf-paper="cream"] { --pdf-paper-tint: #f6ecd8; }
[data-pdf-paper="sepia"] { --pdf-paper-tint: #eadcc0; }
[data-pdf-paper="grey"]  { --pdf-paper-tint: #dcdcda; }
/* "white" y "auto"→claro no llevan tinte: el papel del PDF ya es blanco. */

/* El tinte es un ::after del wrapper: no añade DOM por página ni toca el render. Va por
   encima del canvas (base Y parche de detalle, ambos dentro del .pdf-scaler) y por DEBAJO
   de los subrayados (.pdf-hl-layer, z-index 2), que deben teñirse como la tinta y no
   quedar "bajo el papel". Solo se pinta cuando hay tinte: si no, ni se crea la capa. */
.pdf-page::after {
  content: '';
  display: none;
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: var(--pdf-paper-tint);
  mix-blend-mode: multiply;
}
[data-pdf-paper="cream"] .pdf-page::after,
[data-pdf-paper="sepia"] .pdf-page::after,
[data-pdf-paper="grey"] .pdf-page::after { display: block; }

/* Noche: invertir los canvas. El `hue-rotate` devuelve el tono a los colores (sin él, un
   diagrama azul sale naranja). Coge base y parche de detalle a la vez, y deja fuera la capa
   de texto y los subrayados, que viven fuera del .pdf-scaler.
   El `contrast(0.88)` final evita el par negro puro / blanco puro: deja el papel en gris muy
   oscuro y la tinta en hueso. Invertir a pelo es más agresivo de lo que parece en una sesión
   larga, que es justo cuando se usa este modo. */
[data-pdf-paper="night"] .pdf-scaler canvas {
  filter: invert(1) hue-rotate(180deg) contrast(0.88);
}
/* Con el papel ya en negro, `multiply` haría desaparecer los subrayados (color × negro =
   negro). En noche el blend se invierte a `screen`: el color ilumina el papel oscuro y la
   tinta, ahora blanca, sigue legible por encima. */
[data-pdf-paper="night"] .pdf-hl-group {
  mix-blend-mode: screen;
  opacity: 0.3;
}

/* Contenido de la página (canvas + capa de texto) a tamaño fit; se escala al zoom actual. */
.pdf-scaler {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
}
.pdf-scaler canvas, #pdf-canvas { display: block; }

/* Parche de detalle: solo el trozo visible, rasterizado a la resolución exacta del zoom y
   superpuesto al canvas base (que NUNCA se retira → nada de huecos en blanco). Posición y
   tamaño en unidades fit, puestos por JS, así escala con el .pdf-scaler como todo lo demás.
   Va encima del base y DEBAJO de la capa de texto, que debe seguir seleccionable. */
.pdf-scaler canvas.pdf-detail {
  position: absolute;
  pointer-events: none;
}

/* Flash al saltar a una página desde una cita del agente (señala el destino). */
@keyframes pdfCiteFlash {
  0%, 100% { box-shadow: 0 2px 8px var(--shadow); }
  20%, 60% { box-shadow: 0 0 0 3px var(--accent), 0 2px 12px var(--shadow); }
}
.pdf-page.pdf-cite-flash { animation: pdfCiteFlash 1.5s var(--ease); }

/* Resaltado transitorio del TROZO citado (cuando sí localizamos el pasaje en la página). */
.pdf-cite-layer { position: absolute; inset: 0; pointer-events: none; overflow: hidden; z-index: 3; }
.pdf-cite-hl {
  position: absolute;
  border-radius: 2px;
  background: var(--accent);
  opacity: 0.35;
  mix-blend-mode: multiply;
  animation: pdfCiteHlIn 0.18s var(--ease);
}
@keyframes pdfCiteHlIn { from { opacity: 0; } to { opacity: 0.35; } }

/* pdf.js selectable text layer overlaid on the canvas */
.textLayer {
  position: absolute;
  inset: 0;
  overflow: hidden;
  line-height: 1;
  text-align: initial;
  opacity: 1;
  forced-color-adjust: none;
  transform-origin: 0 0;
}

.textLayer span,
.textLayer br {
  position: absolute;
  white-space: pre;
  color: transparent;
  cursor: text;
  transform-origin: 0% 0%;
}

.textLayer ::selection {
  background: rgba(79, 140, 255, 0.35);
}

/* En táctil la selección la lleva la app (pdf-touch-select.js): se desactiva la nativa para
   que no compitan y para que no salga el menú del sistema, que tapa nuestra barra. */
@media (pointer: coarse) {
  .textLayer,
  .textLayer span {
    -webkit-user-select: none;
    user-select: none;
    -webkit-touch-callout: none;
  }
}

/* PDF3 · Overlay de subrayados: rectángulos (coords fraccionales → px) sobre el canvas.
   Multiply para que se vea como un marcador sobre el texto. No captura eventos: la capa de
   texto de encima sigue siendo seleccionable. */
.pdf-hl-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Los rects se guardan en fracciones de página ENTERA y se remapean a la caja, así que
     con recorte pueden salirse por los lados (una nota al margen, un número de página). */
  overflow: hidden;
  /* Por ENCIMA del .pdf-scaler (canvas) pase lo que pase con el orden del DOM: en modo
     scroll el subrayado se crea sobre el placeholder ANTES de que el observer perezoso
     añada el scaler, así que sin z-index el canvas opaco lo tapaba. */
  z-index: 2;
}

/* Un subrayado = un GRUPO. El blend/opacidad se aplican UNA sola vez al grupo (que crea su
   propio contexto de composición): así los rects de líneas contiguas —que se solapan un poco
   por el alto de línea— se funden en un color sólido uniforme antes de mezclarse, sin las
   bandas más oscuras que salían al multiplicar dos veces en cada solape. */
.pdf-hl-group {
  position: absolute;
  inset: 0;
  mix-blend-mode: multiply;
  opacity: 0.4;
}
/* Rects individuales: color sólido, SIN blend ni opacidad propios (los pone el grupo). */
.pdf-hl {
  position: absolute;
  border-radius: 2px;
}

/* ============ AI PANEL ============ */
/* ============ GUARDIA HASTA QUE LLEGUE agent.css ============
   El panel del agente y su FAB existen en index.html desde el primer momento, pero sus
   estilos viven ahora en css/agent.css, que se carga al abrir el agente (ver
   js/css-loader.js). Sin esto, entre el primer pintado y esa carga se verían los dos en
   crudo: el panel como un bloque en el flujo, tapando la biblioteca.

   Solo lo justo para que NO se vean. Las reglas de verdad están en agent.css, que va
   después en el orden de hojas y las pisa con la misma especificidad. */
#ai-panel { position: fixed; top: 0; right: 0; transform: translateX(100%); }
.ai-fab { display: none; }

:root { --ai-panel-width: 380px; }

/* En escritorio, abrir la barra lateral izquierda EMPUJA el contenido (el texto
   se re-pagina al nuevo ancho), igual que el panel del agente a la derecha —al
   estilo de NotebookLM. En tablet/móvil se superpone como drawer (media query
   de abajo lo revierte a margin 0). */
body:has(#sidebar.open) .reader-main { margin-left: var(--sidebar-width); }

/* En la biblioteca, las sidebars de la vista de libro nunca deben verse (van por encima
   de la estantería). El id en el selector gana en especificidad a .open/.ai-open. */
body.in-library #sidebar { transform: translateX(-100%); }
body.in-library #ai-panel { transform: translateX(100%); }          /* borde interior del panel del agente (izquierda) */
.sidebar-resizer { right: -5px; }
.panel-resizer:hover::before,
body.resizing-panel .panel-resizer::before { background: var(--accent); }
/* Durante el arrastre: sin selección de texto, cursor global y sin transición de márgenes
   (para que el texto siga el tirador sin retardo). */
body.resizing-panel { cursor: col-resize; user-select: none; }
body.resizing-panel #ai-panel,
body.resizing-panel #sidebar,
body.resizing-panel .reader-main { transition: none !important; }
@keyframes ai-mic-pulse { 50% { opacity: 0.62; } }

/* ---- barra de grabación (patrón WhatsApp) ---- */
/* Ocupa el hueco del textarea mientras grabas, con su mismo alto para que el composer no dé
   un salto al entrar y salir. */
.mic-bar {
  flex: 1;
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--surface-0);
}
textarea.is-recording { display: none; }

.mic-bar-cancel {
  display: none;              /* solo con el motor del proveedor (ver mic.js) */
  align-items: center;
  padding: 2px;
  border: 0;
  background: none;
  color: var(--text-soft);
  cursor: pointer;
}
.mic-bar.is-provider .mic-bar-cancel { display: inline-flex; }
.mic-bar-cancel:hover { color: #e53935; }

/* Punto latiendo: el "estoy grabando" de toda la vida. Rojo aunque el acento sea verde —
   grabando es grabando, y es el código de color que todo el mundo ya tiene aprendido. */
.mic-bar-dot {
  width: 9px;
  height: 9px;
  flex-shrink: 0;
  border-radius: 50%;
  background: #e53935;
  animation: mic-dot 1.3s ease-in-out infinite;
}
@keyframes mic-dot { 50% { opacity: 0.25; } }

.mic-bar-time {
  font-variant-numeric: tabular-nums;   /* que el ancho no baile al pasar de 0:09 a 0:10 */
  font-size: 13px;
  color: var(--text);
}

/* Vúmetro: se enciende de dentro afuera. Solo con el motor del proveedor, que es el único
   que da stream que medir. */
.mic-bar-level { display: none; align-items: center; gap: 2px; flex: 1; }
.mic-bar.is-provider .mic-bar-level { display: flex; }
.mic-bar-level i {
  flex: 1;
  height: 4px;
  border-radius: 2px;
  background: var(--border);
  transition: height 80ms linear, background-color 80ms linear;
}
.mic-bar-level i.on { height: 14px; background: var(--accent); }

.mic-bar-hint { font-size: 12px; color: var(--text-soft); white-space: nowrap; }
/* Con vúmetro el hueco se queda corto: la pista sobra, el medidor ya lo dice todo. */
.mic-bar.is-provider .mic-bar-hint { display: none; }

/* Parar: la acción principal mientras grabas, y por eso es lo único con acento en la barra.
   Va a la DERECHA (margin-left:auto), enfrente de la papelera: destructivo a un extremo,
   confirmar al otro. Antes esto solo estaba en el botón del micro, que queda fuera de donde
   miras porque la barra sustituye al textarea. */
.mic-bar-stop {
  margin-left: auto;
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--accent);
  color: var(--on-accent);
  cursor: pointer;
  transition: background var(--transition-fast);
}
.mic-bar-stop:hover { background: var(--accent-hover); }
.mic-bar-stop .icon { color: currentColor; }

/* ---- transcribiendo ---- */
/* Al soltar, la barra se queda: cronómetro, vúmetro y botones se van (ya no hay nada que
   parar ni que medir) y el punto rojo pasa a spinner. Lo que era "te estoy oyendo" se
   convierte en "te estoy transcribiendo" sin que el composer dé un salto ni aparezca un
   textarea vacío que hace pensar que el audio se ha perdido. */
.mic-bar.is-busy .mic-bar-cancel,
.mic-bar.is-busy .mic-bar-stop,
.mic-bar.is-busy .mic-bar-time,
.mic-bar.is-busy .mic-bar-level { display: none; }
/* El vúmetro le robaba el hueco a la pista; sin vúmetro, la pista vuelve — y ahora es lo
   único que queda diciendo qué pasa. */
.mic-bar.is-provider.is-busy .mic-bar-hint { display: block; }
.mic-bar.is-busy .mic-bar-dot {
  width: 14px;
  height: 14px;
  background: none;
  border: 2px solid var(--border);
  border-top-color: var(--accent);   /* ya no es rojo: no se está grabando */
  animation: mic-spin 0.7s linear infinite;
}
@keyframes mic-spin { to { transform: rotate(360deg); } }

@media (prefers-reduced-motion: reduce) {
  .mic-bar-dot { animation: none; }
  .mic-bar-level i { transition: none; }
  /* Aquí el movimiento SÍ se quita, pero la espera tiene que seguir viéndose viva: el
     spinner deja de girar y late en opacidad, que no es movimiento. */
  .mic-bar.is-busy .mic-bar-dot { animation: mic-dot 1.6s ease-in-out infinite; }
}


/* Atenuación de capítulos en el índice según el objetivo (E6.4) */
#toc-list a.ai-toc-low { opacity: 0.4; }
#toc-list a.ai-toc-mid { opacity: 0.7; }
#toc-list a.ai-toc-high { font-weight: 600; opacity: 1; }
#toc-list a.ai-toc-high::before {
  content: '★'; color: var(--accent); margin-right: 6px; font-size: 11px;
}
/* La marca de "estoy aquí" gana a la atenuación por objetivo: da igual lo poco que
   importe el capítulo para el objetivo, si es donde se lee tiene que verse. */
#toc-list a.current { opacity: 1; }

/* P13 · Resumen citado */
.sum-card { max-width: 680px; max-height: 90vh; overflow-y: auto; }
.sum-depth-hint { font-size: 12px; color: var(--text-soft); margin: 6px 0 2px; line-height: 1.45; }
/* Documento del resumen (markdown → HTML): portada, secciones por capítulo y cierre. */
.sum-doc { font-size: 15px; line-height: 1.65; color: var(--text); }
.sum-doc h1 { font-size: 20px; margin: 4px 0 10px; }
.sum-doc h2 {
  font-size: 15px; font-weight: 600; color: var(--text-soft);
  margin: 22px 0 8px; padding-bottom: 4px; border-bottom: 1px solid var(--border);
}
.sum-doc h1 + p {   /* TL;DR: primer párrafo tras el título → tarjeta destacada */
  font-size: 16px; background: var(--surface-2); border-radius: 12px;
  padding: 14px 16px; margin: 8px 0 4px;
}
.sum-doc p { margin: 8px 0; }
.sum-doc ul { margin: 6px 0; padding-left: 20px; }
.sum-doc li { margin-bottom: 8px; }

/* Trabajos de IA en segundo plano: vista "en curso", chip flotante y toast de aviso */
@keyframes ai-spin { to { transform: rotate(360deg); } }
.sum-resulthead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
#fc-generate { width: 100%; margin-top: 20px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }
#fc-study small { font-weight: 700; opacity: .8; }
#fc-study[disabled] { opacity: .5; cursor: default; }

/* Chip de la estantería: la cola diaria de repaso (bucle de retorno de P10). */
.lib-study-chip {
  display: inline-flex; align-items: center; gap: 8px;
  border: 1px solid var(--accent); border-radius: 999px; background: var(--accent-soft);
  color: var(--accent); padding: 7px 14px; font-size: 13px; font-weight: 600;
  cursor: pointer; font-family: inherit; white-space: nowrap;
}
.lib-study-chip:hover { background: var(--accent); color: var(--on-accent); }

/* P12 · Selector de ámbito de repaso (Todo / estanterías) */
.lib-toolbar { position: relative; }
.lib-study-menu {
  position: absolute;
  z-index: 50;
  min-width: 240px; max-width: 320px;
  max-height: 60vh; overflow-y: auto;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow-3);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.lib-study-sec {
  font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-faint); padding: 8px 12px 4px;
}
.lib-study-opt {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; padding: 9px 12px; border: none; border-radius: 8px;
  background: none; color: var(--text); font: inherit; font-size: 14px;
  cursor: pointer; text-align: left;
}
.lib-study-opt:hover { background: var(--surface-3); }
.lib-study-opt-lbl { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lib-study-opt-n {
  flex: none; font-size: 12px; font-weight: 600; color: var(--accent);
  background: var(--accent-soft); border-radius: 999px; padding: 2px 9px;
}
/* Árbol: estantería = categoría padre (negrita); libro = hijo (sangrado, con guía). */
.lib-study-opt--shelf { font-weight: 600; }
.lib-study-opt--shelf .lib-study-opt-lbl::before { content: '▸ '; color: var(--text-faint); font-weight: 400; }
.lib-study-opt--book { padding-left: 30px; position: relative; color: var(--text-soft); }
.lib-study-opt--book .lib-study-opt-lbl::before { content: '└ '; color: var(--text-faint); }
.lib-study-opt--book .lib-study-opt-n { color: var(--text-soft); background: var(--surface-3); }

/* ============ AJUSTES GENERALES (overlay global) ============ */
/* Hogar de la config global de la app (agente + P1/P2/P3). Mismo patrón modal que
   el onboarding; settings de LECTURA siguen en la sidebar. */
.appset {
  position: fixed; inset: 0; z-index: 300;
  background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center;
  padding: 20px;
}
/* ---- Selector de modelo (models.dev) ---- */
.mp-card { width: min(560px, 92vw); }
.mp-card .appset-input { margin-top: 4px; }
.mp-source { margin: 8px 0 6px; font-size: 12px; }
.mp-source.is-error { color: var(--danger, #e5484d); }
.mp-list {
  max-height: min(52vh, 420px); overflow-y: auto;
  border: 1px solid var(--border-soft); border-radius: 10px;
}
.mp-item {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 9px 12px; border: none; border-bottom: 1px solid var(--border-soft);
  background: none; color: var(--text);
}
.mp-item:last-child { border-bottom: none; }
.mp-item:hover { background: var(--surface-3); }
.mp-item.active { background: var(--accent-soft); }
.mp-name { display: block; font-size: 14px; font-weight: 600; }
.mp-id { display: block; font-size: 11px; color: var(--text-faint); font-family: var(--font-mono, monospace); word-break: break-all; }
.mp-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
.mp-tag {
  font-size: 10px; line-height: 1.6; padding: 0 6px; border-radius: var(--r-pill);
  background: var(--surface-3); color: var(--text-soft);
}
.mp-cost { font-variant-numeric: tabular-nums; }
.mp-empty { padding: 14px 12px; margin: 0; }

@media (max-width: 560px) {
  .appset { padding: 0; align-items: flex-end; }
}


/* ============ HIGHLIGHT TOOLTIP (v2) ============ */
/* Barra de selección de texto (estilo O'Reilly): preguntar al agente arriba;
   colores + nota + copiar abajo. */
/* La barra crece con el número de acciones y es `fixed`: sin un tope, en un móvil de
   375px se salía de la pantalla (447px ya antes de añadir "Con números"). Se acota al
   viewport y la fila de acciones envuelve. */
.sel-toolbar {
  position: fixed; z-index: 200;
  display: flex; flex-direction: column;
  max-width: calc(100vw - 20px);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-md); box-shadow: var(--shadow-3);
  overflow: hidden;
}
.sel-ask {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 18px; border: none; background: none; cursor: pointer;
  color: var(--text); font-size: var(--fs-md); font-weight: 600; text-align: left;
  transition: background var(--transition-fast);
}
.sel-ask:hover { background: var(--surface-3); }
.sel-ask .icon { color: var(--accent); }
.sel-row {
  display: flex; align-items: stretch; gap: 0;
  flex-wrap: wrap;
  border-top: 1px solid var(--border-soft);
}
/* Al envolver, el separador vertical del primer botón de cada línea sobra; se sustituye
   por el horizontal de la propia línea. */
.sel-row > .sel-act { border-top: 1px solid transparent; }
.sel-colors { display: flex; align-items: center; gap: 8px; padding: 10px 14px; }
.highlight-color {
  width: 24px; height: 24px; border: 2px solid transparent; border-radius: 50%;
  cursor: pointer; padding: 0;
  transition: transform var(--transition-fast), border-color var(--transition-fast);
}
.highlight-color:hover { transform: scale(1.15); border-color: var(--text); }
/* Editando un subrayado ya hecho: su color va marcado con un anillo (el mismo recurso que
   el hover, fijo). Así se ve de qué color es sin salir a la lista. */
.highlight-color.is-current { border-color: var(--text); transform: scale(1.15); }
.sel-act {
  display: flex; align-items: center; gap: 8px;
  padding: 10px 16px; border: none; border-left: 1px solid var(--border-soft);
  background: none; cursor: pointer; color: var(--text); font-size: var(--fs-sm); font-weight: 500;
  transition: background var(--transition-fast);
}
.sel-act:hover { background: var(--surface-3); }
.sel-act .icon { color: var(--text-soft); }

/* Acciones rápidas: mismo peso visual que el resto de la barra, pero con el icono teñido
   de acento — son turnos del agente, no utilidades del subrayado. Cada botón ocupa la
   mitad para que se lean como un par y no como el principio de una lista larga. */
.sel-quick > .sel-act { flex: 1 1 0; justify-content: center; }
.sel-quick > .sel-act:first-child { border-left: none; }
.sel-quick .icon { color: var(--accent); }

/* Eliminar: destructivo, pero no grita. Solo el icono va en rojo hasta que se apunta. */
.sel-act--danger:hover { background: var(--danger-soft, var(--surface-3)); color: var(--danger, #e5484d); }
.sel-act--danger .icon { color: var(--danger, #e5484d); }

/* La nota se escribe dentro de la propia barra, bajo las acciones: se anota mirando el
   pasaje, sin un modal que lo tape. Sin botón Guardar — se guarda al cerrar la barra. */
.sel-note-box { border-top: 1px solid var(--border-soft); padding: 10px 12px; }
.sel-note-box textarea {
  display: block; width: 100%; min-width: 240px; box-sizing: border-box;
  padding: 8px 10px; resize: vertical;
  background: var(--surface-1); color: var(--text);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  font: inherit; font-size: var(--fs-sm); line-height: 1.5;
}
.sel-note-box textarea:focus { outline: none; border-color: var(--accent); }

/* Nota personal bajo un subrayado en la lista. */
.highlight-note {
  display: flex; align-items: flex-start; gap: 6px; margin-top: 6px;
  font-size: 12px; color: var(--text-soft);
}
.highlight-note .icon { color: var(--text-faint); flex: 0 0 auto; margin-top: 1px; }

/* ============================================================================
   FOCO accesible (Fase 3): borde verde limpio, sin halo/glow. Coherente con la
   dirección "silenciosa y precisa": el foco es un borde de acento, no un resplandor.
   ========================================================================== */
:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }