/* ==========================================================================
   Docked preview pane  --  the product preview used by the standalone shopping
   surfaces: /shopping and the New Niguro feed's shopping rows.

   This is the same pane the search results page opens when you click a product,
   an image or a video: same markup shape, same classes (.serp-dock-*), same
   "More products" thumbnail wall, same "See more like this" pill, same
   full-screen sheet below 1024px. search-engine.js carries its own copy of
   these rules for the SERP; this file is the standalone one. They are kept in
   step by hand for now (see the note at the top of serp-preview-pane.js).

   Layout, and the one difference between the two surfaces:
     * /shopping -- the pane is a sibling of the products inside
       .niriv-pane-split, so they REFLOW to make room for it instead of being
       covered, the same relationship the SERP's rail has to its results
       column. It docks there whether the products are the results grid or the
       landing board's keyword columns.
     * the New Niguro feed -- the pane is appended to <body> and positioned
       against the edge of the screen. Nothing inside the feed is wrapped,
       moved or re-columned, so the feed is pixel-identical whether the panel is
       open or closed.
   ========================================================================== */

.serp-viewer { display: none; }
.niriv-pane-split { display: block; }

/* How wide the FEED's floating panel is: the size of the search results page's
   image preview, which is what that surface is judged against. 40vw is the same
   guard the SERP uses, so a small laptop gets a proportionally narrower panel
   instead of the fixed 430px taking over the page.

   The docked /shopping pane does NOT use this: it takes its width from a grid
   track, so it measures the same as a product column beside it (see
   .niriv-pane-split-grid below). */
