/* ==========================================================================
   /search page — global-search experience over Meilisearch index.
   Two states the page can be in:
     1. .search-hero      — empty/initial state with big input + 2 cards
     2. .desktop-grid-wrapper — 3-col results layout (FilterPanel | grid | Hot Products)
   The 3-col grid is duplicated from browse.css (scoped under .page-browse)
   into .page-search below — intentional duplication per the round 1 plan
   (see [[global-search-on-browse]]). Defer DRY-ing into components.css
   until /search is proven.
   ========================================================================== */

/* Match /browse: no horizontal padding on the page wrapper.
   Inner sections (search-hero, ProductCard grid, etc.) own their own
   horizontal spacing where needed — see .search-hero margin/padding rules. */
.page-search { padding: 24px 0 80px; }

/* ── Page background image ─────────────────────────────────────────────────
   Drop the prepared assets into public/img/ with these EXACT names and the
   background lights up automatically — no other change needed:
     search-bg-desktop.avif      1920×1080   (1× desktop)
     search-bg-desktop@2x.avif   2560×1440+  (hi-DPI / Retina desktop)
     search-bg-mobile.avif       1080×1920   (portrait crop for phones)
   AVIF is chosen for size; add .webp twins + extra image-set() entries if you
   need broader fallback. The plain url() line is the fallback for engines
   without image-set(); image-set() picks the right DPR where supported.
   Tip: if you want the image to fill the viewport on the short hero state,
   add `min-height: 100vh` here too once the assets are in. */
.page-search {
    /* Plain white until the bg images are uploaded (then the image covers it).
       Was var(--bg-light) — the grey fallback read as a "colored" background. */
    background-color: #fff;
    /* COMMENTED OUT 2026-09-25 until the owner provides the images: the files
       are not in public/img/, so every /search load 404'd on them. Restore this
       block and the 640px rule below once they are uploaded.
    background-image: url("../../img/search-bg-desktop.avif");
    background-image: -webkit-image-set(
        url("../../img/search-bg-desktop.avif") 1x,
        url("../../img/search-bg-desktop@2x.avif") 2x);
    background-image: image-set(
        url("../../img/search-bg-desktop.avif") 1x,
        url("../../img/search-bg-desktop@2x.avif") 2x);
    background-repeat: no-repeat;
    background-position: center top;
    background-size: cover;
    background-attachment: fixed;   (covers the viewport behind short content)
    */
}
/* Restore with the images (see above).
@media (max-width: 640px) {
    .page-search {
        background-image: url("../../img/search-bg-mobile.avif");
        (fixed is janky/unsupported on mobile — let it scroll with content)
        background-attachment: scroll;
    }
}
*/

/* ── Mobile / tablet: stack the columns ──────────────────────────────────── */
@media (max-width: 1200px) {
    .page-search {
        padding-top: 30px !important;
        max-width: 100%;
        display: block;
    }
    .page-search .desktop-col-left,
    .page-search .desktop-col-right {
        display: none !important;
    }
}

/* ── Desktop ≥ 1201px: 3-col grid (matches /browse). Same column widths
       so the FilterPanel + HotProducts components render at their tuned
       sizes; same gap, same alignment. ────────────────────────────────── */
@media (min-width: 1201px) {
    .page-search {
        max-width: 1200px !important;
        margin: 0 auto;
        padding-top: 80px !important;
    }
    .page-search .desktop-grid-wrapper {
        display: grid !important;
        grid-template-columns: 250px 1fr 336px !important;
        gap: 20px;
        align-items: start;
        width: 100%;
    }
    .page-search .desktop-col-left,
    .page-search .desktop-col-right {
        display: block !important;
        background: var(--bg-light);
        border-radius: 12px;
        overflow-y: auto;
        overflow-x: hidden;
    }

    /* The left rail grows with its filters — no cap, no inner scroll. */
    .page-search .desktop-col-right {
        max-height: 1000px;
    }
    /* Mirror of browse.css: the HotProducts content needs its header sticky
       and the items list scrollable inside the column. */
    .page-search .desktop-col-right .hot-products-content {
        display: flex;
        flex-direction: column;
        height: 100%;
    }
    /* z-index 20: keep the sticky header above the card bookmark (z-index:10). */
    .page-search .desktop-col-right .component-header {
        flex-shrink: 0;
        position: sticky;
        top: 0;
        background: var(--bg-light);
        z-index: 20;
    }
    .page-search .desktop-col-right .items-wrapper {
        flex: 1;
        overflow-y: auto;
        padding: 10px;
    }
    .page-search .app-content {
        width: auto !important;
        max-width: 100% !important;
        margin: 0 !important;
        padding: 0 !important;
    }
}

