/* =============================================================================
   PRODUCTS INDEX — big.dk-style scrollable listing
   ============================================================================= */

/* === ROOT CONTAINER === */

#bersama-products-root {
    width: 100%;
    min-height: 100vh;
    background: var(--color-bg);
    /* `clip`, NOT `hidden`: overflow-x hidden forces overflow-y to compute
       to `auto`, turning this div into a scroll container. The unscaled
       1686px scaler content overflows it vertically when a row expands, so
       the root grew its own second scrollbar next to the page one. `clip`
       clips without ever creating a scroll container. (`hidden` kept only
       as a fallback for browsers without `clip`.) */
    overflow-x: hidden;
    overflow-x: clip;
}

/* The scaler div is set to the design width; JS applies transform: scale().
   No grid-line background — the image-led rows carry the structure, and the
   big.dk projects index has no rules slicing across the imagery. */
#products-scaler {
    width: var(--design-width, 1686px);
    transform-origin: top left;
    padding-top: 16px;
}

/* Below the scaler threshold the JS clears its inline styles and responsive
   CSS takes over; this fallback also keeps the layout sane if JS never runs. */
@media (max-width: 1023px) {
    #products-scaler {
        width: 100%;
        transform: none;
    }
}

/* === ROW REVEAL ON SCROLL === */
/* Each .product-row fades + rises in as it enters the viewport. products.js
   plays the fade+rise with WAAPI (not a CSS transition) and adds .is-revealed —
   deliberately NO `transition` on .product-row here, because the scroll squash
   (initScrollSquash) writes the row's transform every frame and a lingering
   transform transition would smear it. This rule is only the no-flash gate:
   rows start invisible until revealed. Gated on .reveal-on (set by the template
   before the rows paint) and wrapped in prefers-reduced-motion: no-preference so
   reduced-motion visitors never get the hidden state. The .reveal-fallback
   escape hatch (set by the template if products.js never runs) drops the rule so
   content can't get stuck invisible. */
@media (prefers-reduced-motion: no-preference) {
    #bersama-products-root.reveal-on:not(.reveal-fallback) .product-row:not(.is-revealed) {
        opacity: 0;
    }
}

/* === SEARCH FILTER === */
/* Rows whose title doesn't match the nav search query are removed from the
   flow (products.js toggles .is-search-hidden on input). */
.product-row.is-search-hidden {
    display: none;
}

/* === CATEGORY FILTER BAR === */

#products-filter-bar {
    display: flex;
    gap: 32px;
    padding: 20px 40px;
    border-bottom: 1px solid #e8e8e8;
    /* Keep the category filter on-screen while the index scrolls under it.
       Parks just below the fixed header (--sticky-offset). Needs an opaque
       background so rows don't show through, and a z-index above the scaler
       (which has its own stacking context from transform: scale) but below the
       fixed header (99998). */
    position: sticky;
    top: var(--sticky-offset);
    z-index: 50;
    background: var(--color-bg);
}

/* Mobile: the 5 categories don't fit one line on a phone — without wrapping,
   MIXED-USE and RESIDENTIAL spilled off the right edge and were clipped by the
   root's overflow-x: clip. Allow wrapping and reclaim width via tighter
   padding/gap so every category stays on-screen. */
@media (max-width: 768px) {
    #products-filter-bar {
        flex-wrap: wrap;
        gap: 10px 18px;
        padding: 14px 16px;
    }
}

.products-filter__link {
    font-size: 11px;
    /* Category names show with their stored casing (set in Products →
       Product Categories); no forced uppercase. */
    letter-spacing: 0.08em;
    color: var(--color-muted);
    text-decoration: none;
    transition: color 0.2s ease;
}

.products-filter__link:hover,
.products-filter__link.is-active {
    color: var(--color-text);
}

/* === PRODUCT ROW — image-led, two-column ============================== */
/* Sizes in 1686px design units (rows live inside #products-scaler). Left
   label column hugs the media edge (right-aligned); right column carries one
   large landscape image. Rows separated by whitespace only — no dividers. */

.product-row {
    display: grid;
    /* Centered narrow media column flanked by flexible whitespace — the
       big.dk index keeps images small and lets the page breathe on both
       sides. Label sits in the left gutter, hugging the image edge. */
    grid-template-columns: minmax(0, 1fr) 540px minmax(0, 1fr);
    column-gap: 24px;
    align-items: start;          /* label block top-aligns with the image top */
    cursor: pointer;
    margin-bottom: 40px;
    /* containing block for the .product-morph-clone (row-local coordinates) */
    position: relative;
    /* reset article defaults */
    list-style: none;
}

