/* =============================================================================
 * online-lookup.css — styles for the online-metadata lookup band that
 * sits at the top of the tag editor body. State machine: idle → searching
 * → results → reviewing → applied.
 * ========================================================================== */

/* The tag editor body is a 6-column grid — span the full width so the
   band doesn't get squashed into one column. */
.ol-band {
    grid-column: 1 / -1;
    margin: 0 0 6px 0;
    min-width: 0;    /* allow children to shrink/scroll inside the grid cell */
}

/* ---- Standalone modal --------------------------------------------------
   The online lookup now lives in its own modal layered on top of the tag
   editor. Backdrop click + Escape close. After Apply the modal closes
   itself so the tag editor regains focus with populated fields. */
.online-lookup-modal {
    position: fixed;
    inset: 0;
    background: var(--surface-overlay);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 11000;     /* above .tag-editor-modal (z-index ~10000) */
    /* Backdrop scroll fallback for the rare case the inner content's
       own scroll regions don't cover everything (e.g. extreme review
       diffs on a 720p screen). Modal-content's max-height: 94vh + body
       overflow:auto handle the normal case. */
    overflow-y: auto;
    padding: 20px;
}
.online-lookup-content {
    background: var(--bg-card, #1a1a1f);
    border: 1px solid var(--border-subtle, var(--overlay-active));
    border-radius: 12px;
    width: min(1600px, 98vw);
    max-height: 94vh;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    box-shadow: var(--shadow-lg);
}
.online-lookup-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 18px;
    border-bottom: 1px solid var(--border-subtle, var(--overlay-selected));
}
.online-lookup-head h2 {
    margin: 0;
    font-size: 16px;
    font-weight: 600;
}
.online-lookup-close {
    background: transparent;
    border: none;
    color: var(--text-muted);
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    padding: 4px 10px;
    border-radius: 6px;
}
.online-lookup-close:hover {
    background: var(--overlay-selected);
    color: var(--text-primary);
}
.online-lookup-body {
    padding: 18px;
    overflow: auto;
    flex: 1;
}
.ol-band-modal {
    margin: 0;
    grid-column: auto;       /* not in a grid here */
}

/* "Lookup online" trigger button at the top of the tag editor body */
.tag-online-trigger {
    grid-column: 1 / -1;
    margin: 0 0 12px 0;
}
.tag-online-btn {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    padding: 10px 16px;
    background: linear-gradient(135deg, rgba(var(--accent-primary-rgb),0.18), rgba(var(--accent-primary-rgb),0.06));
    border: 1px solid rgba(var(--accent-primary-rgb),0.35);
    color: var(--text-primary);
    border-radius: 8px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    transition: filter 0.12s ease, transform 0.08s ease;
}
.tag-online-btn:hover {
    filter: brightness(1.1);
    transform: translateY(-1px);
}
.tag-online-icon {
    font-size: 16px;
}
.tag-online-sub {
    margin-left: auto;
    color: var(--text-muted);
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 500;
}

/* When the user enters the review step, the modal grows to fit the diff
   table comfortably. Width is a viewport-aware target — at 1080p+
   it's the full 1240px; at 720p (1280×720) it caps at 97vw so the
   user actually sees the modal chrome. The body's max-height uses
   `flex: 1` + parent's max-height instead of a hardcoded pixel offset
   so header/footer size changes don't break the layout. */
.tag-editor-content.ol-wide {
    width: min(1240px, 97vw);
    max-width: 97vw;
    max-height: 94vh;
    transition: width 0.18s ease, max-width 0.18s ease;
}
.tag-editor-content.ol-wide .tag-editor-body {
    /* Let flex shrink-to-fit pick the right body height inside the
       column-flex parent; the `min-height: 0` is required so the body
       can actually shrink below its content size and own the scroll. */
    flex: 1 1 auto;
    min-height: 0;
    max-height: none;
    overflow-y: auto;
}

