/* ==========================================================================
   app.css — Hoja de estilos global de la plataforma Biblioteca Musical/karaoke (Spec 001)
   -------------------------------------------------------------------------
   Única fuente de los TOKENS DE DISEÑO. Los valores NO son una paleta nueva:
   se derivan del tema oscuro ya en uso en las plantillas (spotify_ai,
   biblioteca_musical, login...).

   Convenciones:
   - Los tokens se definen aquí en :root. Una plantilla PUEDE sobreescribir
     alguno en su <style> inline (al ir después en el DOM gana), mientras
     dure la Fase 2 de migración.
   - Los estilos NUEVOS (Fase 2 en adelante) deben usar siempre var(--token),
     nunca valores literales (constitution §4).
   ========================================================================== */

:root {
  /* --- Colores: fondo y superficies (tema oscuro) ----------------------
     Derivados del :root de spotify_ai.html. */
  --bg: #121212;            /* fondo general de la app */
  --bg-elev: #181818;       /* superficies elevadas (headers, barras) */
  --bg-card: #1f1f1f;       /* tarjetas y paneles */

  /* --- Colores: texto --------------------------------------------------- */
  --text: #f4f4f4;          /* texto principal */
  --muted: #b3b3b3;         /* texto secundario / atenuado */

  /* --- Colores: acento --------------------------------------------------
     Verde principal de la marca (spotify_ai). --accent-2 es su variante
     más viva para hover/estados activos. */
  --accent: #1db954;
  --accent-2: #23d362;
  --btn-text: #ffffff;              /* texto sobre botones de color */
  --resizer-line: rgba(255, 255, 255, 0.45); /* indicador resize columnas */

  /* --- Colores: estados y avisos -----------------------------------------
     Spec 007 (F2.4) — auditoría de contraste (WCAG 2.1, ratios reales
     calculados; ver §3 "Auditoría de contraste" de docs/specs/007):
       --text  #f4f4f4  17.0:1 sobre --bg · 15.0:1 sobre --bg-card  (OK)
       --muted #b3b3b3   8.9:1 sobre --bg ·  7.9:1 sobre --bg-card  (OK)
       --warning #f6c84c 11.9:1 sobre --bg (solo iconos, no texto)
       --accent #1db954  7.2:1 sobre --bg (acento grande: ≥3:1 OK)
       --success #3ddc6e 9.2:1 sobre --bg-card (era #28a745 → 5.3:1, OK)
       --danger  #f25c6b 5.1:1 sobre --bg-card (era #dc3545 → 3.6:1,
          insuficiente como TEXTO de .invalid-feedback y .app-state--error).
     Los botones de Bootstrap usan texto BLANCO sobre el color, no var(--bg):
       #f25c6b → 3.2:1 (era 3.9:1) — no alcanza 4.5:1 como texto; queda
       documentado como excepción aceptada en la spec (cambiar la marca
       Bootstrap roja sin acuerdo está fuera de alcance). */
  --success: #3ddc6e;
  --warning: #f6c84c;       /* dorado de biblioteca_musical (solo iconos) */
  --danger: #f25c6b;
  --info: #4dabf7;          /* azul informativo (solo iconos; consola live) */

  /* --- Bordes ------------------------------------------------------------- */
  --border: rgba(255, 255, 255, 0.08);

  /* --- Superficies funcionales ----------------------------------------------
     Pista (fondo) de controles deslizantes sobre fondos oscuros: blanco
     estándar a baja opacidad. Usado por la barra de progreso del
     mini-reproductor de mis_listas. */
  --track: rgba(255, 255, 255, 0.15);

  /* --- Tipografía --------------------------------------------------------- */
  --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
    "Helvetica Neue", Arial, sans-serif; /* stack de Bootstrap 4 */
  --font-size-base: 1rem;
  --line-height-base: 1.5;

  /* --- Espaciado (escala 4/8/16/24/32 px) ---------------------------------- */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 16px;
  --space-4: 24px;
  --space-5: 32px;

  /* --- Radios --------------------------------------------------------------- */
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 16px;

  /* --- Sombras ---------------------------------------------------------------- */
  --shadow-1: 0 2px 8px rgba(0, 0, 0, 0.4);
  --shadow-2: 0 8px 24px rgba(0, 0, 0, 0.5);

  /* --- Layout (medidas de barras ya usadas en biblioteca_musical) -------------- */
  --navbar-h: 40px;
  --playerbar-h: 80px;

  /* --- Z-index de modales (Spec 006): por encima del navbar (2000) ------------
     El backdrop lo gestiona Bootstrap (1040), siempre por debajo. Las vistas
     con necesidad especial (car_mode: 2500 bajo el navbar) no usan .app-modal. */
  --z-modal: 2100;

  /* --- Foco accesible (Spec 007): anillo para :focus-visible ----------------
     Halos alrededor del acento. WCAG 1.4.11 (no texto): el halo + el borde
     del propio control superan 3:1 respecto al fondo. Se define aquí (token)
     porque las secciones de componentes están auditadas "sin literales". */
  --focus-ring: 0 0 0 3px rgba(29, 185, 84, 0.35);
}

/* ==========================================================================
   Spec 017 (tarea 6): Generador YouTube → MP3 — sección "Generador".
   -------------------------------------------------------------------------
   Mueve el <style> inline de generator.html a app.css con tokens. Anclaje
   por [data-view="generador"]. El botón morado de "Trasladar" (color fuera
   de paleta) desaparece: la acción principal de cada fase usa .btn-accent
   (--accent). El resize de columnas de la tabla de metadatos se conserva
   (misma UX de initMetadataColumnResize en generator.js).
   Solo var(--token), sin colores literales.
   ========================================================================== */

[data-view="generador"] {
  background: var(--bg);
  color: var(--text);
  padding-top: 90px !important;
  overflow-x: hidden;
  position: relative;
  z-index: 0;
}

/* Tarjetas de la vista: tema oscuro por tokens (mismo patrón que
   [data-view="ajustes"] y tools-admin). Sin esto, .card heredaba el fondo
   BLANCO de Bootstrap 4 y el texto --text (#f4f4f4) quedaba ilegible:
   era la causa de que "apenas se viera" el contenido de cada fase. */
[data-view="generador"] .card {
  background-color: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  color: var(--text);
}

[data-view="generador"] .card .table td,
[data-view="generador"] .card .table th {
  color: var(--text);
}

[data-view="generador"] .card .thead-dark th,
[data-view="generador"] .card .thead-light th {
  background-color: var(--bg-elev);
  border-color: var(--border);
  color: var(--text);
}

[data-view="generador"] .card .table-striped tbody tr:nth-of-type(odd) {
  background-color: rgba(255, 255, 255, 0.03);
}

[data-view="generador"] .card .table-bordered td,
[data-view="generador"] .card .table-bordered th {
  border-color: var(--border);
}

[data-view="generador"] .card .table-hover tbody tr:hover {
  background-color: rgba(255, 255, 255, 0.05);
  color: var(--text);
}

[data-view="generador"] .card .form-control {
  background-color: var(--bg-elev);
  border-color: var(--border);
  color: var(--text);
}

[data-view="generador"] .card .form-control::placeholder {
  color: var(--muted);
}

[data-view="generador"] .card .form-control:focus {
  background-color: var(--bg-elev);
  border-color: var(--accent);
  color: var(--text);
  box-shadow: var(--focus-ring);
}

[data-view="generador"] .card .btn-outline-secondary,
[data-view="generador"] .card .btn-outline-primary,
[data-view="generador"] .card .btn-outline-danger,
[data-view="generador"] .card .btn-outline-info,
[data-view="generador"] .card .btn-outline-warning {
  color: var(--muted);
  border-color: var(--border);
}

[data-view="generador"] .card .btn-outline-secondary.active {
  background-color: var(--accent);
  border-color: var(--accent);
  color: var(--btn-text);
}

[data-view="generador"] .card .btn-outline-secondary:hover,
[data-view="generador"] .card .btn-outline-primary:hover,
[data-view="generador"] .card .btn-outline-danger:hover,
[data-view="generador"] .card .btn-outline-info:hover,
[data-view="generador"] .card .btn-outline-warning:hover {
  color: var(--text);
  border-color: var(--muted);
}

[data-view="generador"] .card .badge-secondary {
  background-color: var(--bg-elev);
  color: var(--muted);
  border: 1px solid var(--border);
}

[data-view="generador"] .card .progress {
  background-color: var(--bg-elev);
}

[data-view="generador"] .card .close {
  color: var(--text);
  text-shadow: none;
}

[data-view="generador"] .background-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-image: url("/static/fondo.png");
  background-repeat: repeat;
  background-size: cover;
  opacity: 0.08;
  z-index: -1;
}

[data-view="generador"] .card-title {
  font-weight: 700;
  color: var(--text);
  font-size: 1.15rem;
  margin-bottom: 14px;
}

/* Título de fase: el número es un token de secuencia (el flujo SÍ es una
   secuencia, spec 017); fondo de marca + texto blanco, mismo contraste que
   .btn-accent. El resto del título va en --text a 1.15rem/700. */
[data-view="generador"] .card-title .gen-phase__num {
  display: inline-block;
  min-width: 1.7rem;
  padding: .1rem .5rem;
  margin-right: .55rem;
  border-radius: var(--radius-sm);
  background: var(--accent);
  color: var(--btn-text);
  font-size: .95rem;
  line-height: 1.5;
  text-align: center;
  vertical-align: baseline;
}

/* Etiquetas de subbloques (Progreso global, Progreso recorte, Género
   existente): patrón de subetiqueta — en mayúsculas SOLO para estas
   etiquetas auxiliares, nunca para los títulos de fase. */
[data-view="generador"] .gen-label {
  display: block;
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--space-1);
}

[data-view="generador"] .table td,
[data-view="generador"] .table th {
  vertical-align: middle;
}

