/* =================================================================
   tag-editor.css — Bulk/single tag editor modal.
   Modal chrome, field grid, cover / artwork slots, booklet preview
   slot, tag-progress overlay (live progress during a save), and
   visual states for mixed / edited / work-grouped fields.
   ================================================================= */

.tag-editor-modal {
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: var(--surface-overlay);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 3000;
    /* Backdrop-level scroll as a safety net for tall content on 720p
       (40+ track albums, big artwork sections); modal body's own
       overflow:auto handles the normal path. */
    overflow-y: auto;
    padding: 20px;
}

.tag-editor-content {
    background: var(--bg-card);
    border-radius: 15px;
    /* Width target: 760px when room allows, otherwise 95vw. min() is
       cleaner than relying on width + max-width override semantics
       and matches the rest of the modal family. */
    width: min(760px, 95vw);
    max-width: 95vw;
    max-height: 90vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

/* Album-mode hero variant — wider modal so the prominent front cover
   has room without squashing the title / artist column next to it.
   Track / selection / multi-album modes keep the default 760px. */
.tag-editor-content.tag-editor-content-hero {
    width: min(960px, 95vw);
}

.tag-editor-header {
    display: flex;
    align-items: center;
    padding: 20px;
    border-bottom: 1px solid var(--border-subtle);
}

.tag-editor-header h2 {
    flex: 1;
    font-size: 18px;
}

.tag-editor-mode {
    font-size: 12px;
    color: var(--text-muted);
    background: var(--bg-dark);
    padding: 5px 10px;
    border-radius: 15px;
    margin-right: 15px;
}

.tag-editor-close {
    background: none;
    border: none;
    color: var(--text-secondary);
    font-size: 24px;
    cursor: pointer;
}

.tag-editor-body {
    padding: 22px;
    overflow-y: auto;
    flex: 1;
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: 14px;
    align-content: start;
}

.tag-field {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
    grid-column: span 3;
}

.tag-field[data-span="1"] { grid-column: span 1; }
.tag-field[data-span="2"] { grid-column: span 2; }
.tag-field[data-span="3"] { grid-column: span 3; }
.tag-field[data-span="4"] { grid-column: span 4; }
.tag-field[data-span="6"] { grid-column: span 6; }

.tag-field-full {
    grid-column: 1 / -1;
}

.tag-section-header {
    grid-column: 1 / -1;
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 6px;
}

.tag-section-header:first-child {
    margin-top: 0;
}

/* Inter-section breathing room. Emitted between successive sections
   so the boundary between a titled section and an adjacent untitled
   one (or two untitled ones in a row) is visible. Height is roughly
   half a title-line — enough to read as "new group" without doubling
   the vertical real estate the editor takes. */
.tag-section-spacer {
    grid-column: 1 / -1;
    height: 10px;
    pointer-events: none;
}

.tag-section-title {
    font-size: 11px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--accent-secondary);
    font-weight: 600;
}

.tag-section-line {
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--border-subtle), transparent);
}

.tag-field label {
    display: flex;
    align-items: center;
    gap: 6px;
}

.tag-mixed-badge {
    font-size: 9px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--accent-purple);
    background: rgba(var(--accent-purple-rgb), 0.15);
    padding: 2px 6px;
    border-radius: 10px;
    font-weight: 600;
}

.tag-field input.tag-mixed,
.tag-field textarea.tag-mixed {
    border-color: rgba(var(--accent-purple-rgb), 0.45);
    background: rgba(var(--accent-purple-rgb), 0.05);
}

.tag-field input.tag-mixed::placeholder,
.tag-field textarea.tag-mixed::placeholder {
    color: var(--accent-purple);
    opacity: 0.75;
    font-size: 12px;
    font-style: italic;
    letter-spacing: 0.01em;
}

.tag-field input.tag-edited,
.tag-field textarea.tag-edited {
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.04);
}

.tag-field input.tag-empty::placeholder,
.tag-field textarea.tag-empty::placeholder {
    color: var(--text-muted);
    font-style: italic;
    font-size: 12px;
    opacity: 1;
}

.tag-work-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 2px;
    flex-wrap: wrap;
}

/* Pill shown inside the Work field's actions row when the user has
   confirmed multi-group assignments. The Work input stays enabled — this
   pill just announces the staged writes and lets the user clear them. */
.tag-work-group-pill {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    background: rgba(var(--state-success-rgb), 0.14);
    border: 1px solid rgba(var(--state-success-rgb), 0.35);
    color: #bce8ca;
    font-size: 12px;
    padding: 4px 6px 4px 10px;
    border-radius: 14px;
    font-weight: 500;
    line-height: 1;
}
.tag-work-group-pill[hidden] { display: none; }
.tag-work-group-pill-clear {
    background: rgba(0, 0, 0, 0.25);
    border: none;
    color: #cfeedc;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    font-size: 10px;
    font-weight: 700;
    cursor: pointer;
    line-height: 1;
    padding: 0;
    display: flex; align-items: center; justify-content: center;
}
.tag-work-group-pill-clear:hover {
    background: rgba(0, 0, 0, 0.5);
    color: #fff;
}
/* Soft tint on the input when groups are staged, so the user can tell at a
   glance that something non-default is in flight. */