/* Keyboard focus indicator */
.product-row:focus {
    outline: 2px solid var(--color-text);
    outline-offset: 4px;
}

.product-row:focus:not(:focus-visible) {
    outline: none;
}

/* --- Left label column (right-aligned: glyph, title, subtitle) --- */
.product-row__label {
    grid-column: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-end;       /* hug the media edge */
    text-align: right;
    gap: 4px;
}

/* Small square mark above the title, level with the image top.
   Sized at 72px per request — tune this single width/height pair to taste. */
.product-row__glyph {
    width: 72px;
    height: 72px;
    object-fit: cover;
    margin-bottom: 10px;
}

/* Quiet title — the image carries the row, but property names need to read
   clearly, so slightly larger than the big.dk product-object scale. */
.product-row__title {
    font-size: 20px;
    font-weight: 400;
    color: var(--color-text);
    margin: 0;
    line-height: 1.35;
    letter-spacing: 0;
}

/* Subtitle — ACF location field; rendered only when non-empty. */
.product-row__location {
    font-size: 11px;
    color: var(--color-muted);
    text-transform: uppercase;
    letter-spacing: 0.1em;
}

/* While the title glides between its index spot and the meta column (on expand /
   collapse, driven by flipLabel in products.js), lift it above the carousel so it
   stays visible mid-flight. */
.product-row__label.is-gliding {
    position: relative;
    z-index: 4;
}

/* --- Media column (fixed width, natural image height + hover overlay) ---
   Width is fixed by the grid; height follows each image's own proportions,
   as on big.dk (no forced aspect ratio — rows vary in height). */
.product-row__media {
    grid-column: 2;
    position: relative;
    overflow: hidden;
    background: #f0f0f0;
}

.product-row__feature {
    width: 100%;
    height: auto;
    display: block;
    /* No transform transition: products.js drives a per-frame parallax transform
       on this image (it slides within the overflow:hidden media frame as the row
       scrolls); a transition here would smear that motion. */
}

/* Placeholder has no intrinsic height — give it a landscape ratio. */
.product-row__feature--placeholder {
    width: 100%;
    aspect-ratio: 3 / 2;
    background: #e0e0e0;
}

/* Hover (desktop only): meta fade-in. (The image no longer zooms on hover — the
   feature's transform is owned by the scroll parallax in products.js.) Scoped
   above the mobile breakpoint so it never fires on touch. */
@media (min-width: 1025px) {
    .product-row:hover .product-row__overlay {
        opacity: 1;
    }
}

.product-row__overlay {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 24px;
    padding: 24px 28px;
    opacity: 0;
    transition: opacity 0.35s ease;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.55), rgba(0, 0, 0, 0));
    pointer-events: none;
}

.product-row__cat {
    font-size: 13px;
    color: #fff;
    text-transform: uppercase;
    letter-spacing: 0.1em;
}

.product-row__yr {
    font-size: 13px;
    color: #fff;
    font-variant-numeric: tabular-nums;
}

/* === EXPAND PANEL === */

/* Expanded row: hide the index feature image — the expand panel's large
   image REPLACES it, as on big.dk. Showing both stacked the same hero
   twice for one product. Collapsing restores it. */
.product-row.is-expanded .product-row__media {
    display: none;
}

/* Three-column reflow mirroring the index row: metadata in the left gutter
   (right-aligned, under the title), the large image centered, and the
   description in the right gutter.
   The center column is 898px (was 1123px — decreased 20% per request; the 16:9
   ratio is held by .product-carousel's aspect-ratio, so width and height both
   drop 20%). It's still ~66% wider than the index row's 540px media so the
   carousel grows on expand, big.dk-style. The image morph measures both ends,
   so it animates 540 -> 898 automatically; the gutters (~370px each at the
   1686px design width) still hold the meta and description columns.
   `overflow: visible` keeps the morph clone from being clipped. */
.product-row__expand {
    display: none;
    grid-column: 1 / -1;
    grid-template-columns: minmax(0, 1fr) 898px minmax(0, 1fr);
    column-gap: 24px;
    align-items: start;
    padding: 8px 0 0;
    overflow: visible;
}