/* Botón ancho de acción de fase (Importar / Procesar / Trasladar) */
[data-view="generador"] .btn-lg-wide {
  width: 100%;
  padding: 14px 18px;
  font-weight: 700;
  font-size: 1.1rem;
}

/* Acción principal de fase en verde de marca (sustituye al botón morado) */
[data-view="generador"] .btn-accent {
  background-color: var(--accent);
  border-color: var(--accent);
  color: var(--btn-text);
}
[data-view="generador"] .btn-accent:hover,
[data-view="generador"] .btn-accent:focus,
[data-view="generador"] .btn-accent:active {
  background-color: var(--accent-2);
  border-color: var(--accent-2);
  color: var(--btn-text);
}

/* Carátula en tabla de resultados y metadatos */
[data-view="generador"] .cover-thumb {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border);
}

[data-view="generador"] .badge-ext {
  font-size: 0.85rem;
  padding: 6px 10px;
}

/* Log del proceso (bajo "Inicio del proceso"): cada línea es una descarga.
   Antes heredaba .text-muted de Bootstrap 4 (#6c757d) sobre fondo oscuro:
   el texto apenas se leía. Se usa --muted de la plataforma y un cuerpo
   legible; el estado de cada canción (completado/error) manda el color. */
[data-view="generador"] .process-log-row {
  padding: 6px 10px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  font-size: .95rem;
}

[data-view="generador"] .process-log-row strong {
  color: var(--text);
}

[data-view="generador"] .process-log-row small {
  color: var(--muted);
}

[data-view="generador"] .process-log-row[data-failed="1"] {
  border-color: var(--danger);
}

[data-view="generador"] #process-log > .text-muted {
  color: var(--muted) !important;
}
[data-view="generador"] .generator-shell {
  max-width: 100%;
  /* Sin override de padding: el gutter -15px de .row (Bootstrap) requiere
     el padding 15px de container-fluid o la pagina hace overflow (F7) */
}

/* Tabla de metadatos: layout fijo + inputs compactos */
[data-view="generador"] .metadata-table {
  table-layout: fixed;
  width: 100%;
}

[data-view="generador"] .metadata-table td,
[data-view="generador"] .metadata-table th {
  padding: var(--space-1);
}

[data-view="generador"] .metadata-table .form-control {
  min-width: 70px;
  padding: .2rem .45rem;
  font-size: .9rem;
}

[data-view="generador"] .metadata-table .form-control[data-field="comment"],
[data-view="generador"] .metadata-table .form-control[data-field="album"],
[data-view="generador"] .metadata-table .form-control[data-field="year"],
[data-view="generador"] .metadata-table .form-control[data-field="genre_internet_sugerido"] {
  min-width: 50px;
}

/* Resize de columnas (misma UX que antes, en app.css) */
[data-view="generador"] .metadata-table th {
  position: relative;
  user-select: none;
  white-space: nowrap;
}

[data-view="generador"] .metadata-table th .col-resizer {
  position: absolute;
  top: 0;
  right: 0;
  width: 8px;
  height: 100%;
  cursor: col-resize;
  z-index: 2;
}

[data-view="generador"] .metadata-table th .col-resizer::after {
  content: '';
  position: absolute;
  top: 20%;
  bottom: 20%;
  right: 3px;
  width: 2px;
  background: var(--resizer-line);
}

[data-view="generador"] .metadata-table td {
  overflow: hidden;
}

/* Stepper de 6 fases: botones clickeables para saltar (spec 017, t7) */
[data-view="generador"] .gen-stepper {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-4);
}

[data-view="generador"] .gen-stepper__step {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background-color: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--muted);
  font-weight: 600;
  cursor: pointer;
}

[data-view="generador"] .gen-stepper__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1px solid var(--border);
  font-size: .8rem;
}

[data-view="generador"] .gen-stepper__step:hover {
  color: var(--text);
  border-color: var(--muted);
}

[data-view="generador"] .gen-stepper__step.is-done {
  color: var(--text);
}

[data-view="generador"] .gen-stepper__step.is-done .gen-stepper__num {
  background-color: var(--success);
  border-color: var(--success);
  color: var(--btn-text);
}

[data-view="generador"] .gen-stepper__step.is-current {
  border-color: var(--accent);
  color: var(--text);
}

[data-view="generador"] .gen-stepper__step.is-current .gen-stepper__num {
  background-color: var(--accent);
  border-color: var(--accent);
  color: var(--btn-text);
}

/* Editor de onda (spec 017, t11): contenedor relativo + marcadores
   arrastrables in/out sobre la onda de wavesurfer.js */
[data-view="generador"] .wave-wrap {
  position: relative;
  overflow: hidden;
}

[data-view="generador"] .wave-marker {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 10px;
  margin-left: -5px;
  background-color: var(--accent);
  opacity: 0.6;
  cursor: col-resize;
  touch-action: none;
  z-index: 2;
}

[data-view="generador"] .wave-marker--out {
  background-color: var(--danger);
}

[data-view="generador"] .wave-marker span {
  position: absolute;
  top: 2px;
  left: 50%;
  transform: translateX(-50%);
  font-size: .7rem;
  white-space: nowrap;
  color: var(--text);
  background-color: var(--bg-elev);
  padding: 0 4px;
  border-radius: var(--radius-sm);
}

@media (max-width: 991.98px) {
  [data-view="generador"] {
    padding-top: 76px !important;
  }
  [data-view="generador"] .gen-stepper__step {
    padding: var(--space-1) var(--space-2);
  }
  [data-view="generador"] .gen-stepper__label {
    font-size: .85rem;
  }
  [data-view="generador"] .card-body {
    padding: .9rem;
  }
  [data-view="generador"] .btn-lg-wide {
    padding: 10px 12px;
    font-size: 1rem;
  }
  [data-view="generador"] .form-row > [class*='col-'] {
    margin-bottom: .35rem;
  }
  [data-view="generador"] .metadata-table .form-control {
    min-width: 70px;
  }
}



/* ==========================================================================
   Spec 014 (tarea 6): Tools Admin — rediseño en tema oscuro por tokens.
   -------------------------------------------------------------------------
   Sustituye el bloque light de Spec 011 (fondo.png + overlay blanco +
   .admin-tools-card). La vista ahora usa los mismos estándares que el
   resto de la app:
     - fondo y superficie por tokens (:root), sin literales;
     - tablas .app-table (Spec 005), modales .app-modal (Spec 006),
       estados .app-state (Spec 004) y formularios .app-form (Spec 006);
     - anclaje por [data-view="tools-admin"] (body.admin-tools-page se
       conserva por compatibilidad con tools_admin.js).

   Solo var(--token); nada de valores literales.
   ========================================================================== */

[data-view="tools-admin"] {
  background: var(--bg);
  color: var(--text);
  padding-top: var(--navbar-h) !important;
  min-height: 100vh;
}

/* Pestañas de la consola: doble señal (fondo + lengüeta + negrita),
   coherente con el resto de vistas. */
[data-view="tools-admin"] .nav-tabs .nav-link {
  color: var(--muted);
  background: var(--bg-elev);
  border-color: var(--border);
}

[data-view="tools-admin"] .nav-tabs .nav-link:hover {
  color: var(--text);
  background: var(--bg-card);
}

[data-view="tools-admin"] .nav-tabs .nav-link.active {
  color: var(--text);
  font-weight: 700;
  background: var(--bg-card);
  border-bottom: 3px solid var(--accent);
}

/* Tarjetas de sección */
[data-view="tools-admin"] .card {
  background-color: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  color: var(--text);
}

/* Consola Live: bloques de métrica */
[data-view="tools-admin"] .console-stat {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  transition: transform 0.2s;
}

[data-view="tools-admin"] .console-stat h3 {
  color: var(--text);
}

/* % de la consola por umbral (ajuste usuario 30-10): recupera el
   verde/amarillo/rojo dinámico de la consola clara (spec 014 lo igualó). */
[data-view="tools-admin"] .console-stat h3.console-ok {
  color: var(--success);
}

[data-view="tools-admin"] .console-stat h3.console-warn {
  color: var(--warning);
}

[data-view="tools-admin"] .console-stat h3.console-crit {
  color: var(--danger);
}

[data-view="tools-admin"] .console-stat h5 {
  color: var(--muted);
  font-weight: 600;
}

/* Iconos de las métricas de la consola (ajuste usuario 30-10): recuperan
   los colores de la consola clara (text-danger/info/secondary/success/
   warning), adaptados a tokens. Un color por métrica para que cada bloque
   tenga su identidad de antes. */
[data-view="tools-admin"] .console-icon--danger {
  color: var(--danger);
}

[data-view="tools-admin"] .console-icon--info {
  color: var(--info);
}

[data-view="tools-admin"] .console-icon--muted {
  color: var(--muted);
}

[data-view="tools-admin"] .console-icon--success {
  color: var(--success);
}

[data-view="tools-admin"] .console-icon--warning {
  color: var(--warning);
}

[data-view="tools-admin"] .console-icon--accent {
  color: var(--accent);
}

/* Insignias del resumen de BD (sustituyen badge-*/ de Bootstrap,
   que son superficies de color sin contraste sobre tema oscuro) */
[data-view="tools-admin"] .console-db-badge {
  font-size: 14px;
  background-color: var(--bg-elev);
  color: var(--text);
  border: 1px solid var(--border);
}

/* Variantes de color de las insignias (ajuste usuario 30-10): recuperan
   los 5 colores de la consola clara (badge-primary/success/info/dark/
   danger de Bootstrap) adaptados al tema oscuro: borde + icono + número
   en el color, sobre el fondo neutro (sin superficies sólidas). */
[data-view="tools-admin"] .console-db-badge--accent {
  border-color: var(--accent);
}

[data-view="tools-admin"] .console-db-badge--success {
  border-color: var(--success);
}

