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

    /* ── Design tokens ── */
    /* :root carries the DARK values canonically — dark is the app's default
       look. html.light-theme / body.light-theme below only overrides the
       deltas. Declared for <html> as well as <body> because `html { background:
       var(--bg) }` resolves tokens on the <html> element: with the theme class
       only on <body> the page's outermost background showed the wrong theme
       around the content. Both elements carry the theme class (see
       setThemeClass() in app.js). */
    :root {
      color-scheme: dark;

      /* Neutrals */
      --bg:         #000000;
      --surface:    #1c1c1e;
      --surface2:   #2c2c2e;
      --surface3:   #3a3a3c;
      --border:     #38383a;
      --border2:    #545458;
      --text:       #f5f5f7;
      --text2:      #d1d1d6;
      --muted:      #98989f;
      --text-dim:   #7c7c82;
      --input-bg:      #2c2c2e;
      --input-text:    #f5f5f7;
      --input-border:  #38383a;
      --button-bg:     #2c2c2e;
      --button-text:   #f5f5f7;
      --button-border: #545458;
      --scrim:      rgba(0,0,0,.58);
      --overlay:    rgba(0,0,0,.72);   /* chip sopra artwork */
      --scrollbar:  #545458;
      /* A <select> is the one control the browser still draws itself, in the
         OS's own chrome and font. Owning the chevron lets it be styled like
         every other surface in the window. */
      --chevron:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2398989f' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");

      /* Accent — verde del logo */
      --green-500:      #30d158;
      --accent:         #30d158;
      --accent-hover:   #4be06e;
      --accent-press:   #28c24d;
      --accent-fg:      #07190c;
      --accent-text:    #52d979;
      --accent-subtle:  rgba(48,209,88,.14);
      --accent-border:  rgba(48,209,88,.42);
      --accent-ring:    rgba(48,209,88,.34);

      /* Stato / utility */
      --status-queued-fg:  #6cb8ff;  --status-queued-bg:  rgba(90,155,255,.16);
      --status-active-fg:  #8ab4ff;  --status-active-bg:  rgba(90,140,255,.18);
      --status-done-fg:    #55e08d;  --status-done-bg:    rgba(48,209,88,.15);
      --status-skipped-fg: #f0c674;  --status-skipped-bg: rgba(240,180,90,.15);
      --status-failed-fg:  #ff8a8a;  --status-failed-bg:  rgba(255,90,90,.15);
      --status-warn-fg:    #f5c469;  --status-warn-bg:    rgba(245,180,80,.14);
      --red:  #ff5a5a;  --blue: #5a9bff;  --green: #30d158;
      --util-lyrics:     #6fb3ff;  --util-lyrics-bd:  rgba(111,179,255,.4);  --util-lyrics-bg: rgba(111,179,255,.15);
      --util-preview:    #f0b45a;  --util-preview-bd: rgba(240,180,90,.4);  --util-preview-bg: rgba(240,180,90,.15);
      --util-cover:      #b48bff;  --util-cover-bd:   rgba(180,139,255,.4);  --util-cover-bg: rgba(180,139,255,.15);
      --util-link:       var(--accent-text);  --util-link-bd: rgba(82,217,121,.4);
      --log-info:  var(--blue);  --log-ok: var(--accent);  --log-warn: var(--status-warn-fg);
      --log-error: var(--red);   --log-debug: var(--muted);
      --diff-fg: var(--status-warn-fg);
      --dot-close: #ff5f57;  --dot-min: #febc2e;  --dot-max: #28c840;
      --dot-close-win: #e81828;  --dot-min-win: #f0ad4e;  --dot-max-win: #28a745;

      /* Spacing — griglia 4px */
      --sp-1: 2px;  --sp-2: 4px;  --sp-3: 8px;  --sp-4: 12px;
      --sp-5: 16px; --sp-6: 20px; --sp-7: 24px; --sp-8: 32px;
      --sp-9: 40px; --sp-10: 48px; --sp-12: 64px; --sp-16: 96px;

      /* System-like body scale; JetBrains Mono remains available for data
         that benefits from fixed-width numerals and user font preferences. */
      --fs-50:  11px; --lh-50:  1.4;
      --fs-75:  12px; --lh-75:  1.45;
      --fs-100: 13px; --lh-100: 1.45;
      --fs-200: 15px; --lh-200: 1.4;
      --fs-300: 16px; --lh-300: 1.4;   /* titoli riga, enfasi — text-base */
      --fs-400: 18px; --lh-400: 1.3;   /* titoli card, H2 sezione — text-lg */
      --fs-500: 24px; --lh-500: 1.25;  /* H1 vista — text-2xl */
      --fs-600: 30px; --lh-600: 1.15;  /* titolo album — text-3xl */
      --fs-700: 36px; --lh-700: 1.1;   /* wordmark hero — text-4xl */
      --fw-regular:  400; --fw-medium: 500; --fw-semibold: 600; --fw-bold: 700;
      --tracking-tight: -0.02em; --tracking-label: 0.12em; --tracking-wide: 0.04em;
      --font-mono: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
      --app-font:  -apple-system, BlinkMacSystemFont, Inter, 'Segoe UI', sans-serif;

      /* Compact grouped surfaces; sheets use their own larger radius. */
      --radius-xs:   5px;
      --radius-sm:   8px;
      --radius-md:   10px;
      --radius-lg:   12px;
      --radius-pill: 999px;  /* SOLO status chip / contatori / toggle / avatar */
      --radius:    var(--radius-md);

      /* Elevazione — flat music-app, niente blur da 44px né bevel bianchi */
      --elev-0: none;
      --elev-1: 0 1px 2px rgba(0,0,0,.3);     /* card a riposo */
      --elev-2: 0 4px 14px rgba(0,0,0,.38);   /* card hover, dock, dropdown */
      --elev-3: 0 16px 40px rgba(0,0,0,.5);   /* modali, drawer, toast */
      --elev-pop: 0 8px 24px rgba(0,0,0,.42); /* tooltip, popover */
      --shadow-soft:   var(--elev-2);  --shadow-strong: var(--elev-3);

      /* Width of the left nav rail. Shared so anything anchored to the
         viewport (the download dock) can sit beside it instead of guessing
         the number and ending up underneath it. */
      --rail-w: 58px;

      /* Z-index — preserva l'ordine attuale */
      --z-content: 1;    --z-tooltip: 100;  --z-sidebar: 150;
      --z-titlebar: 200; --z-nav-tooltip: 250;
      --z-dock: 300; --z-drawer-backdrop: 390; --z-drawer: 400;
      --z-save-toast: 800; --z-modal: 1000; --z-toast: 1100;

      /* Motion */
      --ease-out:    cubic-bezier(0.22, 1, 0.36, 1);
      --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
      --dur-1: 120ms; --dur-2: 180ms; --dur-3: 240ms; --dur-4: 320ms;
      --t:      var(--dur-2) var(--ease-out);
      --ease:   var(--ease-out);
      --t-slow: var(--dur-4) var(--ease-out);

      /* Breakpoint (documentati — le var non funzionano dentro @media):
         --bp-sm:600  --bp-md:900  --bp-lg:1200  --bp-xl:1440 */

      /* Alias back-compat verso i nuovi token — così index.html/app.js che
         risolvono ancora --yellow/--yellow-d/--yellow-bg/--ring ottengono il
         verde senza bisogno di toccare JS/HTML. */
      --yellow:    var(--accent);
      --yellow-d:  var(--accent-press);
      --yellow-bg: var(--accent-subtle);
      --ring:      var(--accent-ring);
    }

    /* ── Light theme (solo delta) ── */
    html.light-theme,
    body.light-theme {
      color-scheme: light;
      --bg:        #f2f2f7;
      --surface:   #ffffff;
      --surface2:  #f2f2f7;
      --surface3:  #e5e5ea;
      --border:    #e5e5ea;
      --border2:   #c6c6c8;
      --text:      #1c1c1e;
      --text2:     #3a3a3c;
      --muted:     #6c6c70;
      --input-bg:      #ffffff;
      --input-text:    #111113;
      --input-border:  #c6c6c8;
      --button-bg:     #ffffff;
      --button-text:   #111113;
      --button-border: #c6c6c8;
      --scrim:      rgba(15,23,42,.4);
      --scrollbar:  #c6c6c8;
      --chevron:    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236c6c70' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");

      --accent:        #24863d;
      --accent-hover:  #1f7936;
      --accent-press:  #19652d;
      --accent-text:   #24863d;
      --accent-subtle: rgba(36,134,61,.12);
      --accent-border: rgba(36,134,61,.4);
      --accent-ring:   rgba(36,134,61,.28);
      --green: #24863d;  --red: #e5484d;  --blue: #3b82f6;

      --status-queued-fg:  #2f7dd1;  --status-queued-bg:  rgba(59,130,246,.12);
      --status-active-fg:  #3b6fe0;  --status-active-bg:  rgba(59,110,224,.12);
      --status-done-fg:    #24863d;  --status-done-bg:    rgba(36,134,61,.12);
      --status-skipped-fg: #a06a00;  --status-skipped-bg: rgba(200,140,0,.12);
      --status-failed-fg:  #c8353c;  --status-failed-bg:  rgba(229,72,77,.12);
      --status-warn-fg:    #a06a00;  --status-warn-bg:    rgba(217,150,20,.12);

      --util-lyrics:  #2f7dd1;  --util-lyrics-bd:  rgba(47,125,209,.4);  --util-lyrics-bg: rgba(47,125,209,.12);
      --util-preview: #b3720a;  --util-preview-bd: rgba(179,114,10,.4);  --util-preview-bg: rgba(179,114,10,.12);
      --util-cover:   #7c4fd6;  --util-cover-bd:   rgba(124,79,214,.4);  --util-cover-bg: rgba(124,79,214,.12);
      --util-link:    var(--accent-text);  --util-link-bd: rgba(36,134,61,.4);

      --elev-1: 0 1px 2px rgba(15,23,42,.06);
      --elev-2: 0 6px 20px rgba(15,23,42,.1);
      --elev-3: 0 20px 48px rgba(15,23,42,.16);
    }

    /* ── Accent presets ──
       Everything accent-coloured in the app resolves through the eight
       --accent-* tokens, so an alternative accent is those eight values and
       nothing else — no per-component overrides anywhere. The default green
       stays in :root / .light-theme above; these only exist for the other
       choices, set as data-accent on <html> and <body> (same pair the theme
       classes go on) by applyAccent() in app.js.

       The light selectors carry the extra .light-theme class deliberately:
       that outranks the dark block regardless of source order, so a light
       window can never pick up a dark accent value.

       Values are plain hex/rgba rather than oklch()/color-mix() to match the
       rest of the file — the desktop web view's floor is kept low on purpose.
       Generated from one base colour per accent (hover/press are lightness
       steps, the tints are the same alphas the green uses). */
    html[data-accent="blue"], body[data-accent="blue"] {
      --accent: #4d9dff;  --accent-hover: #76b3ff;  --accent-press: #2989ff;
      --accent-fg: #0b0b0d;  --accent-text: #67abff;
      --accent-subtle: rgba(77,157,255,.14);  --accent-border: rgba(77,157,255,.42);  --accent-ring: rgba(77,157,255,.34);
    }
    html.light-theme[data-accent="blue"], body.light-theme[data-accent="blue"] {
      --accent: #1f6feb;  --accent-hover: #1463dd;  --accent-press: #1258c5;
      --accent-fg: #ffffff;  --accent-text: #125ccf;
      --accent-subtle: rgba(31,111,235,.12);  --accent-border: rgba(31,111,235,.4);  --accent-ring: rgba(31,111,235,.28);
    }
    html[data-accent="purple"], body[data-accent="purple"] {
      --accent: #a97bff;  --accent-hover: #c4a4ff;  --accent-press: #9257ff;
      --accent-fg: #0b0b0d;  --accent-text: #ba95ff;
      --accent-subtle: rgba(169,123,255,.14);  --accent-border: rgba(169,123,255,.42);  --accent-ring: rgba(169,123,255,.34);
    }
    html.light-theme[data-accent="purple"], body.light-theme[data-accent="purple"] {
      --accent: #7c4fd6;  --accent-hover: #6d3ad1;  --accent-press: #602ec4;
      --accent-fg: #ffffff;  --accent-text: #6430cd;
      --accent-subtle: rgba(124,79,214,.12);  --accent-border: rgba(124,79,214,.4);  --accent-ring: rgba(124,79,214,.28);
    }
    html[data-accent="pink"], body[data-accent="pink"] {
      --accent: #ff6fae;  --accent-hover: #ff98c5;  --accent-press: #ff4b9a;
      --accent-fg: #0b0b0d;  --accent-text: #ff89bc;
      --accent-subtle: rgba(255,111,174,.14);  --accent-border: rgba(255,111,174,.42);  --accent-ring: rgba(255,111,174,.34);
    }
    html.light-theme[data-accent="pink"], body.light-theme[data-accent="pink"] {
      --accent: #d6337b;  --accent-hover: #c7286e;  --accent-press: #b22463;
      --accent-fg: #ffffff;  --accent-text: #bb2667;
      --accent-subtle: rgba(214,51,123,.12);  --accent-border: rgba(214,51,123,.4);  --accent-ring: rgba(214,51,123,.28);
    }
    html[data-accent="orange"], body[data-accent="orange"] {
      --accent: #ff9f45;  --accent-hover: #ffb46e;  --accent-press: #ff8d21;
      --accent-fg: #0b0b0d;  --accent-text: #ffac5e;
      --accent-subtle: rgba(255,159,69,.14);  --accent-border: rgba(255,159,69,.42);  --accent-ring: rgba(255,159,69,.34);
    }
    html.light-theme[data-accent="orange"], body.light-theme[data-accent="orange"] {
      --accent: #c76a10;  --accent-hover: #af5d0e;  --accent-press: #98510c;
      --accent-fg: #ffffff;  --accent-text: #a1560d;
      --accent-subtle: rgba(199,106,16,.12);  --accent-border: rgba(199,106,16,.4);  --accent-ring: rgba(199,106,16,.28);
    }
    html[data-accent="red"], body[data-accent="red"] {
      --accent: #ff6b6b;  --accent-hover: #ff9494;  --accent-press: #ff4747;
      --accent-fg: #0b0b0d;  --accent-text: #ff8585;
      --accent-subtle: rgba(255,107,107,.14);  --accent-border: rgba(255,107,107,.42);  --accent-ring: rgba(255,107,107,.34);
    }
    html.light-theme[data-accent="red"], body.light-theme[data-accent="red"] {
      --accent: #d13a3a;  --accent-hover: #c42e2e;  --accent-press: #af2929;
      --accent-fg: #ffffff;  --accent-text: #b72b2b;
      --accent-subtle: rgba(209,58,58,.12);  --accent-border: rgba(209,58,58,.4);  --accent-ring: rgba(209,58,58,.28);
    }
    html[data-accent="cyan"], body[data-accent="cyan"] {
      --accent: #3fd6d6;  --accent-hover: #61dddd;  --accent-press: #2ac7c7;
      --accent-fg: #0b0b0d;  --accent-text: #54dada;
      --accent-subtle: rgba(63,214,214,.14);  --accent-border: rgba(63,214,214,.42);  --accent-ring: rgba(63,214,214,.34);
    }
    html.light-theme[data-accent="cyan"], body.light-theme[data-accent="cyan"] {
      --accent: #0d9a9a;  --accent-hover: #0b8282;  --accent-press: #096b6b;
      --accent-fg: #ffffff;  --accent-text: #0a7474;
      --accent-subtle: rgba(13,154,154,.12);  --accent-border: rgba(13,154,154,.4);  --accent-ring: rgba(13,154,154,.28);
    }
    html[data-accent="amber"], body[data-accent="amber"] {
      --accent: #f5c451;  --accent-hover: #f7d178;  --accent-press: #f3b92f;
      --accent-fg: #0b0b0d;  --accent-text: #f6cc69;
      --accent-subtle: rgba(245,196,81,.14);  --accent-border: rgba(245,196,81,.42);  --accent-ring: rgba(245,196,81,.34);
    }
    html.light-theme[data-accent="amber"], body.light-theme[data-accent="amber"] {
      --accent: #a67c00;  --accent-hover: #8c6900;  --accent-press: #735600;
      --accent-fg: #ffffff;  --accent-text: #7d5e00;
      --accent-subtle: rgba(166,124,0,.12);  --accent-border: rgba(166,124,0,.4);  --accent-ring: rgba(166,124,0,.28);
    }

    html, body { height: 100%; overflow: hidden; background: var(--bg); }
    body {
      font-family: var(--app-font);
      color: var(--text);
      display: flex; flex-direction: column;
      -webkit-app-region: no-drag;
      user-select: none;
      font-size: var(--fs-200);
      min-height: 0;
      background: var(--bg);
    }
    ::selection {
      background: var(--accent-subtle);
      color: var(--text);
    }

    /* ── Titlebar ── */
    #titlebar {
      height: 46px;
      background: var(--surface);
      border-bottom: 1px solid var(--border);
      display: flex; align-items: center;
      padding: 0 14px;
      gap: 10px;
      -webkit-app-region: drag;
      flex-shrink: 0;
      z-index: var(--z-titlebar);
      box-shadow: var(--elev-1);
    }
    
    /* Disable drag ONLY on the colored buttons container so they remain clickable */
    #titlebar .dots {
      -webkit-app-region: no-drag;
    }
    /* In a browser tab (webapp.py adds .web-mode to <body>) the titlebar has
       no job: the traffic-light buttons call pyWin() methods that only exist
       on the pywebview bridge, and the drag region drags nothing. It was a
       46px empty strip above every view. The version and the update badge it
       also held are already shown in the home hero. The desktop build is
       frameless and keeps it as the real window chrome. */
    body.web-mode #titlebar { display: none; }
    .dots { display: flex; gap: 6px; }
    .dot {
      width: 12px; height: 12px; border-radius: 50%; cursor: pointer;
      transition: filter .15s, transform .15s; flex-shrink: 0;
    }
    .dot:hover { filter: brightness(1.15); transform: scale(1.05); }
    
    /* ── macOS style (default) ── */
    .dot.c { background: var(--dot-close); }
    .dot.m { background: var(--dot-min); }
    .dot.x { background: var(--dot-max); }

    /* ── Windows style ── */
    body.windows-style .dot {
      width: 14px; height: 14px; border-radius: 2px;
    }
    body.windows-style .dot.c { background: var(--dot-close-win); }
    body.windows-style .dot.m { background: var(--dot-min-win); }
    body.windows-style .dot.x { background: var(--dot-max-win); }

    #tb-logo {
      display: none;
    }
    #tb-logo .logo-icon {
      width: 22px; height: 22px;
      background: var(--accent); border-radius: 6px;
      display: flex; align-items: center; justify-content: center;
      font-size: var(--fs-100);
    }
    #tb-version {
      font-family: var(--font-mono);
      font-size: var(--fs-50); font-weight: 500;
      background: var(--accent-subtle); color: var(--accent-text);
      border: 1px solid var(--accent-border);
      padding: 1px 6px; border-radius: 4px;
    }
    .update-badge {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      font-family: var(--font-mono);
      font-size: var(--fs-50);
      font-weight: 700;
      color: var(--accent-fg);
      background: var(--accent);
      border: 1px solid var(--accent-border);
      border-radius: 999px;
      padding: 0 7px;
      margin-left: 8px;
      cursor: pointer;
      transition: transform 0.15s ease, opacity 0.15s ease;
    }
    .update-badge.hidden {
      display: none;
    }
    .update-badge:hover {
      transform: translateY(-1px);
    }
    .hero-update-badge,
    .clickable-version {
      cursor: pointer;
    }

    .hero-update-badge {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      font-family: var(--font-mono);
      font-size: var(--fs-50);
      font-weight: 700;
      color: var(--accent-fg);
      background: var(--accent);
      border-radius: 999px;
      padding: 5px var(--sp-4);
      margin-left: 10px;
      transition: transform 0.15s ease, opacity 0.15s ease;
    }
    .hero-update-badge:hover {
      transform: translateY(-1px);
    }
    .tb-spacer { flex: 1; }

    /* ── Layout ── */
    #app-body { flex: 1; display: flex; overflow: hidden; min-height: 0; }

    /* ── Sidebar ── */
    #sidebar {
      width: var(--rail-w);
      background: var(--surface);
      border-right: 1px solid var(--border);
      display: flex; flex-direction: column;
      align-items: center; padding: 12px 0 10px; gap: 6px;
      flex-shrink: 0;
      overflow: visible;
      /* No longer a blur-induced stacking context, but the nav tooltips
         still need to overhang #content — position + z-index keeps that
         true without any blur. */
      position: relative;
      z-index: var(--z-sidebar);
    }
    .nav-btn {
      width: 40px; height: 40px;
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: var(--radius-md); color: var(--muted);
      cursor: pointer; font-size: 16px;
      display: flex; align-items: center; justify-content: center;
      transition: all 0.2s var(--ease-spring), box-shadow 0.15s ease;
      box-shadow: none;
      position: relative;
    }
    .nav-btn:hover {
      background: var(--surface3);
      color: var(--text);
      border-color: var(--border2);
      /* Was translateY(-2px). In a horizontal toolbar a lift reads as the
         button rising towards the pointer; in a vertical rail it just reads
         as the row above it drifting out of line. */
      transform: scale(1.06);
      box-shadow: var(--elev-1);
    }
    .nav-btn:active { transform: scale(0.95); }
    .nav-btn.active {
      background: var(--accent);
      color: var(--accent-fg);
      border-color: var(--accent);
      box-shadow: none;
      font-weight: 600;
    }
    /* A tab marker on the rail edge. The accent pill already says which view
       is open, but only while you are looking at the pill; this reads as
       position — which of eight stops down the rail you are at — from the
       corner of the eye. */
    .nav-btn.active::before {
      content: '';
      position: absolute;
      left: -9px; top: 50%;  /* the sidebar's own left edge: 58px rail, 40px button */
      width: 3px; height: 18px;
      transform: translateY(-50%);
      border-radius: 0 3px 3px 0;
      background: var(--accent);
    }
    .nav-btn.active:hover::before { opacity: .8; }
    .nav-spacer { flex: 1; }

    /* ── Content ── */
    #content {
      flex: 1;
      /* Kept below #sidebar (--z-sidebar) so nav tooltips overhang it. */
      position: relative;
      z-index: var(--z-content);
      display: flex;
      flex-direction: column;
      overflow-y: auto;
      min-height: 0;
      scroll-behavior: smooth;
    }
    #content::-webkit-scrollbar { width: 8px; }
    #content::-webkit-scrollbar-thumb {
      background: var(--scrollbar);
      border-radius: 999px;
      border: 2px solid transparent;
      background-clip: padding-box;
    }

    .view { display: none; flex-direction: column; flex: 1; overflow: visible; min-height: 0; }
    .view.active { display: flex; }

    /* ══════════ HOME VIEW ══════════ */
    #view-home { overflow: visible; }

    #home-hero {
      padding: var(--sp-8) var(--sp-7) var(--sp-6);
      display: flex; justify-content: center;
    }
    /* This was a bordered card around the title — a hard-edged rectangle
       whose only content was the thing it framed, which is what made the
       top of the window read as a box inside a box. The light it was
       carrying is worth keeping; the edge is not, so the panel becomes a
       radial glow that fades out before it reaches a border. */
    .hero-text {
      max-width: 800px; text-align: center;
      padding: var(--sp-6) var(--sp-8);
      border-radius: var(--radius-lg);
    }
    .hero-headline {
      display: flex; align-items: center; justify-content: center; gap: var(--sp-6);
      flex-wrap: wrap;
    }
    .hero-logo {
      display: grid; place-items: center;
      gap: var(--sp-3); text-align: center;
    }
    .hero-icon {
      width: 72px; height: 72px; border-radius: var(--radius-lg);
      background: #204d4e;
      display: block;
      box-shadow: var(--elev-1);
      object-fit: contain; padding: var(--sp-3); box-sizing: border-box;
    }
    .hero-badge {
      background: var(--accent); color: var(--accent-fg); font-family: var(--font-mono);
      font-size: var(--fs-50); font-weight: 700; letter-spacing: var(--tracking-label);
      padding: var(--sp-2) 10px; border-radius: 999px; text-transform: uppercase;
    }
    .hero-main {
      display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; justify-content: center;
    }
    /* Keep the wordmark compact and let the system display face carry it. */
    .hero-title {
      font-size: var(--fs-700); line-height: var(--lh-700); font-weight: var(--fw-bold);
      letter-spacing: var(--tracking-tight);
      color: var(--text);
      text-shadow: none;
    }
    .hero-version {
      display: inline-flex; align-items: center; justify-content: center;
      font-family: var(--font-mono); font-size: var(--fs-100); font-weight: 700;
      color: var(--accent-text); background: var(--accent-subtle);
      padding: var(--sp-2) 10px; border-radius: 20px;
      box-shadow: none;
    }
    .hero-subtitle {
      margin-top: var(--sp-4); color: var(--muted); font-size: var(--fs-200); line-height: var(--lh-200);
      max-width: 720px; margin-left: auto; margin-right: auto;
      /* No single trailing word on its own line. */
      text-wrap: balance;
    }

    /* ── SEARCH BAR v7 ── */
    #search-bar {
      padding: var(--sp-7) var(--sp-7) var(--sp-6);
      border-bottom: 1px solid var(--border);
    }

    /* The one line that says what the app is doing right now — see the
       #status-bar comment in index.html for why it is not decorative. */
    .status-bar {
      width: 100%;
      max-width: 860px;
      margin: 10px auto 0;
      display: flex;
      align-items: center;
      gap: var(--sp-3);
      min-height: 15px;
      font-size: 11.5px;
      color: var(--muted);
    }
    .status-spinner {
      display: none;
      flex: none;
      width: 11px; height: 11px;
      border: 2px solid var(--border2);
      border-top-color: var(--accent);
      border-radius: 50%;
      animation: status-spin 0.7s linear infinite;
    }
    @keyframes status-spin { to { transform: rotate(360deg); } }
    @media (prefers-reduced-motion: reduce) {
      .status-spinner { animation-duration: 2.4s; }
    }

    .search-container {
      width: 100%;
      max-width: 860px;
      margin: 0 auto;
      display: flex;
      gap: 10px;
      align-items: center;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      padding: 11px;
      box-shadow: var(--elev-1);
      position: relative;
    }
    .search-container > * { position: relative; z-index: 1; }
    .mode-wrap {
      display: flex;
      align-items: center;
      gap: var(--sp-3);
      flex-shrink: 0;
    }
    .mode-btn {
      width: 36px;
      height: 36px;
      border-radius: var(--radius-sm);
      border: 1px solid var(--button-border);
      background: var(--button-bg);
      color: var(--button-text);
      font-size: 14px;
      font-weight: 500;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 0;
      cursor: pointer;
      transition: all var(--t);
      padding: 0;
    }
    
    .mode-btn:hover { background: var(--surface3); }
    .mode-btn.active { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
    /* Set while a CSV is being matched — a file of titles is one catalogue
       lookup per row, so the button stays busy for a while and the running
       count sits in its tooltip. */
    .mode-btn.is-busy { border-color: var(--accent-press); }
    .mode-btn.is-busy .mode-btn-icon { animation: csv-matching 1.2s ease-in-out infinite; }
    @keyframes csv-matching { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
    @media (prefers-reduced-motion: reduce) {
      .mode-btn.is-busy .mode-btn-icon { animation: none; opacity: .7; }
    }
    .mode-btn-icon { display: inline-flex; align-items: center; justify-content: center; width: 18px; height: 18px; font-size: 14px; }
    .search-source {
      height: 36px;
      max-width: 120px;
      padding: 0 var(--sp-2);
      border-radius: var(--radius-sm);
      border: 1px solid var(--button-border);
      background: var(--button-bg);
      color: var(--button-text);
      font-size: 13px;
      cursor: pointer;
    }
    .search-source:hover { background: var(--surface3); }
    /* Fetch mode shows the chain, Search mode the magnifier. The button
       already gets .active in search mode, so the swap needs no JS — and
       because these are `stroke: currentColor` SVGs they invert with the
       button when .mode-btn.active turns it yellow, which an emoji could
       not do. */
    .mode-btn-icon .icon-search,
    .mode-btn.active .mode-btn-icon .icon-link { display: none; }
    .mode-btn.active .mode-btn-icon .icon-search { display: block; }
    #searchModeText { display: none; }

    .url-wrap { 
      flex: 1; /* Takes up exactly all the remaining space up to 820px */
      position: relative; 
      min-width: 0; 
    }
    .url-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
    /* 36px is the control height the whole app now uses — their h-9, applied
       to the input, the two mode buttons and the fetch button so the search
       bar is one continuous band of equal-height controls rather than three
       different heights (40 / 40 / 44) sharing a row. */
    #urlInput {
      width: 100%; height: 36px; padding: 0 32px 0 40px; font-size: var(--fs-200);
      border-radius: var(--radius-sm); background: var(--input-bg); color: var(--input-text);
      border: 1px solid var(--input-border);
    }
    #urlInput:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-subtle); }
    #urlInput::placeholder { color: var(--muted); font-size: var(--fs-200); }
    #urlInput:disabled { opacity: .45; }

    #fetchBtn {
      width: 36px;
      height: 36px;
      padding: 0;
      border-radius: var(--radius-sm);
      font-size: var(--fs-200);
      font-weight: 600;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 0;
      box-shadow: none;
      background: var(--accent);
      color: var(--accent-fg);
      border: 1px solid var(--accent);
      transition: transform var(--t-slow), box-shadow var(--t-slow), opacity var(--t), background var(--t);
    }
    #fetchBtn:hover { transform: translateY(-2px); background: var(--accent-hover); }
    #fetchBtn:active { transform: scale(.96); background: var(--accent-press); }
    #fetchBtn:disabled { opacity: .35; cursor: not-allowed; }

    #track-render-status {
      margin: 10px 0 0;
      padding: var(--sp-3) var(--sp-4);
      font-size: var(--fs-100);
      line-height: 1.35;
      color: var(--muted);
      border: 1px solid var(--border2);
      border-radius: 12px;
      background: var(--surface2);
    }

    /* Main split */
    #main-split { flex: 1; display: flex; overflow: visible; min-height: 0; }

    /* Result panel */
    #result-panel { flex: 1; display: flex; flex-direction: column; overflow: visible; min-width: 0; }

    /* Album card */
    #album-card {
      width: 100%;
      margin: var(--sp-6) 0 0;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      padding: var(--sp-7);
      display: flex; gap: var(--sp-6); align-items: flex-start;
      flex-shrink: 0; position: relative; overflow: hidden;
      box-shadow: var(--elev-1);
    }
    #album-card.hidden { display: none; }
    /* Artwork-derived glow: app.js samples the cover's average colour once
       it loads and sets --album-glow on this element; unset (no JS support,
       CORS-blocked canvas read, or no cover) it falls back to transparent —
       the card is exactly the flat --surface it was before. Sits behind
       #album-card's own content (z-index:0 vs the >* rule below) and is
       capped low-alpha so it reads as ambient light, not a tinted panel. */
    #album-card::before {
      content: '';
      position: absolute; inset: -20%;
      background: radial-gradient(ellipse at 0% 0%, var(--album-glow, transparent) 0%, transparent 65%);
      opacity: .5;
      pointer-events: none;
      z-index: 0;
    }
    #album-card > * { position: relative; z-index: 1; }
    /* The card is a cover plus a text column whose every line is short, so
       the right third of it was always empty. #album-tech fills that with
       the facts a download actually carries — label, the ℗ line, ISRC/UPC,
       runtime — in a column of its own, divided off with a hairline. It
       hides itself when there is nothing to show (artist pages), and it
       drops below the text column on a narrow window (see the media query)
       rather than crushing either side. */
    #album-tech {
      flex-shrink: 0;
      width: 216px;
      align-self: stretch;
      padding-left: 20px;
      border-left: 1px solid var(--border);
      display: flex; flex-direction: column; gap: 11px;
    }
    #album-tech.hidden { display: none; }
    #album-tech .tech-row { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
    #album-tech .tech-k {
      font-size: 9.5px; font-weight: 600; text-transform: uppercase;
      letter-spacing: .14em; color: var(--muted);
    }
    #album-tech .tech-v {
      font-size: 11.5px; line-height: 1.4; color: var(--text2);
      font-family: var(--font-mono);
      overflow-wrap: anywhere;
      /* A copyright line runs long; four lines is plenty of it, the rest is
         on the title attribute. */
      display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 4;
      overflow: hidden;
      /* body sets user-select: none window-wide; an ISRC or a catalogue
         number is here specifically to be copied out. */
      user-select: text; -webkit-user-select: text;
    }
    #album-cover {
      width: clamp(200px, 22vw, 240px); height: clamp(200px, 22vw, 240px); border-radius: 10px;
      background: var(--surface3); flex-shrink: 0;
      border: 1px solid var(--border);
      display: flex; align-items: center; justify-content: center;
      font-size: 42px; overflow: hidden; position: relative;
      box-shadow: var(--elev-2);
    }
    @media (min-width: 1200px) {
      #album-cover { width: clamp(240px, 18vw, 280px); height: clamp(240px, 18vw, 280px); }
    }
    #album-cover img {
      width: 100%; height: 100%; object-fit: cover; border-radius: 9px;
      filter: blur(0); opacity: 1;
      transition: filter var(--t-slow), opacity var(--t-slow);
    }
    /* Blur-up: app.js sets this class the instant the <img> is inserted and
       clears it once decode() resolves — on a cached/local image that's one
       frame (no visible flicker), on a slow remote cover it's the difference
       between an empty tile and something recognisable while the real
       pixels arrive. */
    #album-cover img.cover-loading {
      filter: blur(14px);
      opacity: .65;
      transform: scale(1.04); /* hides the blur's soft edge against the tile's border */
    }
    #album-cover:hover .cover-download-btn { opacity: 1; }
    .cover-download-btn {
      position: absolute; width: 48px; height: 48px; display: flex; align-items: center; justify-content: center;
      background: var(--overlay); border-radius: var(--radius-md); opacity: 0; transition: all 0.2s ease;
      border: none; cursor: pointer; color: var(--text); font-family: inherit;
    }
    .cover-download-btn:hover { background: rgba(0, 0, 0, 0.85); transform: scale(1.1); }
    .cover-download-btn svg { width: 24px; height: 24px; }
    .cover-download-btn.loading svg { animation: spin 0.8s linear infinite; }
    .cover-download-btn.success { background: var(--status-done-fg); opacity: 1; }
    .cover-download-btn.error { background: var(--status-failed-fg); opacity: 1; }
    .cover-download-btn.success:hover, .cover-download-btn.error:hover { cursor: default; transform: none; }
    @keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
    /* The cover is the tallest thing in the card, so the text column is
       always shorter — the question is only where its leftover height goes.
       Top-aligned put all of it under the buttons (a card that stopped
       halfway down); bottom-aligned put all of it above them (a hole
       between the artist and the actions). Centred, it splits in two and
       reads as margin on both sides of a block set against the artwork,
       which is what it actually is. */
    #album-info {
      flex: 1; min-width: 0;
      align-self: stretch;
      display: flex; flex-direction: column; justify-content: center;
    }
    #album-type-badge {
      display: inline-block;
      /* #album-info is a flex column now, and a flex item stretches to the
         column's width by default — which turned this pill into a full-width
         bar. It sizes to its own text again. */
      align-self: flex-start;
      margin-bottom: 10px;
      padding: 3px 10px;
      font-size: var(--fs-75);
      font-weight: var(--fw-semibold);
      letter-spacing: var(--tracking-label);
      text-transform: uppercase;
      color: var(--accent-text);
      background: var(--accent-subtle);
      border: 1px solid var(--accent-border);
      border-radius: var(--radius-pill);
    }
    #album-type-badge:empty { display: none; }
    /* The album name is the subject of the card and sits beside ~270px of
       artwork; at --fs-600 it read as a caption next to a poster. --fs-700
       gives the text column enough mass to hold its own half of the card,
       which is also what stops the space between it and the buttons from
       reading as a gap rather than as breathing room. Clamped at two lines
       so a long title cannot push the layout around. */
    #album-title {
      font-size: var(--fs-700); line-height: var(--lh-700);
      font-weight: var(--fw-bold); letter-spacing: var(--tracking-tight);
      margin-bottom: var(--sp-2); overflow: hidden; text-overflow: ellipsis;
      display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical;
    }
    #album-subtitle {
      font-size: var(--fs-200); color: var(--text2); margin-bottom: 12px;
      line-height: 1.5;
    }
    /* Not display:none by default — app.js's updateAlbumMeta() already
       toggles this per fetch (hidden for an album, where the artist is
       folded into #album-subtitle instead; shown for a track/single, where
       #album-subtitle is hidden and this span is the only place the artist
       name appears at all). A hardcoded display:none here silently beat
       that toggle every time, since JS setting style.display='' just falls
       back to this rule instead of overriding it — a single track fetch
       showed a title with no artist under it, no matter what the JS did. */
    #album-artist {
      display: inline;
    }
    /* Clickable artist name — app.js's artistNameHtml() only ever wraps a
       name in this span when a real artist_url resolved, so the cursor/
       hover treatment never shows on a name that goes nowhere. */
    .artist-link {
      cursor: pointer;
      transition: color var(--t);
    }
    .artist-link:hover { color: var(--accent-text); text-decoration: underline; }
    /* "Drake · 2026-05-15 · 18 tracks" was one flat grey line in which the
       only clickable thing looked exactly like the two facts either side of
       it. The artist steps forward (it is the name of the thing, and the
       one part that does something when clicked); the date and the count
       step back to a supporting weight. */
    #album-subtitle .artist-link {
      color: var(--text);
      font-weight: var(--fw-medium);
    }
    #album-meta {
      font-size: var(--fs-100); color: var(--text2); margin-bottom: 12px;
      display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
    }
    #album-meta.no-artist .dot-sep { display: none; }
    #album-meta-details {
      font-size: var(--fs-100); color: var(--text2); margin-bottom: 12px;
      display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
    }
    .owner-avatar {
      width: 32px; height: 32px; border-radius: 50%;
      background: var(--surface2); display: inline-grid; place-items: center;
      color: var(--text); font-weight: 700; font-size: var(--fs-100); flex-shrink:0;
      border: 1px solid var(--border); margin-right: 6px;
      overflow: hidden;
      background-size: cover;
      background-position: center;
      background-repeat: no-repeat;
    }
    .owner-avatar.hidden { display: none; }
    #album-description {
      font-size: var(--fs-200); color: var(--text2); line-height: 1.55;
      margin-bottom: 12px; display: none; max-height: 4.65em;
      overflow: hidden; text-overflow: ellipsis;
      -webkit-line-clamp: 3; line-clamp: 3; -webkit-box-orient: vertical;
    }
    #album-description.visible {
      display: -webkit-box;
      display: block;
    }
    /* margin-top, not auto: an auto margin here would eat all the free
       height and defeat #album-info's centring. A fixed step just sets the
       buttons apart from the text above them. */
    #album-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-5); }
    /* Single-track view only (app.js's showSingleTrackCard) — quality +
       duration chips under the action row, filling what was otherwise dead
       space below the buttons on a short left column next to the taller
       technical-sheet column. */
    .track-quality-row { display: flex; gap: var(--sp-3); flex-wrap: wrap; margin-top: var(--sp-5); }
    .track-quality-row.hidden { display: none; }
    .track-quality-chip {
      display: inline-flex; align-items: center;
      padding: var(--sp-2) var(--sp-4); border-radius: var(--radius-pill);
      background: var(--surface2); border: 1px solid var(--border);
      color: var(--text2); font-family: var(--font-mono);
      font-size: var(--fs-75); letter-spacing: var(--tracking-wide);
    }
    /* Their button default: h-9 (36px), px-4 (16px), text-sm, gap-2, and a
       rounded-md corner. Height set rather than implied by padding so every
       button lines up with the inputs beside it whatever its label. */
    .act-btn {
      display: flex; align-items: center; justify-content: center; gap: var(--sp-3);
      min-height: 36px;
      padding: 0 var(--sp-5); border-radius: var(--radius-sm);
      font-family: var(--app-font); font-size: var(--fs-200); font-weight: 600;
      cursor: pointer; border: 1px solid var(--border);
      transition: all 0.2s var(--ease-spring);
      box-shadow: var(--elev-1);
    }
    .act-btn.primary { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
    .act-btn.primary:hover { transform: translateY(-2px); background: var(--accent-hover); box-shadow: var(--elev-2); }
    .act-btn.secondary { background: var(--surface2); color: var(--text2); }
    .act-btn.secondary:hover { background: var(--surface3); border-color: var(--border2); color: var(--text); transform: translateY(-1px); }
    /* Clearing a queue or a history is not the same kind of act as saving a
       setting, and until now it looked identical to it. Tinted rather than
       filled: a solid red button competes with the primary action for
       attention, which is backwards — this one should be findable, not
       inviting. Uses the same --status-failed-* tokens as a failed row, so
       "destructive" and "went wrong" stay one colour story. */
    .act-btn.danger {
      background: var(--status-failed-bg);
      color: var(--status-failed-fg);
      border-color: var(--status-failed-bg);
    }
    .act-btn.danger:hover {
      border-color: var(--status-failed-fg);
      color: var(--status-failed-fg);
      transform: translateY(-1px);
    }
    .act-btn:active { transform: scale(.97); }

    /* Folder browser modal — was a hardcoded rgba(0,0,0,.5) scrim plus a
       card of inline style="" in index.html, so it never picked up --scrim
       or any theme token. Moved to real classes: same DOM/ID contract, no
       JS change needed for this half of it (openFolderBrowser/closeFolderBrowser
       only toggle .hidden). */
    #folder-browser-modal {
      position: fixed; inset: 0;
      background: var(--scrim);
      display: flex; align-items: center; justify-content: center;
      z-index: var(--z-modal);
    }
    .fb-card {
      background: var(--surface); border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      width: 90%; max-width: 500px; max-height: 80vh;
      display: flex; flex-direction: column;
      box-shadow: var(--elev-3);
    }
    .fb-header {
      padding: var(--sp-5) var(--sp-6);
      border-bottom: 1px solid var(--border);
      display: flex; justify-content: space-between; align-items: center;
    }
    #folder-browser-title { font-weight: var(--fw-semibold); color: var(--text); }
    .fb-close {
      background: none; border: none; color: var(--muted); cursor: pointer;
      font-size: var(--fs-400); padding: 0; line-height: 1;
      transition: color var(--t);
    }
    .fb-close:hover { color: var(--text); }
    .fb-toolbar {
      padding: var(--sp-4) var(--sp-6);
      border-bottom: 1px solid var(--border);
      display: flex; gap: var(--sp-3); align-items: center;
    }
    #fb-back {
      background: var(--surface2); border: 1px solid var(--border);
      border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4);
      cursor: pointer; color: var(--text); font-size: var(--fs-100);
      font-family: var(--app-font); transition: opacity var(--t);
    }
    #fb-back.is-root { opacity: .5; }
    #fb-path {
      flex: 1; background: var(--surface2); border: 1px solid var(--border);
      border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4);
      color: var(--text2); font-size: var(--fs-75); font-family: var(--font-mono);
    }
    .fb-select {
      background: var(--accent); border: 1px solid var(--accent);
      border-radius: var(--radius-sm); padding: var(--sp-3) var(--sp-4);
      cursor: pointer; color: var(--accent-fg); font-weight: var(--fw-semibold);
      font-size: var(--fs-100); font-family: var(--app-font);
      transition: background var(--t);
    }
    .fb-select:hover { background: var(--accent-hover); }
    #fb-entries { flex: 1; overflow-y: auto; padding: var(--sp-3); }
    .fb-entry {
      padding: var(--sp-3) var(--sp-4); cursor: pointer;
      border-radius: var(--radius-sm);
      display: flex; align-items: center; gap: var(--sp-3);
      color: var(--text); font-size: var(--fs-200);
      border: 1px solid transparent;
      transition: background var(--t);
    }
    .fb-entry:hover { background: var(--surface2); }
    .fb-empty { padding: var(--sp-6); text-align: center; color: var(--muted); font-size: var(--fs-100); }

    .api-modal {
      position: fixed;
      inset: 0;
      z-index: var(--z-modal);
      display: flex;
      align-items: center;
      justify-content: center;
      padding: var(--sp-6);
    }
    .api-modal.hidden { display: none; }
    .api-modal-backdrop {
      position: absolute;
      inset: 0;
      background: var(--scrim);
    }
    .api-modal-card {
      position: relative;
      width: min(520px, 100%);
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      box-shadow: var(--elev-3);
      padding: var(--sp-6);
      z-index: 1;
    }
    .api-modal-header {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      gap: var(--sp-4);
    }
    .api-modal-body input {
      width: 100%;
      border: 1px solid var(--border);
      border-radius: 12px;
      background: var(--input-bg);
      color: var(--input-text);
      padding: var(--sp-4) 14px;
      font-size: var(--fs-200);
    }
    .api-modal-actions {
      display: flex;
      justify-content: space-between;
      align-items: center;
      gap: 10px;
      margin-top: 18px;
      flex-wrap: wrap;
    }
    .api-modal-actions > div {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
      justify-content: flex-end;
    }
    .api-modal-header .close-btn {
      border: none;
      background: var(--surface2);
      color: var(--text2);
      width: 34px;
      height: 34px;
      border-radius: 12px;
      cursor: pointer;
      font-size: 16px;
    }
    .api-modal-header .close-btn:hover {
      background: var(--surface3);
    }
    .api-config-pill {
      display: inline-flex;
      align-items: center;
      gap: var(--sp-3);
      padding: 6px 10px;
      border-radius: 999px;
      border: 1px solid var(--border);
      font-size: var(--fs-100);
      line-height: 1.3;
      color: var(--text2);
      background: var(--surface2);
      white-space: nowrap;
    }
    .api-config-pill::before {
      content: '';
      display: inline-block;
      width: 8px;
      height: 8px;
      border-radius: 50%;
      background: var(--muted);
    }
    .api-config-pill.configured {
      border-color: var(--status-done-fg);
      color: var(--status-done-fg);
      background: var(--status-done-bg);
    }
    .api-config-pill.configured::before {
      background: var(--status-done-fg);
      box-shadow: 0 0 0 1px var(--status-done-fg);
    }
    .api-config-help {
      font-size: var(--fs-100);
      color: var(--yellow);
      text-decoration: none;
      white-space: nowrap;
      border-bottom: 1px dashed transparent;
    }
    .api-config-help:hover {
      border-bottom-color: var(--yellow);
    }

    /* Track controls */
    #track-controls {
      width: 100%; /* ADDED: forces the container to take up all the space */
      max-width: min(100%, 950px);
      margin: 12px auto 8px;
      display: flex; gap: var(--sp-5); align-items: center; flex-shrink: 0;
      justify-content: space-between;
      padding: 0 14px; /* MODIFICATO: da 32px a 14px per allinearlo alla tabella */
    }
    #track-search-wrap { 
      flex: 1; 
      position: relative; 
      min-width: 0; /* Impedisce che la barra rompa il layout restringendo la finestra */
    }
    .sort-wrap { 
      display: flex; 
      align-items: center; 
      gap: 6px; 
      flex-shrink: 0; /* Impedisce che il menu si rimpicciolisca o si sposti male */
    }
    #trackSearch {
      width: 100%; padding: 7px 11px 7px 30px;
      background: var(--surface2); border: 1.5px solid var(--border);
      border-radius: var(--radius-sm); color: var(--text);
      font-family: var(--app-font); font-size: var(--fs-100);
      outline: none; transition: border-color var(--t);
      user-select: text;
    }
    #trackSearch:focus { border-color: var(--yellow); }
    #trackSearch::placeholder { color: var(--muted); }
    .ts-icon {
      position: absolute; left: 9px; top: 50%; transform: translateY(-50%);
      color: var(--muted); pointer-events: none; font-size: var(--fs-200);
    }
    .sort-wrap { display: flex; align-items: center; gap: 6px; }
    .sort-btn {
      width: 32px; height: 32px;
      background: var(--surface2); border: 1.5px solid var(--border);
      border-radius: var(--radius-sm); color: var(--muted); cursor: pointer;
      display: flex; align-items: center; justify-content: center; font-size: 14px;
      transition: all var(--t);
    }
    .sort-btn:hover { border-color: var(--border2); color: var(--text2); }
    #sort-select {
      padding: 6px 28px 6px 10px; background-color: var(--surface2);
      border: 1.5px solid var(--border); border-radius: var(--radius-sm);
      color: var(--text2); font-family: var(--app-font); font-size: var(--fs-100);
      outline: none; cursor: pointer;
      background-position: right 8px center;
    }

    /* ── Text Search Results ── */
    #text-search-container {
      padding: 0 var(--sp-6) var(--sp-4);
      border-bottom: 1px solid var(--border);
      max-width: 100%;
    }
    /* ── Search results panel (tab-based) ─────────────────────────────────── */
    #text-search-results {
      display: flex;
      flex-direction: column;
      gap: var(--sp-4);
      max-width: 820px;
      margin: 0 auto;
    }

    .sr-panel {
      background: var(--surface);
      border: 1.5px solid var(--border);
      border-radius: var(--radius-sm);
      overflow: hidden;
    }

    /* Tab bar */
    .sr-tab-bar {
      display: flex;
      align-items: stretch;
      background: var(--surface2);
      border-bottom: 1px solid var(--border);
      overflow-x: auto;
      scrollbar-width: none;
    }
    .sr-tab-bar::-webkit-scrollbar { display: none; }

    .sr-tab {
      padding: 10px var(--sp-5);
      font-size: var(--fs-75);
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: .06em;
      color: var(--muted);
      cursor: pointer;
      border-bottom: 2px solid transparent;
      white-space: nowrap;
      background: none;
      border-top: none;
      border-left: none;
      border-right: none;
      transition: color var(--t), border-color var(--t);
      display: flex;
      align-items: center;
      gap: 6px;
    }
    .sr-tab:hover { color: var(--text2); }
    .sr-tab.active { color: var(--yellow); border-bottom-color: var(--yellow); }

    .sr-tab-badge {
      font-family: var(--font-mono);
      font-size: var(--fs-50);
      background: var(--surface3);
      color: var(--muted);
      border-radius: 99px;
      padding: 1px 6px;
      font-weight: 600;
    }
    .sr-tab.active .sr-tab-badge { background: var(--accent-subtle); color: var(--accent-text); }

    /* Filter row */
    .sr-filter-row {
      padding: 10px 14px;
      border-bottom: 1px solid var(--border);
      background: var(--surface);
    }
    .sr-filter-input {
      width: 100%;
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: 6px;
      padding: 6px 10px;
      font-size: var(--fs-100);
      color: var(--text);
      outline: none;
      box-sizing: border-box;
      font-family: inherit;
      transition: border-color var(--t);
    }
    .sr-filter-input:focus { border-color: var(--yellow); }
    .sr-filter-input::placeholder { color: var(--muted); }

    /* Results list */
    .sr-tab-content {
      display: flex;
      flex-direction: column;
      max-height: 380px;
      overflow-y: auto;
      scrollbar-width: thin;
      scrollbar-color: var(--border2) transparent;
    }
    .sr-tab-content::-webkit-scrollbar { width: 4px; }
    .sr-tab-content::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 99px; }

    /* Individual result row */
    .search-result-item {
      padding: var(--sp-4) 14px;
      border-bottom: 1px solid var(--border);
      display: flex;
      align-items: center;
      gap: var(--sp-4);
      cursor: pointer;
      transition: background var(--t);
    }
    .search-result-item:last-child { border-bottom: none; }
    .search-result-item:hover { background: var(--surface2); }

    .search-result-thumbnail {
      width: 44px;
      height: 44px;
      border-radius: 6px;
      background: var(--surface3);
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: var(--fs-400);
      flex-shrink: 0;
      overflow: hidden;
      border: 1px solid var(--border);
    }
    .search-result-thumbnail.artist-thumb { border-radius: 50%; }
    .search-result-thumbnail img { width: 100%; height: 100%; object-fit: cover; }

    .search-result-info {
      display: flex;
      flex-direction: column;
      gap: 3px;
      flex: 1;
      min-width: 0;
    }
    .search-result-title {
      font-size: var(--fs-200);
      font-weight: 600;
      color: var(--text);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .search-result-meta {
      font-size: var(--fs-75);
      color: var(--muted);
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }

    .sr-duration {
      font-family: var(--font-mono);
      font-size: var(--fs-75);
      color: var(--muted);
      flex-shrink: 0;
    }

    .search-result-empty {
      padding: 28px 14px;
      color: var(--muted);
      font-size: var(--fs-100);
      text-align: center;
    }

    /* Track table */

    #track-table-wrap {
      flex: 1; overflow: visible; padding: 0 18px 14px; min-height: 0;
      /* Column collapse below (@container tracktable) reacts to this
         element's own width, not the viewport's — correct today since it's
         always full-bleed, and correct if it's ever dropped into a narrower
         split/sidebar layout later without redoing the breakpoints.
         width:100% looks redundant next to flex:1, but it isn't: a flex
         item with flex-basis 0% (what `flex:1` shorthand sets) normally
         gets its size from the flex algorithm regardless of content — until
         container-type adds size containment, which for a "flex-basis:0
         + containment" combination made Chrome compute this item's box as
         near-zero (confirmed live: dropped to 36px) instead of filling the
         row. An explicit width gives it a definite size the flex algorithm
         no longer needs to derive, sidestepping the interaction. */
      width: 100%;
      container-type: inline-size;
      container-name: tracktable;
    }
    #track-table-wrap::-webkit-scrollbar { width: 4px; }
    #track-table-wrap::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 99px; }
    /* Columns: title | album | duration | actions */
   .track-table-header, .track-row {
     width: 100%;
     display: grid;
     /* Change this line to use 1fr on the flexible columns */
     grid-template-columns: 24px 40px 1fr 1fr 80px 150px;
     align-items: center;
     padding: var(--sp-3) var(--sp-4);
     border-radius: var(--radius-sm);
     transition: background var(--t);
     box-sizing: border-box; /* Assicura che il padding sia incluso nella larghezza */
   }
    .track-table-header > div:first-child { display:flex; align-items:center; justify-content:center; }
    .track-table-header input { accent-color: var(--yellow); cursor: pointer; width:14px; height:14px; }
    /* Header style for album column (4th child) */
    .track-table-header > div:nth-child(4) {
      text-align: center; padding-left: 6px;
    }
    /* "Actions" over a right-aligned button cluster — was left-aligned, so
       the word floated on its own halfway across an otherwise idle column. */
    .track-table-header > div:nth-child(6) {
      text-align: right; padding-right: 4px;
    }
    .track-row {
      cursor: default;
      position: relative;
      transition: all 0.15s ease;
    }
    .track-row:hover {
      background: var(--surface2);
      box-shadow: inset 0 0 0 1px var(--accent-ring);
      transform: translateY(-0.5px);
    }
    #track-table-wrap {
        max-width: 950px;
        margin: 0 auto;
    }
    #pagetion-controls {
      display: flex; justify-content: center; align-items: center;
      gap: var(--sp-3); padding: var(--sp-5) var(--sp-3);
      margin: 0 auto; max-width: 100%; font-size: var(--fs-100);
    }
    .page-btn {
      padding: var(--sp-3) var(--sp-4); background: var(--surface2);
      border: 1px solid var(--border); border-radius: var(--radius-sm);
      cursor: pointer; color: var(--text2); font-family: var(--app-font);
      font-size: var(--fs-100); transition: all var(--t);
    }
    .page-btn:hover:not(:disabled) { background: var(--surface3); color: var(--text); }
    .page-btn:disabled { opacity: .4; cursor: not-allowed; }
    #page-info { color: var(--muted); min-width: 120px; text-align: center; font-family: var(--font-mono); }
    @media (min-width: 1440px) {
      #track-table-wrap { max-width: 1160px; }
      /* Same ultrawide widening as the track table for the other capped
         containers, so the window doesn't leave a growing idle margin on
         very wide displays while the table stretches. */
      .status-bar, .search-container { max-width: 1060px; }
      .artist-section-hdr, .artist-albums-grid,
      .artist-tabs-bar, .artist-gallery-grid { max-width: min(100%, 1160px); }
    }
    .track-row.done    { background: var(--status-done-bg); }
    .track-row.failed  { background: var(--status-failed-bg); }
    .track-row.skipped { background: var(--status-skipped-bg); }
    /* Per-row download progress — app.js's syncTrackRowsWithQueue() mirrors
       each queue item onto its row (matched by data-spotify-id, so it
       survives a re-sort) whenever the queue re-renders, setting
       --row-progress and toggling .dl-active. A thin bar along the row's
       own bottom edge means "how is *this* download doing" reads without
       leaving the table for the queue drawer. */
    .track-row.dl-active { background: var(--status-active-bg); }
    .track-row.dl-active::after {
      content: '';
      position: absolute; left: 0; bottom: 0; height: 2px;
      width: var(--row-progress, 0%);
      background: var(--accent);
      transition: width var(--t);
      border-radius: 0 2px 2px 0;
    }

    .tr-check input { accent-color: var(--yellow); cursor: pointer; width: 14px; height: 14px; }
    .tr-num {
      font-family: var(--font-mono); font-size: var(--fs-200);
      color: var(--muted); text-align: left; padding-right: 6px;
    }
    .tr-title-cell { display: flex; align-items: center; gap: 9px; min-width: 0; }
    .tr-info { min-width: 0; display: flex; flex-direction: column; }

    /* Cover thumbnail next to title */
    .tr-thumb {
      width: 48px; height: 48px; border-radius: 6px;
      background: var(--surface3); flex-shrink: 0;
      display: flex; align-items: center; justify-content: center;
      font-size: 16px; overflow: hidden; border: 1px solid var(--border);
      background-size: cover; background-position: center;
      box-shadow: var(--elev-1);
      transition: transform var(--t);
    }
    .tr-thumb img { width: 100%; height: 100%; object-fit: cover; border-radius: 4px; }
    /* Neutralized by the global reduced-motion rule further down. */
    .track-row:hover .tr-thumb { transform: scale(1.04); }

    .tr-info { min-width: 0; }
    .tr-name {
      font-size: 14.5px; font-weight: 600;
      white-space: normal; /* allow wrapping vertically */
      overflow: visible; /* don't clip */
      word-break: break-word;
      display: block;
      line-height: 1.3;
      gap: 5px;
    }
    .explicit-badge {
      background: var(--surface3); color: var(--text2);
      font-family: var(--font-mono); font-size: 8.5px;
      padding: 1px var(--sp-2); border-radius: 3px; border: 1px solid var(--border2);
      flex-shrink: 0; font-weight: 700;
    }
    .tr-artist { font-size: 13.5px; color: var(--text2); white-space: normal; overflow: visible; text-overflow: unset; }

    .tr-dur { font-family: var(--font-mono); font-size: 13.5px; color: var(--text2); text-align: right; }
    .tr-playcount {
      font-family: var(--app-font); font-size: 13.5px; color: var(--text2);
      display: flex; align-items: center; justify-content: center;
      text-align: center; white-space: normal; overflow: visible;
      word-break: break-word; line-height: 1.2;
    }

    /* Spacing between duration and actions */
    .tr-dur { padding-right: 22px; }
    .tr-actions { margin-left: 10px; }

    /* Action area. Download is the one thing a row is for, so it is the only
       button shown at rest; lyrics / preview / cover / open are identical on
       every one of 25+ rows, which turns them into noise, so they wait for
       the row to be hovered or to hold keyboard focus. The column keeps its
       width whichever state it is in — the hidden buttons still take their
       space — so a row never reflows as the pointer crosses it. */
    .tr-actions { display: flex; gap: 3px; align-items: center; justify-content: flex-end; width: 100%;}
    .ta-btn {
      width: 30px; height: 30px; border-radius: 6px;
      background: var(--surface2); border: 1px solid var(--border);
      color: var(--muted); cursor: pointer; font-size: var(--fs-100);
      display: flex; align-items: center; justify-content: center;
      transition: all var(--t); flex-shrink: 0;
      position: relative;
    }
    .ta-btn:hover { background: var(--surface3); border-color: var(--border2); color: var(--text2); }
    .tr-actions .ta-btn:not(.dl) {
      opacity: 0;
      transition: opacity var(--t), background var(--t), border-color var(--t), color var(--t);
    }
    .track-row:hover  .tr-actions .ta-btn:not(.dl),
    .track-row:focus-within .tr-actions .ta-btn:not(.dl) {
      opacity: 1;
    }
    /* No hover to reveal them with — keep the whole set visible. */
    @media (hover: none) {
      .tr-actions .ta-btn:not(.dl) { opacity: 1; }
    }
    .ta-btn.dl { background: var(--accent); color: var(--accent-fg); border-color: var(--accent); }
    .ta-btn.dl:hover { background: var(--accent-hover); }
    .ta-btn.ta-lyrics:hover { color: var(--util-lyrics); border-color: var(--util-lyrics-bd); }
    .ta-btn.ta-preview:hover { color: var(--util-preview); border-color: var(--util-preview-bd); }
    .ta-btn.ta-preview.active { background: var(--util-preview-bg); border-color: var(--util-preview-bd); color: var(--util-preview); }
    .ta-btn.ta-cover:hover { color: var(--util-cover); border-color: var(--util-cover-bd); }
    .ta-btn.ta-link:hover { color: var(--util-link); border-color: var(--util-link-bd); }

    #preview-player {
      width: 100%; max-width: 100%; margin: 12px 0 12px; display: none;
    }

    /* Tooltip — .ta-btn and .act-btn[data-tip] were two copies of the same
       above-anchored bubble (only the offset differed by 1px, not a real
       distinction); merged into one rule. The sidebar's .nav-btn[data-label]
       tooltip (further down) stays separate — it anchors to the side with a
       border + drop shadow, a deliberately richer treatment for the rail. */
    .ta-btn::after,
    .act-btn[data-tip]::after {
      content: attr(data-tip);
      position: absolute; bottom: calc(100% + 6px); left: 50%;
      transform: translateX(-50%);
      background: var(--surface3); color: var(--text);
      font-family: var(--font-mono); font-size: var(--fs-50);
      padding: 3px 7px; border-radius: 4px; white-space: nowrap;
      pointer-events: none; opacity: 0; transition: opacity .15s;
      z-index: 100;
    }
    .act-btn[data-tip] { position: relative; }
    .ta-btn:hover::after,
    .act-btn[data-tip]:hover::after { opacity: 1; }

    /* ── RECENT FETCHES ── */
    #recent-wrap { 
      padding: 24px 0 0; 
      overflow: visible; 
      min-height: 0; 
    }
    
    /* New container to align it with the Search Bar */
    .recent-container {
      width: 100%;
      max-width: 820px;
      margin: 0 auto;
      padding: 0 18px; /* Added padding for consistency */
    }
    .list-container {
      width: 100%;
      /* Was 820px. The album card lives here and now has a third column;
         the tracklist below keeps its own 950px cap and stays centred
         inside the wider track. */
      max-width: 960px;
      margin: 0 auto;
      padding: 0 18px;
      display: flex;
      flex-direction: column;
      min-height: 0;
    }
    /* Same ultrawide tiers the track table below already uses, so the card
       and the list stay the same width instead of stepping in and out of
       alignment. It also stops #album-actions wrapping its last icon button
       onto a line of its own: at 960px the three buttons are ~30px wider
       than the middle column, which is the whole reason it wrapped. */
    @media (min-width: 1200px) {
      .list-container { max-width: 1060px; }
    }
    @media (min-width: 1440px) {
      .list-container { max-width: 1160px; }
    }
    #recent-grid {
      display: grid;
      /* auto-FILL, not auto-fit. auto-fit collapses the empty tracks and
         lets the surviving ones absorb the freed space, so a single recent
         fetch stretched across the full 820px row — and since .rc-cover is
         aspect-ratio 1, that made the artwork ~780px tall. auto-fill keeps
         the empty tracks, so one card is one card whatever the count. */
      grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
      justify-content: start;
      gap: var(--sp-4);
      width: 100%;
      margin: 0 auto;
    }
    .recent-card {
      width: 100%;
      max-width: none;
    }
    /* Recent *searches* share this grid but are one-line rows, not artwork
       tiles — a 120px track would squeeze and truncate them — so the grid
       drops to a single full-width column for them (see the .searches class
       set by renderRecentSearches()). */
    /* Recent *searches* are chips that wrap, not cards in a grid: each one is
       a couple of words, and a row apiece meant five past queries filled the
       screen. display:flex overrides the artwork grid the same element uses
       for recent fetches. */
    #recent-grid.searches {
      display: flex;
      flex-wrap: wrap;
      gap: var(--sp-3);
    }
    .search-chip {
      display: inline-flex; align-items: center; gap: var(--sp-3);
      max-width: 100%;
      padding: var(--sp-3) var(--sp-4);
      background: var(--surface); border: 1px solid var(--border);
      border-radius: var(--radius-pill);
      color: var(--text); font-family: var(--app-font); font-size: var(--fs-100);
      cursor: pointer;
      transition: background var(--t), border-color var(--t), color var(--t);
    }
    .search-chip:hover { background: var(--surface2); border-color: var(--accent-border); color: var(--text); }
    .search-chip .rc-search-icon { color: var(--muted); }
    .search-chip:hover .rc-search-icon { color: var(--accent-text); }
    /* A pasted URL can be a search too, and one of those is 90 characters —
       it gets an ellipsis rather than a chip the width of the window. */
    .search-chip-text {
      overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
      max-width: 340px;
    }
    .rc-search-icon {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      color: var(--muted);
      flex-shrink: 0;
    }

    .recent-label {
      /* Matched to .s-title: the two are the same kind of heading and were
         set two different ways. */
      font-size: 10.5px; font-weight: var(--fw-semibold); text-transform: uppercase;
      letter-spacing: var(--tracking-label); color: var(--accent-text); margin-bottom: 14px;
    }
    .recent-card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      overflow: hidden; cursor: pointer;
      transition: border-color var(--t), transform .18s ease, box-shadow .18s ease, background var(--t);
      box-shadow: var(--elev-1);
    }
    .recent-card:hover {
      border-color: var(--accent);
      transform: translateY(-4px);
      box-shadow: var(--elev-2);
    }
    .recent-card.active {
      border-color: var(--accent);
      background: var(--accent-subtle);
      transform: scale(1.04);
      box-shadow: var(--elev-2);
    }
    .rc-cover {
      width: 100%; aspect-ratio: 1; background: var(--surface3);
      background-size: cover; background-position: center;
      display: flex; align-items: center; justify-content: center;
      font-size: 35px; color: var(--muted);
      position: relative;
    }
    .rc-remove {
      position: absolute; top: 7px; right: 7px;
      width: 24px; height: 24px;
      border: none; border-radius: 999px;
      background: var(--overlay); color: var(--text);
      font-size: var(--fs-75); cursor: pointer;
      display: flex; align-items: center; justify-content: center;
      opacity: 0; transition: opacity .2s ease;
    }
    .recent-card:hover .rc-remove { opacity: 1; }
    .rc-remove:hover { background: var(--status-failed-fg) !important; opacity: 1 !important; }
    .rc-info { padding: var(--sp-3) 10px 10px; display: flex; flex-direction: column; gap: var(--sp-1); }
    .rc-title { font-size: var(--fs-75); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .rc-sub { font-size: var(--fs-50); color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .rc-badge {
      display: inline-flex; align-items: center; gap: var(--sp-2);
      font-size: 9px; font-weight: 600; letter-spacing: .01em;
      padding: var(--sp-1) 6px; border-radius: 999px;
      align-self: flex-start; margin-top: 5px;
    }
    /* Was per-card inline color/background from a JS BADGE_CFG map — moved
       to the same util-color families used elsewhere (lyrics/preview/cover/
       link) so a recent-fetch type reads with the app's existing palette
       instead of its own one-off hues. */
    .rc-badge.track    { color: var(--util-lyrics);  background: var(--util-lyrics-bg); }
    .rc-badge.album    { color: var(--status-done-fg); background: var(--status-done-bg); }
    .rc-badge.artist   { color: var(--util-preview); background: var(--util-preview-bg); }
    .rc-badge.playlist { color: var(--util-cover);   background: var(--util-cover-bg); }

    /* Queue drawer */
    #queue-dock {
      /* Beside the rail, not on top of it: the dock is viewport-anchored, so
         left:20px put its icon behind the 58px nav rail. */
      position: fixed; left: calc(var(--rail-w) + 20px); bottom: 20px;
      z-index: var(--z-dock);
      display: none; grid-template-columns: auto 1fr auto;
      align-items: center; gap: 14px;
      padding: 14px 18px; background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg); cursor: pointer;
      color: var(--text); box-shadow: var(--elev-2);
      transition: transform var(--t), background var(--t), border-color var(--t), opacity var(--t), box-shadow var(--t);
      min-width: 240px;
    }
    #queue-dock.visible { display: grid; }
    #queue-dock:hover {
      transform: translateY(-2px);
      border-color: var(--accent);
      box-shadow: var(--elev-3);
    }
    #queue-dock .qd-icon {
      width: 38px; height: 38px; border-radius: var(--radius-md);
      background: var(--surface2); display: grid;
      place-items: center; color: var(--accent-text); font-size: var(--fs-400);
    }
    #queue-dock .qd-icon svg {
      width: 20px; height: 20px; display: block;
    }
    #queue-dock .qd-body { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
    #queue-dock .qd-downloaded {
      font-family: var(--font-mono);
      font-size: var(--fs-300); font-weight: 700; color: var(--text);
      letter-spacing: .02em;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    #queue-dock .qd-speed {
      font-family: var(--font-mono);
      font-size: var(--fs-100); color: var(--muted);
    }
    #queue-dock .qd-chevron {
      font-size: var(--fs-400); color: var(--muted); line-height: 1;
    }

    /* The dock's second line is a live rate while one is known and a
       count of what is done when it is not — see renderQueue(). */
    #queue-dock .qd-bar {
      grid-column: 1 / -1;
      height: 3px; border-radius: 999px;
      background: var(--surface2); overflow: hidden;
      margin-top: 2px;
    }
    #queue-dock .qd-bar > i {
      display: block; height: 100%; width: 0%;
      background: linear-gradient(90deg, var(--yellow-d), var(--yellow));
      border-radius: inherit;
      transition: width .35s cubic-bezier(.22,.61,.36,1);
    }
    #queue-dock.done .qd-bar > i { background: var(--green); }

    /* ── Empty states ─────────────────────────────────────────────────
       Three screens open on nothing at all — no fetches, nobody followed,
       no downloads logged — and each said so in its own way, in a different
       size and colour. One component, so "there is nothing here yet" reads
       the same everywhere and reads as designed rather than as missing. */
    .empty-state {
      display: flex; flex-direction: column; align-items: center;
      justify-content: center; text-align: center;
      gap: 6px; padding: 38px var(--sp-7);
      color: var(--muted);
    }
    .empty-state .es-icon {
      width: 44px; height: 44px; margin-bottom: 6px;
      display: grid; place-items: center;
      border-radius: 14px;
      background: var(--surface2);
      color: var(--muted);
      opacity: .9;
    }
    .empty-state .es-icon svg { width: 20px; height: 20px; }
    .empty-state .es-title {
      font-size: var(--fs-200); font-weight: 600; color: var(--text2);
      letter-spacing: .01em;
    }
    .empty-state .es-hint {
      font-size: 11.5px; line-height: 1.6; max-width: 46ch;
      color: var(--muted);
    }

    /* Two components already honoured this preference on their own (the
       status spinner, the CSV button's pulse) while every hover lift, panel
       slide and toast still moved. One rule, at the end of the cascade for
       the whole window: motion is reduced to a state change, and nothing
       that conveys progress is removed — a spinner that has stopped says
       something untrue, so those keep turning, slowly. */
    @media (prefers-reduced-motion: reduce) {
      *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
      }
      .status-spinner,
      .cover-download-btn.loading svg {
        animation-duration: 2.4s !important;
        animation-iteration-count: infinite !important;
      }
      /* A hover that moves the thing under the pointer is the part that
         causes trouble; the colour change alone still answers "am I on it". */
      .nav-btn:hover, .nav-btn:active,
      #fetchBtn:hover, #fetchBtn:active,
      .act-btn:hover, .act-btn:active,
      .recent-card:hover {
        transform: none;
      }
    }

    /* Keyboard focus was invisible everywhere: the outline was suppressed by
       the reset and never replaced, so tabbing through Settings moved a
       selection nobody could see. Pointer clicks are unaffected. */
    :where(button, a, input, select, textarea, [tabindex]):focus-visible {
      outline: 2px solid var(--accent);
      outline-offset: 2px;
      border-radius: 8px;
    }

    #queue-drawer {
      position: fixed; inset: 0;
      display: none; align-items: center; justify-content: center;
      z-index: var(--z-drawer); padding: var(--sp-6);
      pointer-events: none;
    }
    #queue-drawer.open { display: flex; pointer-events: auto; }
    #queue-backdrop {
      position: absolute; inset: 0;
      background: var(--scrim);
      opacity: 0; transition: opacity var(--t);
      pointer-events: none;
      z-index: var(--z-drawer-backdrop);
    }
    #queue-drawer.open #queue-backdrop {
      opacity: 1; pointer-events: auto;
    }
    .queue-panel {
      position: relative;
      width: min(700px, calc(100vw - 40px));
      max-height: min(78vh, 760px);
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      box-shadow: var(--elev-3);
      overflow: hidden;
      display: flex; flex-direction: column;
      z-index: var(--z-drawer);
    }

    .queue-panel header {
      display: flex; align-items: center; justify-content: space-between;
      padding: 22px var(--sp-7) var(--sp-5);
      gap: 14px;
    }
    .queue-panel h3 { margin: 0; font-size: 20px; font-weight: var(--fw-bold); color: var(--text); }
    .export-link {
      display: inline-flex; align-items: center; gap: var(--sp-3);
      font-size: 14px; color: var(--text2); cursor: pointer;
      border-radius: 14px; padding: var(--sp-3) 10px;
      background: transparent; border: none;
      transition: background var(--t), color var(--t);
    }
    .export-link:hover { background: var(--surface2); color: var(--text); }
    .export-link svg { width: 16px; height: 16px; }

    .queue-actions { display: flex; align-items: center; gap: 10px; }
    .clear-link {
      display: inline-flex; align-items: center; gap: var(--sp-3);
      font-size: 14px; color: var(--text2); cursor: pointer;
      border-radius: 14px; padding: var(--sp-3) 10px; border: none; background: transparent;
      transition: background var(--t), color var(--t);
    }
    .clear-link:hover { background: var(--surface2); color: var(--text); }
    .clear-link svg { width: 16px; height: 16px; }
    .close-btn {
      width: 38px; height: 38px; border-radius: 14px;
      display: grid; place-items: center;
      border: 1px solid var(--border); background: var(--surface);
      color: var(--text2); cursor: pointer;
    }
    .close-btn:hover { background: var(--surface2); color: var(--text); }
    .queue-summary {
      padding: 0 24px 16px;
      display: grid; grid-template-columns: repeat(4, minmax(0,1fr)); gap: var(--sp-4);
    }
    .queue-summary .qs-item {
      display: flex; align-items: center; gap: var(--sp-4);
      padding: 14px var(--sp-4); background: var(--surface2);
      border-radius: 16px; border: 1px solid var(--border);
      color: var(--text2); font-size: var(--fs-200);
    }
    .queue-summary .qs-item strong { color: var(--text); font-weight: 700; }
    /* They are buttons now (each one filters the list below), so they need to
       look pressable and to show which one is holding the list down. */
    .queue-summary .qs-item {
      cursor: pointer; font-family: var(--app-font); text-align: left;
      transition: border-color var(--t), background var(--t);
    }
    .queue-summary .qs-item:hover { background: var(--surface3); border-color: var(--border2); }
    .queue-summary .qs-item.is-filtering {
      border-color: var(--accent);
      background: var(--accent-subtle);
    }

    .queue-toolbar {
      display: flex; align-items: center; gap: var(--sp-3);
      padding: 0 24px var(--sp-4);
      position: relative;
    }
    .queue-toolbar .ts-icon { left: 36px; }
    #queue-search {
      flex: 1; min-width: 0;
      padding: 7px 11px 7px 30px;
      background: var(--surface2); border: 1px solid var(--border);
      border-radius: var(--radius-sm); color: var(--text);
      font-family: var(--app-font); font-size: var(--fs-100);
      outline: none; transition: border-color var(--t);
      user-select: text;
    }
    #queue-search:focus { border-color: var(--accent); }
    #queue-search::placeholder { color: var(--muted); }
    .queue-toolbar .act-btn { padding: 7px 12px; font-size: var(--fs-75); }
    .queue-no-match {
      padding: var(--sp-8) var(--sp-5);
      text-align: center; color: var(--muted); font-size: var(--fs-100);
    }
    .queue-stats-bar {
      padding: 0 24px 16px;
      display: flex; gap: var(--sp-6);
      font-size: var(--fs-200); color: var(--text2);
    }
    .queue-stats-bar strong { color: var(--text); font-family: var(--font-mono); }
    /* Tracks still missing (see the #failed-panel comment in index.html).
       Same surface and radius as the counters above it, so it reads as part
       of the queue rather than as a warning banner dropped on top. */
    .failed-panel {
      margin: 0 24px var(--sp-4);
      padding: 12px 14px;
      background: var(--surface2);
      border: 1px solid var(--border);
      border-radius: 16px;
      font-size: var(--fs-200); color: var(--text2);
    }
    .failed-panel .fp-head { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; }
    .failed-panel .fp-text { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 160px; }
    .failed-panel .fp-text strong { color: var(--status-failed-fg); font-weight: 700; }
    .failed-panel .fp-sub { font-size: var(--fs-75); color: var(--muted); }
    .failed-panel .fp-actions { display: flex; gap: var(--sp-3); flex-wrap: wrap; }
    .failed-panel .act-btn { padding: 7px 12px; font-size: var(--fs-75); }
    .fp-list {
      list-style: none; margin: 10px 0 0; padding: 0;
      max-height: 220px; overflow-y: auto;
      display: flex; flex-direction: column; gap: 6px;
    }
    .fp-item {
      display: flex; align-items: center; gap: var(--sp-3);
      padding: 6px 8px; border-radius: var(--radius-sm);
      border: 1px solid var(--border);
    }
    .fp-item .fp-main { flex: 1; min-width: 0; }
    .fp-item .fp-name, .fp-item .fp-error { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .fp-item .fp-name { color: var(--text); }
    .fp-item .fp-error { font-size: var(--fs-75); color: var(--muted); }
    .fp-item .fp-attempts { font-size: var(--fs-75); color: var(--muted); font-family: var(--font-mono); white-space: nowrap; }
    .queue-summary .qs-icon {
      width: 34px; height: 34px; border-radius: var(--radius-md);
      display: grid; place-items: center; background: var(--status-active-bg);
      color: var(--status-active-fg);
      flex-shrink: 0;
    }
    .queue-summary .qs-item.queued .qs-icon { background: var(--status-queued-bg); color: var(--status-queued-fg); }
    .queue-summary .qs-item.completed .qs-icon { background: var(--status-done-bg); color: var(--status-done-fg); }
    .queue-summary .qs-item.skipped .qs-icon { background: var(--status-skipped-bg); color: var(--status-skipped-fg); }
    .queue-summary .qs-item.failed .qs-icon { background: var(--status-failed-bg); color: var(--status-failed-fg); }
    .queue-content {
      padding: 0 24px 20px;
      display: flex;
      flex-direction: column;
      gap: var(--sp-5);
      min-height: 240px;
      max-height: min(500px, 78vh); 
      overflow-y: auto;
    }

    .queue-content::-webkit-scrollbar {
      width: 4px;
    }
    .queue-content::-webkit-scrollbar-thumb {
      background: var(--border2);
      border-radius: 99px;
    }

    .queue-empty {
      flex: 1; display: flex; align-items: center; justify-content: center;
      color: var(--muted); font-size: 16px; text-align: center;
      padding: 36px var(--sp-4);
    }
    .queue-empty .q-icon { font-size: 36px; margin-bottom: 10px; opacity: .32; }
    .queue-list { display: flex; flex-direction: column; gap: 14px; }
    .queue-item {
      background: var(--surface2); border: 1px solid var(--border);
      border-radius: 18px; padding: 18px;
      display: flex; flex-direction: column; gap: var(--sp-4);
    }
    .queue-item .qi-title { font-size: 14px; font-weight: 700; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .queue-item .qi-artist { font-size: var(--fs-100); color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .queue-item .qi-pill.done { background: var(--surface); color: var(--text2); border: 1px solid var(--border); font-weight: 600; }
    /* The row the title and the status pill share. It had no rule at all, so
       it was a plain block: the pill (also a div, also block) stretched the
       full width of the item and rendered as a coloured bar under the title
       instead of a pill beside it. */
    .queue-item .qi-top {
      display: flex; align-items: flex-start; justify-content: space-between;
      gap: var(--sp-4);
    }
    .queue-item .qi-meta { min-width: 0; }
    .queue-item .qi-pill {
      display: inline-block; flex-shrink: 0;
      padding: 5px var(--sp-4); border-radius: 999px;
      font-size: var(--fs-75); font-weight: 700; text-transform: capitalize; white-space: nowrap;
    }
    .queue-item .qi-pill.waiting { background: var(--status-warn-bg); color: var(--status-warn-fg); }
    .queue-item .qi-pill.active { background: var(--status-active-bg); color: var(--status-active-fg); }
    .queue-item .qi-pill.error { background: var(--status-failed-bg); color: var(--status-failed-fg); }
    .queue-item .qi-pill.skipped { background: var(--status-skipped-bg); color: var(--status-skipped-fg); }
    .queue-item .qi-bottom { font-family: var(--font-mono); font-size: var(--fs-100); color: var(--muted); }
    .queue-item .qi-bottom-meta { display: flex; flex-direction: column; gap: 3px; font-family: var(--font-mono); font-size: 11.5px; color: var(--muted); }
    .queue-item .qi-bottom-meta .qi-bm-path { color: var(--text-dim); font-size: var(--fs-75); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%; }
    @keyframes fadeup { from { opacity:0; transform: translateY(4px); } to { opacity:1; transform:none; } }

    #queue-counter {
      padding: 0 24px 22px;
      font-family: var(--font-mono); font-size: var(--fs-100); color: var(--muted);
      display: flex; justify-content: space-between; align-items: center; gap: var(--sp-4);
    }

    /* ══════════ LOGS VIEW ══════════ */
    #view-logs { padding: 18px; }
    #log-panel {
      flex: 1; min-height: 0;
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      overflow: hidden;
      display: flex; flex-direction: column;
      box-shadow: var(--elev-1);
      position: relative;
    }
    #log-header {
      padding: var(--sp-4) var(--sp-5); 
      background: var(--surface2);
      border-bottom: 1px solid var(--border);
      display: flex; align-items: center; justify-content: space-between; flex-shrink: 0;
    }
    #log-header span { 
      font-family: var(--font-mono); 
      font-size: 11.5px; font-weight: 700; text-transform: uppercase; 
      letter-spacing: .08em; color: var(--text); 
      display: flex; align-items: center; gap: var(--sp-3);
    }
    /* Pallino di stato in stile terminale/UI accanto al titolo */
    #log-header span::before {
      content: ''; display: inline-block; width: 8px; height: 8px;
      border-radius: 50%; background: var(--accent);
    }
    .log-actions { display: flex; gap: var(--sp-3); }
    .log-btn { 
      background: var(--surface); border: 1.5px solid var(--border); color: var(--muted); 
      cursor: pointer; display: flex; align-items: center; justify-content: center;
      width: 32px; height: 32px; border-radius: var(--radius-sm); transition: all var(--t);
    }
    .log-btn:hover { 
      background: var(--surface3); border-color: var(--border2); 
      color: var(--text2); transform: translateY(-1px); 
    }
    .log-btn:active { transform: translateY(0); }
    #logArea {
      flex: 1; min-height: 0; overflow-y: auto;
      padding: 14px var(--sp-5); font-family: var(--font-mono); font-size: 11.5px;
      line-height: 1.7; background: var(--surface); user-select: text;
    }
    #logArea::-webkit-scrollbar { width: 5px; }
    #logArea::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 99px; }
    .log-line { display: flex; gap: var(--sp-4); margin-bottom: 4px; animation: fadein .12s ease; }
    @keyframes fadein { from { opacity:0; transform: translateY(2px); } to { opacity:1; transform: none; } }
    .log-ts { color: var(--muted); flex-shrink: 0; opacity: .7; user-select: none; }
    .log-msg { color: var(--text2); word-break: break-word; }
    .log-msg.info  { color: var(--blue); }
    .log-msg.ok    { color: var(--green); }
    .log-msg.error { color: var(--red); }
    .log-msg.warn  { color: var(--yellow); }
    /* Startup diagnostics: visible here, but deliberately never toasted. */
    .log-msg.debug { color: var(--muted); }

    /* ══════════ SETTINGS VIEW ══════════ */
    #view-settings { padding: 0; overflow: hidden; }
    .s-header { padding: var(--sp-6) var(--sp-5) var(--sp-4); flex-shrink: 0; display: flex; align-items: center; justify-content: space-between; gap: var(--sp-5); flex-wrap: wrap; }
    .s-header h1 { font-size: var(--fs-500); font-weight: var(--fw-semibold); letter-spacing: -.04em; }
    .s-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
    /* The settings header buttons follow the same 36px control height as the
       rest now; they were 44, which made that one row noticeably chunkier
       than every other button in the app. */
    .s-actions .act-btn { padding: 0 18px; font-size: var(--fs-200); min-height: 36px; border-radius: var(--radius-sm); }
    .s-actions .act-btn.secondary { background: var(--surface); color: var(--text2); border-color: var(--border); }
    .s-actions .act-btn.secondary:hover { background: var(--surface2); color: var(--text); }
    .s-actions .act-btn.primary { background: var(--accent); color: var(--accent-fg); border-color: transparent; }
    .s-actions .act-btn.primary:hover { background: var(--accent-hover); }
    
    .folder-pill { display: inline-flex; align-items: center; gap: var(--sp-3); background: var(--surface); border: 1px solid var(--border); color: var(--text2); padding: 9px 14px; border-radius: 999px; font-size: var(--fs-100); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .folder-pill::before { content: '📁'; display: inline-block; }
    .tab-bar {
      display: flex; padding: 0 18px;
      border-bottom: 1px solid var(--border);
      flex-shrink: 0; gap: var(--sp-2); margin-top: 0; overflow-x: auto;
    }
    .tab-bar::-webkit-scrollbar { display: none; }
    .stab {
      display: inline-flex; align-items: center; gap: var(--sp-3);
      padding: var(--sp-3) var(--sp-5); background: transparent; border: 1px solid transparent; border-bottom: none;
      color: var(--text); font-family: var(--font-mono); font-size: 14px; font-weight: 600;
      cursor: pointer; border-radius: 12px 12px 0 0; white-space: nowrap;
      transition: all var(--t); margin-bottom: -1px;
    }
    .stab svg { width: 18px; height: 18px; stroke-width: 2.2; }
    .stab:hover {
      background: var(--surface2);
      transform: translateY(-1px);
    }
    .stab.active {
      color: var(--accent-fg); background: var(--accent);
      border-color: var(--accent); border-bottom: 1px solid var(--accent);
      box-shadow: none;
    }
    .s-body { flex: 1; overflow-y: auto; padding: var(--sp-4) var(--sp-5) var(--sp-5); min-height: 0; }
    /* Settings rows are label-left / control-right, so on a wide window the
       two ends of one setting ended up 1200px apart with nothing between
       them. Capped rather than centred: the heading, the tabs and the cards
       all keep the same left edge, and only the empty middle stops growing. */
    #view-settings > .s-header,
    #view-settings > .s-body {
      width: 100%;
      max-width: 1180px;
    }
    .s-body::-webkit-scrollbar { width: 3px; }
    .s-body::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 99px; }
    .s-footer { position: sticky; bottom: 0; display: flex; justify-content: flex-start; padding: 10px 0 0; background: transparent; }
    .save-toast {
      position: fixed; bottom: 24px; left: 24px;
      min-width: 220px; padding: var(--sp-4) var(--sp-5);
      background: var(--surface3);
      color: var(--text); border-radius: var(--radius-md);
      border: 1px solid var(--border2);
      box-shadow: var(--elev-3);
      font-size: var(--fs-200); font-weight: 600;
      z-index: var(--z-save-toast);
      opacity: .98;
    }
    .save-toast.hidden { display: none; }
    .save-toast.show { display: block; }
    /* Volume slider + its readout as one control. The native range input is
       themed rather than replaced: accent-color is enough to make it follow
       the app's accent, and a hand-built slider would be a lot of markup to
       reproduce keyboard and drag behaviour that already works. */
    .volume-control { display: inline-flex; align-items: center; gap: var(--sp-3); }
    .volume-control input[type="range"] {
      width: 140px; accent-color: var(--accent); cursor: pointer;
    }
    .volume-value {
      font-family: var(--font-mono); font-size: var(--fs-75); color: var(--text2);
      min-width: 38px; text-align: right;
    }

    /* Accent picker: swatch + select as one control. */
    .accent-picker { display: inline-flex; align-items: center; gap: var(--sp-3); }
    .accent-dot {
      width: 14px; height: 14px; border-radius: 50%;
      background: var(--accent);
      border: 1px solid var(--accent-border);
      flex-shrink: 0;
    }

    .tc { display: none; }
    .tc.active { display: block; }
    /* Two columns of setting cards once there is room for them. A settings
       row is label-left / control-right and rarely more than ~500px of
       actual content, so one column down the middle of a wide window meant
       scrolling past a lot of air to reach things that could have been on
       screen together.
       CSS columns rather than a grid: the cards have very different heights
       (a section with two toggles next to one with a dozen), and columns
       fill by flowing, so the two sides stay roughly level instead of
       leaving a grid row as tall as its tallest cell. break-inside stops a
       card being split down the middle across the column boundary. */
    @media (min-width: 1100px) {
      .tc.active { columns: 2; column-gap: var(--sp-4); }
      #tc-download.active,
      #tc-status.active { columns: 1; }
      .tc.active .s-section {
        break-inside: avoid;
        -webkit-column-break-inside: avoid;
        /* A column box has no margin collapsing to rely on, and the last
           card in a column still needs its gap to the next one below. */
        margin-bottom: var(--sp-4);
      }
      .tc.active .s-section:last-child { margin-bottom: var(--sp-4); }
      /* Sections that are a wide table or a grid of their own read badly at
           half width — they keep the full row. */
      .tc.active .s-section.s-wide { column-span: all; }
    }
    .s-section {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: var(--radius-lg);
      padding: var(--sp-6);
      margin-bottom: var(--sp-4);
      box-shadow: var(--elev-1);
      position: relative;
      overflow: hidden;
    }
    .s-section:last-child { margin-bottom: 0; }
    .s-title {
      font-size: 10.5px; font-weight: var(--fw-semibold); text-transform: uppercase;
      /* Wider tracking, lighter weight: at this size a heavier weight fills
         the counters in and the word turns into a block. Uppercase needs the
         air, not the weight, to read as a heading. */
      letter-spacing: var(--tracking-label); color: var(--muted); margin-bottom: 14px;
    }
    .s-row { display: flex; align-items: center; justify-content: space-between; min-height: 30px; margin-bottom: 8px; gap: 10px; }
    .s-row:last-child { margin-bottom: 0; }
    .s-row label { display: flex; align-items: center; gap: var(--sp-3); cursor: pointer; }
    .network-destination-layout {
      display: grid;
      grid-template-columns: minmax(0, 1.35fr) minmax(320px, .9fr);
      gap: clamp(20px, 3vw, 36px);
      align-items: start;
    }
    .network-destination-group { min-width: 0; }
    .network-destination-heading {
      margin-bottom: 14px; padding-bottom: 9px;
      border-bottom: 1px solid var(--border);
      color: var(--text); font-size: var(--fs-100); font-weight: 600;
    }
    .network-destination-fields {
      display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 15px 16px; align-items: start;
    }
    .network-destination-connection-fields {
      grid-template-columns: minmax(0, 1fr) minmax(130px, .62fr);
    }
    .network-destination-field {
      display: flex; flex-direction: column; align-items: stretch; gap: 6px;
      min-width: 0; color: var(--text2); font-size: 12px; font-weight: 500;
    }
    .network-destination-field > span { display: block; line-height: 1.4; }
    .network-destination-field small,
    .network-destination-encryption small {
      display: block; max-width: 58ch; margin-top: 3px;
      color: var(--muted); font-family: var(--font-mono);
      font-size: var(--fs-50); font-weight: 400; line-height: 1.45;
    }
    .network-destination-field select,
    .network-destination-field input,
    .network-destination-retry-controls input {
      box-sizing: border-box; width: 100%; min-width: 0; min-height: 38px;
    }
    .network-destination-field-wide { grid-column: 1 / -1; }
    .network-destination-inline {
      display: grid; grid-template-columns: minmax(0, 1.1fr) minmax(0, .9fr); gap: 8px;
    }
    .network-destination-timeout-control { display: flex; align-items: center; gap: 9px; }
    .network-destination-timeout-control input { max-width: 110px; }
    .network-destination-timeout-control > span { color: var(--muted); font-size: var(--fs-75); }
    .network-destination-encryption {
      display: flex; align-items: center; justify-content: space-between; gap: 12px;
      min-height: 62px; padding: 10px 12px;
      border: 1px solid var(--border); border-radius: var(--radius-sm);
      color: var(--text2); font-size: 12px; font-weight: 500;
    }
    .network-destination-encryption > span { min-width: 0; }
    .network-destination-encryption .toggle { flex: 0 0 42px; }
    .network-destination-actions {
      display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 16px;
    }
    .network-destination-actions .act-btn { min-height: 38px; padding: 0 14px; }
    #network-destination-status {
      flex: 1 1 100%; min-height: 18px; color: var(--muted); font-size: 12px;
    }
    .network-destination-retry {
      display: grid; grid-template-columns: minmax(240px, .85fr) minmax(320px, 1.15fr);
      align-items: end; gap: 20px; margin-top: 22px; padding-top: 18px;
      border-top: 1px solid var(--border);
    }
    .network-destination-retry-controls { display: flex; gap: 9px; min-width: 0; }
    .network-destination-retry-controls input { flex: 1 1 auto; }
    .network-destination-retry-controls .act-btn { flex: 0 0 auto; min-height: 38px; }
    @media (max-width: 900px) {
      .network-destination-layout,
      .network-destination-retry { grid-template-columns: minmax(0, 1fr); }
      .network-destination-layout { gap: 24px; }
    }
    @media (max-width: 600px) {
      .network-destination-fields,
      .network-destination-connection-fields,
      .network-destination-inline { grid-template-columns: minmax(0, 1fr); }
      .network-destination-field-wide { grid-column: auto; }
      .network-destination-retry-controls { flex-wrap: wrap; }
      .network-destination-retry-controls input { flex-basis: 100%; }
      .network-destination-retry-controls .act-btn,
      .network-destination-actions .act-btn { flex: 1 1 auto; min-height: 44px; }
    }
    /* Shrinkable, with min-width:0 so the <small> help text can wrap. It was
       flex-shrink:0, which is fine for a two-word label but not for the ones
       carrying a paragraph of explanation (the AcoustID key row is three
       lines of prose): those could not wrap, so the row grew past the panel
       and the whole section scrolled sideways. Short labels don't wrap
       anyway — there is nothing in them to break. */
    .s-label { color: var(--text2); font-size: 12.5px; font-weight: 500; min-width: 0; }
    .s-label small { display: block; color: var(--muted); font-size: var(--fs-50); margin-top: 1px; font-family: var(--font-mono); }
    .s-sep { height: 1px; background: var(--border); margin: 8px 0; }

    select, input[type="text"], input[type="password"], input[type="number"] {
      /* background-color, not the `background` shorthand: the select below
         paints its chevron as a background-image, and a shorthand here (or
         in the dark-theme override) would quietly erase it. */
      background-color: var(--surface); color: var(--text);
      border: 1.5px solid var(--border); border-radius: var(--radius-sm);
      padding: 7px 11px; outline: none;
      font-family: var(--app-font); font-size: 12.5px; cursor: pointer;
      transition: border-color var(--t), box-shadow var(--t), background-color var(--t);
    }
    /* A native select renders in the OS's own font and chrome — the one
       control in the window that was still being drawn by something other
       than this stylesheet, and next to these rounded mono surfaces it read
       as an unfinished part of the page. */
    select {
      appearance: none;
      -webkit-appearance: none;
      padding-right: 30px;
      background-image: var(--chevron);
      background-repeat: no-repeat;
      background-position: right 10px center;
      background-size: 13px 13px;
      text-overflow: ellipsis;
    }
    select:hover,
    input[type="text"]:hover, input[type="password"]:hover, input[type="number"]:hover {
      border-color: var(--border2);
    }
    select:focus, input:focus {
      border-color: var(--yellow);
      box-shadow: 0 0 0 3px var(--ring);
    }
    input[type="number"] { width: 70px; text-align: center; }
    input[type="text"].full, input[type="password"].full { width: 100%; }

    input[type="checkbox"], input[type="radio"], progress {
      accent-color: var(--yellow);
    }

    /* Toggle */
    .toggle { position: relative; width: 42px; height: 24px; flex-shrink: 0; }
    .toggle input { opacity: 0; width: 0; height: 0; }
    .toggle-sl {
      position: absolute; inset: 0; background: var(--surface3);
      border-radius: 99px; border: 1.5px solid var(--border2); cursor: pointer;
      transition: background var(--t), border-color var(--t), box-shadow var(--t);
    }
    .toggle-sl::before {
      content: ''; position: absolute; width: 16px; height: 16px; left: 3px; top: 50%;
      transform: translateY(-50%); background: var(--muted); border-radius: 50%;
      transition: transform var(--t-slow), background var(--t), width var(--t);
      box-shadow: 0 1px 3px rgba(15,23,42,0.18);
    }
    .toggle input:checked + .toggle-sl { background: var(--yellow-bg); border-color: var(--yellow); }
    .toggle input:checked + .toggle-sl::before { transform: translateX(18px) translateY(-50%); background: var(--yellow); }
    /* The checkbox itself is 0x0 and invisible, so the global focus-visible
       ring had nothing to draw around: tabbing through Settings skipped
       every switch as far as the eye could tell. */
    .toggle input:focus-visible + .toggle-sl {
      box-shadow: 0 0 0 3px var(--ring);
      border-color: var(--yellow);
    }
    /* A switch is a physical thing; give it something to push against. */
    .toggle-sl:active::before { width: 20px; }

    /* Sortable lists */
    .sort-list { display: flex; flex-direction: column; gap: var(--sp-2); }
    #services-list {
      display: flex;
      flex-direction: column; /* Changed from row to column */
      flex-wrap: nowrap;      /* No longer wraps */
      gap: 6px;               /* Slightly more compact */
      width: 100%;            /* Takes full width */
    }

    #services-list .sort-item {
      width: 100%;            /* Takes up the full width */
      flex: none;             /* Rimuovi il flex-grow */
      background: var(--surface);
      border: 1.5px solid var(--border);
      border-radius: var(--radius-sm);
      transition: all var(--t);
    }
    .sort-item.inactive {
      opacity: 0.6;
      filter: grayscale(1);
    }
    .priority-num {
      font-family: var(--font-mono);
      font-size: var(--fs-75);
      color: var(--muted);
      width: 20px;
      text-align: center;
      font-weight: 700;
    }
    .svc-icon {
      display: inline-flex; align-items: center; justify-content: center;
      width: 36px; height: 36px; border-radius: 12px;
      background: var(--surface);
      border: none;
      flex-shrink: 0; overflow: hidden;
      color: var(--text);
    }
    .svc-icon.icon-image {
      background: var(--surface);
      border: none;
    }
    .svc-icon img {
      height: 20px; width: auto; max-width: 80%; object-fit: contain;
      display: block;
    }
    /* Fix visual size discrepancies for specific icons with extra whitespace */
    .svc-icon.kuwo img,
    .svc-icon.netease img {
      height: 24px; max-width: 90%; transform: scale(1.05); transform-origin: center;
    }
    .svc-icon.tidal,
    .svc-icon.qobuz,
    .svc-icon.deezer,
    .svc-icon.amazon,
    .svc-icon.soundcloud,
    .svc-icon.youtube,
    .svc-icon.apple,
    .svc-icon.pandora {
      background: var(--surface);
      color: var(--text);
    }
    .sort-item {
      display: flex; align-items: center; gap: var(--sp-3);
      padding: 7px 10px; background: var(--surface);
      border: 1.5px solid var(--border2); border-radius: var(--radius-sm);
      cursor: grab; transition: border-color var(--t), background var(--t);
    }
    .sort-item:hover { border-color: var(--border2); background: var(--surface2); }
    .sort-item.dragging  { opacity: .3; }
    .sort-item.drag-over { border-color: var(--accent); background: var(--accent-subtle); }
    .drag-h { color: var(--muted); font-size: var(--fs-100); cursor: grab; }
    .svc-name  { flex: 1; font-size: 12.5px; color: var(--text2); }
    .svc-badge { font-family: var(--font-mono); font-size: 9.5px; padding: 1.5px 5px; border-radius: 4px; background: var(--surface3); color: var(--muted); border: 1px solid var(--border); }

    /* Extension registry list */
    .reg-item { cursor: default; align-items: center; justify-content: space-between; gap: var(--sp-4); }
    .reg-item-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
    .reg-url { font-family: var(--font-mono); font-size: 11.5px; color: var(--text2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .reg-badges { display: flex; flex-wrap: wrap; gap: 5px; }
    .reg-badge { font-size: 9.5px; padding: 1.5px 6px; border-radius: 999px; background: var(--surface3); color: var(--muted); border: 1px solid var(--border); white-space: nowrap; }
    .reg-badge-environment { color: var(--status-warn-fg); background: var(--status-warn-bg); border-color: var(--status-warn-fg); }
    .reg-badge-env_file { color: var(--util-lyrics); background: var(--status-queued-bg); border-color: var(--util-lyrics-bd); }
    .reg-badge-custom { color: var(--status-done-fg); background: var(--status-done-bg); border-color: var(--status-done-fg); }
    .reg-badge-off { color: var(--red); border-color: var(--status-failed-fg); }
    .reg-item-disabled { opacity: .55; }
    .reg-badge-reachable { color: var(--status-done-fg); background: var(--status-done-bg); border-color: var(--status-done-fg); }
    .reg-badge-unreachable { color: var(--status-failed-fg); background: var(--status-failed-bg); border-color: var(--status-failed-fg); }
    .reg-badge-signed { color: var(--status-done-fg); background: var(--status-done-bg); border-color: var(--status-done-fg); }
    .reg-badge-checksum-only { color: var(--status-warn-fg); background: var(--status-warn-bg); border-color: var(--status-warn-fg); }
    .reg-badge-unverified { color: var(--muted); }
    .reg-remove-btn { padding: 5px var(--sp-4); font-size: var(--fs-75); color: var(--red); border-color: var(--status-failed-fg); flex-shrink: 0; }

    .status-panel-header {
      display: flex; align-items: flex-start; justify-content: space-between; gap: var(--sp-5);
      margin-bottom: 14px;
    }
    .status-toolbar {
      display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; justify-content: flex-end;
    }
    .status-check-all {
      display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-3);
      padding: 10px 18px; background: var(--accent); color: var(--accent-fg);
      border: none; border-radius: 18px; font-family: var(--app-font); font-weight: 700;
      cursor: pointer; transition: background var(--t), transform var(--t);
    }
    .status-check-all:hover { background: var(--accent-hover); transform: translateY(-1px); }
    .status-overall {
      display: inline-flex; align-items: center; gap: 10px;
      padding: var(--sp-3) 14px; border-radius: 999px; border: 1px solid var(--border2);
      background: var(--surface2); color: var(--muted); font-size: var(--fs-100);
    }
    .status-overall.online { border-color: var(--status-done-fg); background: var(--status-done-bg); color: var(--status-done-fg); }
    .status-overall.offline { border-color: var(--status-failed-fg); background: var(--status-failed-bg); color: var(--status-failed-fg); }
    .status-overall-icon {
      width: 20px; height: 20px; border-radius: 999px;
      display: inline-flex; align-items: center; justify-content: center;
      background: currentColor; color: #fff; font-size: var(--fs-100);
    }
    .status-grid {
      display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px;
      margin-bottom: 6px;
    }
    .status-section-title {
      display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
      margin: 18px 0 12px; padding-top: 14px; border-top: 1px solid var(--border);
      font-size: var(--fs-200); font-weight: 700; color: var(--text);
    }
    .status-section-hint { font-size: var(--fs-75); font-weight: 400; color: var(--muted); }
    .status-grid-extensions { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
    .svc-icon.icon-glyph {
      font-size: 17px; background: var(--surface);
    }
    .svc-icon.extensions {
      background: var(--accent-subtle); color: var(--accent-text);
    }
    /* Status card reduced and made more compact */
    .status-card {
      padding: var(--sp-5) 18px; border-radius: 18px; background: var(--surface);
      border: 1px solid var(--border2); box-shadow: var(--elev-1);
    }
    .status-card-header { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
    .status-card-left { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 1; }
    .status-card-name { font-size: var(--fs-300); font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .status-icon-dot { flex-shrink: 0; font-weight: 600; }
    
    /* HC */
    .hc-prov-group { margin-bottom: 8px; }
    .hc-prov-name { font-size: var(--fs-100); font-weight: 700; color: var(--text2); display: flex; align-items: center; gap: 5px; margin-bottom: 4px; }
    .hc-dot { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; background: var(--muted); }
    .hc-dot.ok { background: var(--green); }
    .hc-dot.err { background: var(--red); }
    .hc-row {
      display: flex; align-items: center; gap: 7px;
      padding: var(--sp-2) 9px; margin-bottom: 3px;
      background: var(--surface2); border: 1px solid var(--border);
      border-radius: 6px; font-family: var(--font-mono); font-size: 10.5px;
    }
    .hc-row.ok-r  { border-left: 3px solid var(--green); }
    .hc-row.err-r { border-left: 3px solid var(--red); }
    .hc-ind    { font-size: var(--fs-50); flex-shrink: 0; }
    .hc-meth   { padding: 1px 5px; background: var(--surface3); border: 1px solid var(--border); border-radius: 4px; font-size: 9px; color: var(--muted); }
    .hc-url    { flex: 1; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .hc-detail { color: var(--text2); flex-shrink: 0; max-width: 80px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .hc-lat    { color: var(--muted); flex-shrink: 0; width: 60px; text-align: right; }
    .hc-lat.good { color: var(--status-done-fg); }
    .hc-lat.slow { color: var(--status-warn-fg); }

    .hidden { display: none !important; }
    :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

    /* Windows High Contrast Mode / forced-colors: the app's borders and
       status tints are mostly low-alpha rgba() fills that forced-colors
       flattens to the system palette anyway, but flat surfaces with no
       border (act-btn.primary, .nav-btn.active, #fetchBtn — accent fill,
       no border) lose their edge entirely once the fill is forced to
       Canvas. A visible border in that mode is enough to keep every
       button legible as a button without touching normal-mode styling. */
    @media (forced-colors: active) {
      .act-btn, .nav-btn, #fetchBtn, .mode-btn, .ta-btn, .page-btn,
      .fb-select, #fb-back, .toggle-sl, input, select, textarea {
        forced-color-adjust: none;
        border: 1px solid ButtonText;
      }
      .act-btn.primary, .nav-btn.active, #fetchBtn, .mode-btn.active,
      .fb-select, .ta-btn.dl {
        background: Highlight; color: HighlightText; border-color: HighlightText;
      }
      :focus-visible { outline: 2px solid Highlight; }
    }

    /* ── Sidebar nav tooltips ── */
    #sidebar { overflow: visible; }
    .nav-btn[data-label] { position: relative; }
    .nav-btn[data-label]::after {
      content: attr(data-label);
      position: absolute;
      left: calc(100% + 10px);
      top: 50%; transform: translateY(-50%);
      background: var(--surface3);
      border: 1px solid var(--border2);
      color: var(--text);
      font-family: var(--app-font); font-size: 11.5px; font-weight: 600;
      padding: 5px 11px; border-radius: var(--radius-sm);
      white-space: nowrap; pointer-events: none;
      opacity: 0; transition: opacity .12s ease;
      z-index: var(--z-nav-tooltip);
      box-shadow: var(--elev-pop);
    }
    .nav-btn[data-label]:hover::after { opacity: 1; }

    /* ── Action button feedback states ── */
    @keyframes ta-spin { to { transform: rotate(360deg); } }
    .ta-spin { animation: ta-spin .65s linear infinite; transform-origin: center; }
    .ta-btn.ta-loading, .act-btn.ta-loading {
      pointer-events: none;
      color: var(--text2) !important;
      border-color: var(--border2) !important;
      background: var(--surface2) !important;
    }
    .ta-btn.ta-state-success, .act-btn.ta-state-success {
      background: var(--status-done-bg) !important;
      border-color: var(--status-done-fg) !important;
      color: var(--status-done-fg) !important;
      pointer-events: none;
    }
    .ta-btn.ta-state-error, .act-btn.ta-state-error {
      background: var(--status-failed-bg) !important;
      border-color: var(--status-failed-fg) !important;
      color: var(--status-failed-fg) !important;
      pointer-events: none;
    }

    
    .track-detail-card.hidden {
      display: none;
    }


    .track-detail-header {
      display: flex; justify-content: flex-end;
      padding: var(--sp-4) var(--sp-5);
      border-bottom: 1px solid var(--border);
      flex-shrink: 0;
    }

    .track-detail-close {
      background: none; border: none;
      color: var(--muted); cursor: pointer;
      padding: var(--sp-2);
      display: flex; align-items: center; justify-content: center;
      transition: color var(--t);
    }
    .track-detail-close:hover { color: var(--text2); }

    .track-detail-body {
      display: flex;
      gap: var(--sp-7);
      padding: var(--sp-7);
      flex-direction: row;
    }

    @media (max-width: 600px) {
      .track-detail-body {
        flex-direction: column;
        gap: var(--sp-5);
      }
    }

    .track-detail-cover-section {
      flex-shrink: 0;
    }

    .track-detail-cover {
      width: 200px;
      height: 200px;
      border-radius: 10px;
      overflow: hidden;
      background: var(--surface3);
      display: flex; align-items: center; justify-content: center;
      position: relative;
      box-shadow: var(--elev-2);
    }

    .track-detail-cover img {
      width: 100%; height: 100%;
      object-fit: cover;
      display: block;
    }

    .track-detail-duration {
      position: absolute;
      bottom: 8px; right: 8px;
      background: var(--overlay);
      color: var(--text);
      padding: var(--sp-2) var(--sp-3);
      border-radius: 4px;
      font-size: var(--fs-100);
      font-weight: 600;
      font-family: var(--font-mono);
    }

    .track-detail-info {
      flex: 1;
      display: flex; flex-direction: column;
      gap: var(--sp-5);
      min-width: 0;
    }

    .track-detail-title-section {
      display: flex;
      align-items: flex-start;
      gap: var(--sp-3);
      flex-wrap: wrap;
    }

    .track-detail-title {
      font-size: var(--fs-600);
      font-weight: var(--fw-semibold);
      color: var(--text);
      margin: 0;
      line-height: 1.2;
      word-break: break-word;
    }

    .explicit-badge-lg {
      background: var(--red);
      color: var(--text);
      font-size: var(--fs-50);
      font-weight: 700;
      padding: var(--sp-2) 6px;
      border-radius: 3px;
      font-family: var(--font-mono);
      flex-shrink: 0;
      margin-top: 2px;
    }

    .track-detail-artist {
      font-size: 16px;
      color: var(--text2);
      margin: 0;
      line-height: 1.4;
      word-break: break-word;
    }

    .track-detail-meta-grid {
      display: grid;
      grid-template-columns: 1fr 1fr;
      gap: var(--sp-5);
      padding: 16px 0;
      border-top: 1px solid var(--border);
      border-bottom: 1px solid var(--border);
    }

    .track-detail-meta-item {
      display: flex; flex-direction: column;
      gap: var(--sp-2);
    }

    .track-detail-meta-label {
      font-size: var(--fs-75);
      color: var(--muted);
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.5px;
    }

    .track-detail-meta-value {
      font-size: 14px;
      color: var(--text);
      margin: 0;
      font-weight: 500;
      word-break: break-word;
    }

    .track-detail-actions {
      display: flex;
      gap: 10px;
      flex-wrap: wrap;
    }

    .track-detail-btn {
      display: inline-flex;
      align-items: center;
      gap: var(--sp-3);
      padding: 10px var(--sp-5);
      border: 1px solid var(--border);
      background: var(--surface2);
      color: var(--text2);
      border-radius: var(--radius-sm);
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      transition: all var(--t);
      font-family: var(--app-font);
      white-space: nowrap;
    }

    .track-detail-btn:hover {
      background: var(--surface3);
      border-color: var(--border2);
      color: var(--text);
    }

    .track-detail-btn:active {
      transform: scale(0.96);
    }

    .track-detail-btn.primary {
      background: var(--accent);
      color: var(--accent-fg);
      border-color: var(--accent);
      font-weight: 600;
    }

    .track-detail-btn.primary:hover {
      opacity: 0.9;
    }

    .track-detail-btn svg {
      width: 16px;
      height: 16px;
      flex-shrink: 0;
    }

  /* ── SINGLE TRACK CARD ENHANCEMENTS ── */
#album-cover .cover-duration {
  position: absolute; bottom: 6px; right: 6px;
  background: var(--overlay); color: var(--text);
  font-family: var(--font-mono);
  font-size: var(--fs-50); font-weight: 700;
  padding: 3px 7px; border-radius: 4px;
  pointer-events: none; z-index: 2;
}
.track-explicit-title {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--red); color: var(--text);
  font-family: var(--font-mono);
  font-size: 9px; font-weight: 700;
  width: 16px; height: 16px; border-radius: 3px;
  vertical-align: middle; margin-left: 7px; flex-shrink: 0;
}
/* The single-track card's below-the-title meta grid (.track-meta-grid) was
   replaced by #album-tech — the card's right-hand column — which carries the
   same fields plus ISRC and label. #track-meta-section is kept in the markup
   as an empty, hidden container so the JS that clears it stays harmless. */

/* ── ARTIST VIEW: sections Albums + Tracks ── */
.artist-section-hdr {
  max-width: min(100%, 950px); margin: 16px auto 8px;
  padding: 0 14px;
  display: flex; align-items: center; justify-content: space-between;
}
.artist-section-hdr-title {
  font-size: var(--fs-75); font-weight: var(--fw-semibold); text-transform: uppercase;
  letter-spacing: var(--tracking-label); color: var(--muted);
}
.artist-section-hdr-count {
  font-family: var(--font-mono);
  font-size: var(--fs-75); color: var(--muted);
}
.artist-albums-grid {
  max-width: min(100%, 950px); margin: 0 auto 4px;
  padding: 0 14px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 10px;
}
.artist-album-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius-md); overflow: hidden; cursor: pointer;
  transition: border-color var(--t), transform .15s, box-shadow .15s;
}
.artist-album-card:hover {
  border-color: var(--accent); transform: translateY(-2px);
  box-shadow: var(--elev-2);
}
.aac-cover {
  width: 100%; aspect-ratio: 1; background: var(--surface2);
  display: flex; align-items: center; justify-content: center;
  font-size: 26px; overflow: hidden;
  position: relative;
}
.aac-cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Downloading a whole album is now a deliberate press on this, not a side
   effect of clicking the card (which opens the album). Hidden until the card
   is hovered or holds focus, the same way the per-row track actions are —
   twenty covers each wearing a button is noise. */
.aac-dl {
  position: absolute; right: 8px; bottom: 8px;
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--accent); color: var(--accent-fg);
  border: none; cursor: pointer;
  box-shadow: var(--elev-2);
  opacity: 0; transform: translateY(4px);
  transition: opacity var(--t), transform var(--t), background var(--t);
}
.artist-album-card:hover .aac-dl,
.artist-album-card:focus-within .aac-dl { opacity: 1; transform: none; }
.aac-dl:hover { background: var(--accent-hover); }
@media (hover: none) {
  .aac-dl { opacity: 1; transform: none; }
}
.aac-body { padding: var(--sp-3) 10px 10px; }
.aac-name {
  font-size: 11.5px; font-weight: var(--fw-semibold); color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 2px;
}
.aac-meta { font-size: var(--fs-50); color: var(--muted); }
/* ── ARTIST TABS ── */
.artist-tabs-bar {
  max-width: min(100%, 950px); margin: 14px auto 0;
  padding: 0 14px;
  display: flex; gap: var(--sp-1);
  border-bottom: 2px solid var(--border);
}
.artist-tab {
  padding: var(--sp-3) 18px;
  font-size: 12.5px; font-weight: var(--fw-semibold);
  color: var(--muted); background: none; border: none;
  border-bottom: 2px solid transparent; margin-bottom: -2px;
  cursor: pointer; font-family: var(--app-font);
  transition: color var(--t), border-color var(--t);
  white-space: nowrap;
}
.artist-tab:hover { color: var(--text2); }
.artist-tab.active { color: var(--text); border-bottom-color: var(--accent); }