.product-row__expand.is-open {
    display: grid;
    /* No CSS entry animation here — products.js fades the panel in and
       animates the row height together, so the timing stays coordinated. */
}

/* While JS animates a row's height, clip the panel overflowing the
   shrinking/growing box so content unfolds instead of spilling out. */
.product-row.is-animating {
    overflow: hidden;
}

/* === IMAGE MORPH === */
/* big.dk's signature move: the index image and the expanded hero are the
   same picture in flight. products.js absolutely positions a clone in
   row-local design px, so it rides along with scroll and with rows
   collapsing above it. */

.product-morph-clone {
    position: absolute;
    object-fit: cover;
    z-index: 3;
    pointer-events: none;
    margin: 0;
}

/* While the clone flies, the real image at its destination stays hidden.
   Expanding: the clone lands on the carousel. Collapsing: it lands on the
   row's index image (which the mutate just brought back). */
.product-row.is-morphing .product-carousel {
    visibility: hidden;
}

.product-row.is-morphing:not(.is-expanded) .product-row__media {
    visibility: hidden;
}

/* Metadata → left gutter, right-aligned so it hugs the image edge like big.dk.
   grid-row: 1 on all three children is REQUIRED: the carousel comes first in
   the DOM, and grid auto-placement can't backtrack — without explicit rows,
   meta and desc were pushed to row 2, BELOW the image, leaving the gutters
   beside it empty. */
.product-row__expand .product-meta {
    grid-column: 1;
    grid-row: 1;
    align-items: flex-end;
    text-align: right;
}

/* Large image → center column, aligned under the row feature.
   NO position: sticky here — Chrome computes sticky offsets wrongly inside
   the transform: scale()'d #products-scaler, so the image drifted over rows
   below and the footer. The big.dk-style pinning will be done in JS
   (scale-aware) as part of the vertical-stack gallery work. */
.product-row__expand .product-carousel {
    grid-column: 2;
    grid-row: 1;
}

/* Description → right gutter, on the same row beside the carousel (big.dk-style
   body column). It's left-aligned, hugging the carousel's right edge; the
   max-width stops it stretching all the way to the page's right edge, so it no
   longer leans against the edge — leaving whitespace on the right that mirrors
   the metadata hugging the carousel on the left. Tune max-width for the gap. */
.product-row__desc {
    grid-column: 3;
    grid-row: 1;
    max-width: 300px;
    padding-top: 8px;
}

/* === PRODUCT CAROUSEL === */

.product-carousel {
    position: relative;
    overflow: hidden;
    aspect-ratio: 16 / 9;
    background: #111;
    /* Vertical page scrolling stays native; horizontal swipes drive the
       carousel drag (products.js). Without pan-y the browser eats horizontal
       swipes as scroll/back-gesture on touch. */
    touch-action: pan-y;
    cursor: grab;
}

.product-carousel.is-dragging {
    cursor: grabbing;
}

/* Slides are stacked absolutely so adjacent ones can slide horizontally past
   each other (big.dk-style push). Inactive slides sit at opacity 0 rather than
   visibility:hidden — visibility:visible on the active slide would override the
   morph's `.is-morphing .product-carousel { visibility: hidden }` and reveal the
   real hero behind the flying clone. products.js sets the transform/opacity of
   the two slides in motion via WAAPI; CSS only defines the resting state. */
.product-carousel__slide {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
}

.product-carousel__slide.is-active {
    opacity: 1;
}

.product-carousel__slide img,
.product-carousel__slide video {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    /* No native image drag: left-click-dragging a slide must NOT peel off a ghost
       copy. draggable="false" (template) covers Firefox; -webkit-user-drag covers
       WebKit/Blink. Pointer events (the swipe) are unaffected. */
    -webkit-user-drag: none;
    user-select: none;
}

/* Slide videos are play/pause only (products.js toggles on click). When the
   ACTIVE video is paused, show a minimal ▶ so the visitor can resume — scoped to
   .is-active so the non-active slide never shows it. pointer-events:none so the
   click still reaches the video. */
.product-carousel__slide.is-active.is-video-paused::before {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 64px;
    height: 64px;
    transform: translate(-50%, -50%);
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.45);
    pointer-events: none;
    z-index: 2;
}

.product-carousel__slide.is-active.is-video-paused::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    /* Play triangle, nudged right of centre so it reads as optically centred. */
    transform: translate(-40%, -50%);
    width: 0;
    height: 0;
    border-style: solid;
    border-width: 11px 0 11px 18px;
    border-color: transparent transparent transparent #fff;
    pointer-events: none;
    z-index: 3;
}