[data-view="tools-admin"] .console-db-badge--info {
  border-color: var(--info);
}

[data-view="tools-admin"] .console-db-badge--danger {
  border-color: var(--danger);
}

[data-view="tools-admin"] .console-db-badge--muted {
  border-color: var(--muted);
}

[data-view="tools-admin"] .console-db-badge--accent,
[data-view="tools-admin"] .console-db-badge--accent i,
[data-view="tools-admin"] .console-db-badge--accent span {
  color: var(--accent);
}

[data-view="tools-admin"] .console-db-badge--success,
[data-view="tools-admin"] .console-db-badge--success i,
[data-view="tools-admin"] .console-db-badge--success span {
  color: var(--success);
}

[data-view="tools-admin"] .console-db-badge--info,
[data-view="tools-admin"] .console-db-badge--info i,
[data-view="tools-admin"] .console-db-badge--info span {
  color: var(--info);
}

[data-view="tools-admin"] .console-db-badge--danger,
[data-view="tools-admin"] .console-db-badge--danger i,
[data-view="tools-admin"] .console-db-badge--danger span {
  color: var(--danger);
}

[data-view="tools-admin"] .console-db-badge--muted,
[data-view="tools-admin"] .console-db-badge--muted i,
[data-view="tools-admin"] .console-db-badge--muted span {
  color: var(--muted);
}

/* Botón naranja de la Consola Live (Buscar/Añadir Nuevas Canciones) */
.btn-anaranjado {
  color: #fff;
  background-color: #fd7e14;
  border-color: #fd7e14;
  font-weight: 600;
}
.btn-anaranjado:hover,
.btn-anaranjado:focus {
  color: #fff;
  background-color: #e8590c;
  border-color: #d9480f;
}
.btn-anaranjado:disabled {
  color: #fff;
  background-color: #ffa85a;
  border-color: #ffa85a;
}

/* Tablas: cabezas y hover sobre tema oscuro (refina .app-table) */
[data-view="tools-admin"] .app-table thead th {
  background-color: var(--bg-elev);
  border-bottom: 2px solid var(--border);
}

[data-view="tools-admin"] .app-table tbody tr:hover td {
  background-color: var(--bg-elev);
}

/* Listas (huérfanas, preview de borrado) */
[data-view="tools-admin"] .list-group-item {
  background-color: var(--bg-elev);
  border-color: var(--border);
  color: var(--text);
}

/* Formularios (Spec 006) sobre tema oscuro */
[data-view="tools-admin"] .form-control {
  background-color: var(--bg-elev);
  border-color: var(--border);
  color: var(--text);
}

[data-view="tools-admin"] .form-control::placeholder {
  color: var(--muted);
}

[data-view="tools-admin"] .form-control:focus {
  background-color: var(--bg-elev);
  border-color: var(--accent);
  color: var(--text);
  box-shadow: none;
}

[data-view="tools-admin"] .form-check-input:checked {
  background-color: var(--accent);
  border-color: var(--accent);
}

[data-view="tools-admin"] .app-field label,
[data-view="tools-admin"] .form-check-label,
[data-view="tools-admin"] label {
  color: var(--muted);
}

/* Salidas de diagnóstico (audit de tokens) */
[data-view="tools-admin"] .token-audit-output {
  background-color: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  color: var(--text);
  max-height: 280px;
  overflow: auto;
}

/* Mini-botones de las filas de géneros (los genera tools_admin.js):
   ➡️ genre→genre_internet y ⬅️ lo inverso. Prefijo admin-tools-arrow
   para no colisionar (nombres heredados de Spec 011). */
.admin-tools-arrow-cell {
  vertical-align: middle;
}

.admin-tools-arrow-btn {
  padding: 0.1rem 0.18rem;
  font-size: 12px;
  line-height: 1;
  width: 26px;
  height: 20px;
  display: block;
  text-align: center;
  margin: 1px 0;
}

.admin-tools-arrow-fallback {
  font-size: 12px;
  color: var(--accent);
  font-weight: 600;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  line-height: 1;
}

.admin-tools-action {
  padding: 0.12rem 0.35rem;
  font-size: 11px;
  line-height: 1;
  height: 24px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

.admin-tools-arrow-cell .d-flex {
  gap: 1px;
  display: inline-block;
}

/* Responsive (F7): pestañas compactas a 375/600 px sin overflow */
@media (max-width: 575.98px) {
  [data-view="tools-admin"] .nav-tabs .nav-link {
    padding: 0.4rem 0.6rem;
    font-size: 0.85rem;
  }
}
/* ==========================================================================
   Estilos de vista — migrados de <style> inline (Spec 008, F2.5)
   --------------------------------------------------------------------------
   Fuente: detalle_cancion.html y detalle_album.html (pilotos F2.5).
   Reglas de la migración (spec 008):
     - Mismos selectores y mismos valores: cambio de ubicación, no de aspecto.
     - Los colores que coinciden con tokens (:root) usan la variable:
       #f6c84c (dorado estrella) → var(--warning).
     - Excepción documentada: .carousel-item img NO se migra (3 valores
       distintos en 3 vistas: 400px/300px/auto) — queda inline en cada
       template.
     - .background-overlay y body NO se migran (T4, opción B acordada):
       8 vistas con 4 variantes distintas; se estudia en una spec futura.
   ========================================================================== */

/* --- detalle_cancion.html (Spec 008) --------------------------------------
   Botón de favorito (estrella). COEXISTE con la copia local de
   biblioteca_musical (más extensa: incluye .favorite, .d-flex, etc.),
   que NO se toca en esta fase. Misma apariencia. */
.favorite-toggle {
  background-color: #6c757d !important;
  border: none !important;
  color: #fff !important;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: .25rem .5rem;
  border-radius: .25rem;
  min-height: 36px;
  height: 36px;
  line-height: 1;
}
.favorite-toggle .star {
  color: rgba(255, 255, 255, 0.95);
  font-weight: 700;
  font-size: 1.05em;
}
.favorite-toggle.favorited .star {
  color: var(--warning);
  text-shadow: 0 0 6px rgba(246, 200, 76, 0.25);
}

/* --- detalle_album.html (Spec 008) -----------------------------------------
   Scroll interno de la tabla de canciones. El tema lo da .app-table (Spec 005). */
.song-table {
  max-height: 300px;
  overflow-y: auto;
}

/* Estrellas de favorito de la tabla: por defecto blanca con contorno negro,
   si favorited → dorada. */
.btn-fav .star {
  color: #ffffff;
  /* 75% más grande que antes (1.05rem * 1.75 ≈ 1.84rem) */
  font-size: 1.85rem;
  line-height: 1;
  /* contorno negro simulado con text-shadows (stroke) */
  text-shadow:
    -1px -1px 0 #000,
    1px -1px 0 #000,
    -1px 1px 0 #000,
    1px 1px 0 #000;
  display: inline-block;
  vertical-align: middle;
}
.btn-fav .star.favorited {
  color: var(--warning);
  text-shadow: 0 0 6px rgba(246, 200, 76, 0.25);
}
.btn-fav {
  min-height: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: .25rem .5rem;
}

/* Carrusel fijo de imágenes del álbum (esquina inferior derecha). */
.image-carousel {
  position: fixed;
  bottom: 10px;
  right: 10px;
  width: 250px;
  z-index: 10;
}
@media (max-width: 768px) {
  .image-carousel {
    position: static;
    width: 100%;
    margin-top: 20px;
  }
}

/* Trazas de enlaces azules en la tabla. COMPARTIDA con explorador_musical
   (la copia inline de explorador se eliminó en la misma tarea). */
.azulita {
  font-weight: bold;
  color: blue;
}

/* ==========================================================================
   Componente de estados (Spec 004, Fase 2 / F2.1)
   -------------------------------------------------------------------------
   Único punto para comunicar el estado de una operación en la UI:
   carga / vacío / error / éxito. Definido una vez, reutilizado en toda la app (P2).
   NO se comunica información solo con color (F4): cada estado lleva icono + texto.
   Solo usa var(--token); nada de valores literales.

   Estructura de marcaje (la genera app-states.js):
     <div class="app-state app-state--error" role="status" aria-live="polite">
       <i class="fas fa-exclamation-triangle app-state__icon" aria-hidden="true"></i>
       <span class="app-state__text">Mensaje</span>
       <button class="app-state__retry" type="button">Reintentar</button>
     </div>
   ========================================================================== */

.app-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-4);
  min-height: 80px;
  text-align: center;
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  color: var(--muted);
}

.app-state__icon {
  font-size: 1.5rem;
  line-height: 1;
}

.app-state__text {
  max-width: 32ch;
}

/* --- Carga: spinner + texto --------------------------------------------- */
.app-state--loading .app-state__icon {
  color: var(--accent);
}

/* --- Vacío: icono neutro + texto atenuado -------------------------------- */
.app-state--empty .app-state__icon {
  color: var(--muted);
}

/* --- Error: icono + texto en color de peligro, con botón de reintento ---- */
.app-state--error .app-state__icon,
.app-state--error .app-state__text {
  color: var(--danger);
}

/* --- Éxito: icono + texto en color de éxito ------------------------------ */
.app-state--success .app-state__icon,
.app-state--success .app-state__text {
  color: var(--success);
}

/* Botón de reintento (solo presente en el estado de error) */
.app-state__retry {
  margin-top: var(--space-1);
  padding: var(--space-1) var(--space-3);
  font-size: var(--font-size-base);
  color: var(--bg);
  background: var(--danger);
  border: none;
  border-radius: var(--radius-md);
  cursor: pointer;
}

  .app-state__retry:hover {
    filter: brightness(1.1);
  }