/* Gallery */
.artist-gallery-grid {
  max-width: min(100%, 950px); margin: 12px auto;
  padding: 0 14px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 10px;
}
.artist-gallery-img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: 10px; border: 1.5px solid var(--border);
  cursor: pointer; transition: transform .15s, border-color var(--t);
  display: block;
}
.artist-gallery-img:hover { transform: scale(1.02); border-color: var(--accent); }
.artist-gallery-empty {
  grid-column: 1 / -1; color: var(--muted);
  font-size: var(--fs-200); padding: 32px 0; text-align: center;
}

/* ── ARTIST CARD ENHANCEMENTS ── */
.artist-verified-badge {
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle; margin-left: 7px; flex-shrink: 0;
}
#album-subtitle.artist-bio {
  font-size: 12.5px; color: var(--text2); line-height: 1.6;
  display: -webkit-box; -webkit-line-clamp: 4; line-clamp: 4; -webkit-box-orient: vertical;
  overflow: hidden; margin-bottom: 8px;
}

/* =========================================
   RESPONSIVITY ADJUSTMENTS
   ========================================= */

@media (max-width: 900px) {
  .search-container { flex-direction: column; gap: 10px; height: auto; padding: 10px; }
  .url-wrap { width: 100%; }

  /* The technical sheet can't hold a 216px column next to the cover and the
     text at this width — it moves under the text as a wrapping row. */
  #album-card { flex-wrap: wrap; }
  #album-tech {
    width: 100%; align-self: auto;
    flex-direction: row; flex-wrap: wrap; gap: var(--sp-4) var(--sp-7);
    margin-top: 4px; padding: 14px 0 0;
    border-left: none; border-top: 1px solid var(--border);
  }
  #album-tech .tech-row { flex: 0 1 auto; }
}