/* ── Hero state ──────────────────────────────────────────────────────────── */
.search-hero {
    max-width: 800px;
    margin: 56px auto 0;
    text-align: center;
    /* Internal horizontal pad — was previously provided by .page-search's
       32px wrapper padding. Wrapper now matches /browse (0 padding) so the
       hero itself owns its breathing room. Invisible at >800px viewports
       (max-width caps it). */
    padding: 0 16px;
}

.search-hero-title {
    font-size: var(--fs-4xl);
    font-weight: 600;
    margin: 0 0 8px;
    color: var(--text-color);
}

.search-hero-sub {
    font-size: var(--fs-lg);
    color: var(--text-light);
    margin: 0 0 32px;
}

.search-hero-form { margin: 0 0 36px; }

/* Recent-search bubbles under the hero bar. LAYOUT ONLY — the bubbles
   themselves are the universal .chip / .chip-row register in components.css
   (white + border = "an item you can act on"), so nothing about their look is
   redefined here. This rule only centres the row on the centred hero, holds it
   to the search bar's own 640px width, and pulls it up into the form's 36px
   bottom margin so the chips read as belonging to the bar above them rather
   than to the cards below. */
/* A SINGLE-LINE drag-scroll track (10 bubbles since 2026-08-14), not a wrapping
   chip row. The count no longer costs vertical space: 10 phrases measure ~1713px
   and simply scroll, where the old wrapping row turned 8 chips into five stacked
   lines and pushed the Browse / Scan / Paste-a-URL cards below the fold — the
   reason the count had been cut to 3. On a phone this now costs LESS height than
   before, since three long phrases used to wrap to two lines.

   `width: max-content` + `margin-inline: auto` is what keeps a short history
   centred under the centred hero while letting a long one become a scroll
   container at the search bar's own 640px width.

   ⚠️ NOT `justify-content: center`. With overflow, centring pushes the leading
   chips into negative space that scrollLeft cannot reach — measured at 10 chips:
   the first chip sat 537px off the left edge, permanently unreachable, and the
   browser reported scrollWidth 1177 instead of 1713. */
.search-hero-recents {
    /* Page-specific spacing only — the row itself now lives in components.css
       because the homepage renders it too. This pull tucks it under the search
       hero on /search; the homepage sets its own. */
    margin: -22px auto 30px;
}


/* The bar itself (<search-field hero>, .search-hero-input-wrap) lives in
   components.css. The empty-state hero holds it to 640px, centred; the results
   bar fills its column. */
.search-hero-form .search-hero-input-wrap {
    max-width: 640px;
    margin: 0 auto;
}

/* ── Paste-a-URL spotlight ─────────────────────────────────────────────────
   Clicking the "Paste a product URL" card dims the page and lifts the hero
   search bar above the dim, so it's obvious the link goes into the MAIN bar
   (rather than opening a second, duplicate search box). Dismiss by clicking
   the dim, pressing Escape, or submitting. */
.search-spotlight-overlay {
    position: fixed;
    inset: 0;
    background: rgba(20, 20, 20, 0.55);
    z-index: 1000;
    animation: search-spotlight-fade 0.18s ease-out;
}
@keyframes search-spotlight-fade { from { opacity: 0; } to { opacity: 1; } }

/* Raise the bar (and its hint) above the dim. */
.search-hero-form.search-spotlight-lift {
    position: relative;
    z-index: 1001;
}
.search-spotlight-lift .search-hero-input-wrap {
    border-color: var(--pink-highlight);
    box-shadow: 0 8px 28px rgba(0, 0, 0, 0.28);
}

.search-spotlight-hint {
    display: flex;
    align-items: center;
    gap: 6px;
    width: fit-content;
    max-width: 640px;
    margin: 0 auto 10px;
    padding: 8px 14px;
    background: #fff;
    /* Match the search bar's corner radius (.search-hero-input-wrap: 12px). */
    border-radius: 12px;
    font-size: var(--fs-md);
    color: var(--text-color);
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.18);
}
@media (max-width: 640px) {
    /* Full width reads poorly on phones — keep the hint a compact ~60% pill
       (the text wraps inside it) centred over the bar. */
    .search-spotlight-hint {
        width: 60%;
        text-align: left;
    }
    /* Enlarge the hint's copy-plus icon on phones (16px → 32px, ~2×). CSS
       overrides the size attribute AppIcon sets; flex-shrink:0 stops the
       flex row from squishing it next to the wrapping text. */
    .search-spotlight-hint svg {
        width: 32px;
        height: 32px;
        flex-shrink: 0;
    }
}

