/* music-discover.css — /search aggregator cards (.ms-
   discover-*), merged-card pill rows + downloadable
   actions, preview play overlay, filesystem sync banners
   + status row, install banner. */

/* ===================== /search aggregator =====================
   Renders inside .ms-view when state.view === 'search'. Search-only —
   every card links out to the source platform. Page chrome (`Search`
   h1 + muted subtitle + input) mirrors the Settings layout exactly:
   720px column max, left-anchored content, no centered hero. */
.ms-discover {
    /* Fills the full .ms-view width — the search input + results grid
       benefit from the extra horizontal real estate. Other pages
       (Settings, Playlists) still cap themselves at 720 internally.
       `flex: 1; min-height: 0` lets the empty-state child grow into
       the full .ms-view height so .ms-empty's `flex: 1; min-height:
       50vh` can center vertically the same way library/playlists do. */
    width: 100%;
    padding: 0.5rem 0 2rem;
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    gap: 1.25rem;
}
/* Head is a flex-row that puts the H1 inline with the search input.
   The filter chip strip lives in a separate child with flex-basis:
   100% so it always wraps to its own row beneath, regardless of
   width. On very narrow viewports the h1 + search row itself can
   also wrap (search drops below when there's no room next to it). */
.ms-discover-head {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.85rem;
}
.ms-discover-head h1 {
    margin: 0;
    font-size: 1.5rem;
    letter-spacing: 0.02em;
    flex-shrink: 0;
}
.ms-discover-head .ms-discover-search {
    flex: 1 1 240px;
    min-width: 0;
}
.ms-discover-head .ms-discover-filters {
    flex-basis: 100%;
}

/* (The in-page ".ms-discover-get-musiced" CTA used to live here, but
   the persistent topbar's #ms-topbar-get-musiced replaced it — the
   in-page <a> + its styles were removed. The topbar version is
   styled in music-topbar.css.) */

/* Search input — visually identical to the Library topbar input
   (.ms-search-wrap). Same padding, font-size, icon size, border,
   rounded-rectangle corners — only difference is that this one
   stretches to the full discover column instead of being capped at
   560px. Currently rendered hidden via the per-view rules above
   (the topbar's #ms-search-input drives /search now); kept in CSS
   for parity if the layout ever needs to fall back. */
.ms-discover-search {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    background: var(--panel-bg);
    border: 1px solid var(--panel-border);
    border-radius: 6px;
    padding: 0.5rem 1rem;
    transition: border-color var(--dur-fast) var(--ease-smooth),
                background var(--dur-fast) var(--ease-smooth);
}
.ms-discover-search:focus-within {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 4%, var(--panel-bg));
}
.ms-discover-search .icon { color: var(--fg-muted); }
.ms-discover-search input {
    flex: 1;
    background: transparent;
    border: none;
    color: var(--fg);
    font: inherit;
    font-size: var(--text-sm);
    outline: none;
}
.ms-discover-search input::placeholder { color: var(--fg-dim); }

/* Filter chip strip (All / YouTube / iTunes / …). Active chip is a
   solid-accent fill matching the .ms-mode-toggle active pattern;
   inactive chips are outlined-on-muted, so the row reads clearly
   even at a glance. 8px gap between chips. */
.ms-discover-filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.ms-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.9rem;
    /* Visible outline + subtle fill — the inactive chip has to read
       as a tappable control at a glance, not as plain text.
       --panel-border was too dim against --bg; --btn-border +
       --btn-bg gives clear contrast. 6 px corner radius matches every
       other interactive control. */
    border: 1px solid var(--btn-border);
    border-radius: 6px;
    background: var(--btn-bg);
    color: var(--fg-muted);
    font: inherit;
    font-size: var(--text-xs);
    line-height: 1;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease-smooth),
                border-color var(--dur-fast) var(--ease-smooth),
                background var(--dur-fast) var(--ease-smooth);
}
.ms-chip:hover {
    color: var(--fg);
    background: var(--btn-bg-hover);
    border-color: var(--accent);
}
.ms-chip[aria-pressed="true"] {
    color: var(--bg);
    background: var(--accent);
    border-color: var(--accent);
}
.ms-chip[aria-pressed="true"]:hover {
    color: var(--bg);
    background: color-mix(in srgb, var(--accent) 85%, var(--fg));
    border-color: color-mix(in srgb, var(--accent) 85%, var(--fg));
}

/* Results column — inherits the 720px cap from .ms-discover so
   groups + cards share the same left edge as the title and input.
   `flex: 1; min-height: 0` mirrors the parent so the empty-state
   card (.ms-empty) — which uses `flex: 1; min-height: 50vh` to
   center its content — gets a growable container to live in. When
   real results are present they still stack from the top because
   the children themselves don't grow. */