.ol-card {
    background: linear-gradient(180deg,
        var(--overlay-selected),
        var(--overlay-hover));
    border: 1px solid var(--overlay-active);
    border-radius: 12px;
    padding: 16px 18px;
    box-shadow: var(--shadow-sm);
}

.ol-idle {
    display: flex;
    flex-direction: column;
    gap: 14px;
}
.ol-idle-head {
    display: flex;
    align-items: center;
    gap: 14px;
}
.ol-idle-icon {
    font-size: 28px;
    width: 48px; height: 48px;
    display: flex; align-items: center; justify-content: center;
    background: linear-gradient(135deg,
        rgba(var(--accent-primary-rgb), 0.18),
        rgba(var(--accent-primary-rgb), 0.06));
    border-radius: 10px;
    border: 1px solid rgba(var(--accent-primary-rgb), 0.25);
    flex-shrink: 0;
}
.ol-idle-title {
    font-weight: 600;
    font-size: 15px;
    color: var(--text-color, #eee);
    letter-spacing: -0.01em;
}
.ol-idle-sub {
    font-size: 12px;
    color: var(--text-muted, #999);
    margin-top: 3px;
    line-height: 1.4;
}
.ol-idle-actions {
    display: flex;
    gap: 8px;
    flex-wrap: nowrap;
    align-items: center;
}

/* Tag-field checkboxes + provider button row + fingerprint button. Each
   block has a small label and its content is a single horizontal row that
   wraps on narrow widths. */
.ol-fields-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-muted, #999);
    margin-bottom: 6px;
    font-weight: 600;
}
.ol-fields-row,
.ol-providers-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* Tag-field pill — a <button> styled as a chip with a pressed state.
   Switched from <label><input type=checkbox> to a button so click
   handling is fully explicit and not at the mercy of browser
   label-toggle quirks. */
.ol-field-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--overlay-hover);
    border: 1px solid var(--overlay-active);
    color: var(--text-muted, #aaa);
    padding: 5px 10px;
    border-radius: 999px;
    font-size: 12px;
    cursor: pointer;
    user-select: none;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
    font-family: inherit;
}
.ol-field-pill:hover { background: var(--overlay-active); }
.ol-field-pill:focus-visible {
    outline: 2px solid var(--accent-color, #ff6b35);
    outline-offset: 1px;
}
.ol-field-pill.checked {
    background: rgba(var(--accent-primary-rgb), 0.16);
    border-color: rgba(var(--accent-primary-rgb), 0.45);
    color: var(--accent-color, #ff6b35);
}

/* The pill is now "Field: value" — render the value in a slightly muted
   tone so the field name still reads first. Long values truncate via
   ellipsis while the full string remains in the title attribute. */
.ol-field-pill-name {
    font-weight: 600;
}
.ol-field-pill-name::after {
    content: ':';
    margin-right: 4px;
    opacity: 0.55;
}
.ol-field-pill-value {
    font-family: 'Consolas', 'SF Mono', monospace;
    font-size: 11.5px;
    color: var(--text-color, #eee);
    max-width: 220px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ol-field-pill.checked .ol-field-pill-value {
    color: var(--accent-color, #ff6b35);
}
.ol-field-pill-empty {
    color: var(--text-muted, #888) !important;
    font-style: italic;
    font-family: inherit;
    opacity: 0.7;
}

/* Provider buttons — like .ol-btn but with a small leading badge that
   colour-codes which API the user is talking to. */
.ol-btn-provider {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding-left: 6px;
}
.ol-btn-busy {
    position: relative;
    pointer-events: none;
    opacity: 0.7;
}
.ol-btn-busy::after {
    content: '';
    width: 12px; height: 12px;
    margin-left: 4px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent-primary);
    border-radius: 50%;
    display: inline-block;
    animation: ol-spin 0.8s linear infinite;
}
@keyframes ol-spin { to { transform: rotate(360deg); } }

.ol-btn-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 22px;
    padding: 0 6px;
    border-radius: 4px;
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.04em;
    background: var(--overlay-active);
    color: var(--text-color, #eee);
}

/* Per-provider badge tints (used both on buttons and on result cards).
   Tokens defined in base.css; themes override for contrast on light
   surfaces (light.css darkens each hue ~10% for white-bg legibility,
   sepia.css desaturates slightly to sit on the cream palette). */
.ol-badge-mb  .ol-btn-badge,
.ol-cand-provider-badge.ol-badge-mb  { background: var(--provider-musicbrainz-bg); color: var(--provider-musicbrainz-text); }
.ol-badge-dg  .ol-btn-badge,
.ol-cand-provider-badge.ol-badge-dg  { background: var(--provider-discogs-bg);     color: var(--provider-discogs-text); }
.ol-badge-dz  .ol-btn-badge,
.ol-cand-provider-badge.ol-badge-dz  { background: var(--provider-deezer-bg);      color: var(--provider-deezer-text); }
.ol-badge-adb .ol-btn-badge,
.ol-cand-provider-badge.ol-badge-adb { background: var(--provider-audiodb-bg);     color: var(--provider-audiodb-text); }

.ol-cand-provider-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 26px;
    height: 18px;
    padding: 0 6px;
    border-radius: 4px;
    font-size: 9.5px;
    font-weight: 700;
    letter-spacing: 0.04em;
    margin-bottom: 4px;
}

.ol-results-link {
    text-align: right;
    font-size: 12px;
}
.ol-fp-block {
    border-top: 1px dashed var(--overlay-active);
    padding-top: 12px;
}
.ol-cand-cover-missing {
    background: var(--overlay-hover);
    width: 60px; height: 60px;
    border-radius: 6px;
    display: block;
}

/* "Search more" toolbar inside the results card — keeps provider buttons
   visible so the user can keep extending without leaving the list. */
.ol-results-toolbar {
    border-top: 1px solid var(--overlay-selected);
    border-bottom: 1px solid var(--overlay-selected);
    padding: 10px 0 10px;
    margin: 8px 0;
}
.ol-results-toolbar-row {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
}

/* Already-searched providers get a small green checkmark + tinted border. */
.ol-btn-searched {
    border-color: rgba(var(--state-success-rgb), 0.55);
}
.ol-btn-check {
    margin-left: 4px;
    color: var(--state-success);
    font-size: 12px;
    font-weight: 700;
}

/* Per-result "via fingerprint / via tags" indicator. The icon sits next
   to the provider badge, sharing one stack so the side column doesn't
   sprawl. */
.ol-cand-provider-stack {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    margin-bottom: 4px;
}
.ol-cand-via {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 16px;
    font-size: 10px;
    color: var(--text-muted, #aaa);
    cursor: help;
}

/* ---- Layout toolbar: list/grid toggle + card-size slider ----------- */
.ol-layout-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    padding: 8px 0 4px;
    flex-wrap: wrap;
}
.ol-layout-toggle {
    display: inline-flex;
    background: var(--overlay-hover);
    border: 1px solid var(--overlay-active);
    border-radius: 8px;
    overflow: hidden;
}
.ol-layout-btn {
    background: transparent;
    border: none;
    color: var(--text-muted, #aaa);
    padding: 6px 12px;
    font-size: 12px;
    cursor: pointer;
    transition: background 0.12s, color 0.12s;
}
.ol-layout-btn:hover { background: var(--overlay-selected); }
.ol-layout-btn.active {
    background: var(--accent-color, #ff6b35);
    color: var(--text-on-accent);
}
.ol-layout-size {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    color: var(--text-muted, #aaa);
    font-size: 12px;
}
.ol-layout-size-label {
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    font-size: 11px;
}
.ol-layout-size input[type="range"] {
    width: 160px;
    accent-color: var(--accent-color, #ff6b35);
}
.ol-layout-size-readout {
    font-family: 'Consolas', 'SF Mono', monospace;
    color: var(--text-color, #eee);
    min-width: 30px;
    text-align: right;
}

/* ---- List mode (existing) — keep as-is when .ol-candlist-list ------- */
.ol-candlist-list {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

/* ---- Grid mode — auto-fill columns whose minimum width is the user's
   chosen card size. Cards are vertical: cover takes full card width
   (square aspect), info sits below it.
   Selectors are chained (.ol-candlist.ol-candlist-grid) to outweigh the
   later-in-file `.ol-candlist { grid-template-columns: 1fr }` legacy rule
   that would otherwise force a single column on us. Same trick for
   .ol-candidate.ol-candidate-grid to override the 3-column row layout
   that .ol-candidate establishes for list mode. */
.ol-candlist.ol-candlist-grid {
    display: grid !important;
    grid-template-columns: repeat(auto-fill, minmax(var(--ol-card-size, 200px), 1fr)) !important;
    gap: 12px;
    padding: 4px 0 8px;
    /* Grid mode wants a bit more vertical space than list mode (cards
       are bigger), but still capped to a viewport fraction so the
       footer stays reachable on small screens. */
    max-height: min(70vh, 900px);
}
.ol-candidate.ol-candidate-grid {
    display: flex !important;
    flex-direction: column !important;
    grid-template-columns: none !important;
    background: var(--overlay-hover);
    border: 1px solid var(--overlay-active);
    border-radius: 10px;
    overflow: hidden;
    cursor: pointer;
    padding: 0 !important;
    min-height: 0 !important;
    transition: border-color 0.12s, transform 0.08s, background 0.12s;
    align-items: stretch !important;
    gap: 0 !important;
}
.ol-candidate.ol-candidate-grid:hover {
    border-color: rgba(var(--accent-primary-rgb), 0.45);
    background: var(--overlay-selected);
    transform: translateY(-1px);
}
.ol-cand-grid-cover {
    position: relative;
    width: 100%;
    aspect-ratio: 1 / 1;
    background: var(--overlay-active);
    overflow: hidden;
    display: flex;
    align-items: center;
    justify-content: center;
}
.ol-cand-grid-cover img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}
.ol-cand-grid-cover-missing {
    width: 100%;
    height: 100%;
    background: linear-gradient(135deg,
        var(--overlay-hover),
        var(--overlay-active));
}
/* Cover overlays (provider badge, via icon, score chip) scale with the
   user-chosen card size so they stay legible when the slider is at the
   high end. The clamp() floor/ceiling keep them sane at the extremes:
   small enough to fit at 120px, big enough to read at 320px without
   eating the cover. The .ol-cand-grid-cover element exposes
   --ol-card-size via the parent grid; calc() reads it directly. */
.ol-cand-grid-overlay {
    position: absolute;
    top: clamp(4px, calc(var(--ol-card-size, 200px) * 0.04), 12px);
    left: clamp(4px, calc(var(--ol-card-size, 200px) * 0.04), 12px);
    display: flex;
    gap: clamp(2px, calc(var(--ol-card-size, 200px) * 0.025), 8px);
    align-items: center;
}
/* Make the provider badge inside the overlay scale too. The base
   .ol-cand-provider-badge rule sets fixed sizes for list-mode; in
   grid-mode we override them with clamped, card-size-relative values. */
.ol-candidate-grid .ol-cand-provider-badge {
    font-size:    clamp(10px, calc(var(--ol-card-size, 200px) * 0.06), 18px);
    height:       clamp(20px, calc(var(--ol-card-size, 200px) * 0.11), 36px);
    min-width:    clamp(28px, calc(var(--ol-card-size, 200px) * 0.16), 56px);
    padding:      0 clamp(6px, calc(var(--ol-card-size, 200px) * 0.04), 12px);
    border-radius: 5px;
    margin: 0;
}
.ol-cand-via-overlay {
    background: rgba(var(--surface-app-rgb), 0.65);
    color: var(--text-on-accent);
    width:  clamp(20px, calc(var(--ol-card-size, 200px) * 0.11), 36px);
    height: clamp(20px, calc(var(--ol-card-size, 200px) * 0.11), 36px);
    border-radius: 5px;
    font-size: clamp(11px, calc(var(--ol-card-size, 200px) * 0.07), 22px);
}
/* Score chip floats over the cover, top-right. */
.ol-cand-grid-score {
    position: absolute;
    top:   clamp(4px, calc(var(--ol-card-size, 200px) * 0.04), 12px);
    right: clamp(4px, calc(var(--ol-card-size, 200px) * 0.04), 12px);
    padding:
        clamp(2px, calc(var(--ol-card-size, 200px) * 0.018), 6px)
        clamp(7px, calc(var(--ol-card-size, 200px) * 0.05), 14px);
    border-radius: 999px;
    font-size: clamp(11px, calc(var(--ol-card-size, 200px) * 0.07), 20px);
    font-weight: 700;
    color: var(--text-on-accent);
    line-height: 1.1;
}
.ol-cand-grid-score.ol-score-hi  { background: var(--state-success); }
.ol-cand-grid-score.ol-score-mid { background: var(--accent-color, #ff9b35); }
.ol-cand-grid-score.ol-score-lo  { background: var(--surface-overlay); }

.ol-cand-grid-info {
    padding: 10px 12px 12px;
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}
.ol-candidate-grid .ol-cand-title {
    font-weight: 600;
    color: var(--text-color, #eee);
    font-size: 13px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ol-candidate-grid .ol-cand-sub {
    color: var(--text-muted, #aaa);
    font-size: 11.5px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ol-candidate-grid .ol-cand-sub-dim { opacity: 0.7; }

@media (max-width: 820px) {
    .ol-idle-head { flex-wrap: wrap; }
}

.ol-btn {
    background: var(--overlay-selected);
    border: 1px solid var(--border-strong);
    color: var(--text-color, #eee);
    padding: 7px 14px;
    font-size: 13px;
    border-radius: 6px;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s;
}
.ol-btn:hover { background: var(--overlay-active); }
.ol-btn-primary {
    background: var(--accent-color, #ff6b35);
    border-color: var(--accent-color, #ff6b35);
    color: var(--text-on-accent);
}
.ol-btn-primary:hover { filter: brightness(1.1); }
.ol-btn-disabled, .ol-btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.ol-btn-link {
    background: none; border: none; color: var(--accent-color, #ff6b35);
    cursor: pointer; font-size: 12px;
}
.ol-btn-link:hover { text-decoration: underline; }

/* --- Searching --- */
.ol-search-status {
    display: flex; align-items: center; gap: 10px;
    color: var(--text-muted, #ccc); font-size: 13px;
    margin-bottom: 8px;
}
.ol-search-detail {
    flex: 1;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ol-spinner {
    display: inline-block; width: 14px; height: 14px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent-color, #ff6b35);
    border-radius: 50%;
    animation: ol-spin 0.9s linear infinite;
    flex-shrink: 0;
}
@keyframes ol-spin { to { transform: rotate(360deg); } }

.ol-progress-bar {
    width: 100%; height: 4px;
    background: var(--overlay-active);
    border-radius: 2px;
    overflow: hidden;
    margin-top: 4px;
}
.ol-progress-fill {
    height: 100%;
    background: var(--accent-color, #ff6b35);
    transition: width 0.2s ease;
    border-radius: 2px;
}

/* --- Head / sections --- */
.ol-head {
    display: flex; justify-content: space-between; align-items: flex-start;
    gap: 12px; margin-bottom: 12px;
}
.ol-head-title { font-size: 14px; font-weight: 600; color: var(--text-color, #eee); }
.ol-head-sub { font-size: 12px; color: var(--text-muted, #999); margin-top: 2px; }
.ol-mb-link {
    color: var(--accent-color, #ff6b35);
    text-decoration: none;
    margin-left: 4px;
}
.ol-mb-link:hover { text-decoration: underline; }
.ol-head-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.ol-section-head {
    display: flex; justify-content: space-between; align-items: center;
    margin: 18px 0 8px 0; padding-bottom: 4px;
    border-bottom: 1px solid var(--overlay-active);
    font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--text-muted, #999);
}
.ol-master { display: flex; gap: 6px; align-items: center; font-size: 11px; cursor: pointer; text-transform: none; letter-spacing: 0; }

/* --- Candidate list ---
   The container's max-height was a hard 460px which made the results
   list run out of room on 720p (the modal body itself only has ~600px
   before the user can't scroll the rest of the page). Cap to a viewport
   fraction with a sensible floor so the list always has at least ~3
   cards visible no matter the resolution, but doesn't dominate the
   viewport on 4K either. The modal-body's own overflow:auto handles
   scrolling beyond this. */
.ol-candlist {
    display: grid;
    grid-template-columns: 1fr;
    gap: 6px;
    max-height: min(60vh, 720px);
    overflow-y: auto;
    padding-right: 4px;
}

.ol-candidate {
    display: grid;
    grid-template-columns: 104px 1fr auto;
    align-items: center;
    gap: 16px;
    padding: 12px 16px;
    border-radius: 10px;
    background: var(--overlay-hover);
    border: 1px solid var(--overlay-selected);
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, transform 0.08s;
    min-height: 104px;
}
.ol-candidate:hover {
    background: var(--overlay-active);
    border-color: var(--border-strong);
}
.ol-candidate:focus {
    background: var(--overlay-active);
    border-color: var(--accent-color, #ff6b35);
    outline: none;
}
.ol-candidate:active { transform: translateY(1px); }

.ol-cand-cover-wrap {
    width: 96px; height: 96px;
    border-radius: 8px;
    overflow: hidden;
    background: linear-gradient(135deg, var(--overlay-active), var(--overlay-hover));
    box-shadow: var(--shadow-md);
    display: flex; align-items: center; justify-content: center;
    position: relative;
    flex-shrink: 0;
}
.ol-cand-cover {
    width: 100%; height: 100%;
    object-fit: cover;
    display: block;
    background: transparent;
}
/* 404 or load error — keep the shaded placeholder visible. */
.ol-cand-cover-missing { visibility: hidden; }
.ol-cand-cover-wrap::before {
    content: '♪';
    position: absolute;
    font-size: 36px;
    color: var(--border-strong);
    font-weight: 300;
}
.ol-cand-cover-wrap:has(img:not(.ol-cand-cover-missing))::before { display: none; }

.ol-cand-body {
    min-width: 0;     /* allow inner text-overflow to clip */
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.ol-cand-title {
    font-size: 14px;
    color: var(--text-color, #eee);
    font-weight: 600;
    line-height: 1.3;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ol-cand-sub {
    font-size: 12px;
    color: var(--text-muted, #bbb);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.ol-cand-sub-dim { color: var(--text-muted); font-size: 11px; }

.ol-cand-side {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    min-width: 56px;
}
.ol-cand-score {
    background: var(--accent-color, #ff6b35);
    color: var(--text-on-accent);
    font-size: 14px;
    font-weight: 700;
    text-align: center;
    padding: 6px 10px;
    border-radius: 8px;
    min-width: 52px;
    letter-spacing: -0.01em;
}
.ol-cand-score-pct { font-size: 10px; font-weight: 500; opacity: 0.7; }
.ol-cand-score.ol-score-hi  { background: var(--state-success); }
.ol-cand-score.ol-score-mid { background: var(--accent-color, #ff9b35); }
.ol-cand-score.ol-score-lo  { background: var(--overlay-active); color: var(--text-muted, #aaa); }
.ol-cand-arrow {
    color: var(--text-muted, #666);
    font-size: 20px;
    line-height: 1;
    transition: color 0.12s, transform 0.12s;
}
.ol-candidate:hover .ol-cand-arrow,
.ol-candidate:focus .ol-cand-arrow {
    color: var(--accent-color, #ff6b35);
    transform: translateX(2px);
}

.ol-empty {
    padding: 20px 16px;
    text-align: center;
    font-size: 13px;
    color: var(--text-muted, #999);
    line-height: 1.5;
    background: var(--overlay-hover);
    border-radius: 8px;
    border: 1px dashed var(--overlay-active);
}

/* Banner above the candidate list when some files failed to fingerprint
   or AcoustID-lookup. Surfaces swallowed errors without drowning the UI. */
.ol-error-banner {
    margin-bottom: 10px;
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 12px;
    color: var(--state-warning);
    background: rgba(var(--state-warning-rgb), 0.12);
    border: 1px solid rgba(var(--state-warning-rgb), 0.3);
    cursor: help;
}

/* --- Review --- */
.ol-review-top {
    display: flex; gap: 18px; align-items: flex-start;
    margin-bottom: 14px;
    padding: 12px;
    background: var(--overlay-hover);
    border-radius: 10px;
}
.ol-review-cover {
    width: 260px; height: 260px; border-radius: 12px;
    background-size: cover; background-position: center;
    flex-shrink: 0;
    box-shadow: var(--shadow-lg);
    background-color: var(--bg-card);
}
img.ol-review-cover { object-fit: cover; display: block; }
img.ol-review-cover.ol-review-cover-broken {
    /* When even the remote URL fails, swap to the empty placeholder look
       in place so the user sees "no cover" instead of a broken-image
       glyph. The image element stays in the DOM so width/height are
       preserved without a layout shift. */
    background: var(--overlay-hover);
    border: 1px dashed var(--overlay-active);
}
.ol-review-cover-empty {
    display: flex; align-items: center; justify-content: center;
    font-size: 13px; color: var(--text-muted, #999);
    background: var(--overlay-hover); border: 1px dashed var(--overlay-active);
}
.ol-review-top-body {
    flex: 1; min-width: 0;
    display: flex; flex-direction: column; gap: 8px; justify-content: center;
}

/* The cover-include checkbox + "Set as cover now" button live next to the
   big preview. Stack them vertically when the user is short on width;
   the action button gets the accent so it's discoverable. */
.ol-cover-actions {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    flex: 1 1 auto;
    min-width: 0;
    justify-content: center;
}
.ol-cover-row {
    display: flex; gap: 8px; align-items: center;
    font-size: 13px; color: var(--text-secondary, #ccc);
    cursor: pointer;
}
.ol-cover-filename {
    color: var(--text-muted);
    font-size: 12px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 240px;
}
.ol-set-cover-btn {
    /* Accent treatment on top of .ol-btn-primary for visual emphasis —
       this is the explicit "just grab the cover" path. */
    align-self: flex-start;
}

.ol-album-grid { display: flex; flex-direction: column; gap: 4px; }
.ol-album-field {
    display: grid;
    grid-template-columns: 120px 1fr 16px 1fr 28px;
    gap: 10px;
    align-items: center;
    padding: 6px 10px;
    border-radius: 6px;
    background: var(--overlay-hover);
    font-size: 13px;
}
.ol-album-field.ol-diff { background: rgba(var(--state-warning-rgb), 0.08); }
.ol-field-label { font-weight: 500; color: var(--text-muted, #999); font-size: 12px; }
.ol-field-cur { color: var(--text-muted, #888); }
.ol-field-new { color: var(--text-color, #eee); }
.ol-field-arrow { color: var(--text-muted, #666); text-align: center; }
.ol-field-apply { display: flex; justify-content: center; }

/* Track table — scrollable on BOTH axes so even wide diffs (many fields ×
   many tracks) stay contained within the review panel. Sticky header row
   AND a sticky first column so the user can always see track-number +
   current-title anchors while scrolling through the proposed fields. */
.ol-track-table-wrap {
    overflow: auto;
    border-radius: 8px;
    background: var(--overlay-active);
    border: 1px solid var(--overlay-selected);
    /* On 720p, 50vh is ~360px — too cramped to see more than 4-5 rows.
       Use min-height to guarantee a usable rows count regardless of
       resolution; the upper cap keeps the table from dominating 4K
       screens. The modal-body still scrolls if everything overflows. */
    max-height: min(55vh, 720px);
    min-height: 240px;
    max-width: 100%;
    position: relative;
}
.ol-track-table {
    border-collapse: separate;
    border-spacing: 0;
    font-size: 12px;
    color: var(--text-color, #eee);
}
.ol-track-table th, .ol-track-table td {
    padding: 8px 10px;
    text-align: left;
    border-bottom: 1px solid var(--overlay-selected);
    vertical-align: top;
    min-width: 140px;
    max-width: 260px;
}
.ol-track-table th {
    background: var(--surface-elevated);
    font-weight: 500;
    color: var(--text-muted, #999);
    text-transform: uppercase;
    font-size: 11px;
    letter-spacing: 0.04em;
    position: sticky;
    top: 0;
    z-index: 2;
    white-space: nowrap;
    border-bottom: 1px solid var(--border-strong);
}
/* Sticky first TWO columns — track # and current title. These are the
   anchors the user uses to navigate rows while they scroll right. */
.ol-track-table th.ol-col-num,
.ol-track-table td.ol-col-num {
    position: sticky;
    left: 0;
    z-index: 3;
    background: var(--surface-elevated);
    width: 56px;
    min-width: 56px;
    max-width: 56px;
}
.ol-track-table th.ol-col-cur,
.ol-track-table td.ol-col-cur-title {
    position: sticky;
    left: 56px;
    z-index: 3;
    background: var(--surface-elevated);
    min-width: 180px;
    max-width: 220px;
    box-shadow: var(--shadow-sm);
}
.ol-track-table th.ol-col-num { z-index: 4; }       /* header corners need to win over body stickies */
.ol-track-table th.ol-col-cur { z-index: 4; }
.ol-cell-cur-title { color: var(--text-muted, #aaa); font-size: 12px; }
.ol-cell-empty { color: var(--border-strong); }

.ol-track-cell {
    position: relative;
}
.ol-track-cell.ol-diff { background: rgba(var(--state-warning-rgb), 0.06); }
.ol-cell-new { color: var(--text-color, #eee); }
.ol-cell-cur { color: var(--text-muted, #888); font-size: 11px; font-style: italic; margin-top: 2px; }
.ol-track-cell label { display: block; margin-top: 4px; }

.ol-review input[type="checkbox"] {
    accent-color: var(--accent-color, #ff6b35);
    cursor: pointer;
}

/* --- Applied banner — staged-but-not-saved state ------------------ */
.ol-applied {
    display: flex; align-items: flex-start; gap: 12px;
    background: rgba(var(--state-success-rgb), 0.10);
    border-color: rgba(var(--state-success-rgb), 0.35);
    color: var(--text-color, #eee);
    font-size: 13px;
}
.ol-applied-icon {
    font-size: 22px; color: var(--state-success);
    line-height: 1.2;
}
.ol-applied-body { flex: 1; min-width: 0; }
.ol-applied-title {
    font-weight: 600;
    color: var(--text-color, #eee);
    font-size: 14px;
}
.ol-applied-sub {
    color: var(--text-muted, #ccc);
    font-size: 12px;
    margin-top: 2px;
    line-height: 1.4;
}