@media (max-width: 600px) {
  #main-split { flex-direction: column; }
}

/* Track table column collapse — was @media, now keyed off #track-table-wrap's
   own inline size (container-type set where #track-table-wrap is defined).
   Same two breakpoints, same nth-child hides, same !important grid override
   — only what they measure changed, not what they do. */
/* Thresholds are 900/600 minus 102px, not 900/600 — a container query
   measures #track-table-wrap's own box, which sits inside the 58px sidebar
   + the surrounding containers' padding chain, so it's consistently 102px
   narrower than the viewport at the widths that matter here (measured live:
   innerWidth 900/700/600 -> wrap 798/598/498, offset exactly 102px each
   time, no dependency on #track-table-wrap's own 950px cap since that
   never engages this low). Using literal 900/600 here would collapse the
   table ~102px too early compared to the old @media behaviour. */
@container tracktable (max-width: 798px) {
  /* Nascondi Playcount e Duration in modo forzato sia nell'header che nelle righe */
  .track-table-header > div:nth-child(4),
  .track-table-header > div:nth-child(5),
  .track-row > div:nth-child(4),
  .track-row > div:nth-child(5) {
    display: none !important;
  }

  /* Layout updated per le 4 colonne rimanenti (Checkbox, #, Titolo, Azioni) */
  .track-table-header, .track-row {
    grid-template-columns: 24px 30px 1fr 140px !important;
  }
}