/* ==========================================================================
   Componente tabla (Spec 005, Fase 2 / F2.2)
   -------------------------------------------------------------------------
   Tabla de datos con tema oscuro (solo tokens) para sustituir las
   "table table-striped" de Bootstrap (tema claro) sobre fondo oscuro.
   Las variantes de tamaño de fuente por pantalla (pantalla grande, etc.)
   se ajustan con la clase de modificador .app-table--xl, no con CSS
   literal por vista (P2: un componente, un sitio).

   Estructura de marcado:
     <table class="app-table [app-table--xl]">
       <thead><tr><th scope="col">Título</th> ... </tr></thead>
       <tbody><tr> <td> ... </td> </tr> </tbody>
     </table>
   ========================================================================== */

.app-table {
  color: var(--text);
  background: var(--bg-card);
  border: var(--border);
  border-radius: var(--radius-md);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  border-collapse: separate;
  border-spacing: 0;
}

.app-table th {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  border-bottom: var(--border);
  color: var(--muted);
  font-weight: 600;
  white-space: nowrap;
}

.app-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--border);
  vertical-align: middle;
}

/* Última fila sin borde inferior (el borde exterior de la tabla lo cierra el contenedor) */
.app-table tbody tr:last-child td {
  border-bottom: none;
}

/* Cabecera sticky dentro de un contenedor con scroll (fija la superficie del th) */
.app-table thead th {
  position: sticky;
  top: 0;
  background: var(--bg-elev);
  z-index: 1;
}

/* Hover sutil de fila (estado de interacción, no de información: F4) */
.app-table tbody tr:hover td {
  background: var(--bg-elev);
}

/* Variante XL: listas que se muestran a pantalla completa (pantallas grandes) */
.app-table--xl {
  font-size: 1.125rem;
}

/* ==========================================================================
   Componente lista (Spec 005, Fase 2 / F2.2)
   -------------------------------------------------------------------------
   Lista de elementos (p. ej. cola de canciones) con filas clicables,
   separadores y estado activo. Solo tokens.

   Estructura de marcado:
     <ul class="app-list">
       <li class="app-list__item [is-active]" role="listitem">Texto de fila</li>
     </ul>
   ========================================================================== */

.app-list {
  list-style: none;
  margin: 0;
  padding: 0;
  color: var(--text);
  background: var(--bg-card);
  /* Fijado a 1px: el componente usa border en los lados/arriba/abajo y
     border-bottom de la fila; un borde variable colapsaría a 0 con tokens. */
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.app-list__item {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
}

.app-list__item:last-child {
  border-bottom: none;
}

.app-list__item:hover {
  background: var(--bg-elev);
}

/* Fila activa (elemento en reproducción / seleccionado) */
.app-list__item.is-active {
  background: var(--accent);
  color: var(--bg);
  font-weight: 600;
}

/* Fila con layout horizontal (portada + textos + acciones de botón).
   Spec 009 (F3.1): sustituye los style="" inline que pintaba mis_listas.js. */
.app-list__item--row {
  display: flex;
  align-items: center;
}

/* Mini-portada dentro de una fila de lista (canción en playlist / fav). */
.app-list__cover {
  width: 36px;
  height: 36px;
  object-fit: cover;
  border-radius: var(--radius-sm);
  display: block;
}

/* Variante grande (fila de favoritos de canción). */
.app-list__cover--lg {
  width: 48px;
  height: 48px;
}

/* Fila arrastrable (modal ver/reordenar playlist, SortableJS). */
.app-list__item--draggable {
  cursor: grab;
}

/* Bootstrap 4.5 no trae .text-truncate: la aportamos para los títulos
   de fila en los listados de app-list. */
.text-truncate {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ==========================================================================
   Vista "Mis listas" (Spec 009, Fase 3 / F3.1) — <body data-view="mis-listas">
   -------------------------------------------------------------------------
   Mini-reproductor propio de la vista (estilo busquedas.html pero con
   tokens y sin <style> inline): la barra va pegada bajo el navbar
   (top: var(--navbar-h)) y el contenido empieza debajo. Layout en dos
   líneas para que nada compita por espacio al reducir la ventana:
     - Línea principal (alto fijo, sin scroll): ⏮ ⏭ [▶ Play Aleatorio]
       + carátula siempre juntos a la izquierda; el <audio> ocupa el
       resto con flex y se encoge (nunca se ocultan botones ni
       carátula).
     - Debajo, a ancho completo, "Artista - Título" en texto pequeño
       (#main-song-info) con ellipsis.
   El <body> lleva data-view="mis-listas" para no afectar a otras vistas
   (biblioteca_musical gestiona su barra con su propio <style> inline).
   ========================================================================== */

[data-view="mis-listas"] .app-player {
  top: var(--navbar-h) !important;
  z-index: 1000;
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  /* El body es tema oscuro pero los <p>/<span> heredaban negro del
     default: el "artista - canción" (#main-song-info) era ilegible. */
  color: var(--text);
}

[data-view="mis-listas"] .app-player .text-muted {
  color: var(--muted);
}

[data-view="mis-listas"] {
  /* Alto de la línea principal del mini-reproductor (más baja que los
     80px de biblioteca_musical/busquedas): 40px, el alto de un botón
     btn-secondary, y el "artista - título" pasa a una segunda línea
     dentro de la barra. */
  --playerbar-h-mis: 40px;
}

[data-view="mis-listas"] .page-content {
  padding-top: calc(var(--navbar-h) + var(--playerbar-h-mis) + var(--space-2));
}

/* Línea principal de la barra: tan alta como un botón (40px) y una sola
   línea sin salto de flex. */
[data-view="mis-listas"] .app-player__main {
  height: var(--playerbar-h-mis);
  flex-wrap: nowrap !important;
  overflow: hidden;
}

/* Carátula del mini-reproductor: se adapta a la línea de 40px. */
[data-view="mis-listas"] .mini-caratula {
  width: 60px;
  height: 34px;
  object-fit: cover;
  margin-left: var(--space-2);
  border-radius: var(--radius-sm);
  flex: 0 0 auto;
}

/* El audio se lleva todo el ancho que sobre tras controles+carátula.
   min-width: 0 permite que el flex se encoga; los botones y la carátula
   son flex: 0 0 auto y nunca se pierden. */
[data-view="mis-listas"] .app-player__audio {
  flex: 1 1 0;
  min-width: 0;
}

[data-view="mis-listas"] .app-player__audio {
  display: flex;
  align-items: center;
  gap: 6px;
}

/* El <audio> nativo se oculta: lo sustituyen los controles propios
   (el nativo mide 54px fijos y desbordaba la línea de 40px). */
[data-view="mis-listas"] .app-player__audio audio {
  display: none;
}

/* Barra de progreso (0:00 / 3:12) — clic/drag para saltar. */
[data-view="mis-listas"] .app-player__seek {
  flex: 1 1 0;
  min-width: 60px;
  height: 6px;
  background: var(--track);
  border-radius: 3px;
  cursor: pointer;
  position: relative;
}

[data-view="mis-listas"] .app-player__seek-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: 3px;
  pointer-events: none;
}

/* Tiempo transcurrido, fijo para no saltar al cambiar de dígito. */
[data-view="mis-listas"] .app-player__time {
  flex: 0 0 auto;
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  min-width: 72px;
  text-align: center;
}

/* Volumen: botón de mudo + deslizador compacto (48px). */
[data-view="mis-listas"] .app-player__vol {
  flex: 0 0 auto;
  font-size: 13px;
  cursor: pointer;
  user-select: none;
}

[data-view="mis-listas"] .app-player__audio input[type="range"] {
  flex: 0 0 48px;
  width: 48px;
  height: 4px;
  accent-color: var(--accent);
  cursor: pointer;
}

/* "Artista - Título" debajo de todo: texto pequeño, una línea con
    ellipsis. */
[data-view="mis-listas"] .app-player__info {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  color: var(--muted);
  padding: 0 var(--space-2) var(--space-1);
}

/* Móvil (<768px): botones un poco más compactos y carátula reducida.
   La etiqueta del botón "Play Aleatorio" ya se degrada con clases
   d-none d-md-inline / d-none d-sm-inline de la plantilla. */
@media (max-width: 767.98px) {
  [data-view="mis-listas"] .btn {
    padding: var(--space-1) var(--space-2);
    font-size: 13px;
  }

  [data-view="mis-listas"] .mini-caratula {
    width: 52px;
    height: 28px;
  }
}

/* Fondo decorado igual que el resto de vistas (fondo.png a 15 % de
   opacidad; ver busquedas.html). */
[data-view="mis-listas"] .background-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-image: url("/static/fondo.png");
  background-repeat: repeat;
  background-size: cover;
  opacity: 0.15;
  z-index: -1;
  pointer-events: none;
}

/* ==========================================================================
   Explorador Musical (Spec 013) — vista /explorador_musical
   -------------------------------------------------------------------------
   Vista de dos paneles a >=992px (árbol / reproductor+canciones+carátulas)
   y en móvil el árbol pasa a un panel deslizante lateral, deslizable con el
   dedo (overflow-y + overscroll-behavior: contain para que el gesto no se
   pierda en la página).

   El mini-reproductor reutiliza el marcado de /mis_listas (spec 009) pero
   acotado a [data-view="explorador"]: NO se toca el bloque de mis_listas
   (reversible y sin colisión).

   El árbol es el web component @webreflection/file-tree (vendor local):
   usa un Shadow DOM que se thema vía propiedades personalizadas (custom
   properties) heredadas desde el elemento <file-tree> — ver abajo.
   ========================================================================== */

[data-view="explorador"] {
  /* Altos de la cabecera fija (navbar + mini-reproductor) para posicionar
     el contenido; y dimensiones del panel del árbol. */
  --playerbar-h-expl: 62px;
  --explorer-header-h: 52px;
  /* Ancho del panel del árbol (pedido 2026-10-29): 384 px, un 20% más
     que los 320 originales; en pantallas grandes (≥1440px) crece hasta
     el 50% de la pantalla. */
  --explorer-aside-w: 384px;
  --explorer-aside-max: 700px;
}

[data-view="explorador"] .background-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-image: url("/static/fondo.png");
  background-repeat: repeat;
  background-size: cover;
  opacity: 0.15;
  z-index: -1;
  pointer-events: none;
}