.tag-field input.tag-work-grouped {
    background: rgba(var(--state-success-rgb), 0.07);
    border-color: rgba(var(--state-success-rgb), 0.35);
    /* The save handler ignores typed values while groups are active
       — the input is read-only in that mode. Match the cursor and
       opacity so the lock is visible to the user. */
    cursor: not-allowed;
    color: var(--text-secondary);
}

/* --- Per-track titles strip (album mode only) ----------------------------
   Inline list so the user can fix track titles without leaving album mode.
   Each row edits a single file; changes save as per-track patches via
   modal._onlineTrackPatches (shared with the online-lookup flow). */
.tag-tracks-list {
    display: flex;
    flex-direction: column;
    gap: 4px;
    /* 260px hardcap blocked scrolling on 720p when combined with the
       editor's 90vh chrome; switched to viewport-aware bounds with a
       sensible floor so a 60-track album still gets a workable
       scrolling region on every resolution. */
    max-height: min(40vh, 480px);
    min-height: 140px;
    overflow-y: auto;
    padding: 4px;
    background: rgba(0, 0, 0, 0.15);
    border-radius: 6px;
    border: 1px solid var(--overlay-selected);
}
.tag-track-row {
    display: grid;
    grid-template-columns: 48px 1fr;
    gap: 10px;
    align-items: center;
    padding: 4px 8px;
}
.tag-track-num {
    text-align: right;
    font-size: 11px;
    font-variant-numeric: tabular-nums;
    color: var(--text-muted);
}
.tag-track-title-input {
    background: var(--bg-input, var(--overlay-hover));
    border: 1px solid var(--overlay-active);
    border-radius: 4px;
    color: var(--text-primary);
    font-size: 13px;
    padding: 6px 10px;
    width: 100%;
}
.tag-track-title-input:focus {
    outline: none;
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.04);
}

.tag-set-work-btn,
.tag-prepend-work-btn,
.tag-clear-groups-btn {
    background: var(--bg-dark);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    padding: 6px 12px;
    border-radius: 16px;
    font-size: 12px;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s;
}

.tag-set-work-btn:hover,
.tag-prepend-work-btn:hover {
    border-color: var(--accent-primary);
    color: var(--text-primary);
}

.tag-clear-groups-btn:hover {
    border-color: #ff5a6b;
    color: #ff8a95;
}

.tag-work-status {
    font-size: 12px;
    color: var(--text-muted);
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tag-work-status-ok {
    color: var(--accent-cyan);
}

.tag-field input.tag-work-grouped,
.tag-field textarea.tag-work-grouped {
    border-color: var(--accent-cyan);
    background: rgba(var(--accent-cyan-rgb), 0.05);
    color: var(--accent-cyan);
    font-style: italic;
}

.tag-cover-row {
    display: flex;
    align-items: center;
    gap: 16px;
}

.tag-cover-preview {
    width: 180px;
    height: 180px;
    border-radius: 12px;
    background: var(--bg-dark);
    background-size: cover;
    background-position: center;
    border: 1px solid var(--border-subtle);
    flex-shrink: 0;
    transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
}

/* Drop-target visual cue — applied while a file drag is hovering the
   slot. The wiring lives in tagEditor.js (_wireDropTarget). The accent
   ring + tinted background tell the user "drop here". */
.tag-cover-preview.tag-cover-preview-drop {
    border-color: var(--accent-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent-primary) 25%, transparent);
    background-color: color-mix(in srgb, var(--accent-primary) 8%, var(--bg-dark));
}

.tag-cover-btn {
    background: var(--bg-dark);
    border: 1px solid var(--border-subtle);
    border-radius: 10px;
    padding: 10px 14px;
    color: var(--text-primary);
    cursor: pointer;
}

.tag-cover-btn:hover {
    border-color: var(--accent-primary);
}

.tag-cover-remove-btn {
    background: transparent;
    border: 1px solid rgba(var(--state-danger-rgb), 0.5);
    border-radius: 8px;
    padding: 4px 10px;
    color: #e06060;
    font-size: 12px;
    cursor: pointer;
    margin-top: 4px;
    transition: background 0.15s, color 0.15s;
}

.tag-cover-remove-btn:hover {
    background: rgba(var(--state-danger-rgb), 0.15);
    color: #ff8080;
}

.tag-cover-remove-btn[hidden] { display: none; }