@container tracktable (max-width: 498px) {
  /* Also hide the index (#) on smartphones */
  .track-table-header > div:nth-child(2),
  .track-row > div:nth-child(2) {
    display: none !important;
  }

  /* Layout per 3 colonne: Checkbox, Titolo, Azioni */
  .track-table-header, .track-row {
    grid-template-columns: 24px 1fr 130px !important;
  }
}

/* =========================================
   SKELETON ANIMATIONS
   ========================================= */
.skeleton {
  background: linear-gradient(90deg, var(--surface2) 25%, var(--border) 50%, var(--surface2) 75%);
  background-size: 200% 100%;
  animation: skeleton-pulse 1.5s infinite linear;
  border-radius: var(--radius-xs);
}

@keyframes skeleton-pulse {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

.skeleton-text { height: 16px; margin: 4px 0; width: 80%; border-radius: 4px; }
.skeleton-text.short { width: 40%; }
.skeleton-row { 
  display: grid; 
  grid-template-columns: 40px 30px 1fr 80px 80px 90px; 
  gap: 10px; 
  padding: var(--sp-4) 10px; 
  border-bottom: 1px solid var(--border);
}

/* ── ffmpeg warning banner ── */
.ffmpeg-banner {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px var(--sp-5);
  background: var(--status-failed-bg);
  border-bottom: 1px solid var(--status-failed-fg);
  font-size: var(--fs-100);
  color: var(--text2);
  flex-shrink: 0;
  animation: fadein .2s ease;
}
.ffmpeg-banner-icon {
  font-size: 16px;
  flex-shrink: 0;
  color: var(--status-failed-fg);
}
.ffmpeg-banner-body {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.ffmpeg-banner-body strong { color: var(--status-failed-fg); }
.ffmpeg-banner-link {
  color: var(--accent-text);
  text-decoration: underline;
  white-space: nowrap;
}
.ffmpeg-banner-close {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 14px;
  padding: var(--sp-1) 6px;
  border-radius: 4px;
  flex-shrink: 0;
  transition: color var(--t);
}
.ffmpeg-banner-close:hover { color: var(--text2); }

/* --- EXPLORE VIEW --- */
.explore-section-title {
  font-size: var(--fs-400);
  font-weight: var(--fw-semibold);
  margin: 24px 0 16px 0;
  color: var(--text);
}
.explore-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: var(--sp-5);
  margin-bottom: 32px;
}
.explore-card {
  background: transparent;
  border: none;
  border-radius: 0;
  padding: 0;
  cursor: pointer;
  transition: transform 0.2s;
}
.explore-card:hover {
  transform: translateY(-3px);
}
.explore-card img {
  width: 100%;
  border-radius: var(--radius-md);
  aspect-ratio: 1/1;
  object-fit: cover;
  margin-bottom: 12px;
  box-shadow: var(--elev-1);
  transition: box-shadow 0.2s;
}
.explore-card:hover img {
  box-shadow: var(--elev-2);
}
.explore-card-title {
  font-size: 14px;
  font-weight: 600;
  color: var(--text);
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.explore-card-subtitle {
  font-size: var(--fs-100);
  color: var(--muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2; /* Standard property */
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ==========================================================================
   TOAST SYSTEM
   ========================================================================== */
.toast-container {
  position: fixed;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  z-index: var(--z-toast);
  pointer-events: none;
}

/* Posizionamenti */
.toast-container.bottom-right { bottom: 24px; right: 24px; }
.toast-container.bottom-left { bottom: 24px; left: 24px; }
.toast-container.top-right { top: 24px; right: 24px; flex-direction: column-reverse; }
.toast-container.top-left { top: 24px; left: 24px; flex-direction: column-reverse; }
.toast-container.top-center { top: 24px; left: 50%; transform: translateX(-50%); flex-direction: column-reverse; }
.toast-container.bottom-center { bottom: 24px; left: 50%; transform: translateX(-50%); }

.toast {
  display: flex;
  align-items: center;
  background-color: var(--surface); /* Uses your theme card background */
  color: var(--text);               /* Uses the correct text color */
  padding: 14px var(--sp-5);
  border-radius: var(--radius-md);  /* More rounded corners in Mornye style */
  box-shadow: var(--elev-3); /* Softer, more modern shadow */
  border: 1px solid var(--border);  /* Usa il bordo nativo del tema */
  min-width: 280px;
  max-width: 400px;
  pointer-events: auto;
  position: relative;
  overflow: hidden;
  border-left: 4px solid transparent;
  font-family: var(--app-font, sans-serif);
}

/* Colori Tipo */
.toast.success { border-left-color: var(--green, #1ed760); }
.toast.success .toast-icon { color: var(--green, #1ed760); }
.toast.error { border-left-color: var(--red, #ff5a5a); }
.toast.error .toast-icon { color: var(--red, #ff5a5a); }
.toast.warning { border-left-color: var(--status-warn-fg, #f5c469); }
.toast.warning .toast-icon { color: var(--status-warn-fg, #f5c469); }
.toast.info { border-left-color: var(--blue, #5a9bff); }
.toast.info .toast-icon { color: var(--blue, #5a9bff); }
.toast.loading { border-left-color: var(--text, #fff); }
.toast.loading .toast-icon { color: var(--text, #fff); }

.toast-icon {
  width: 20px;
  height: 20px;
  margin-right: 14px;
  flex-shrink: 0;
}
.toast-icon svg { width: 100%; height: 100%; }

.toast-content {
  flex-grow: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.toast-title {
  font-weight: 600;
  font-size: 14px;
  letter-spacing: 0.02em;
}

.toast-message {
  font-size: var(--fs-200);
  color: var(--text2); /* Colore del testo secondario dinamico */
  line-height: 1.4;
}

.toast-close {
  background: none;
  border: none;
  color: var(--muted); /* Colore icona dinamico */
  font-size: 20px;
  cursor: pointer;
  padding: 0 4px;
  margin-left: 12px;
  line-height: 1;
  transition: color 0.2s;
}

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

/* Progress Bar */
.toast-progress {
  position: absolute;
  bottom: 0;
  left: 0;
  height: 3px;
  background-color: currentColor;
  opacity: 0.3;
  width: 100%;
  transform-origin: left;
  animation: toastProgress linear forwards;
}

/* Animations */
@keyframes slideInRight { from { transform: translateX(120%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@keyframes slideInLeft { from { transform: translateX(-120%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
@keyframes slideOutRight { from { transform: translateX(0); opacity: 1; } to { transform: translateX(120%); opacity: 0; } }
@keyframes slideOutLeft { from { transform: translateX(0); opacity: 1; } to { transform: translateX(-120%); opacity: 0; } }
@keyframes toastProgress { from { transform: scaleX(1); } to { transform: scaleX(0); } }
@keyframes toastSpin { 100% { transform: rotate(360deg); } }

.toast-spin { animation: toastSpin 1s linear infinite; }

/* ══════════ LOCAL AUTO-TAGGER ══════════ */
.local-drop-zone { display: flex; flex-direction: column; align-items: center; gap: 10px; padding: 22px var(--sp-5); border: 1.5px dashed var(--border); border-radius: 10px; background: var(--surface2); color: var(--muted); transition: border-color var(--t), background var(--t), color var(--t); }
.local-drop-zone svg { color: var(--muted); transition: color var(--t); }
.local-drop-zone-text { font-size: 12.5px; text-align: center; }
.local-drop-zone.drag-over { border-color: var(--status-active-fg); background: var(--status-active-bg); color: var(--status-active-fg); }
.local-drop-zone.drag-over svg { color: var(--status-active-fg); }
.local-cell-content { display: flex; align-items: center; gap: 10px; min-width: 0; }
.local-thumb { width: 44px; height: 44px; border-radius: 6px; background: var(--surface3); display: flex; align-items: center; justify-content: center; font-size: 16px; overflow: hidden; border: 1px solid var(--border); flex-shrink: 0; }
.local-thumb img { width: 100%; height: 100%; object-fit: cover; }
.local-info { min-width: 0; display: flex; flex-direction: column; flex: 1; gap: var(--sp-1); }

.local-title { font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); transition: color var(--t); }
.local-artist { font-size: var(--fs-100); color: var(--text2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: color var(--t); }

/* Highlight for changed tags (Diffing) */
.local-title.diff, .local-artist.diff { color: var(--diff-fg); }

.local-badge { font-family: var(--font-mono); font-size: var(--fs-50); padding: 3px var(--sp-3); border-radius: 999px; font-weight: 700; display: inline-block; }
.local-badge.ok { background: var(--status-done-bg); color: var(--status-done-fg); }
.local-badge.warn { background: var(--status-warn-bg); color: var(--status-warn-fg); }
.local-badge.err { background: var(--status-failed-bg); color: var(--status-failed-fg); }
    /* ── Your library, in numbers (view-stats) ─────────────────────────────
       Deliberately built from the same tokens as the rest of the interface
       (surface/border/muted/yellow) so the dashboard inherits both themes
       without a second palette to keep in agreement. */
    .stat-tiles {
      display: grid;
      grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
      gap: 10px;
      margin-bottom: 18px;
    }
    .stats-intro { margin: -4px 0 14px; color: var(--muted); font-size: 12px; line-height: 1.6; }
    .stats-actions { gap: 8px; }
    .stats-smb-controls { display: flex; align-items: center; gap: 8px; min-width: 0; }
    #stats-smb-destination { width: min(310px, 28vw); min-width: 200px; min-height: 38px; }
    #stats-smb-scan { display: inline-flex; align-items: center; justify-content: center; gap: 7px; min-height: 38px; white-space: nowrap; }
    #stats-smb-scan svg { width: 16px; height: 16px; }
    #stats-smb-scan:disabled { opacity: .5; cursor: not-allowed; }
    .stats-smb-status {
      margin: 0 0 14px; padding: 9px 12px; border-left: 2px solid var(--accent);
      background: var(--surface2); color: var(--text2); font-size: 12px; line-height: 1.5;
    }
    .stats-smb-status.error { border-left-color: var(--red); color: var(--red); }
    .stats-source-note {
      display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 12px;
      margin-bottom: 14px; color: var(--text2); font-size: 12px;
    }
    .stats-source-note strong { color: var(--accent); font-weight: 600; }
    .stats-source-note span { color: var(--muted); }
    .stat-tile {
      background: var(--surface3);
      border: 1px solid var(--border);
      border-radius: var(--radius-sm);
      padding: 14px var(--sp-5);
    }
    .stat-tile-value {
      font-size: var(--fs-500);
      font-weight: var(--fw-semibold);
      color: var(--text);
      line-height: 1.2;
      overflow-wrap: anywhere;
    }
    .stat-tile-label {
      font-size: var(--fs-75);
      text-transform: uppercase;
      letter-spacing: var(--tracking-label);
      color: var(--muted);
      margin-top: 4px;
    }
    .stat-tile-hint { font-size: var(--fs-75); color: var(--muted); margin-top: 6px; }

    .stat-bar-row {
      display: flex;
      align-items: center;
      gap: 10px;
      padding: 4px 0;
      font-size: var(--fs-100);
    }
    .stat-bar-label {
      flex: 0 0 34%;
      max-width: 34%;
      color: var(--text2);
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .stat-bar-track {
      flex: 1;
      height: 8px;
      min-width: 0;
      background: var(--surface2);
      border-radius: 999px;
      overflow: hidden;
    }
    .stat-bar-fill {
      height: 100%;
      border-radius: 999px;
      background: var(--accent);
    }
    .stat-bar-value {
      flex: 0 0 52px;
      text-align: right;
      color: var(--muted);
      font-variant-numeric: tabular-nums;
    }

    @media (max-width: 640px) {
      .stat-bar-label { flex-basis: 44%; max-width: 44%; }
      .stats-actions { width: 100%; flex-wrap: wrap; justify-content: flex-start; }
      .stats-smb-controls { flex: 1 1 100%; }
      #stats-smb-destination { flex: 1 1 auto; width: auto; min-width: 0; max-width: none; }
      #stats-period { flex: 1 1 auto; }
    }

/* ── Scrolling panels whose cards must not be squeezed ────────────────────
   #view-local's .s-body is `display:flex; flex-direction:column` (inline,
   in index.html), which makes its .s-section cards flex items — and a flex
   item's default flex-shrink is 1. In a container tall enough for all of
   them that changes nothing, which is why this looks fine on a desktop.
   When the content is taller than the panel, though, the cards are shrunk
   to fit it instead of overflowing it, and .s-section is overflow:hidden,
   so the surplus is simply cut off: the panel reports nothing to scroll and
   the bottom of each card is unreachable. Measured at 360x740 that is 130px
   of the Target Directory card — its path field's help line, and the start
   of the row below it — gone with no way to reach it.

   A card keeps its content height and the panel scrolls, which is what
   .s-body's own overflow-y:auto was always there to do. Not inside a media
   query: the trigger is content taller than the panel, which a short
   desktop window reaches the same way a phone does. */
#view-local > .s-body > * { flex-shrink: 0; }

/* =========================================
   PHONE LAYOUT
   =========================================
   The window this app was drawn for is a desktop one: a 58px vertical nav
   rail on the left, hover to reveal a row's secondary actions, controls
   sized for a mouse. Served over --web (webapp.py, which marks <body> with
   .web-mode) that same document is what a phone gets, and several of those
   choices stop working there rather than merely looking tight:

     - the rail costs 16% of a 360px viewport and puts navigation in the
       thumb's worst corner;
     - a 14px <input> makes iOS Safari zoom the page in on focus, and it
       does not zoom back out;
     - the track row's action column is 130px wide but holds five 30px
       buttons — 162px of content — because @media (hover:none) correctly
       stops hiding four of them on a device that cannot hover;
     - 30px and 36px hit targets are below the 44px both platforms' guidance
       asks for;
     - every viewport-anchored element (queue dock, toasts, modals) is
       positioned in raw px from an edge that, on a notched phone, is under
       the status bar or the home indicator.

   What follows fixes those, in two deliberately different scopes:

     - `@media (max-width: 700px)` alone — sizing, wrapping and overflow
       fixes that are simply correct at that width whoever is looking. A
       narrow desktop window gets them too, and wants them.
     - `body.web-mode` as well — the structural change (the rail becomes a
       bottom bar) and the safe-area insets. The desktop build is a frameless
       pywebview window with real chrome and no notch, and never carries that
       class, so nothing in those blocks can reach it.

   700px rather than the 600px used elsewhere in this file: the rail-to-
   bottom-bar switch is about thumb reach, which a 7" tablet in portrait
   shares with a phone, while the 600px rules above are about a layout that
   has genuinely run out of horizontal room. */

/* ── Touch ergonomics, at any width ───────────────────────────────────────
   Pointer-type questions, not width questions: an iPad in landscape is
   1024px wide and still has no hover. */
@media (hover: none) and (pointer: coarse) {
  /* Chromium and Safari wait ~300ms after a tap on an element that might be
     the first half of a double-tap-to-zoom before they fire click.
     Declaring that these never zoom removes the wait, which is most of what
     makes the UI feel slower under a finger than the same buttons feel
     under a mouse. */
  button, .nav-btn, .mode-btn, .act-btn, .ta-btn, .stab, .page-btn,
  .track-row, .toggle, .recent-card, .explore-card, .artist-album-card,
  select, summary, [onclick] {
    touch-action: manipulation;
  }
  /* The grey flash Android paints on tap lands on top of our own :active
     styling and outlasts it. */
  button, a, label, .track-row, [onclick] {
    -webkit-tap-highlight-color: transparent;
  }
  /* Rubber-banding the page behind a scrolled panel is how a modal ends up
     scrolling the list underneath it. */
  #content, .s-body, #track-table-wrap, .queue-list, .api-modal-card, #fb-entries {
    overscroll-behavior: contain;
  }
}

@media (max-width: 700px) {
  /* 100% of the initial containing block on a mobile browser is measured
     with the URL bar collapsed, so a bottom-anchored bar sits under it
     until you scroll. dvh is the viewport as it is right now. Guarded by
     @supports so a browser without it keeps the height:100% it had. */
  @supports (height: 100dvh) {
    html, body { height: 100dvh; }
  }

  /* iOS zooms the page when a control smaller than 16px takes focus — the
     single most disorienting thing the app does on a phone, because nothing
     zooms it back out. 16px is the threshold, not a preference. */
  input[type="text"], input[type="password"], input[type="number"],
  input[type="search"], input[type="email"], input[type="url"],
  select, textarea, #urlInput {
    font-size: 16px;
  }
  #urlInput::placeholder { font-size: 15px; }

  /* ── Hero ──
     32px of padding above a 72px logo pushed the search bar — the reason
     the page exists — below the fold on a 667px-tall screen. */
  #home-hero { padding: var(--sp-5) var(--sp-4) var(--sp-4); }
  .hero-text { padding: var(--sp-4) 0; }
  .hero-icon { width: 52px; height: 52px; }
  .hero-headline { gap: var(--sp-4); }

  /* ── Search bar ──
     Touch sizing only; the two-row arrangement itself is in the 900px block
     further down, which is where the stacking it replaces already lived. */
  .search-container { padding: 10px; }
  .mode-btn, #fetchBtn { height: 44px; min-width: 44px; }
  #urlInput { height: 44px; }
  .status-bar { padding: 0 var(--sp-4); }

  /* ── Album card ──
     The 900px rule already drops the tech column below the text. Below
     700px the cover has to go above the text as well: clamp(200px, 22vw,
     240px) resolves to its 200px floor on a phone, which left ~110px for a
     title, an artist list and four metadata lines. */
  #album-card {
    flex-direction: column;
    align-items: stretch;
    padding: var(--sp-5);
    gap: var(--sp-5);
  }
  #album-cover {
    width: min(200px, 52vw); height: min(200px, 52vw);
    align-self: center;
  }

  /* ── Card grids ──
     minmax(180px, 1fr) inside a ~330px content box resolves to exactly one
     column, so each card drew at double its design size and the view became
     a very long single file. 140px gives two. */
  .explore-grid, .artist-gallery-grid {
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: var(--sp-3);
  }
  .artist-albums-grid {
    grid-template-columns: repeat(auto-fill, minmax(130px, 1fr));
    gap: var(--sp-3);
  }

  /* ── Settings ──
     s-header is title-left / buttons-right; at this width its three action
     buttons wrapped into a ragged block. They take the full row instead. */
  .s-header { padding: var(--sp-5) var(--sp-4) var(--sp-3); gap: var(--sp-4); }
  .s-header h1 { font-size: var(--fs-400); }
  .s-actions { width: 100%; }
  .s-actions .act-btn { flex: 1 1 auto; min-height: 44px; }
  .s-body { padding: var(--sp-4) var(--sp-4) var(--sp-5); }
  .s-section { padding: var(--sp-5) var(--sp-4); }
  .tab-bar { padding: 0 var(--sp-4); -webkit-overflow-scrolling: touch; }
  .stab { min-height: 44px; padding: var(--sp-3) var(--sp-4); }

  /* A settings row is `label` then `control`. A <select> or a text field
     next to a label that wraps to three lines has nowhere to go, so it
     takes a line of its own — flex-basis 100% forces the wrap — at full
     width, which is also the only way its now-16px text fits. Toggles and
     buttons are narrow and stay on the label's line, where they still read
     as that label's control. */
  .s-row { flex-wrap: wrap; }
  /* A basis, so the label shrinks to share its line instead of measuring
     its full text and pushing whatever follows onto the next one. Without
     it every toggle row with a sentence of help text under its label sent
     the toggle to a second line — and, being the only thing on that line,
     space-between parked it on the left, where it no longer read as
     belonging to the label above it. */
  .s-row > .s-label { flex: 1 1 55%; }
  .s-row > select,
  .s-row > input[type="text"],
  .s-row > input[type="password"],
  .s-row > input[type="number"],
  .s-row > .accent-picker,
  .s-row > .volume-control {
    flex: 1 1 100%;
    min-width: 0;
    max-width: none;
    min-height: 44px;
  }
  .s-row > .accent-picker select { flex: 1; min-width: 0; }
  .s-row > .volume-control input[type="range"] { flex: 1; width: auto; }
  /* Anything that does still wrap to its own line keeps the right edge the
     unwrapped version put it at. */
  .s-row > .toggle { margin-left: auto; }

  /* ── Modals as bottom sheets ──
     A centred 520px card on a 360px screen is a full-screen dialog with a
     20px margin pretending not to be. Anchored to the bottom edge it is
     both within thumb reach and the shape a phone user expects. dvh again,
     so a sheet is never taller than the visible viewport while the URL bar
     is showing. */
  .api-modal { padding: 0; align-items: flex-end; }
  .api-modal-card {
    width: 100%;
    max-height: 92dvh;
    overflow-y: auto;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-bottom: none;
    padding: var(--sp-6) var(--sp-5) calc(var(--sp-6) + env(safe-area-inset-bottom));
  }
  .api-modal-header .close-btn { min-width: 44px; min-height: 44px; }
  /* The action row is a space-between pair: a link (or, in the login sheet,
     an empty <div> holding the left slot open) and a cluster of buttons. At
     this width the cluster gets the whole row and its buttons split it, so
     the primary action is a full-width target at the bottom of the sheet
     rather than a content-width one tucked into the corner. The empty
     spacer would otherwise contribute a blank row of its own. */
  .api-modal-actions { flex-wrap: wrap; gap: var(--sp-3); }
  .api-modal-actions > div { flex: 1 1 100%; }
  .api-modal-actions > div:empty { display: none; }
  .api-modal-actions .act-btn { flex: 1 1 auto; min-height: 44px; }

  #queue-drawer { padding: 0; align-items: flex-end; }
  .queue-panel {
    width: 100%;
    max-height: 88dvh;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-bottom: none;
  }
  .queue-toolbar { flex-wrap: wrap; }
  .queue-toolbar .act-btn { min-height: 40px; }

  #folder-browser-modal { align-items: flex-end; }
  .fb-card {
    width: 100%;
    max-height: 88dvh;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    border-bottom: none;
    padding-bottom: env(safe-area-inset-bottom);
  }
  /* A folder list is a column of things to tap, one per row, and picking
     the wrong directory here is the sort of mistake that is only obvious
     three screens later. */
  .fb-entry { min-height: 44px; }
  .fb-toolbar { flex-wrap: wrap; }
  #fb-back, .fb-select, .fb-close { min-height: 44px; min-width: 44px; }

  /* ── Toasts ──
     Every corner variant becomes the same full-width strip: a 24px inset
     from a corner is a third of the screen on a phone, and the four
     positions stop meaning anything once the card is as wide as the
     viewport. The bottom ones clear the nav bar — see the web-mode block. */
  .toast-container.bottom-right, .toast-container.bottom-left,
  .toast-container.bottom-center, .toast-container.top-right,
  .toast-container.top-left, .toast-container.top-center {
    left: var(--sp-3);
    right: var(--sp-3);
    transform: none;
    max-width: none;
  }
  .toast-container.top-right, .toast-container.top-left,
  .toast-container.top-center {
    top: calc(var(--sp-3) + env(safe-area-inset-top));
  }
  .toast { width: auto; max-width: none; }

  /* The Local view's path field carries an inline `flex:1`, so it shares a
     line with the Browse button and gets ~250px of it — an absolute path is
     longer than that at the best of times. !important is the only thing
     that outranks an inline style, and this is what it is for. */
  #local-path-input { flex: 1 1 100% !important; }

  #pagetion-controls { flex-wrap: wrap; gap: var(--sp-2); }
  .page-btn { min-height: 40px; }
  #page-info { min-width: 0; }
}

/* ── Search bar, narrow windows ───────────────────────────────────────────
   The responsivity section above turns the bar into a column below 900px,
   which gives each of its three children a row: the two 36px mode buttons
   centred on one, the 36px fetch button centred and alone on another, and
   the input — the one control anyone came here to use — on a third. Three
   rows of mostly air.

   Two rows instead, by re-wrapping rather than re-stacking. The input
   claims a whole line with flex-basis:100%, the three buttons fall onto the
   next, and fetch takes whatever the two fixed-width mode buttons leave, so
   the primary action is also the widest target on the row. `order` rather
   than moving anything in index.html: the DOM order is mode / input /
   fetch, which is the tab order and the order a screen reader reads, and
   both are right as they stand. */
@media (max-width: 900px) {
  .search-container {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--sp-3);
    height: auto;
  }
  .url-wrap { order: 1; flex: 1 1 100%; width: 100%; }
  .mode-wrap { order: 2; flex: 0 0 auto; }
  #fetchBtn { order: 3; flex: 1 1 auto; }
}

/* ── Track row action column ──────────────────────────────────────────────
   A container query, not a media query, because that is what the two column
   collapses above already use — #track-table-wrap's own box, not the
   viewport (see their comment for the 102px offset).

   At 498px the grid gives the actions column 130px. A row renders five
   .ta-btn at 30px with a 3px gap: 162px. On a desktop that overflow is
   invisible because four of the five are opacity:0 — but they still occupy
   their width, and @media (hover:none) makes them opaque again on exactly
   the devices where the column is narrowest.

   The fix is to stop asking one line to hold both. Checkbox and title keep
   the first line, the action cluster drops to a second, right-aligned,
   where there is room to give each button a 38px target instead of shaving
   pixels off five of them. The header's "Actions" label has nothing left to
   sit over and goes. */
@container tracktable (max-width: 498px) {
  .track-table-header, .track-row {
    grid-template-columns: 24px 1fr !important;
    row-gap: var(--sp-3);
  }
  .track-table-header > div:nth-child(6) { display: none; }
  .tr-actions {
    grid-column: 1 / -1;
    margin-left: 0;
    justify-content: flex-end;
    gap: var(--sp-2);
  }
  .ta-btn { width: 38px; height: 38px; }
}

/* =========================================
   PHONE NAV BAR — --web mode only
   =========================================
   The rail becomes a bottom bar. #app-body is already `display:flex` with
   #sidebar first and #content second, so column-reverse alone moves the nav
   to the bottom edge without touching index.html's DOM order — which
   matters, because that order is also the tab order, and navigation should
   stay reachable first by keyboard.

   Scoped to .web-mode: webapp.py adds that class, and only on the
   browser-served route, so the desktop build keeps its rail at every window
   size. */
@media (max-width: 700px) {
  body.web-mode {
    /* #queue-dock positions itself at `calc(var(--rail-w) + 20px)` to clear
       the rail. There is no rail here, so the variable it reads says so —
       cheaper and less brittle than restating the dock's left edge. */
    --rail-w: 0px;
    /* Bar height, for everything that has to sit above it. */
    --nav-h: 56px;
    --nav-total-h: calc(var(--nav-h) + env(safe-area-inset-bottom));
  }

  body.web-mode #app-body {
    flex-direction: column-reverse;
    /* The titlebar is display:none in web mode, so nothing else holds the
       content out from under a notch or a translucent status bar. Left and
       right matter in landscape, where the notch is on one side. */
    padding-top: env(safe-area-inset-top);
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
  }

  body.web-mode #sidebar {
    width: 100%;
    height: var(--nav-total-h);
    flex-direction: row;
    align-items: center;
    gap: var(--sp-2);
    /* The inset is padding, not margin, so the bar's own background reaches
       the bottom of the screen and the home indicator sits on it rather
       than on whatever is behind the page. */
    padding: 0 var(--sp-3) env(safe-area-inset-bottom);
    border-right: none;
    border-top: 1px solid var(--border);
    /* The rail needed overflow:visible to let its tooltips escape; those
       are suppressed below, and hiding overflow keeps the active marker or
       a stray badge from drawing over the content above the bar. */
    overflow: hidden;
    flex-shrink: 0;
  }

  /* flex:1 with min-width:0 rather than a fixed 40px: the eight buttons
     divide whatever the screen gives them (45px each at 360px, more on a
     larger handset) instead of overflowing a narrow one. */
  body.web-mode .nav-btn {
    flex: 1 1 0;
    min-width: 0;
    width: auto;
    height: 44px;
    border-radius: var(--radius-sm);
  }
  /* A flex:1 button has no gap beside it to sit a marker in, and the accent
     fill already says which view is open. The rail's edge tab would draw as
     a 3px bar 9px to the button's left — i.e. on top of its neighbour. */
  body.web-mode .nav-btn.active::before { display: none; }
  /* `left: calc(100% + 10px)` puts this off the right edge of the screen
     for the last button, and no touch device can trigger it anyway. */
  body.web-mode .nav-btn[data-label]::after { display: none; }
  body.web-mode .nav-spacer { display: none; }
  /* A rail hover lifts the button by scaling it; in a row of touching
     buttons that reads as the neighbours being shoved aside. */
  body.web-mode .nav-btn:hover { transform: none; }

  /* The dock is fixed to the viewport, so it knows nothing about the bar
     now occupying the bottom of it. Full width as well: a 240px pill
     carrying a title, a speed and a progress bar is legible at 360px only
     if it gets the whole row. */
  body.web-mode #queue-dock {
    left: var(--sp-3);
    right: var(--sp-3);
    bottom: calc(var(--nav-total-h) + var(--sp-3));
    min-width: 0;
    padding: 10px var(--sp-4);
    gap: 10px;
  }
  body.web-mode #queue-dock:hover { transform: none; }

  /* Same for anything else anchored to the bottom edge. The bottom sheets
     above are deliberately not in this list: a sheet covering the nav bar
     is the point of a sheet. */
  body.web-mode .toast-container.bottom-right,
  body.web-mode .toast-container.bottom-left,
  body.web-mode .toast-container.bottom-center {
    bottom: calc(var(--nav-total-h) + var(--sp-3));
  }
  body.web-mode .save-toast {
    left: var(--sp-3);
    right: var(--sp-3);
    bottom: calc(var(--nav-total-h) + var(--sp-3));
    min-width: 0;
  }
}

/* ── Short viewports ──────────────────────────────────────────────────────
   Eight 40px rail buttons plus their gaps need ~330px of height. #sidebar
   is overflow:visible (for the tooltips), so on anything shorter they did
   not scroll — they drew past the bottom of the window, the last one or two
   unreachable. That is a landscape handset mostly, but a short desktop
   window reaches it too, so this one is not scoped to web-mode or to touch.
   Shrinking rather than scrolling: 8x34 + gaps fits in ~310px, and a
   scrollable rail would have to give up overflow:visible and its tooltips
   with it. */
@media (max-height: 560px) {
  #sidebar { gap: var(--sp-2); padding: var(--sp-3) 0; }
  .nav-btn { height: 34px; flex-shrink: 0; }
}

/* A 360x780 handset turned sideways is not a narrow viewport but a short
   one, so most of the phone block above does not fire while the space it
   was written for is scarcer still. Trimmed by hand: the bar loses 10px,
   the hero gives its padding back, and the sheets may use nearly the whole
   screen. pointer:coarse keeps a short laptop window out of it. */
@media (max-height: 480px) and (orientation: landscape) and (pointer: coarse) {
  body.web-mode { --nav-h: 46px; }
  body.web-mode .nav-btn { height: 38px; }
  #home-hero { padding: var(--sp-3) var(--sp-4); }
  .hero-icon { width: 40px; height: 40px; }
  .api-modal-card, .queue-panel, .fb-card { max-height: 96dvh; }
}

/* Mornye surfaces: grouped iOS neutrals, compact controls, and system-like
   typography. Theme and accent choices continue to resolve through tokens. */
body {
  font-family: var(--app-font);
  text-rendering: optimizeLegibility;
}

#titlebar {
  background: var(--bg);
  border-bottom-color: var(--border);
  box-shadow: none;
}

#sidebar {
  background: var(--bg);
  border-right-color: var(--border);
}

.nav-btn {
  background: transparent;
  border-color: transparent;
  border-radius: 12px;
  color: var(--muted);
  box-shadow: none;
}

.nav-btn:hover {
  background: var(--surface);
  border-color: transparent;
  box-shadow: none;
  transform: none;
}

.nav-btn.active,
.nav-btn.active:hover {
  color: var(--accent-text);
  background: var(--accent-subtle);
  border-color: transparent;
  box-shadow: none;
}

.nav-btn.active::before {
  width: 3px;
  height: 20px;
  left: -9px;
}

#home-hero {
  padding: 28px var(--sp-7) 12px;
}

.hero-text {
  padding: var(--sp-3) var(--sp-5);
}

.hero-headline {
  gap: var(--sp-4);
}

.hero-icon {
  width: 56px;
  height: 56px;
  border-radius: 12px;
  box-shadow: none;
}

.hero-title {
  font-size: 34px;
  font-weight: 700;
  letter-spacing: -0.04em;
  text-shadow: none;
}

.hero-subtitle {
  margin-top: 10px;
  color: var(--muted);
  font-size: 15px;
  line-height: 1.45;
}

#search-bar {
  padding: 12px var(--sp-7) var(--sp-5);
  border-bottom: 0;
}

.search-container {
  border-radius: 16px;
  border-color: var(--border);
  background: var(--surface);
  padding: 10px;
  box-shadow: none;
}

#urlInput,
.search-container .mode-btn,
#fetchBtn {
  border-radius: 12px;
}

#urlInput {
  background: var(--surface2);
  border-color: transparent;
}

#urlInput:focus {
  border-color: var(--accent);
}

.search-container .mode-btn:not(.active) {
  background: var(--surface3);
  color: var(--text2);
  border-color: transparent;
}

#searchModeToggle:hover,
#csvBtn:hover,
#pasteBtn:hover {
  background: var(--surface2);
}

#fetchBtn {
  box-shadow: none;
}

#fetchBtn:hover {
  transform: none;
}

#recent-wrap {
  padding-top: 20px;
}

.recent-container {
  max-width: 1060px;
}

.recent-label {
  color: var(--text);
  font-size: 16px;
  font-weight: 600;
  letter-spacing: -0.02em;
  text-transform: none;
  margin-bottom: 14px;
}

.recent-card {
  border-color: transparent;
  border-radius: 12px;
  box-shadow: none;
}

.recent-card:hover {
  border-color: var(--border2);
  transform: translateY(-2px);
  box-shadow: var(--elev-1);
}

#album-card,
.s-section {
  background: var(--surface);
  border: 0;
  border-radius: 12px;
  box-shadow: none;
}

#album-cover,
.rc-cover,
.search-result-thumbnail {
  border-radius: 10px;
}

.act-btn,
.page-btn,
.mode-btn,
.s-actions .act-btn {
  border-radius: 12px;
  box-shadow: none;
  font-family: var(--app-font);
}

.act-btn:hover,
.act-btn.secondary:hover,
.s-actions .act-btn.secondary:hover {
  transform: none;
  box-shadow: none;
}

.stab {
  border-radius: 10px;
  font-family: var(--app-font);
  font-size: var(--fs-100);
  font-weight: 600;
}

.stab:hover {
  transform: none;
}

.stab.active {
  color: var(--accent-text);
  background: var(--accent-subtle);
  border-color: transparent;
}

select,
input[type="text"],
input[type="password"],
input[type="number"],
input[type="search"],
textarea {
  border-radius: 10px;
}

.api-modal-card,
.queue-panel,
.fb-card {
  border-radius: 16px;
  box-shadow: var(--elev-2);
}

@media (max-width: 700px) {
  .api-modal-card,
  .queue-panel,
  .fb-card {
    border-radius: 32px 32px 0 0;
  }
}

@media (prefers-reduced-motion: reduce) {
  .nav-btn:hover,
  .act-btn:hover,
  .recent-card:hover {
    transform: none;
  }
}