.ms-discover-results {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-height: 0;
    gap: 1.75rem;
}

/* "No results" state — shown when a query returns nothing across all
   active sources. Distinct from "no query yet" (that just shows an
   empty area below the input). */
.ms-discover-empty {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.25rem 0;
    color: var(--fg-muted);
}
.ms-discover-empty h2 {
    margin: 0;
    font-size: var(--text-xl);
    color: var(--fg);
    font-weight: 600;
}
.ms-discover-empty p { margin: 0; font-size: var(--text-sm); }

/* Loading skeletons — grid-shaped placeholders matching .ms-discover-card
   so layout doesn't shift when results arrive. */
.ms-discover-skeleton {
    height: 72px;
    border-radius: 8px;
    background: linear-gradient(
        90deg,
        var(--panel-bg) 0%,
        var(--row-hover) 50%,
        var(--panel-bg) 100%
    );
    background-size: 200% 100%;
    animation: ms-discover-skel 1.4s linear infinite;
    opacity: 0.6;
}
@keyframes ms-discover-skel {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

.ms-discover-error {
    color: var(--status-err);
    font-size: var(--text-sm);
    padding: 1rem 0;
}

/* A grouped block per source. Header is a small label; body is a card
   grid that wraps responsively. */
.ms-discover-group {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}
.ms-discover-group-head h3 {
    margin: 0;
    font-size: var(--text-sm);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--accent);
    font-weight: 600;
}
.ms-discover-source-error {
    color: var(--fg-muted);
    font-size: var(--text-sm);
    padding: 0.5rem 0;
}

.ms-discover-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
    gap: 0.75rem;
}

/* Single result card — clickable surface that opens the source URL in
   a new tab. The play button (preview clip) is a child that stops
   propagation so it doesn't double as "open link."
   `content-visibility: auto` lets the browser skip layout + paint for
   any card that's outside the viewport, which collapses the cost of
   long result grids during scroll from "every card paints every
   frame" to "only the on-screen rows paint." `contain-intrinsic-size`
   gives the browser a height hint so the scrollbar stays accurate
   while offscreen cards are skipped — the second value (280px) is a
   conservative estimate for thumb (≈ column width, square) + meta
   (≈ 36px) + padding (≈ 20px) at the typical 220-260px column. The
   browser overwrites it with the actual measured size after first
   paint (the `auto` keyword). */
.ms-discover-card {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding: 0.6rem;
    border: 1px solid var(--panel-border);
    border-radius: 8px;
    background: var(--panel-bg);
    cursor: pointer;
    content-visibility: auto;
    contain-intrinsic-size: auto 280px;
    transition: border-color var(--dur-fast) var(--ease-smooth),
                background var(--dur-fast) var(--ease-smooth),
                transform var(--dur-fast) var(--ease-smooth);
}
.ms-discover-card:hover {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 4%, var(--panel-bg));
    transform: translateY(-1px);
}
.ms-discover-card:focus { outline: none; }
.ms-discover-card:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

.ms-discover-thumb {
    position: relative;
    aspect-ratio: 1;
    border-radius: 6px;
    overflow: hidden;
    background: var(--bg-raised);
    display: flex;
    align-items: center;
    justify-content: center;
}
/* Fallback icon sits in normal flow, centered by the flex parent. The
   <img> overlays it absolutely when the thumbnail URL is provided +
   loads; if the image fails (.remove() in JS), the icon shows
   through. */
.ms-discover-thumb-fallback {
    color: var(--fg-dim);
    width: 36px;
    height: 36px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ms-discover-thumb img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    background: var(--bg-raised);
}

/* Preview play button — pinned to the thumb's bottom-right and
   always visible on cards that have a preview URL. Stops click
   propagation so opening the source link doesn't double-fire. */
.ms-discover-play {
    position: absolute;
    bottom: 0.4rem;
    right: 0.4rem;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--accent);
    color: var(--bg);
    border: none;
    border-radius: 50%;
    cursor: pointer;
    transition: filter var(--dur-fast) var(--ease-smooth);
    box-shadow: var(--shadow-sm);
}
.ms-discover-play:hover { filter: brightness(1.08); }

/* Download button — pinned to the TOP-right of the thumb on every
   downloadable card (YouTube / SoundCloud / Archive). Always visible:
   this is the path to full-song playback via Musiced Desktop, and
   hiding it behind hover meant users on touch never saw it and
   desktop users had to discover it by accident. Icon is the inline
   Lucide "download" glyph (not in our local sprite). Neutral chip
   styling — the card click still opens the source URL; this button
   hands the URL to Musiced via the lucyna:// deep link instead. */