@media (min-width: 1024px) {
    body.serp-viewer-open {
        --pane-w: min(430px, 40vw);
        /* How far the docked pane sits from the top of the viewport. */
        --pane-top: 20px;
    }

    /* Sticky needs a scrollport that actually scrolls -- see the .serp-viewer
       rule below for the docked pane.

       The page bundle sets `html, body { overflow-x: hidden }`. Half of that is
       harmless and half is not: with x hidden and y left visible, CSS makes the
       computed y `auto`, so BODY becomes a scroll container of its own. Sticky
       then resolves against that box instead of the viewport -- and body never
       scrolls, the root does -- so the pane does not pin, it just travels up and
       off the screen with the page. Measured: pane top 212px, 900px of scroll
       later -688px. Clip clips identically but is NOT a scroll container, which
       hands the pane back to the viewport. Only while the pane is up, and only
       on the docked surface: the feed's panel is position: fixed and needs none
       of this. */
    body.shopping-template.serp-viewer-open {
        overflow-x: clip;
        /* /shopping carries a FIXED 56px site header (header#site-header, z-index
           50). The pane stuck at 20px was therefore sitting behind it, and the
           only part of the panel it hid was the worst part to hide: the counter
           and the next / prev / close buttons across the top of the card. 56 plus
           a breath clears it, and the card's max-height follows this value so the
           panel still ends inside the viewport. */
        --pane-top: 68px;
    }

    /* ---- /shopping: a docked column, exactly like the SERP pane ----
       The products keep their own tracks and the panel takes one more, so the
       grid is never covered and its left edge never moves.

       THREE equal tracks, not "products + whatever is left": the panel has to be
       the same width as one card beside it, and a leftover track can only ever be
       a different width from them. The arithmetic that makes them come out equal:
       the products span the first two tracks and their own column gap is the same
       as the split's, so one product column measures exactly one track -- the very
       track the panel sits in. Change --pane-gap and the match breaks. */
    body.serp-viewer-open .niriv-pane-split-grid {
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        column-gap: var(--pane-gap, 12px);
        row-gap: 20px;
        align-items: start;
    }
    body.serp-viewer-open .niriv-pane-split-grid > :first-child {
        grid-column: 1 / 3;
        min-width: 0;
        margin-top: 0;
    }
    /* The landing board's own columns sit 8px apart (newniguro.css), not the
       results grid's 12px, so the split has to breathe at 8px for the same
       arithmetic to hold there. */
    body.serp-viewer-open .niriv-pane-split-board {
        --pane-gap: 8px;
    }
    body.serp-viewer-open .niriv-pane-split-grid > .serp-viewer {
        display: block;
        position: sticky;
        top: var(--pane-top);
        align-self: start;
        /* Same top edge as the first row of cards. */
        margin-top: 0;
    }
    /* TWO columns on the left. The grid has given the panel four hundred-odd
       pixels at the end of the row, and four product columns in what is left
       puts every card under the width of the panel beside it. */
    body.serp-viewer-open #shopping-results-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    }
    /* /shopping's LANDING board -- same two-column idea, against the theme's own
       board. The board is a grid of .nn-col stacks (3 across from 1024px); with
       the pane taking the end of the row it goes to two, and the third stack
       spans the pair underneath, which is exactly what the theme itself does
       between 900 and 1023px. Reusing that layout rather than inventing a new
       one keeps the landing recognisable while the pane is up. */
    body.serp-viewer-open .niriv-pane-split-grid > .shp-board {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
    body.serp-viewer-open .niriv-pane-split-grid > .shp-board > .nn-col:nth-child(3) {
        grid-column: 1 / -1;
    }

    /* ---- The scroll lives on the CARD, not on the pane ----
       Both the pane and its card are rounded, but only one of them can be the
       scroll box if the corners are to survive scrolling. Scrolling the pane
       slid the card's rounded frame out of view and left the pane's own square
       edge as the visible outline; scrolling the bordered, rounded card keeps
       its radius painted at every offset. Emptied on the pane itself so the
       card's max-height is the only ceiling. */
    body.serp-viewer-open .niriv-pane-split-grid > .serp-viewer,
    body.serp-viewer-open .serp-viewer-feed {
        max-height: none;
        overflow: visible;
    }
    body.serp-viewer-open .serp-dock-card {
        /* Top offset plus the same 20px at the foot, so the panel never runs off
           the bottom of a sticky position it cannot scroll out of. */
        max-height: calc(100vh - var(--pane-top) - 20px);
        overflow-y: auto;
        overscroll-behavior: contain;
        scrollbar-gutter: stable;
    }

    /* ---- New Niguro feed: a side panel off the right edge of the SCREEN ----
       The feed does not move at all -- and that is guaranteed structurally, not
       by rules: the script appends this pane to <body>, so no wrapper is
       inserted into the feed, no column is re-columned and no container is
       narrowed. It comes in over the top of the page from the screen's right
       edge and leaves the feed underneath exactly as it was. */
    body.serp-viewer-open .serp-viewer-feed {
        display: block;
        position: fixed;
        top: 20px;
        right: 20px;
        width: var(--pane-w);
        z-index: 900;
        animation: serpViewerSlideIn .22s ease-out both;
    }
}

@keyframes serpViewerSlideIn {
    from { transform: translateX(24px); opacity: 0; }
    to   { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    body.serp-viewer-open .serp-viewer { animation: none; }
}

/* Below 1024px there is no room for a second column: the pane is a full-screen
   sheet and the document behind it must not scroll. The lock goes on <html>,
   which is the document scroller -- `body { overflow: hidden }` would not stop
   it. */
@media (max-width: 1023px) {
    html.serp-viewer-lock { overflow: hidden !important; }
    body.serp-viewer-open { overflow: hidden !important; }
    body.serp-viewer-open .serp-viewer {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 10000;
        background: hsl(var(--background));
        overflow-y: auto;
        overscroll-behavior: contain;
        animation: serpViewerIn .2s ease-out;
    }
    /* Edge to edge: no card inside a sheet. */
    body.serp-viewer-open .serp-dock-card {
        border: 0;
        border-radius: 0;
        padding: 14px 16px 32px;
        min-height: 100%;
    }
    /* Keep the counter and the close button reachable while the sheet scrolls.
       The fill is the CARD surface, not the page surface, or it paints a grey
       band across the top of a white card. */
    body.serp-viewer-open .serp-dock-head {
        position: sticky;
        top: 0;
        z-index: 2;
        padding: 4px 0 8px;
        background: hsl(var(--card));
    }
    body.serp-viewer-open .serp-dock-media img { max-height: 40vh; }
}

@keyframes serpViewerIn {
    from { transform: translateX(18px); opacity: 0; }
    to   { transform: none; opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
    body.serp-viewer-open .serp-viewer { animation: none; }
}

/* ---- Pane chrome ------------------------------------------------------- */

.serp-dock-card {
    border: 1px solid hsl(var(--border));
    /* 16px, not the card token: this is the one surface on the page big enough
       that the tighter card radius reads as tight rather than crisp. */
    border-radius: 16px;
    background: hsl(var(--card));
    padding: 12px;
}
.serp-dock-head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px; margin-bottom: 10px;
}
.serp-dock-pos { font-size: 12px; font-weight: 600; color: hsl(var(--muted-foreground)); }
.serp-dock-nav { display: flex; align-items: center; gap: 4px; }
.serp-dock-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 32px; height: 32px; padding: 0;
    border-radius: 999px; border: 1px solid hsl(var(--border));
    background: hsl(var(--card)); color: hsl(var(--foreground));
    font-size: 16px; line-height: 1; cursor: pointer;
    transition: background .15s ease;
}
.serp-dock-btn:hover { background: hsl(var(--accent)); }
.serp-dock-media {
    display: flex; align-items: center; justify-content: center;
    min-height: 160px;
    border-radius: 12px; overflow: hidden; background: hsl(var(--muted));
}
.serp-dock-media img {
    display: block; width: 100%; height: auto; max-height: 46vh; object-fit: contain;
}
/* Product shots: on the card surface rather than the grey plate (a product image
   on grey reads as a broken image), padded so the item is not cropped. */
.serp-dock-media-shop {
    background: hsl(var(--card));
    border: 1px solid hsl(var(--border));
    padding: 10px;
}
.serp-dock-media-shop img { max-height: 40vh; }
.serp-dock-frame { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; }
.serp-dock-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.serp-dock-title {
    margin: 12px 2px 0; font-size: 15px; font-weight: 600; line-height: 1.4;
    color: hsl(var(--foreground));
}
.serp-dock-src { margin: 6px 2px 0; font-size: 12px; color: hsl(var(--muted-foreground)); }
.serp-dock-src a { color: hsl(var(--primary)); text-decoration: none; }
.serp-dock-src a:hover { text-decoration: underline; }
.serp-dock-actions { display: flex; flex-wrap: wrap; gap: 8px; margin: 12px 2px 4px; }
.serp-dock-visit {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 16px; border-radius: 999px;
    background: hsl(var(--primary)); color: hsl(var(--primary-foreground));
    font-size: 13px; font-weight: 600; text-decoration: none;
}
.serp-dock-visit:hover { opacity: .9; }
.serp-dock-related-label {
    margin: 16px 2px 8px; font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em;
    color: hsl(var(--muted-foreground));
}
/* The wall is a grid of thumbnails, not a list of rows -- the pictures are the
   point, and a wall lets you compare candidates at a glance. */
.serp-dock-list {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: 8px;
}
.serp-dock-item {
    display: block; width: 100%;
    padding: 0; margin: 0; text-align: left; font: inherit; color: inherit;
    border: 0; border-radius: 10px;
    background: transparent; cursor: pointer;
}
/* The pane is built out of <span>s so its tiles stay valid inside a <button>,
   which means the boxes they are styled as have to be stated explicitly. */
.serp-dock-thumb {
    display: block; width: 100%; aspect-ratio: 4 / 3;
    border: 2px solid transparent; border-radius: 10px;
    overflow: hidden; background: hsl(var(--muted));
}
.serp-dock-item-shop .serp-dock-thumb { aspect-ratio: 1 / 1; background: hsl(var(--card)); }
.serp-dock-item-video .serp-dock-thumb { aspect-ratio: 16 / 9; }
.serp-dock-thumb img {
    display: block; width: 100%; height: 100%; object-fit: cover;
    transition: transform .25s ease;
}
.serp-dock-item-shop .serp-dock-thumb img { object-fit: contain; }
.serp-dock-item:hover .serp-dock-thumb img { transform: scale(1.05); }
/* A transparent border at rest, so highlighting a tile never nudges the grid. */
.serp-dock-item.active .serp-dock-thumb { border-color: hsl(var(--primary)); }
.serp-dock-item-title {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
    margin: 6px 1px 0; font-size: 11.5px; line-height: 1.3; font-weight: 500;
    color: hsl(var(--muted-foreground));
}
.serp-dock-item.active .serp-dock-item-title { color: hsl(var(--foreground)); }
.serp-dock-more {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    margin: 12px 0 2px; padding: 9px 16px;
    border: 1px solid hsl(var(--border)); border-radius: 999px;
    background: hsl(var(--card)); color: hsl(var(--primary));
    font-size: 13px; font-weight: 600; text-decoration: none;
    transition: background .15s ease, border-color .15s ease;
}
.serp-dock-more:hover {
    background: hsl(var(--accent));
    border-color: hsl(var(--primary));
}

/* The wall is two across on a phone and six tiles deep, where the docked pane
   fits three or four across. Full width, the auto-fill track gave four skinny
   columns and a nine-tile wall that pushed the panel's own content off the
   first screen; two columns make each tile the size of a thumb you can actually
   judge. This block has to sit below the base .serp-dock-list rule rather than
   with the rest of the sheet layout above -- same specificity, so the last one
   in the file is the one that applies.

   The seventh tile onwards is hidden rather than dropped from the markup, so
   the same nine-item list serves both widths and a resize mid-view cannot leave
   the pane short. */
@media (max-width: 1023px) {
    .serp-dock-list {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 10px;
    }
    .serp-dock-list .serp-dock-item:nth-child(n + 7) { display: none; }
}