/* ── The action cards under the hero input ───────────────────────────────
   Row 1: the full-width /browse promo (.search-card-wide spans both columns,
   and is an <a> — reset link styling so it matches the div cards).
   Row 2: Scan a barcode + Paste a URL side by side (stacked on mobile). */
.search-cards {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 16px;
    max-width: 640px;
    margin: 0 auto 24px;
}

@media (max-width: 600px) {
    .search-cards { grid-template-columns: 1fr; }
}

.search-card-wide { grid-column: 1 / -1; }
a.search-card { color: inherit; text-decoration: none; }

.search-card {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: 14px;
    padding: 18px 18px 22px;
    background: #fff;
    border: 1px solid var(--border-color);
    border-radius: 12px;
    text-align: left;
    /* Same elevation as .product-card (hairline + --shadow-sm). */
    box-shadow: var(--shadow-sm);
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.search-card:not(.search-card-disabled):hover {
    border-color: var(--pink-highlight);
    box-shadow: var(--shadow-sm), 0 2px 12px rgba(182, 3, 120, 0.08);
    cursor: pointer;
}

.search-card-disabled {
    opacity: 0.7;
    cursor: not-allowed;
}

.search-card-icon {
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(182, 3, 120, 0.08);
    border-radius: 10px;
}

.search-card-body { flex: 1; min-width: 0; }
.search-card-body h3 {
    margin: 0 0 4px;
    font-size: var(--fs-md);
    font-weight: 600;
    color: var(--text-color);
}
.search-card-body p {
    margin: 0;
    font-size: var(--fs-base);
    line-height: 1.4;
    color: var(--text-light);
}

.search-card-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    font-size: var(--fs-3xs);
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 3px 8px;
    background: var(--pink-highlight-pale);
    color: var(--pink-highlight);
    border-radius: 999px;
    font-weight: 600;
}

.search-hero-hint {
    margin-top: 28px;
    font-size: var(--fs-base);
    color: var(--text-light);
    text-align: center;
    font-style: italic;
}

/* The .search-field shell, the large <search-field hero> bar and the
   native-cancel hide for both live in components.css (the shared
   <search-field> section). */

/* Mirror of browse.css .browse-filter-row so the toolbar (view toggle, found
   badge, SortFilterBar) sits on its own row beneath the search input. The
   sort select + filter trigger styling is shared (components.css). */
.browse-filter-row {
    display: flex;
    gap: 8px;
    margin-top: 4px;
    margin-bottom: 16px;
    flex-wrap: nowrap;
}

/* Empty results moved to components.css as the shared .empty-state (2026-06). */

/* "No exact match — showing related products" banner above the relaxed grid. */
.search-relaxed-note {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 4px 0 14px;
    padding: 10px 14px;
    border-radius: 10px;
    /* Pale blue, not grey: this is an explainer panel -- it says WHY these
       rows are here -- and --blue-highlight-pale is the token every other
       explainer on the site already uses. Grey read as chrome. */
    background: var(--blue-highlight-pale);
    color: var(--text-light);
    font-size: var(--fs-md);
}
.search-relaxed-note strong { color: var(--text-color); }
/* An SVG with a width is still a shrinkable flex child. Measured 2026-08-26 at
   390px: the selection note's 24px mark was crushed to 8x24 beside a title and
   a two-clause subtitle. Applies to all three notes -- none of them wants a
   squeezed glyph, the 16px ones were just too small to show it. */
.search-relaxed-note > svg { flex-shrink: 0; }
/* Selection landing only: push its dismiss X to the far end, the same
   margin-left:auto .component-header gives the modal close. The other two
   notes carry no button, so this never fires for them. */
.search-relaxed-note .standard-close-btn { margin-left: auto; }

/* Search-by-URL result on the hero — sits between the search bar and the cards. */
.search-url-result {
    /* Match the search bar and the cards below it — both are max-width 640px
       centred in the 800px hero. Measured 2026-07-30: without the cap this
       notice rendered 768px wide against their 640px, so it read as a
       full-bleed slab that was wider than everything around it. The thumb is
       flex:0 0 auto and can't stretch the row; it was the missing measure. */
    max-width: 640px;
    margin: 0 auto 28px;
    padding: 18px 20px;
    text-align: center;
    background: var(--bg-light);
    border: 1px solid var(--border-color);
    border-radius: 10px;
}
.search-url-result p { margin: 0 0 12px; font-size: var(--fs-md); }
.search-url-result p:last-child { margin-bottom: 0; }