.ms-discover-dl {
    position: absolute;
    top: 0.4rem;
    right: 0.4rem;
    width: 32px;
    height: 32px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* Solid (no transparency) background — backdrop-filter: blur(4px)
       was here originally for a frosted-glass look over the thumbnail,
       but on a grid with dozens of cards the GPU re-runs the blur on
       every scroll frame and the page judders. Solid --bg-raised reads
       fine against any thumbnail and scrolls at 60 fps. */
    background: var(--bg-raised);
    color: var(--fg);
    border: 1px solid var(--panel-border);
    border-radius: 6px;
    cursor: pointer;
    padding: 0;
    transition: background var(--dur-fast) var(--ease-smooth),
                border-color var(--dur-fast) var(--ease-smooth),
                color var(--dur-fast) var(--ease-smooth);
    box-shadow: var(--shadow-sm);
}
.ms-discover-dl:hover {
    background: var(--bg-raised);
    border-color: var(--accent);
    color: var(--accent);
}
.ms-discover-dl:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
.ms-discover-dl .icon {
    width: 16px;
    height: 16px;
}

/* Source badge — small label pinned to the thumb's top-left on the
   "All" tab so each card declares which platform it came from. Sits
   above the thumbnail image (z-stacking is implicit via DOM order) and
   stays clear of the download button (top-right) + preview play button
   (bottom-right). Non-interactive; the whole card is the click target,
   so swallowing pointer events here keeps hover/focus semantics on the
   card rather than the badge. */
.ms-discover-source-badge {
    position: absolute;
    top: 0.4rem;
    left: 0.4rem;
    max-width: calc(100% - 3.4rem);
    padding: 0.18rem 0.45rem;
    /* Solid background (no backdrop blur) — same reasoning as the
       download button above. On the "All" tab every card carries this
       badge, so the per-card blur cost compounded into severe scroll
       jank on long result lists. */
    background: var(--bg-raised);
    color: var(--fg);
    border: 1px solid var(--panel-border);
    border-radius: 4px;
    font-size: var(--text-xs);
    font-weight: 500;
    line-height: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    pointer-events: none;
    box-shadow: var(--shadow-sm);
}

/* Install banner. Pops up on every Download click — see the
   openMusicedDeepLink docstring in music.js for why the prior
   blur/focus detection was ripped out in favour of always-show.
   Position is fixed top-center so it's visible regardless of
   scroll inside .ms-view; z-index sits above the mini-player
   (z-50) and the music topbar but below page-wide modals (none
   currently). Attached to document.body so SPA nav doesn't destroy
   it mid-display. */
.lucyna-install-banner {
    position: fixed;
    top: 12px;
    left: 50%;
    transform: translateX(-50%);
    z-index: 200;
    width: min(440px, calc(100vw - 24px));
    background: var(--bg-elevated);
    color: var(--fg);
    border: 1px solid var(--panel-border);
    border-radius: 10px;
    padding: 14px 16px;
    box-shadow: 0 12px 32px color-mix(in srgb, var(--bg) 70%, transparent),
                0 0 0 1px color-mix(in srgb, var(--accent) 8%, transparent);
    animation: lucyna-install-banner-in 180ms var(--ease-smooth);
}
@keyframes lucyna-install-banner-in {
    from { opacity: 0; transform: translate(-50%, -8px); }
    to   { opacity: 1; transform: translate(-50%, 0); }
}
.lucyna-install-banner-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
}
.lucyna-install-banner-head h2 {
    font-size: 0.92rem;
    font-weight: 600;
    margin: 0;
    line-height: 1.35;
    color: var(--fg);
}
.lucyna-install-banner-close {
    background: transparent;
    border: none;
    color: var(--fg-muted);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 4px;
    margin: -4px -4px 0 0;
    transition: color var(--dur-fast) var(--ease-smooth);
}
.lucyna-install-banner-close:hover { color: var(--fg); }
.lucyna-install-banner-body {
    font-size: 0.8rem;
    color: var(--fg-muted);
    margin: 8px 0 12px;
    line-height: 1.5;
}
.lucyna-install-banner-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}
.lucyna-install-banner-btn {
    flex: 1;
    min-width: 0;
    padding: 7px 10px;
    background: var(--accent);
    color: var(--bg);
    border: 1px solid var(--accent);
    border-radius: 6px;
    font-size: 0.78rem;
    text-align: center;
    text-decoration: none;
    transition: filter var(--dur-fast) var(--ease-smooth);
}
.lucyna-install-banner-btn:hover {
    /* Re-declare `color: var(--bg)` so the global `a:hover { color:
       var(--accent) }` rule in site.css doesn't repaint the text to
       the same cyan as our background — that made the label
       disappear entirely on hover. Specificity of this selector
       (0,2,0) is higher than the global a:hover (0,1,1), but the
       cascade only falls through to `a:hover` when this rule doesn't
       set the property; without an explicit color here, the global
       rule wins by being the only one that declares it. */
    color: var(--bg);
    filter: brightness(1.08);
}
.lucyna-install-banner-btn:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}
/* Musiced filesystem sync UI. Two flavors sit in the same DOM slot
   at the top of the library view: the setup banner (asks the user
   to pick a folder via the File System Access API) and the status
   row (shows folder name + track count once configured). Styled to
   match the install banner's vocabulary but inline-flat — these
   live inside the library content flow, not as floating dialogs, so
   they don't get the fixed-position + box-shadow + max-width that
   the install banner uses.
   See buildSyncSetupBannerEl / buildSyncStatusEl in music.js. */