/* --- Mini-reproductor (misma estructura que mis_listas, acotado) ---------- */

[data-view="explorador"] .explorer-player {
  top: var(--navbar-h);
  z-index: 1000;
  background: var(--bg-card);
  border-bottom: 1px solid var(--border);
  color: var(--text);
}

[data-view="explorador"] .app-player__main {
  display: flex;
  align-items: center;
  height: 40px;
  flex-wrap: nowrap;
  overflow: hidden;
  padding-left: var(--space-3);
  padding-right: var(--space-3);
}

[data-view="explorador"] .app-player__controls {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex: 0 0 auto;
}

[data-view="explorador"] .mini-caratula {
  width: 60px;
  height: 34px;
  object-fit: cover;
  margin-left: var(--space-2);
  border-radius: var(--radius-sm);
  flex: 0 0 auto;
}

[data-view="explorador"] .app-player__audio {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 6px;
}

/* El <audio> nativo se oculta: lo sustituyen los controles propios. */
[data-view="explorador"] .app-player__audio audio {
  display: none;
}

[data-view="explorador"] .app-player__seek {
  flex: 1 1 0;
  min-width: 60px;
  height: 6px;
  background: var(--track);
  border-radius: 3px;
  cursor: pointer;
  position: relative;
}

[data-view="explorador"] .app-player__seek-fill {
  height: 100%;
  width: 0%;
  background: var(--accent);
  border-radius: 3px;
  pointer-events: none;
}

[data-view="explorador"] .app-player__time {
  flex: 0 0 auto;
  font-size: 11px;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  min-width: 40px;
  text-align: center;
}

[data-view="explorador"] .app-player__vol {
  flex: 0 0 auto;
  font-size: 13px;
  cursor: pointer;
  user-select: none;
}

[data-view="explorador"] .app-player__audio input[type="range"] {
  flex: 0 0 48px;
  width: 48px;
  height: 4px;
  accent-color: var(--accent);
  cursor: pointer;
}

[data-view="explorador"] .app-player__info {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 12px;
  color: var(--muted);
  padding: 0 var(--space-3) var(--space-1);
  margin: 0;
}

/* La canción en curso se pinta con <span class="azulita"> (color blue),
   que sobre la barra oscura del mini-reproductor no se lee bien; se
   cambia a blanco (pedido 2026-10-29). */
[data-view="explorador"] .app-player__info .azulita {
  color: #ffffff;
}

/* Móvil (<768px): como mis_listas, botones compactos y carátula reducida,
   para que seek + tiempo + volumen quepan en 375px (F7). */
@media (max-width: 767.98px) {
  [data-view="explorador"] .btn {
    padding: var(--space-1) var(--space-2);
    font-size: 13px;
  }

  [data-view="explorador"] .mini-caratula {
    width: 52px;
    height: 28px;
  }

  /* El deslizador de volumen no cabe en 375px: se oculta y queda el
     botón de mudo (mis_listas tiene el mismo desborde latente). */
  [data-view="explorador"] #vol-range {
    display: none;
  }
}

/* --- Cabecera de vista ---------------------------------------------------- */

[data-view="explorador"] .explorer-header {
  margin-top: calc(var(--navbar-h) + var(--playerbar-h-expl));
  background: var(--bg-elev);
  border-bottom: 1px solid var(--border);
}

[data-view="explorador"] .explorer-header__row {
  height: var(--explorer-header-h);
  padding-left: var(--space-3);
  padding-right: var(--space-3);
}

[data-view="explorador"] .explorer-header__title {
  /* Fila flexible: título fijo, estado de reproducción se trunca y la
     barra de acciones (ml-auto) se mantiene siempre visible (F7 a 375 px). */
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1 1 0;  /* cede su ancho ante la barra de acciones (ml-auto) */
  min-width: 0; /* permite encogerse por debajo del ancho del contenido */
  white-space: nowrap;
}

[data-view="explorador"] .explorer-header__title i {
  flex: 0 0 auto;
}

[data-view="explorador"] .explorer-header__name {
  flex: 0 0 auto;
  font-size: 1.15rem;
  font-weight: 600;
  color: var(--text);
}

[data-view="explorador"] .explorer-header__title .explorer-repro-status {
  flex: 0 1 auto;
  max-width: 100%;
}

[data-view="explorador"] .explorer-header__title i,
[data-view="explorador"] .explorer-panel__title i {
  color: var(--accent);
}

/* Estado de reproducción en la cabecera (spec 013 §10.4): texto atenuado
   tras el título. Se trunca al ancho disponible (con ellipsis) para que
   nunca desborde en los 4 anchos de F7; la ruta completa está en el
   tooltip (lo pinta el JS en el atributo title). */
[data-view="explorador"] .explorer-repro-status {
  margin-left: 0; /* el gap del flex ya separa */
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  vertical-align: baseline;
}

@media (min-width: 768px) {
  [data-view="explorador"] .explorer-repro-status {
    max-width: 320px; /* límite superior: sin exceder la cabecera en 1000/1500 */
  }
}

/* --- Layout de dos paneles ------------------------------------------------- */

[data-view="explorador"] .explorer-layout {
  display: flex;
  gap: var(--space-3);
  height: calc(100vh - var(--navbar-h) - var(--playerbar-h-expl)
               - var(--explorer-header-h));
  padding: var(--space-3);
  overflow: hidden;
}

[data-view="explorador"] .explorer-tree-panel {
  flex: 0 0 var(--explorer-aside-w);
  max-width: var(--explorer-aside-max);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  overflow: hidden;
  display: flex;
}

/* Pantallas grandes (≥1440px): el árbol ocupa hasta el 50% de la
   pantalla (pedido 2026-10-29), con tope de 700 px para que el panel de
   canciones nunca quede en minoría. */
@media (min-width: 1440px) {
  [data-view="explorador"] .explorer-tree-panel {
    flex: 0 0 auto;
    width: min(50vw, var(--explorer-aside-max));
  }
}

/* Interior scrollable: en móvil se desliza con el dedo; overscroll-behavior
   "contain" para que el gesto no se propague a la página (F7). */
[data-view="explorador"] .explorer-tree-panel__inner {
  flex: 1 1 auto;
  min-width: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
  padding: var(--space-2);
}

[data-view="explorador"] .explorer-main {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  overscroll-behavior: contain;
}

[data-view="explorador"] .explorer-panel {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  /* Item de flex (columna): sin min-width: 0 la tabla de la cola fija el
     ancho mínimo al min-content de la tabla y desborda el panel (F7). */
  min-width: 0;
}

[data-view="explorador"] .explorer-panel__title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--text);
}

/* --- Cola ordenable (spec 013 §10.6) ---------------------------------------
   Escritorio (≥768 px): tabla de 4 columnas con cabeceras clicables.
   Móvil (<768 px): select "Ordenar por…" + la lista .app-list de antes. */

[data-view="explorador"] .explorer-sort {
  margin-bottom: var(--space-2);
}

[data-view="explorador"] .explorer-sort__label {
  margin-right: 0.5rem;
  font-size: 0.85rem;
  color: var(--muted);
}

[data-view="explorador"] .explorer-sort__select {
  width: auto;
  flex: 0 1 180px;
  min-width: 0;
}

[data-view="explorador"] .explorer-table-wrap--empty {
  display: none;
}

@media (max-width: 767.98px) {
  /* Móvil: la lista .app-list sustituye a la tabla (spec 013 §10.6). */
  [data-view="explorador"] .explorer-table-wrap {
    display: none;
  }
}

[data-view="explorador"] .explorer-table {
  width: 100%;
  /* Ancho fijo por columna: sin table-layout el min-content de las
     cabeceras nowrap + nombres largos hace desbordar la tabla en los
     paneles estrechos (F7 a 600/1000 px). */
  table-layout: fixed;
  border-collapse: collapse;
  color: var(--text);
}

[data-view="explorador"] .explorer-table th,
[data-view="explorador"] .explorer-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--border);
  font-size: var(--font-size-base);
  line-height: var(--line-height-base);
  overflow: hidden;
  text-overflow: ellipsis;
}

[data-view="explorador"] .explorer-table thead th {
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  text-align: left;
  font-weight: 600;
  color: var(--muted);
}

[data-view="explorador"] .explorer-table thead th:hover,
[data-view="explorador"] .explorer-table thead th:focus-visible {
  color: var(--text);
  outline: none;
}

/* Indicador de orden: flecha en la cabecera activa. */
[data-view="explorador"] .explorer-table thead th.is-sorted-asc::after {
  content: ' \2191';
  color: var(--accent);
}

[data-view="explorador"] .explorer-table thead th.is-sorted-desc::after {
  content: ' \2193';
  color: var(--accent);
}

[data-view="explorador"] .explorer-table tbody tr {
  cursor: pointer;
}

[data-view="explorador"] .explorer-table tbody tr:hover {
  background: var(--bg-elev);
}

/* Fila en reproducción. */
[data-view="explorador"] .explorer-table tbody tr.is-active {
  background: var(--accent);
  color: var(--bg);
  font-weight: 600;
}

[data-view="explorador"] .explorer-table__num {
  text-align: right;
  white-space: nowrap;
}

/* Anchos fijos con table-layout: fixed. Con 'fixed' la primera fila
   (thead) define el ancho de cada columna; la de canción (1ª, sin ancho
   explícito) absorbe el sobrante y se trunca con ellipsis. */
[data-view="explorador"] .explorer-table th:nth-child(2) {
  width: 30%;
}
[data-view="explorador"] .explorer-table th:nth-child(3),
[data-view="explorador"] .explorer-table th:nth-child(4) {
  width: 11%;
}