/* new_shop variant — scraped og:image thumb left of the copy. The parent is
   centred, so the CTA below this row keeps centring while the text beside the
   thumb reads left-aligned. Stays a row down to 360px. */
.search-url-newshop {
    display: flex;
    align-items: center;
    gap: 14px;
    margin-bottom: 12px;
    text-align: left;
}
/* min-width:0 lets the copy shrink below its longest word; anywhere breaks a
   pathological unbroken product title instead of widening the card. */
.search-url-newshop p { min-width: 0; overflow-wrap: anywhere; }
/* CONTAIN, not cover: an og:image is often 1200×630, and cropping a wide hero
   shot to a square shows a corner of it. Same reasoning as .product-img. */
.search-url-thumb {
    flex: 0 0 auto;
    width: 60px;
    height: 60px;
    object-fit: contain;
    background: var(--surface);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
}

/* .product-grid has no rule on /search — same as /mystuff and /browse.
   ProductCards/GroupCards are full-width block elements that stack one per
   row by default. (The previous flex-wrap rule made cards stretch full-width
   in the narrow middle column anyway, just with wrong gap semantics; the
   block-flow stacking ProductCard already does internally is what we want.) */

.loading-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 60px 20px;
    gap: 12px;
}
.loading-spinner {
    width: 32px;
    height: 32px;
    border: 3px solid var(--border-color);
    border-top-color: var(--pink-highlight);
    border-radius: 50%;
    animation: search-spin 0.8s linear infinite;
}
@keyframes search-spin { to { transform: rotate(360deg); } }

.load-more-container {
    display: flex;
    justify-content: center;
    margin: 24px 0 40px;
}

/* ── Barcode scan modal content ─ MOVED to components.css (2026-09-24): the
   homepage hero opens the same <barcode-scan-content> modal and never loads
   this sheet. Single source of truth lives with the component. */

/* ── Search-as-you-type dropdown (Phase 1) ─────────────────────────────────
   MOVED to components.css (the shared <search-field> section): the same
   .search-suggest-* dropdown now backs the homepage hero too, which never
   loads this sheet. Single source of truth lives with the component. */

/* ── Motion opt-out ──────────────────────────────────────────────────────
   Every animated rule in this file, behind the one query that asks. This
   sheet already carried the pattern for a handful of components; the rest
   were simply never enumerated. Listed selector by selector rather than as
   a `*` reset, because a universal reset only beats these rules with
   !important, which this project does not add.
   ──────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    .search-spotlight-overlay,
    .search-card {
        transition: none;
        animation: none;
    }

    /* Progress feedback, not decoration: a spinner that stops spinning
       stops saying "still working". Its transitions go; its spin stays. */
    .loading-spinner {
        transition: none;
    }
}

/* ── Narrow-your-search chips ─────────────────────────────────────────────
   Sits directly above the grid, in the same slot as .search-relaxed-note, so
   every "why these rows / how to get fewer" banner reads from one place. */
.search-refine {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    margin: 0 0 14px;
}

.search-refine-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

/* Reads as the section's heading, not as a caption under the results: at this
   size a 600 weight in --text-light sat quieter than the chips it introduces. */
.search-refine-label {
    color: var(--text-color);
    font-size: var(--fs-base);
    font-weight: 700;
}

.search-refine-chip { cursor: pointer; }

/* The magnifier says "this runs a search" rather than "this filters". It
   inherits currentColor, so the hover rule below tints it with the label. */
.search-refine-chip svg { flex: 0 0 auto; }

.search-refine-chip:hover {
    border-color: var(--pink-highlight);
    color: var(--pink-highlight);
}

/* The count is the whole point of the chip — it is what tells the shopper the
   phrase actually narrows. Muted so the phrase still leads. */
.search-refine-count {
    color: var(--text-light);
    font-size: var(--fs-xs);
    font-variant-numeric: tabular-nums;
}

.search-refine-chip:hover .search-refine-count { color: var(--pink-highlight); }

/* ── Lists rail (SearchListsRail + SearchListCard) ────────────────────────
   Public star lists + shop collections for the query, after the 3rd card.
   FORKED from the homepage rails on 2026-09-25 (owner): the carousel chrome
   (.search-lists-splide) and the tile (.search-list-card*) are this file's
   own copies of .home-rail-splide / .celeb-card + .list-card, so /search can
   be restyled without moving the homepage, /stars or the star cards. Values
   start as the originals; only the CTA differs (.btn-blue). */