.lucyna-sync-banner {
    border: 1px solid var(--panel-border);
    background: var(--panel-bg);
    border-radius: 8px;
    padding: 14px 16px;
    margin: 0 0 14px;
}
.lucyna-sync-banner-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
}
.lucyna-sync-banner-head h2 {
    font-size: 0.95rem;
    margin: 0;
    color: var(--fg);
    font-weight: 600;
}
.lucyna-sync-banner-close {
    background: transparent;
    border: none;
    color: var(--fg-muted);
    font-size: 1.3rem;
    line-height: 1;
    padding: 0 4px;
    margin: -4px -4px 0 0;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease-smooth);
}
.lucyna-sync-banner-close:hover { color: var(--fg); }
.lucyna-sync-banner-body {
    font-size: 0.8rem;
    color: var(--fg-muted);
    margin: 6px 0 10px;
    line-height: 1.5;
}
.lucyna-sync-banner-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
    align-items: center;
}
.lucyna-sync-banner-btn {
    padding: 7px 14px;
    border-radius: 6px;
    font-size: 0.8rem;
    cursor: pointer;
    border: 1px solid var(--panel-border);
    background: var(--bg-raised);
    color: var(--fg);
    transition: filter var(--dur-fast) var(--ease-smooth),
                background var(--dur-fast) var(--ease-smooth),
                color var(--dur-fast) var(--ease-smooth);
}
.lucyna-sync-banner-btn:hover { filter: brightness(1.1); }
.lucyna-sync-banner-primary {
    background: var(--accent);
    color: var(--bg);
    border-color: var(--accent);
    font-weight: 600;
}
.lucyna-sync-banner-secondary {
    background: transparent;
    color: var(--fg-muted);
    border-color: transparent;
    text-decoration: underline;
}
.lucyna-sync-banner-secondary:hover { color: var(--fg); }
/* Unsupported-browser variant. Slightly muted so it reads as
   informational rather than a call-to-action. */
.lucyna-sync-banner-unsupported {
    border-style: dashed;
    background: transparent;
}

/* Status row — compact, single-line. Reuses the banner's frame but
   denser. The ✓ icon picks up the accent on success; ⚠ uses the
   theme's error/warn color via --color-warn (falls back to the
   accent if no warn var is defined, keeping the visual hierarchy
   intact). */
.lucyna-sync-status {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    margin: 0 0 12px;
    background: var(--bg-raised);
    border: 1px solid var(--panel-border);
    border-radius: 6px;
    font-size: 0.78rem;
    color: var(--fg-muted);
}
.lucyna-sync-status-icon {
    font-size: 0.95rem;
    line-height: 1;
}
.lucyna-sync-status-ok .lucyna-sync-status-icon { color: var(--accent); }
.lucyna-sync-status-warn .lucyna-sync-status-icon { color: var(--status-warn); }
.lucyna-sync-status-text {
    flex: 1;
    min-width: 0;
}
.lucyna-sync-status-text strong {
    color: var(--fg);
    font-weight: 600;
}
.lucyna-sync-status-action {
    padding: 4px 10px;
    border-radius: 4px;
    border: 1px solid var(--accent);
    background: var(--accent);
    color: var(--bg);
    font-size: 0.74rem;
    cursor: pointer;
    transition: filter var(--dur-fast) var(--ease-smooth);
}
.lucyna-sync-status-action:hover { filter: brightness(1.08); }

.ms-discover-meta {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}
.ms-discover-title {
    font-size: var(--text-sm);
    font-weight: 600;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.ms-discover-sub {
    font-size: var(--text-xs);
    color: var(--fg-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