/* Subtexto de la fila móvil (artista · duración · año). */
[data-view="explorador"] #pending-songs .explorer-list__sub {
  display: block;
  font-size: 0.78rem;
  color: var(--muted);
}

[data-view="explorador"] #pending-songs .app-list__item.is-active .explorer-list__sub {
  color: inherit;
  opacity: 0.85;
}

/* Carátulas: carrusel Bootstrap contenido en el panel. */
[data-view="explorador"] #cover-carousel .carousel-inner {
  height: auto;
}

[data-view="explorador"] #cover-carousel .carousel-item {
  text-align: center;
}

[data-view="explorador"] #cover-carousel img {
  width: auto;
  max-width: 100%;
  max-height: 220px;
  margin: 0 auto;
  object-fit: contain;
  border-radius: var(--radius-sm);
}

/* --- Árbol: tema del web component <file-tree> ----------------------------
   El componente usa Shadow DOM y se pinta con currentColor + las custom
   properties --padding/--icon-size/--hover-color heredadas desde el
   elemento; por eso el tema se declara EN el elemento, no en los nodos. */

[data-view="explorador"] file-tree {
  --padding: 4px;
  --icon-size: 14px;
  color: var(--text);
  font-size: 0.95rem;
  min-height: 100px;
  /* Spec 013 §10.2: los iconos del componente son data-URIs de SVG con
     stroke="currentColor" usadas como background-image; dentro de una
     imagen currentColor NO hereda el color CSS y cae a negro, por eso
     no se veían sobre el fondo oscuro. Se redefinen las propiedades
     del componente con los MISMOS trazos pero en blanco (#f4f4f4,
     codificado %23 por estar dentro de url()): carpetas y archivos
     (--svg-folder/--svg-file/--svg-text) y los indicadores de
     expandir/colapsar (--svg-opened/--svg-closed). */
  --svg-opened: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="%23f4f4f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none" shape-rendering="geometricPrecision"><path d="M8 12h8" /></svg>');
  --svg-closed: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="%23f4f4f4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none" shape-rendering="geometricPrecision"><path d="M12 8v8" /><path d="M8 12h8" /></svg>');
  --svg-folder: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="%23f4f4f4" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" fill="none" shape-rendering="geometricPrecision"><path d="M2.707 7.454V5.62C2.707 4.725 3.469 4 4.409 4h4.843c.451 0 .884.17 1.204.474l.49.467c.126.12.296.186.473.186h8.399c.94 0 1.55.695 1.55 1.59v.737m-18.661 0h-.354a.344.344 0 00-.353.35l.508 11.587c.015.34.31.609.668.609h17.283c.358 0 .652-.269.667-.61L22 7.805a.344.344 0 00-.353-.35h-.278m-18.662 0h18.662"></path></svg>');
  /* Nota musical en vez de "fichero genérico": todos los ficheros del
     árbol son canciones de audio (mp3/wav/flac), así que el icono de
     nota comunica mejor su contenido. Mismo trazo blanco que el resto. */
  --svg-file: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" stroke="%23f4f4f4" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" fill="none" shape-rendering="geometricPrecision"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg>');
  --svg-text: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24" stroke="%23f4f4f4" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" fill="none" shape-rendering="geometricPrecision"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"></path><path d="M14 2v6h6M16 13H8M16 17H8M10 9H8"></path></svg>');
}

/* Estado de "cargando" del componente (li.waiting): que se note en el tema. */
[data-view="explorador"] .explorer-tree-panel__inner {
  font-variant-numeric: tabular-nums;
}

/* --- Menú contextual del árbol -------------------------------------------- */

[data-view="explorador"] .explorer-ctx {
  position: fixed;
  min-width: 220px;
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-2);
  z-index: var(--z-modal);
  padding: var(--space-1) 0;
  color: var(--text);
  font-size: 0.9rem;
}

[data-view="explorador"] .explorer-ctx__item {
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
}

[data-view="explorador"] .explorer-ctx__item:hover {
  background: var(--bg-card);
}

/* --- Fondo del panel deslizante (móvil) ------------------------------------ */

[data-view="explorador"] .explorer-backdrop {
  position: fixed;
  top: calc(var(--navbar-h) + var(--playerbar-h-expl));
  bottom: 0;
  left: 0;
  right: 0;
  background: var(--bg);
  /* El fondo oscuro a medio opacio atenúa la vista de detrás (F7). */
  opacity: 0.5;
  z-index: 1070;
}

/* --- Toasts (spec 004) ------------------------------------------------------
   Aun sin componente de toasts global, esta es la primera vista que los
   necesita: clases .app-toasts/.app-toast globales (con tokens) para
   reutilizarlas cuando se creen el componente compartido. */

.app-toasts {
  position: fixed;
  top: calc(var(--navbar-h) + 64px + var(--space-2));
  right: var(--space-3);
  z-index: 1300;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  pointer-events: none;
}

.app-toast {
  background: var(--bg-elev);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-2);
  color: var(--text);
  padding: var(--space-2) var(--space-3);
  max-width: 320px;
  font-size: 0.9rem;
}

.app-toast--success {
  border-left-color: var(--success);
}

.app-toast--error {
  border-left-color: var(--danger);
}

/* --- F7: móvil (<992px) — el árbol se convierte en panel deslizante -------- */

@media (max-width: 991.98px) {
  [data-view="explorador"] .explorer-tree-panel {
    position: fixed;
    top: calc(var(--navbar-h) + var(--playerbar-h-expl));
    bottom: 0;
    left: 0;
    width: min(85vw, 340px);
    max-width: none;
    border: none;
    border-right: 1px solid var(--border);
    border-radius: 0;
    box-shadow: var(--shadow-2);
    z-index: 1080;
    transform: translateX(-105%);
    transition: transform 0.25s ease;
  }

  [data-view="explorador"] .explorer-tree-panel.open {
    transform: translateX(0);
  }
}

@media (max-width: 767.98px) {
  /* Cabecera: menos espacio para título y botones. */
  [data-view="explorador"] .explorer-header__title {
    font-size: 1rem;
  }

  [data-view="explorador"] .btn {
    padding: var(--space-1) var(--space-2);
    font-size: 13px;
  }

  [data-view="explorador"] .mini-caratula {
    width: 52px;
    height: 28px;
  }
}

/* ==========================================================================
   Componente tarjeta (Spec 005, Fase 2 / F2.2)
   -------------------------------------------------------------------------
   Tarjeta de datos (álbum o canción): portada + título + metadata +
   acciones. Unifica el marcado repetido en biblioteca_musical (8 secciones
   Jinja + 2 generadores en JS). La SUPERFICIE (fondo, borde, radio) la da
   Bootstrap (.card); esta sección solo aporta la estructura y las
   medidas tipográficas de portada/título/texto, que antes estaban
   duplicadas 4-5 veces en el <style> inline de la plantilla.

   Los tamaños responsivos NO son tokens (son dimensiones de layout): se
   definen una única vez a continuación como propiedades personalizadas y
   las plantillas solo las reajustan por breakpoint cuando hacen falta.

   Estructura de marcado (Jinja y JS usan el mismo):
     <div class="card app-card [app-card--album | app-card--song]">
       <img class="card-img-top app-card__cover" ...>
       <div class="card-body">
         <h5 class="card-title">Título</h5>
         <p class="card-text">Artista: ...</p>
         <div class="d-flex ..."> <p class="card-text mb-0">Año: ...</p> ... </div>
         <div class="d-flex ... app-card__actions"> botones </div>
       </div>
     </div>
   ========================================================================== */

:root {
  /* Propiedades de layout del componente tarjeta (ajustables por breakpoint
     desde la plantilla; no son tokens de color ni tipografía) */
  --app-card-cover-album: 200px; /* alto máx. de la portada de álbum */
  --app-card-cover-song: 100px;  /* alto máx. de la portada de canción */
  --app-card-title-fs: 1.25rem;  /* tamaño de fuente del título */
  --app-card-text-fs: 1rem;      /* tamaño de fuente del texto secundario */
}

.app-card__cover {
  object-fit: cover;
  width: 100%;
  display: block;
}

.app-card--album .app-card__cover {
  max-height: var(--app-card-cover-album);
  min-height: var(--app-card-cover-album); /* portada ausente: no colapsar */
}

.app-card--song .app-card__cover {
  max-height: var(--app-card-cover-song);
  min-height: var(--app-card-cover-song); /* portada ausente: no colapsar */
}

.app-card .card-title {
  font-size: var(--app-card-title-fs);
}

.app-card .card-text {
  font-size: var(--app-card-text-fs);
}

/* Acciones de la tarjeta: botones de altura uniforme (mismo tamaño en todas
   las vistas: + Info / ★ / reset contador) */
.app-card__actions .btn {
  min-height: calc(1.5em + 0.6rem + 2px);
}

/* ==========================================================================
   Componente modal (Spec 006, Fase 2 / F2.3)
   -------------------------------------------------------------------------
   Superficie oscura + z-index estándar para los modales Bootstrap 4.
   Bootstrap aporta el comportamiento (fade, backdrop, show/hide); .app-modal
   solo aporta la APARIENCIA por tokens y un único z-index, que antes se
   repetía inline en cada vista (car_mode 2500/2400, biblioteca suyo).

   CONTRATO DE MARCADO (obligatorio en todo modal de la app):
     <div class="modal fade app-modal" id="..." tabindex="-1"
          role="dialog" aria-labelledby="...Label">
       <div class="modal-dialog" role="document">
         <div class="modal-content">
           <div class="modal-header">
             <h5 class="modal-title" id="...Label">Título</h5>
             <button type="button" class="close" data-dismiss="modal"
                     aria-label="Cerrar"><span aria-hidden="true">&times;</span></button>
           </div>
           <div class="modal-body"> ... </div>
           <div class="modal-footer">
             <button type="button" class="btn btn-secondary" data-dismiss="modal">Cancelar</button>
             <button type="button" class="btn btn-primary">...acción...</button>
           </div>
         </div>
       </div>
     </div>

   Política: `fade` obligatorio · `role="dialog"` + `aria-labelledby` obligatorios ·
   botón × con aria-label="Cerrar" · botones de cierre "Cerrar"/"Cancelar".
   Solo var(--token); nada de valores literales.
   ========================================================================== */