.tag-field label {
    font-size: 12px;
    color: var(--text-muted);
    text-transform: capitalize;
}

.tag-field input,
.tag-field textarea {
    background: var(--bg-dark);
    border: 1px solid var(--border-subtle);
    border-radius: 8px;
    padding: 10px;
    color: var(--text-primary);
    font-size: 14px;
}

.tag-field textarea {
    min-height: 60px;
    resize: vertical;
}

.tag-field input:focus,
.tag-field textarea:focus {
    outline: none;
    border-color: var(--accent-primary);
}

.tag-editor-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 20px;
    border-top: 1px solid var(--border-subtle);
}

.tag-cancel-btn,
.tag-save-btn {
    padding: 12px 25px;
    border-radius: 25px;
    font-size: 14px;
    cursor: pointer;
    border: none;
}

.tag-cancel-btn {
    background: var(--bg-card);
    color: var(--text-secondary);
}

.tag-cancel-btn:hover {
    background: var(--bg-card-hover);
}

.tag-save-btn {
    background: var(--accent-primary);
    color: white;
}

.tag-save-btn:hover {
    opacity: 0.9;
}

/* Pulses after online-lookup stages changes, so the user notices they
   still need to click Save to actually write to disk. */
.tag-save-btn-pulse {
    animation: tag-save-pulse 1.6s ease-in-out infinite;
    box-shadow: 0 0 0 0 rgba(var(--accent-primary-rgb), 0.6);
}
@keyframes tag-save-pulse {
    0%   { box-shadow: 0 0 0 0   rgba(var(--accent-primary-rgb), 0.55); }
    60%  { box-shadow: 0 0 0 10px rgba(var(--accent-primary-rgb), 0); }
    100% { box-shadow: 0 0 0 0   rgba(var(--accent-primary-rgb), 0); }
}

.tag-editor-content {
    position: relative;
}

.tag-editor-busy .tag-editor-body,
.tag-editor-busy .tag-editor-footer {
    pointer-events: none;
    filter: blur(2px);
    opacity: 0.35;
}

.tag-editor-busy .tag-editor-close {
    display: none;
}

.tag-progress-overlay {
    position: absolute;
    inset: 0;
    background: rgba(10, 10, 15, 0.88);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 15px;
    z-index: 5;
    padding: 20px;
    /* The hero panel can outgrow the modal body (tall hero, long error
       lists) — scroll instead of letting the content's overflow:hidden
       clip it, so the action row (Continue in background / Cancel) is
       always reachable. */
    overflow-y: auto;
}

/* Hero variant — the redesigned progress card uses the import-now-*
   layout (cover left, info right, fat bar). Wider so the hero has
   breathing room. */
.tag-progress-panel-hero {
    max-width: 720px !important;
    padding: 18px 22px 20px !important;
}
.tag-progress-panel-hero .import-now {
    padding: 0;
    min-height: auto;
    gap: 14px;
}
.tag-progress-panel-hero .import-now-stage {
    margin: 0 auto;
}

/* Done / error state pills mirror the import-now-done / -error
   variants. The phase pill swaps colour on completion to match. */