.product-carousel__prev,
.product-carousel__next {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(0, 0, 0, 0.35);
    color: #fff;
    border: none;
    padding: 8px 14px;
    cursor: pointer;
    z-index: 2;
    font-family: inherit; /* Syne, like the rest of the site (was the UA default) */
    font-size: 16px;
    line-height: 1;
    transition: background 0.2s ease, opacity 0.25s ease;
}

/* The arrows wait for the image morph to land (.is-morphing removed),
   then ease in over the settled hero. */
.product-row.is-morphing .product-carousel__prev,
.product-row.is-morphing .product-carousel__next {
    opacity: 0;
}

.product-carousel__prev:hover,
.product-carousel__next:hover {
    background: rgba(0, 0, 0, 0.6);
}

.product-carousel__prev {
    left: 12px;
}

.product-carousel__next {
    right: 12px;
}

/* === PRODUCT METADATA === */

.product-meta {
    display: flex;
    flex-direction: column;
    gap: 20px;
    padding-top: 8px;
}

.product-meta__item {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.product-meta__label {
    font-size: 11px;
    color: var(--color-muted);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.product-meta__value {
    font-size: 16px;
    color: var(--color-text);
}

.product-meta__description {
    font-size: 15px;
    color: var(--color-muted);
    line-height: 1.7;
}

.product-meta__description p {
    margin: 0 0 8px;
}

.product-meta__description p:last-child {
    margin-bottom: 0;
}

/* Publication PDF links — View (compressed) + Download (original), in a row
   below the description body. */
.product-meta__pdf-links {
    display: flex;
    flex-wrap: wrap;
    gap: 20px;
    margin-top: 16px;
}

/* Optional per-product shop link — sits just under the Publication PDF row.
   The link itself reuses .product-meta__pdf styling. */
.product-meta__shop-link {
    margin-top: 12px;
}

/* Shop link — text + a Shopify-style filled shopping bag. inline-flex centres the
   (larger) bag with the text; no underline (unlike the PDF link) so the solid icon
   reads cleanly. The bag inherits the Espresso colour via fill:currentColor. */
.product-meta__shop {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    border-bottom: 0;
    padding-bottom: 0;
}

.product-meta__shop:hover {
    opacity: 0.65;
}

.product-meta__shop-icon {
    flex: none;
    width: 1.3em;
    height: 1.3em;
}

.product-meta__pdf {
    font-size: 13px;
    color: var(--color-text);
    text-decoration: none;
    border-bottom: 1px solid currentColor;
    padding-bottom: 2px;
    transition: var(--transition-hover);
}

.product-meta__pdf:hover {
    color: var(--color-muted);
}

/* Publication PDF "Share" button — copies the viewed PDF's URL so visitors can
   pass it on. Styled to match .product-meta__pdf; it sits beside it in the same
   flex row (.product-meta__pdf-links, 20px gap). */
.product-meta__pdf-share {
    font-family: inherit; /* form controls must not fall back to the UA font */
    font-size: 13px;
    color: var(--color-text);
    background: none;
    border: 0;
    border-bottom: 1px solid currentColor;
    padding: 0 0 2px;
    cursor: pointer;
    transition: var(--transition-hover);
}

.product-meta__pdf-share:hover {
    color: var(--color-muted);
}

/* Confirmation state (set by main.js after a click): reads as a status, not a
   link — drop the underline and dim slightly. */
.product-meta__pdf-share.is-copied {
    border-bottom-color: transparent;
    opacity: 0.65;
    cursor: default;
}

/* === EMPTY STATE === */

.products-empty {
    color: var(--color-muted);
    font-size: 14px;
    padding: 60px 40px;
}

/* === RESPONSIVE === */
/* The JS scaler is disabled below 1024px, so these rules apply at true size. */

/* Tablet: single column — image on top, label below (left-aligned). */
@media (max-width: 1024px) {
    .product-row {
        grid-template-columns: 1fr;
        row-gap: 20px;
        margin-bottom: 64px;
        padding: 0 24px;
    }

    /* Single column — undo the desktop column placements. */
    .product-row__media  { order: 1; grid-column: 1; }
    .product-row__label  {
        order: 2;
        grid-column: 1;
        align-items: flex-start;
        text-align: left;
    }

    .product-row__expand {
        order: 3;
        grid-template-columns: 1fr;
        padding: 24px 0;
        gap: 24px;
    }

    /* Undo the desktop three-column placement — single column,
       left-aligned meta. Image first, then description, then metadata.
       grid-row back to auto so the blocks stack instead of sharing row 1. */
    .product-row__expand .product-carousel {
        grid-column: 1;
        grid-row: auto;
        order: 1;
    }
    .product-row__desc {
        grid-column: 1;
        grid-row: auto;
        order: 2;
        max-width: none;   /* undo the desktop cap — full-width single column */
        padding-top: 0;
    }
    .product-row__expand .product-meta {
        grid-column: 1;
        grid-row: auto;
        order: 3;
        align-items: flex-start;
        text-align: left;
    }

    .product-meta {
        flex-direction: row;
        flex-wrap: wrap;
        gap: 16px 32px;
    }

    .product-meta__item--description {
        flex-basis: 100%;
    }
}

/* Mobile */
@media (max-width: 768px) {
    /* === PRODUCT ROW — MOBILE === */
    /* Single column: image on top, label below (left-aligned). */
    .product-row {
        grid-template-columns: 1fr;
        row-gap: 16px;
        margin-bottom: 48px;
        padding: 12px 16px;
    }

    .product-row__media  { order: 1; grid-column: 1; }
    .product-row__label  {
        order: 2;
        grid-column: 1;
        /* Glyph + title share one horizontal line (was stacked). flex-wrap +
           the location's flex-basis:100% below drop the optional subtitle onto
           its own line under the title. */
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        text-align: left;
        gap: 8px;
    }
    /* 20% smaller than the 72px desktop glyph, and no bottom margin now that it
       sits inline beside the title rather than stacked above it. */
    .product-row__glyph {
        width: 57.6px;
        height: 57.6px;
        margin-bottom: 0;
    }
    /* Keep the (optional) location subtitle on its own line under the title. */
    .product-row__location {
        flex-basis: 100%;
    }
    .product-row__expand { order: 3; }

    /* No hover on touch — keep the category/year meta visible. */
    .product-row__overlay {
        opacity: 1;
    }

    /* Extra top padding on an open row so the fixed header (52px) doesn't
       overlap the title when the row is scrolled near the top of the screen. */
    .product-row.is-expanded {
        padding-top: 60px;
    }

    .product-row.is-expanded .product-row__label {
        position: relative;
        z-index: 1;
    }

    /* === EXPAND PANEL — MOBILE === */
    /* Constrain to the viewport and clip — without box-sizing + max-width
       the panel can inherit content wider than the screen and overflow. */
    .product-row__expand,
    .product-row__expand.is-open {
        grid-template-columns: 1fr;
        gap: 20px;
        width: 100%;
        max-width: 100vw;
        box-sizing: border-box;
        padding: 16px;
        overflow: hidden;
        word-wrap: break-word;
        overflow-wrap: break-word;
    }

    /* === CAROUSEL — MOBILE === */
    /* Keep .product-row__media the SAME size before/after the row is tapped open:
       (1) match the carousel to the index image's own aspect ratio (--carousel-ar,
       set per row in the template; 4:3 fallback) so the height doesn't jump, and
       (2) full-bleed it across the expand panel's 16px padding so its WIDTH matches
       the collapsed media too. */
    .product-carousel {
        aspect-ratio: var(--carousel-ar, 4 / 3);
        margin-left: -16px;
        margin-right: -16px;
    }

    /* === METADATA — MOBILE === */
    .product-meta {
        display: flex;
        flex-direction: column;
        /* must reset: the tablet block sets flex-wrap: wrap, which would
           let a height-constrained column push ABOUT into a second column
           off the right edge of the screen */
        flex-wrap: nowrap;
        gap: 16px;
        width: 100%;
        max-width: 100%;
        box-sizing: border-box;
    }

    .product-meta__item {
        display: flex;
        flex-direction: column;
        gap: 4px;
    }

    .product-meta__value {
        word-wrap: break-word;
        overflow-wrap: break-word;
    }

    .product-meta__description {
        width: 100%;
        max-width: 100%;
        overflow-wrap: break-word;
        word-wrap: break-word;
    }

    .product-meta__description p {
        margin: 0;
        max-width: 100%;
    }

    /* === GLOBAL MOBILE === */
    #bersama-products-root {
        overflow-x: hidden;
        overflow-x: clip;
    }
}