.app-modal {
  z-index: var(--z-modal);
}

.app-modal .modal-content {
  background-color: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-2);
}

.app-modal .modal-header {
  border-bottom: 1px solid var(--border);
}

.app-modal .modal-footer {
  border-top: 1px solid var(--border);
}

.app-modal .modal-title {
  color: var(--text);
  font-size: var(--font-size-base);
  font-weight: 600;
}

.app-modal .modal-body {
  color: var(--text);
}

/* La × de cierre hereda el color de texto del modal */
.app-modal .close {
  color: var(--muted);
}

.app-modal .close:hover {
  color: var(--text);
}

/* ==========================================================================
   Componente formulario (Spec 006, Fase 2 / F2.3)
   -------------------------------------------------------------------------
   Campos y estados de validación sobre tema oscuro. Antes el ajuste de
   .form-control se repetía inline en ajustes/generator/busquedas; aquí se
   define una vez. La validación usa HTML5 (required) + Bootstrap
   (novalidate + was-validated): al fallar, el campo toma .is-invalid y
   se muestra .invalid-feedback (reemplaza el alert() nativo).

   CONTRATO DE MARCADO:
     <form class="app-form" novalidate>
       <div class="app-field">
         <label for="x">Etiqueta</label>
         <input type="text" class="form-control" id="x" name="x" required>
         <div class="invalid-feedback">Mensaje de error</div>
       </div>
       <button type="submit" class="btn btn-primary">Guardar</button>
     </form>

   TABLA DE BOTONES DE ACCIÓN (contrato de toda la app):
     guardar/confirmar guardado → .btn-primary
     confirmar/copiar            → .btn-success
     cancelar                    → .btn-secondary
     eliminar (destrutivo)       → .btn-danger
     buscar                      → .btn-primary
   Solo var(--token); nada de valores literales.
   ========================================================================== */

.app-field {
  margin-bottom: var(--space-3);
}

.app-field label {
  display: block;
  margin-bottom: var(--space-1);
  color: var(--muted);
  font-size: var(--font-size-base);
}

.app-form .form-control {
  background-color: var(--bg-elev);
  border-color: var(--border);
  color: var(--text);
}

.app-form .form-control::placeholder {
  color: var(--muted);
}

.app-form .form-control:focus {
  background-color: var(--bg-elev);
  border-color: var(--accent);
  color: var(--text);
  box-shadow: none; /* anillo de focus por borde de acento, no por shadow */
}

/* Texto del select: las <option> no heredan bien sobre fondo oscuro */
.app-form select.form-control option {
  background-color: var(--bg-card);
  color: var(--text);
}

/* Estados de validación:
   - ERROR: pseudo-clase :invalid (p. ej. campo required vacío). Con `novalidate`
     en el <form> el navegador NO muestra su mensaje nativo; el campo se pinta
     con el borde de --danger y se muestra la .invalid-feedback (en vivo).
   - ÉXITO: clase .is-valid (opt-in) para un feedback explícito de campo válido. */
.app-form .form-control:invalid {
  border-color: var(--danger);
}

.app-form .form-control:invalid ~ .invalid-feedback {
  display: block;
}

.app-form .form-control.is-valid {
  border-color: var(--success);
}

.app-form .invalid-feedback {
  color: var(--danger);
}

.app-form .valid-feedback {
  color: var(--success);
}

/* ==========================================================================
   Vistas de detalle — spec 016 (rediseño detalle_album / detalle_cancion)
   --------------------------------------------------------------------------
   Cabecera (volver + título) y paneles de las dos vistas de la biblioteca.
   Ancladas por [data-view="..."] en el <body> para no afectar a otras vistas
   (mismo patrón que tools-admin y mis-listas). Solo var(--token).
   ========================================================================== */

/* --- Cabecera común a ambas vistas -------------------------------------- */
[data-view="detalle-album"] .detalle-header,
[data-view="detalle-cancion"] .detalle-header {
  margin-bottom: var(--space-4);
}

[data-view="detalle-album"] .detalle-header h1,
[data-view="detalle-cancion"] .detalle-header h1 {
  margin-bottom: var(--space-1);
}

/* --- detalle_cancion (data-view="detalle-cancion") -----------------------
   Móvil: una sola columna. ≥768 px: dos columnas (carátula 5/12, detalles +
   form 7/12), la carátula acota su altura para que quepa junto al resto. */
[data-view="detalle-cancion"] .detalle-cancion-main {
  max-width: 960px;
  margin: 0 auto;
}

[data-view="detalle-cancion"] .detalle-pan {
  /* body no define color por defecto en estas vistas: fijamos texto claro
     para que h3/h5 dentro del panel oscuro sean legibles */
  color: var(--text);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  margin-bottom: var(--space-4);
}

[data-view="detalle-cancion"] .detalle-fichero {
  color: var(--muted);
  word-break: break-all;
}

[data-view="detalle-cancion"] .detalle-caratula img {
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-1);
}

[data-view="detalle-cancion"] .detalle-caratula {
  text-align: center;
}

[data-view="detalle-cancion"] .detalle-caratula-img {
  width: 100%;
  height: auto;
}

/* Carátula con protagonismo (ajuste usuario 30-10): la imagen vive dentro de
   .detalle-caratula-fig (position relative) y el botón "Ampliar" queda
   superpuesto arriba a la derecha, visible al hover/focus. El halo de acento
   + sombra separa la carátula del panel sin tocar el componente global. */
[data-view="detalle-cancion"] .detalle-caratula-fig {
  position: relative;
  display: inline-block;
  padding: var(--space-2);
  border-radius: var(--radius-md);
  background: var(--bg-elev);
  box-shadow: 0 0 0 2px var(--accent), var(--shadow-2);
}

[data-view="detalle-cancion"] .detalle-caratula-fig .detalle-caratula-img {
  display: block;
  border-radius: calc(var(--radius-md) - var(--space-1));
}

[data-view="detalle-cancion"] .detalle-caratula-zoom {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 50%;
  background: var(--bg-card);
  color: var(--text);
  box-shadow: var(--shadow-1);
  cursor: pointer;
  opacity: 0;
  transition: opacity .15s;
}

[data-view="detalle-cancion"] .detalle-caratula-fig:hover .detalle-caratula-zoom,
[data-view="detalle-cancion"] .detalle-caratula-zoom:focus-visible {
  opacity: 1;
}

/* Lightbox de carátula: imagen a pantalla (modal estándar .app-modal). */
[data-view="detalle-cancion"] .lightbox-body {
  background: var(--bg);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-3);
}

[data-view="detalle-cancion"] .lightbox-img {
  max-width: min(92vw, 720px);
  max-height: 78vh;
  object-fit: contain;
}

/* Controles admin de carátula (ajuste usuario 30-10): hija directa de
   .detalle-cancion-grid. Móvil: fila compacta bajo el contenido (el usuario
   aprobó que se quede donde caiga). ≥768 px: fila 2 a lo ancho, debajo del
   panel "Detalles de la Canción". */
[data-view="detalle-cancion"] .detalle-caratula-admin {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}

/* Móvil (una sola columna): los controles admin se apilan justo bajo la
   carátula (posición anterior a este ajuste), no tras el panel de detalles. */
@media (max-width: 767.98px) {
  [data-view="detalle-cancion"] .detalle-cancion-grid {
    display: grid;
    grid-template-columns: 1fr;
  }

  [data-view="detalle-cancion"] .detalle-cancion-grid .detalle-caratula {
    order: 1;
  }

  [data-view="detalle-cancion"] .detalle-cancion-grid .detalle-caratula-admin {
    order: 2;
    margin-bottom: var(--space-3);
  }

  [data-view="detalle-cancion"] .detalle-cancion-grid .detalle-cancion-col {
    order: 3;
  }
}

/* ≥768 px: dos columnas (carátula 320px + detalles), el form de edición
   queda fuera de la grid a ancho completo debajo. (Ajuste usuario, spec 016) */
@media (min-width: 768px) {
  [data-view="detalle-cancion"] .detalle-cancion-grid {
    display: grid;
    grid-template-columns: 380px 1fr;
    gap: var(--space-4);
    align-items: start;
  }

  [data-view="detalle-cancion"] .detalle-cancion-grid .detalle-pan {
    margin-bottom: 0;
  }

  /* La carátula se adapta a la columna y acota su altura para que quepa
     junto al panel de detalles */
  [data-view="detalle-cancion"] .detalle-caratula-img {
    max-height: 380px;
    object-fit: contain;
  }

  /* Admin: fila 2 de la grid, a lo ancho (debajo del panel de detalles) */
  [data-view="detalle-cancion"] .detalle-caratula-admin {
    grid-column: 1 / -1;
  }

  [data-view="detalle-cancion"] .detalle-cancion-edit {
    margin-top: var(--space-4);
  }
}

/* Form de edición: a ≥768 los campos van 2 por fila (sin estirar a ancho
   completo) y el botón de guardar ocupa la fila final. */
@media (min-width: 768px) {
  [data-view="detalle-cancion"] .detalle-cancion-edit .app-form {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0 var(--space-4);
  }

  [data-view="detalle-cancion"] .detalle-cancion-edit .app-form > .btn {
    grid-column: 1 / -1;
    justify-self: start;
  }
}

[data-view="detalle-cancion"] .detalle-caratula .app-form {
  text-align: left;
}