.import-now-phase.tag-progress-phase-done {
    color: var(--state-success, #1ed760);
    background: color-mix(in srgb, var(--state-success, #1ed760) 18%, transparent);
}
.import-now-phase.tag-progress-phase-err {
    color: var(--state-danger);
    background: color-mix(in srgb, var(--state-danger) 18%, transparent);
}

/* Action row at the top of the progress card — Cancel + minimize. */
.tag-progress-actions {
    display: flex;
    justify-content: flex-end;
    gap: 8px;
    margin-bottom: -2px;
}
.tag-progress-cancel,
.tag-progress-minimize {
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    font-size: 12px;
    font-weight: 600;
    padding: 6px 12px;
    border-radius: 16px;
    cursor: pointer;
    transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.tag-progress-cancel:hover {
    color: var(--state-danger);
    border-color: var(--state-danger);
    background: color-mix(in srgb, var(--state-danger) 12%, transparent);
}
.tag-progress-minimize:hover {
    color: var(--accent-primary);
    border-color: var(--accent-primary);
    background: rgba(var(--accent-primary-rgb), 0.10);
}
.tag-progress-cancel-cancelling,
.tag-progress-cancel:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

/* The previous "Continue in background" path re-skinned this overlay
   as a dock above the player-bar (`.tag-progress-minimized` etc.). It
   has been replaced by the player-bar takeover in playerBarProgress.js;
   when backgrounded the parent `.tag-editor-modal` is hidden via inline
   `display: none` and the bar takes over. No dock-specific CSS is
   needed any more. */

/* Tag-editor "waiting in queue" overlay — shown when the user clicks
   Save while another background task is still running. Sibling of the
   .tag-progress-overlay that renders during the actual save; same
   shell, different inner. The waiting hero has no progress bar / log /
   per-file cover — just a centered message + Cancel + Continue-in-
   background row. */
.tag-waiting-panel {
    align-items: center;
    text-align: center;
    gap: 12px;
    padding: 36px 28px;
}
.tag-waiting-icon {
    color: var(--accent-primary);
    opacity: 0.85;
    margin-bottom: 4px;
}
.tag-waiting-title {
    margin: 0;
    font-size: 20px;
    font-weight: 600;
    line-height: 1.25;
    color: var(--text-primary);
}
.tag-waiting-sub {
    margin: 0;
    font-size: 13.5px;
    color: var(--text-secondary);
}
.tag-waiting-sub strong {
    color: var(--text-primary);
    font-weight: 600;
}
.tag-waiting-hint {
    margin: 4px 0 0;
    max-width: 460px;
    font-size: 12px;
    line-height: 1.55;
    color: var(--text-muted);
}
.tag-waiting-hint em {
    font-style: normal;
    color: var(--accent-primary);
}
.tag-waiting-actions {
    margin-top: 10px;
    display: flex;
    gap: 10px;
    justify-content: center;
}

.tag-progress-panel {
    width: 100%;
    max-width: 460px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: 14px;
    padding: 22px;
    display: flex;
    flex-direction: column;
    gap: 14px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}

.tag-progress-head {
    display: flex;
    align-items: center;
    gap: 12px;
}

.tag-progress-title {
    font-size: 15px;
    font-weight: 600;
    color: var(--text-primary);
}

.tag-progress-spinner {
    width: 22px;
    height: 22px;
    border-radius: 50%;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent-primary);
    animation: tag-progress-spin 0.9s linear infinite;
    flex-shrink: 0;
}

.tag-progress-spinner.done {
    animation: none;
    border: 2px solid var(--accent-cyan);
    position: relative;
}

.tag-progress-spinner.done::after {
    content: '\2713';
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent-cyan);
    font-size: 14px;
    font-weight: 700;
}

@keyframes tag-progress-spin {
    to { transform: rotate(360deg); }
}

.tag-progress-counts {
    font-size: 13px;
    color: var(--text-secondary);
    display: flex;
    align-items: center;
    gap: 8px;
}

.tag-progress-pct {
    margin-left: auto;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.tag-progress-bar {
    height: 6px;
    background: var(--bg-dark);
    border-radius: 3px;
    overflow: hidden;
}

.tag-progress-fill {
    height: 100%;
    width: 0%;
    background: linear-gradient(90deg,
        color-mix(in srgb, var(--accent-primary) 100%, white 18%),
        var(--accent-primary));
    transition: width 0.2s ease;
}

.tag-progress-current {
    background: var(--bg-dark);
    border: 1px solid var(--border-subtle);
    border-radius: 10px;
    padding: 12px 14px;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.tpc-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
}

.tpc-name {
    font-size: 14px;
    color: var(--text-primary);
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tpc-path {
    font-size: 11px;
    color: var(--text-muted);
    font-family: 'Consolas', 'Courier New', monospace;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    direction: rtl;
    text-align: left;
}

.tag-progress-log-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
}

.tag-progress-log {
    background: var(--bg-dark);
    border: 1px solid var(--border-subtle);
    border-radius: 10px;
    padding: 8px 10px;
    max-height: 160px;
    overflow-y: auto;
    font-size: 12px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.tpl-entry {
    display: flex;
    align-items: center;
    gap: 8px;
    color: var(--text-secondary);
    padding: 2px 0;
}

.tpl-icon {
    width: 14px;
    text-align: center;
    flex-shrink: 0;
    font-size: 11px;
}

.tpl-pending {
    color: var(--accent-secondary);
    animation: tag-progress-pulse 1.2s ease-in-out infinite;
}

.tpl-ok {
    color: var(--accent-cyan);
}

.tpl-err {
    color: #ff5a6b;
}

.tpl-text {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    flex: 1;
}

.tpl-error {
    color: #ff8a95;
    margin-left: 6px;
    font-size: 11px;
}

@keyframes tag-progress-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.35; }
}

.tag-progress-footer {
    display: flex;
    justify-content: flex-end;
    min-height: 0;
}

.tag-progress-done-btn {
    padding: 10px 22px;
    border-radius: 20px;
    background: var(--accent-primary);
    color: white;
    border: none;
    cursor: pointer;
    font-size: 14px;
}

.tag-progress-done-btn:hover {
    opacity: 0.9;
}

/* =================================================================
   Tag editor — artwork row (front/back/booklet)
   ================================================================= */

.tag-artwork-row {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 14px;
}

.tag-artwork-slot {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 8px;
    padding: 10px;
    background: var(--bg-dark);
    border: 1px solid var(--border-subtle);
    border-radius: 8px;
}

.tag-artwork-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}

.tag-booklet-preview {
    height: 120px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 40px;
    background: var(--bg-card);
    border: 1px dashed var(--border-subtle);
    border-radius: 6px;
    color: var(--text-muted);
}

.tag-booklet-preview.has-booklet {
    color: var(--accent-primary);
    border-style: solid;
}

.tag-booklet-empty {
    font-size: 12px;
}

.tag-cover-btn-ghost {
    background: transparent;
    opacity: 0.8;
}

/* =================================================================
   Tag editor — hero (album mode). Mirrors the importer's review-tags
   hero: large cover on the left, borderless title / artist / year
   inputs on the right. The .cdplayer-* classes provide the bulk of
   the styling; rules below are tag-editor-specific tweaks.
   ================================================================= */

.tag-editor-hero {
    margin: 0 0 18px 0;
    /* The body uses `display: grid; grid-template-columns: repeat(6,1fr)`,
       so a flex hero would render at column-1 only and let the rest of the
       row stay empty. Span the full body width so the hero claims its row
       end-to-end. */
    grid-column: 1 / -1;
}

/* Give the front cover the visual weight the user expects from a music
   app — earlier this inherited .cdplayer-cover's 180×180 (sized for the
   compact CD ripper / importer hero). In the tag editor we have a much
   wider modal and the user wanted the front cover to dominate the
   header instead of looking like a thumbnail. */
.tag-editor-hero .cdplayer-cover {
    width: 320px;
    height: 320px;
}

/* The cover lives inside the hero AND is its own target for "set
   cover…" / "remove" actions. Position the action chips floated over
   the bottom-left of the cover so the user reaches them on hover
   without breaking the visual cleanness when they aren't needed. */
.tag-editor-hero-cover {
    position: relative;
}

.tag-editor-hero-cover-actions {
    position: absolute;
    left: 8px;
    right: 8px;
    bottom: 8px;
    display: flex;
    align-items: center;
    gap: 6px;
    opacity: 0;
    transition: opacity 0.15s ease;
}

.tag-editor-hero-cover:hover .tag-editor-hero-cover-actions,
.tag-editor-hero-cover:focus-within .tag-editor-hero-cover-actions {
    opacity: 1;
}

.tag-editor-hero-cover-actions .tag-cover-btn,
.tag-editor-hero-cover-actions .tag-cover-remove-btn {
    padding: 5px 10px;
    font-size: 11.5px;
    border-radius: 6px;
    background: rgba(0, 0, 0, 0.6);
    color: var(--text-primary);
    border: 1px solid rgba(255, 255, 255, 0.18);
    backdrop-filter: blur(6px);
}
.tag-editor-hero-cover-actions .tag-cover-btn:hover,
.tag-editor-hero-cover-actions .tag-cover-remove-btn:hover {
    background: rgba(0, 0, 0, 0.78);
    border-color: var(--accent-primary, #ff6b35);
}

/* Inline label + input row for the artist/year hero fields. The
   album-title input on top is unlabelled (it's the obvious top-line
   header). Album artist and year get a small "Album artist:" / "Year:"
   prefix so the user knows what they're editing without having to
   guess from placeholder text alone. */
.tag-editor-hero-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 2px;
}
.tag-editor-hero-row .cdplayer-artist,
.tag-editor-hero-row .tag-chip-host {
    flex: 1 1 auto;
    min-width: 0;
    margin-top: 0;   /* parent label-row owns the spacing */
}
/* Chip-host inside the hero — borderless until focused so it reads as
   plain text alongside the other borderless hero inputs. The section-
   level chip-host has its own bordered styling; this overrides for the
   hero context. */
.tag-editor-hero-chip {
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    padding: 2px 4px;
    transition: background 0.12s, border-color 0.12s;
}
.tag-editor-hero-chip:hover {
    background: var(--overlay-hover);
}
.tag-editor-hero-chip:focus-within {
    background: var(--overlay-selected);
    border-color: var(--accent-primary, #ff6b35);
}
.tag-editor-hero-label {
    font-size: 11.5px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted);
    flex: 0 0 auto;
}

/* Hero inputs — borderless until focused. Mirrors the importer's
   review-tags overrides; see importer.css's
   .import-tags-input.import-tags-hero-input rules. */
.tag-editor-hero-input {
    background: transparent;
    border: 1px solid transparent;
    border-radius: 6px;
    transition: background 0.12s, border-color 0.12s;
}
.tag-editor-hero-input:hover {
    background: var(--overlay-hover);
}
.tag-editor-hero-input:focus {
    outline: none;
    background: var(--overlay-selected);
    border-color: var(--accent-primary, #ff6b35);
}
.tag-editor-hero-input.tag-mixed {
    color: var(--accent-secondary);
}
.tag-editor-hero-input.tag-edited {
    border-color: var(--accent-primary, #ff6b35);
}

/* Strip number-input spinners on the year field so the borderless
   look is preserved on hover. */
.tag-editor-hero .cdplayer-meta-input[type="number"]::-webkit-outer-spin-button,
.tag-editor-hero .cdplayer-meta-input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.tag-editor-hero .cdplayer-meta-input[type="number"] {
    -moz-appearance: textfield;
}

/* "Other tags" row — read-only value with a × clear button. The button
   toggles `data-cleared` on the row; staged-for-removal styling makes
   the choice obvious before save. */
.tag-unknown-row {
    display: flex;
    align-items: center;
    gap: 6px;
}
.tag-unknown-row input {
    flex: 1;
    min-width: 0;
}
.tag-unknown-clear-btn {
    flex: 0 0 auto;
    width: 26px;
    height: 26px;
    border-radius: 6px;
    border: 1px solid var(--border-subtle);
    background: transparent;
    color: var(--text-muted);
    font-size: 16px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tag-unknown-clear-btn:hover {
    background: rgba(var(--state-danger-rgb, 200, 60, 60), 0.12);
    color: var(--state-danger, #c83c3c);
    border-color: var(--state-danger, #c83c3c);
}
.tag-field-unknown.tag-unknown-marked-clear label,
.tag-field-unknown.tag-unknown-marked-clear input {
    text-decoration: line-through;
    opacity: 0.55;
}
.tag-field-unknown.tag-unknown-marked-clear .tag-unknown-clear-btn {
    background: var(--state-danger, #c83c3c);
    color: var(--text-on-accent, #fff);
    border-color: var(--state-danger, #c83c3c);
    text-decoration: none;
    opacity: 1;
}

/* Compact back/disk/booklet row — sits under the hero. The default
   .tag-artwork-row is 3-col and 180px tall; scale it down so it
   doesn't dominate the screen now that the hero already has the
   front cover at full size. */
.tag-artwork-row-compact .tag-cover-preview {
    width: 120px;
    height: 120px;
}
.tag-artwork-row-compact .tag-booklet-preview {
    height: 80px;
    font-size: 28px;
}
.tag-editor-extras {
    margin-top: 14px;
    margin-bottom: 8px;
}

/* =================================================================
   Tag editor — multi-disk-cover slots row
   The disk-cover slot used to be a single thumbnail; an album can now
   carry any number of role='disk' assets (cover_disk.png,
   cover_disk_2.png, …), so we render a horizontal row of mini-tiles
   plus a "+" tile that picks a new image. Single-disk albums look
   essentially identical to today.
   ================================================================= */
.tag-artwork-slot-disk {
    overflow: visible;
}
.tag-disk-slots-row {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: 6px;
    margin-top: 4px;
}
.tag-disk-slot {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 3px;
    width: 70px;
}
.tag-disk-slot-thumb {
    width: 70px;
    height: 70px;
    background: var(--bg-card);
    background-size: cover;
    background-position: center;
    border: 1px solid var(--border-subtle);
    border-radius: 6px;
}
.tag-disk-slot-label {
    font-size: 10px;
    color: var(--text-secondary);
    text-align: center;
    line-height: 1;
}
.tag-disk-slot-replace,
.tag-disk-slot-remove {
    position: absolute;
    top: 2px;
    width: 18px;
    height: 18px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.7);
    color: #fff;
    font-size: 12px;
    line-height: 18px;
    text-align: center;
    cursor: pointer;
    opacity: 0;
    transition: opacity 0.15s ease;
}
.tag-disk-slot-replace { right: 22px; }
.tag-disk-slot-remove  { right: 2px;  }
.tag-disk-slot:hover .tag-disk-slot-replace,
.tag-disk-slot:hover .tag-disk-slot-remove {
    opacity: 1;
}
.tag-disk-slot-replace:hover,
.tag-disk-slot-remove:hover {
    background: rgba(0, 0, 0, 0.9);
}
.tag-disk-slot-add {
    width: 70px;
    height: 70px;
    align-self: flex-start;
    border: 1px dashed var(--border-strong);
    border-radius: 6px;
    background: transparent;
    color: var(--text-muted);
    font-size: 28px;
    line-height: 1;
    cursor: pointer;
    transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.tag-disk-slot-add:hover {
    border-color: var(--accent-primary);
    color: var(--accent-primary);
    background: color-mix(in srgb, var(--accent-primary) 8%, transparent);
}

/* =================================================================
   Tag editor — Songs section (per-track list + find/replace titles).
   Renders as a collapsible <details> directly under the hero. Each
   song is a single read-only row; double-click the title to edit it
   in place. The find/replace tool below operates on titles only and
   sits on a single line.
   ================================================================= */
.tag-songs-section {
    margin: 4px 0 8px;
    border: 1px solid var(--border-subtle);
    border-radius: 8px;
    background: var(--bg-card);
    overflow: hidden;
}
.tag-songs-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    cursor: pointer;
    user-select: none;
    font-size: 13px;
    color: var(--text-primary);
    list-style: none;
}
.tag-songs-summary::-webkit-details-marker { display: none; }
.tag-songs-summary::before {
    content: '\25B8';                          /* ▸ — flips to ▾ on open */
    color: var(--text-muted);
    font-size: 10px;
    width: 12px;
    text-align: center;
    transition: transform 0.15s ease, color 0.15s ease;
}
.tag-songs-section[open] > .tag-songs-summary::before {
    transform: rotate(90deg);
    color: var(--accent-primary);
}
.tag-songs-summary-title {
    font-weight: 600;
    flex: 0 0 auto;
}
.tag-songs-body {
    padding: 8px 14px 14px;
    border-top: 1px solid var(--border-subtle);
    background: color-mix(in srgb, var(--bg-card) 80%, transparent);
}

/* Two-column grid of song rows. Collapses to one column when the
   editor body is narrow. */
.tag-songs-grid {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 6px 12px;
}
@media (max-width: 720px) {
    .tag-songs-grid {
        grid-template-columns: 1fr;
    }
}

.tag-song-row {
    border: 1px solid var(--border-subtle);
    border-radius: 6px;
    background: var(--bg-dark);
    padding: 6px 8px;
    transition: border-color 0.12s ease, background 0.12s ease;
}
.tag-song-row-head {
    display: flex;
    align-items: flex-start;     /* num aligns with the FIRST line of a wrapping title */
    gap: 8px;
    min-width: 0;
}
.tag-song-num {
    flex: 0 0 auto;
    font-variant-numeric: tabular-nums;
    font-size: 11px;
    color: var(--text-muted);
    min-width: 28px;
    text-align: right;
    /* Tweak so the right-edge of "01" lines up with the first line of
       the title text (the title is 12.5px / 1.4 line-height vs. num's
       11px / 1 — without padding-top the num sits a hair too high). */
    padding-top: 2px;
    line-height: 1.4;
}
.tag-song-title {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 12.5px;
    line-height: 1.4;
    color: var(--text-primary);
    /* Long titles wrap — the user wants to see the whole thing while
       editing, so no ellipsis. break-word handles the rare case of a
       single un-spaced very-long token. */
    white-space: normal;
    overflow-wrap: anywhere;
    word-break: break-word;
    cursor: text;
    padding: 1px 3px;
    border-radius: 3px;
}
.tag-song-title:hover {
    background: color-mix(in srgb, var(--accent-primary) 10%, transparent);
}
/* While editing the title (after a double-click), the span hosts an
   inline <input>. The span's padding and the input's own padding
   would otherwise stack and grow the row — strip both. */
.tag-song-title.tag-song-title-editing {
    padding: 0;
    overflow: visible;
    background: transparent;
}
.tag-song-title-input {
    width: 100%;
    padding: 1px 4px;
    background: var(--bg-card);
    border: 1px solid var(--accent-primary);
    border-radius: 3px;
    color: var(--text-primary);
    font: inherit;
    line-height: 1.3;
}
.tag-song-title-input:focus { outline: none; }

/* Live find/replace preview row — shown only when the toggle is on
   AND the title would change under the current Look-for / Op / With
   config. Sits directly below the head inside the song-row card; the
   accent tint flags it as "this row will change on Apply".
   Only the NEW value is shown; the old value + arrow stay in the DOM
   so the JS can rewrite their text without churn but are visually
   hidden. The new value gets its left padding tuned so it lines up
   with the title text in the head row above (same indent as the
   28px num column + 8px head-row gap). */
.tag-song-row-preview {
    display: block;
    margin-top: 6px;
    padding: 4px 8px 4px 36px;
    border-radius: 4px;
    background: color-mix(in srgb, var(--accent-primary) 8%, transparent);
    border: 1px solid color-mix(in srgb, var(--accent-primary) 30%, transparent);
    font-size: 12.5px;
}
/* Same cascade-origin fix as .tag-findreplace — the `display: block`
   above out-ranks the UA `[hidden] { display: none }` on equal
   specificity, so the JS-toggled `hidden` attribute wouldn't actually
   hide the row without this explicit override. */
.tag-song-row-preview[hidden] {
    display: none;
}
.tag-song-row-old,
.tag-song-row-arrow {
    display: none;
}
.tag-song-row-new {
    color: var(--text-primary);
    word-break: break-word;
}

/* Find/replace toggle — always visible above the song list. Acts as
   the on/off switch for the entire find-replace surface (toolbar +
   per-row preview rows). */
.tag-fr-toggle {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin: 4px 0 8px;
    font-size: 12px;
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
}
.tag-fr-toggle input[type="checkbox"] {
    accent-color: var(--accent-primary);
}

/* Find/replace tool — single-line layout. Hidden via [hidden] when
   the toggle is off; shown with accent treatment when on.
   The explicit `[hidden]` override is required because `display: flex`
   on `.tag-findreplace` has the same specificity as the UA stylesheet's
   `[hidden] { display: none }`, but author rules win by cascade origin
   — without this override the toolbar would stay flex-laid-out (i.e.
   visible) even when the JS adds the `hidden` attribute. */
.tag-findreplace {
    margin: 0 0 10px;
    padding: 8px 10px;
    border: 1px solid color-mix(in srgb, var(--accent-primary) 35%, transparent);
    border-radius: 6px;
    background: color-mix(in srgb, var(--accent-primary) 6%, transparent);
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
}
.tag-findreplace[hidden] {
    display: none;
}
.tag-fr-label,
.tag-fr-with-label {
    font-size: 11px;
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    flex: 0 0 auto;
}
.tag-fr-op,
.tag-fr-find,
.tag-fr-with {
    padding: 5px 8px;
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: 4px;
    color: var(--text-primary);
    font-size: 12px;
    font-family: inherit;
}
.tag-fr-op    { flex: 0 0 auto; min-width: 96px; }
.tag-fr-find,
.tag-fr-with {
    flex: 1 1 140px;
    min-width: 100px;
}
.tag-fr-op:focus,
.tag-fr-find:focus,
.tag-fr-with:focus {
    outline: none;
    border-color: var(--accent-primary);
}
.tag-fr-case {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 11.5px;
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
    flex: 0 0 auto;
}
.tag-fr-apply {
    padding: 6px 14px;
    border: 1px solid var(--accent-primary);
    border-radius: 6px;
    background: var(--accent-primary);
    color: var(--text-on-accent, #fff);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    transition: opacity 0.12s ease, background 0.12s ease;
    flex: 0 0 auto;
    margin-left: auto;        /* push to the right edge */
}
.tag-fr-apply:hover:not(:disabled) { opacity: 0.92; }
.tag-fr-apply:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}
/* When Operation = Remove the With control collapses out of the way
   (handled by inline `hidden` on the elements; the dataset hook is
   left here so themes can target the state if they want). */
.tag-findreplace[data-fr-op="remove"] .tag-fr-with-label,
.tag-findreplace[data-fr-op="remove"] .tag-fr-with {
    display: none;
}

/* =================================================================
   Tag editor — hero compact extras row (Back / Disc / Booklet)
   Folded into the hero info column right under the year input.
   ================================================================= */
.tag-hero-extras-row {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 10px 12px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px dashed var(--border-subtle);
}
/* The disk-slots wrapper rendered by _renderTagDiskSlotsHtml uses
   `.tag-artwork-slot tag-artwork-slot-disk` — neutralise its outer
   tile chrome when nested inside the hero so it lines up with the
   compact back/booklet slots. */
.tag-hero-extras-row .tag-artwork-slot-disk {
    background: transparent;
    border: none;
    padding: 0;
    flex: 0 0 auto;
}
.tag-hero-extras-slot {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    padding: 6px 8px;
    background: var(--bg-dark);
    border: 1px solid var(--border-subtle);
    border-radius: 6px;
    flex: 0 0 auto;
}
.tag-hero-extras-label {
    font-size: 9.5px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}
.tag-hero-extras-thumb {
    width: 56px;
    height: 56px;
    background: var(--bg-card);
    background-size: cover;
    background-position: center;
    border-radius: 4px;
    border: 1px solid var(--border-subtle);
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    font-size: 22px;
    line-height: 1;
}
.tag-hero-extras-set {
    font-size: 10.5px;
    padding: 2px 8px;
}
.tag-hero-extras-x {
    font-size: 11px;
    padding: 1px 6px;
    line-height: 1;
}
.tag-hero-extras-aux {
    font-size: 11px;
    padding: 1px 6px;
    line-height: 1;
}
/* Disk slots inside the hero shrink to match the back/booklet thumb
   size. The helper renders 70×70 by default for the standalone row;
   in the hero we want 56×56. */
.tag-hero-extras-row .tag-disk-slot,
.tag-hero-extras-row .tag-disk-slot-thumb,
.tag-hero-extras-row .tag-disk-slot-add {
    width: 56px;
}
.tag-hero-extras-row .tag-disk-slot-thumb,
.tag-hero-extras-row .tag-disk-slot-add {
    height: 56px;
}
.tag-hero-extras-row .tag-disk-slot-add {
    font-size: 22px;
}
/* The original "Disc covers" label that's part of the disk-slots row
   isn't needed in the hero (the disk thumbs are obvious next to back
   / booklet), but if it slips through hide it cleanly. */
.tag-hero-extras-row .tag-artwork-slot-disk > .tag-artwork-label {
    display: none;
}