.search-lists-rail { margin: 20px 0 24px; min-width: 0; }
.search-lists-title {
    font-size: var(--fs-xl);
    font-weight: 600;
    letter-spacing: -0.01em;
    line-height: 1.2;
    color: var(--text-color);
    margin: 0 0 12px;
}
/* One list: a lone tile at carousel-slide width, not stretched across the column. */
.search-lists-single { max-width: 200px; }

/* Carousel chrome — copy of the shared rail chrome (components.css): room for
   the tile's hover lift inside the track, and edge-fade arrows that hide when
   there is nothing more on that side. */
.search-lists-splide .splide__track { padding: 4px 0 8px; }
.search-lists-splide .splide__slide { height: auto; }
.search-lists-splide .splide__arrow {
    top: 0;
    height: 100%;
    width: 56px;
    transform: none;
    border-radius: 0;
    box-shadow: none;
    opacity: 1;
    display: flex;
    align-items: center;
    transition: opacity 0.2s ease;
}
.search-lists-splide .splide__arrow--prev {
    left: 0;
    justify-content: flex-start;
    padding-left: 2px;
    background: linear-gradient(90deg, var(--surface) 32%, rgba(255, 255, 255, 0) 100%);
}
.search-lists-splide .splide__arrow--next {
    right: 0;
    justify-content: flex-end;
    padding-right: 2px;
    background: linear-gradient(270deg, var(--surface) 32%, rgba(255, 255, 255, 0) 100%);
}
.search-lists-splide .splide__arrow svg {
    width: 1.5em;
    height: 1.5em;
    fill: var(--text-color);
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.18));
    transition: fill 0.15s ease;
}
@media (hover: hover) {
    .search-lists-splide .splide__arrow:hover svg { fill: var(--pink-highlight); }
}
.search-lists-splide .splide__arrow:disabled { opacity: 0; pointer-events: none; }

/* The tile — starts as .celeb-card + .list-card. */
.search-list-card {
    position: relative;             /* containing block for the whole-tile link */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    min-width: 0;
    height: 100%;                   /* equal heights across the slides */
    padding: 20px 12px 16px;
    text-align: center;
    background: #fff;
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-sm);
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
}
.search-list-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    border-color: var(--pink-highlight);
}
.search-list-card-link { position: absolute; inset: 0; z-index: 1; }

/* Hero: the list's first three products as overlapping 54px discs. */
.search-list-card-discs { display: inline-flex; align-items: center; flex-shrink: 0; }
.search-list-card-disc {
    width: 54px;
    height: 54px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--border-color);
    background: var(--grey-highlight-pale);
    flex-shrink: 0;
}
.search-list-card-disc + .search-list-card-disc { margin-left: -18px; }
.search-list-card-disc--empty { display: flex; align-items: center; justify-content: center; }

.search-list-card-name {
    margin-top: 10px;
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: var(--fs-md);
    font-weight: 700;
    line-height: 1.2;
    color: var(--text-color);
    text-decoration: none;
}
.search-list-card-name:hover { text-decoration: none; }   /* beats core.css a:hover */
.search-list-card-count { font-size: var(--fs-xs); color: var(--text-light); }

/* Owner line — a second link, lifted above the whole-tile link. */
.search-list-card-owner {
    position: relative;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 4px 8px;
    width: 100%;
    margin-top: 2px;
    padding: 8px 0 2px;
    border-top: 1px solid var(--border-color);
    font-size: var(--fs-sm);
    color: var(--text-light);
    text-decoration: none;
}
.search-list-card-owner-avatar {
    width: 28px;
    height: 28px;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid var(--border-color);
    background: var(--grey-highlight-pale);
    flex-shrink: 0;
}
/* A shop logo is a wordmark: fit it whole instead of cropping to the disc. */
.search-list-card-owner-avatar--contain { object-fit: contain; padding: 3px; background: var(--surface); }
.search-list-card-owner-name { line-height: 1.2; white-space: nowrap; }
@media (hover: hover) {
    .search-list-card-owner:hover { text-decoration: none; }
    .search-list-card-owner:hover .search-list-card-owner-name { color: var(--pink-highlight); }
}

/* CTA row pinned to the bottom so buttons line up across tiles. */
.search-list-card-actions {
    display: flex;
    flex-direction: column;
    width: 100%;
    margin-top: auto;
    padding-top: 8px;
}
.search-list-card-actions > * {
    position: relative;
    z-index: 2;
    width: 100%;
    min-width: 0;
    text-decoration: none;
}

@media (prefers-reduced-motion: reduce) {
    .search-lists-splide .splide__arrow,
    .search-lists-splide .splide__arrow svg,
    .search-list-card { transition: none; }
}