/* Imagen central del modal de selección de carátulas (JS) */
[data-view="detalle-cancion"] .caratula-preview {
  min-height: 240px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-elev);
  border-radius: var(--radius-md);
}

[data-view="detalle-cancion"] #caratula-img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}

/* Input de subida de carátula (solo admin): ancho acotado */
[data-view="detalle-cancion"] .detalle-upload-file {
  max-width: 260px;
}

/* --- detalle_album (data-view="detalle-album") ---------------------------
   Carrusel de carátulas + form de edición a la izquierda; tabla de
   canciones + reproductor a la derecha (se apilan solas en móvil, grid
   de Bootstrap). */
[data-view="detalle-album"] .detalle-pan {
  color: var(--text);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

/* Tabla de canciones: en 375 px las celdas con padding desbordan ~4 px.
   table-layout fixed reparte el ancho disponible entre columnas (la de
   título absorbe el resto) sin tocar la .app-table global. (F7) */
@media (max-width: 575.98px) {
  [data-view="detalle-album"] .song-table table {
    table-layout: fixed;
    width: 100%;
  }

  [data-view="detalle-album"] .song-table .app-table th,
  [data-view="detalle-album"] .song-table .app-table td {
    padding-left: var(--space-2);
    padding-right: var(--space-2);
  }
}

[data-view="detalle-album"] .detalle-album-sub {
  margin-top: calc(-1 * var(--space-2));
  color: var(--muted);
}

/* Aspa de borrar carátula sobre el carrusel principal (era style="...") */
[data-view="detalle-album"] .btn-delete-caratula {
  position: absolute;
  top: var(--space-2);
  left: 50%;
  transform: translateX(-50%);
  z-index: 20;
  border-radius: 50%;
  padding: .25rem .5rem;
  font-weight: bold;
}

/* Cuenta atrás del "Buscar en Internet" (la genera detalle_album.js) */
[data-view="detalle-album"] .cuenta-atras {
  position: absolute;
  bottom: 10px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 5rem;
  font-weight: bold;
  color: var(--danger);
}

/* Visor grande de carátulas: la imagen ocupa casi toda la pantalla */
[data-view="detalle-album"] .modal-caratula-visor {
  max-width: 80vw;
}

[data-view="detalle-album"] .modal-caratula-visor .carousel-item img {
  max-height: 80vh;
  object-fit: contain;
}

/* ==========================================================================
   Accesibilidad: foco visible (Spec 007, Fase 2 / F2.4)
   -------------------------------------------------------------------------
   Anillo de foco global para navegación por TECLADO. Con :focus-visible el
   navegador solo lo pinta con foco por teclado (no con click), así la
   apariencia con ratón no cambia. Solo usa var(--token).

  Reglas:
   1. :focus-visible global (botones, enlaces, inputs, select, textarea):
      anillo de --focus-ring. Bootstrap pone outline none sobre .btn,
      .form-control, etc.; estas reglas ganan porque app.css se carga tras
      Bootstrap y la especificidad es igual o mayor.
   2. .app-form .form-control:focus-visible no añade el anillo: el borde de
      acento que pinta Spec 006 ya es el indicador de foco (no duplicar).
   ========================================================================== */

button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* El input range no lleva box-shadow visible; el borde de acento basta */
input[type="range"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Spec 006: los campos .app-form ya indican foco con borde de acento;
   sin anillo doble (el del borde ya supera 3:1). */
.app-form .form-control:focus-visible {
  box-shadow: none;
}

/* ==========================================================================
   Vista "Ajustes" (Spec 015, tareas 4-5) — <body data-view="ajustes">
   -------------------------------------------------------------------------
   Rediseño: cabecera + 5 bloques (Cuenta/Foto, Favoritos, Más reproducidas,
   Copiar unificado). Sustituye el <style> inline antiguo (fondo.png +
   overlay + tarjetas claras) por el fondo estándar de la app por tokens,
   coherente con el resto de las vistas (patrón de tools-admin, spec 014).
   Solo var(--token); nada de valores literales.
   ========================================================================== */

[data-view="ajustes"] {
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
}

/* Tarjetas de la vista: tema oscuro por tokens (igual que tools-admin,
   spec 014); sustituye a las .card claras de Bootstrap de la vieja vista. */
[data-view="ajustes"] .card {
  background-color: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  color: var(--text);
}

[data-view="ajustes"] .card-header {
  background-color: var(--bg-elev);
  border-bottom: 1px solid var(--border);
  color: var(--text);
}

/* Texto secundario de la vista (pequeños, muted) */
[data-view="ajustes"] .text-muted {
  color: var(--muted) !important;
}

/* Contadores de las tarjetas */
[data-view="ajustes"] small {
  color: var(--muted);
}

[data-view="ajustes"] .ajustes-main {
  padding-top: calc(var(--navbar-h) + var(--space-4));
  padding-bottom: var(--space-5);
  max-width: 960px;
}

[data-view="ajustes"] .ajustes-header {
  margin-bottom: var(--space-4);
}

[data-view="ajustes"] .ajustes-header__sub {
  color: var(--muted);
}

/* Cuenta (ajuste usuario 30-10): UNA tarjeta compacta tipo "fila de
   settings" (patrón de productos tipo Spotify): elemento a la izquierda,
   acciones btn-sm a la derecha. Sustituye las dos tarjetas .ajustes-top
   (Cuenta/Foto) que ocupaban la misma altura que los bloques de abajo. */
[data-view="ajustes"] .ajustes-cuenta {
  margin-bottom: var(--space-4);
}

[data-view="ajustes"] .ajustes-cuenta__row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-3);
}

[data-view="ajustes"] .ajustes-cuenta__avatar {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: 50%;
  border: 1px solid var(--border);
  flex: 0 0 auto;
}

[data-view="ajustes"] .ajustes-cuenta__info {
  flex: 1 1 200px;
  min-width: 0;
}

[data-view="ajustes"] .ajustes-cuenta__title {
  font-size: 1.05rem;
  font-weight: 600;
}

[data-view="ajustes"] .ajustes-cuenta__hint {
  color: var(--muted);
  font-size: 0.85rem;
}

[data-view="ajustes"] .ajustes-cuenta__actions {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-left: auto;
  position: relative; /* ancla del <input type=file> sr-only */
}

/* El <input type=file> nativo se oculta (patrón sr-only): el botón
   "Cambiar foto" abre el diálogo; sigue siendo enfocable por teclado. */
[data-view="ajustes"] .ajustes-cuenta__file {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Fila de contraseña: en móvil los campos se apilan a ancho completo;
   a ≥768 van en una sola línea compacta (actual | nueva+confirmar | Guardar) */
[data-view="ajustes"] .ajustes-cuenta__nuevas {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2);
}

@media (min-width: 768px) {
  [data-view="ajustes"] .ajustes-cuenta__row--pass .ajustes-cuenta__form {
    display: grid;
    grid-template-columns: 200px 1fr auto;
    align-items: start;
    gap: 0 var(--space-3);
    flex: 1 1 420px;
  }

  [data-view="ajustes"] .ajustes-cuenta__nuevas {
    grid-template-columns: 1fr 1fr;
  }

  [data-view="ajustes"] .ajustes-cuenta__save {
    justify-self: end;
  }
}

[data-view="ajustes"] .ajustes-cuenta__sep {
  border-top: 1px solid var(--border);
}

/* Listas de canciones: scroll propio (como el antiguo 400px) y rejilla de
   tarjetas estándar .app-card (mínimo 2 columnas a ≥768). */
[data-view="ajustes"] .ajustes-scroll {
  max-height: 420px;
  overflow-y: auto;
}

[data-view="ajustes"] .ajustes-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
}

@media (min-width: 768px) {
  [data-view="ajustes"] .ajustes-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Tarjetas de la rejilla: el .app-card--song lleva portada 100px fija;
   aquí solo aseguramos que el item de la grid no colapse. */
[data-view="ajustes"] .ajustes-grid__item {
  min-width: 0;
}

/* Cabecera de "más reproducidas": el filtro flota a la derecha y se
   ajusta al ancho en móvil (F7: sin overflow a 375 px). */
[data-view="ajustes"] .ajustes-filtro {
  max-width: 220px;
  margin-left: var(--space-2);
}

@media (max-width: 767.98px) {
  [data-view="ajustes"] .ajustes-filtro {
    max-width: none;
    margin-left: 0;
    margin-top: var(--space-2);
  }
}

/* Copiar unificado: los dos botones de acción se apilan en móvil */
[data-view="ajustes"] .ajustes-copiar-acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Vista previa en modal: carátulas pequeñas y cuadradas */
[data-view="ajustes"] .ajustes-previa-caratula {
  width: 40px;
  height: 40px;
  object-fit: cover;
  border-radius: var(--radius-sm);
}

[data-view="ajustes"] .ajustes-copiar-resumen:empty {
  display: none;
}

/* ==========================================================================
   Corrección cross-view (bug reportado 30-10): los tooltips de Bootstrap
   (.favorite-toggle y similares con data-toggle="tooltip") tapaban a su
   trigger y bloqueaban los clics: el tooltip se coloca sobre el botón y,
   sin pointer-events:none, el segundo clic lo atrapa en vez de la estrella.
   El tooltip es solo informativo: nunca debe interceptar eventos.
   ========================================================================== */
.tooltip {
  pointer-events: none;
}
[data-view="generador"] .card .close {
  color: var(--text);
  text-shadow: none;
}

/* El .text-muted de Bootstrap 4 es un gris pensado para tema claro y sobre
   las tarjetas oscuras de la vista apenas se lee. Se remapea al --muted de
   la plataforma (8.9:1 sobre --bg-card, ver auditoría Spec 007). Bootstrap
   lo define con !important, así que aquí también (mismo patrón que ajustes). */
[data-view="generador"] .text-muted {
  color: var(--muted) !important;
}
