:root {
    --brand-900: #373636;
    --brand-700: #bebdbd;
    --brand-500: #f8e89c;
    --brand-100: #f8f2d4;
    --accent: #d97706;
    --accent-soft: #fff4df;
    --success: #1b7f5a;
    --text: #252526;
    --muted: #6b7280;
    --border: #c7c7c7;
    --card-bg: #ffffff;
    --header-bg: #eef1f5;
    --shadow: 0 18px 45px rgba(17, 24, 39, 0.08);
}

html, body {
    background: #f5f7fb;
    color: var(--text);
    font-family: "Inter", "Segoe UI", sans-serif;
}

.product-page {
    max-width: 1280px;
    margin: 0 auto;
    padding: 24px 16px 80px;
}

.product-header-inline {
    /* Three tracks so the centre block stays optically centred regardless of
       how wide the title or the back button happen to be. */
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: 12px;
    margin-bottom: 24px;
    padding: 14px 18px;
    background: var(--header-bg);
    border: 1px solid var(--border);
    border-radius: 14px;
}

.product-header-left {
    display: flex;
    align-items: center;
    justify-self: start;
    min-width: 0;
}

.product-header-center {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
    gap: 10px;
    justify-self: center;
}

.product-header-right {
    display: flex;
    align-items: center;
    justify-self: end;
}

/* Used as an <a> on the list page and as a <button> on the detail page, so it
   sets font-family/line-height explicitly — a button does not inherit the page
   font, and would otherwise render in the browser's default UI face. */
.product-back-link-inline {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 12px;
    border-radius: 8px;
    background: #fff;
    border: 1px solid var(--border);
    text-decoration: none;
    color: var(--brand-900);
    font-family: inherit;
    font-weight: 500;
    font-size: 0.9rem;
    line-height: 1.4;
    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
    cursor: pointer;
}

.product-back-link-inline:hover {
    background: var(--brand-100);
}

/* Detail page bar holding the back button. It had only a media-query rule and
   no base layout, so the row was never actually a flex container. */
.product-topbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 20px;
}

.product-topbar-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
}

.product-title-inline {
    margin: 0;
    font-weight: 700;
    font-size: 1.3rem;
    color: var(--brand-900);
    letter-spacing: -0.02em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.product-search-wrap-inline {
    position: relative;
    flex: 1 1 auto;
    min-width: 484px;
    max-width: 652px;
}

.product-search-wrap-inline input {
    width: 100%;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: #fff;
    /* Right padding leaves room for the clear button so typed text can't run
       underneath it. */
    padding: 10px 34px 10px 36px;
    font-size: 0.9rem;
    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.02);
}

.product-search-wrap-inline .search-ico {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--muted);
    font-size: 16px;
}

/* Revealed by JS only while the field has text. */
.search-clear {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    display: none;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: #e4e9f0;
    color: var(--muted);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.15s ease, color 0.15s ease;
}

.search-clear.is-visible {
    display: flex;
}

.search-clear:hover {
    background: var(--accent-soft);
    color: var(--accent);
}

.toggle-advanced {
    position: relative;
    border: 1px solid var(--border);
    background: #fff;
    color: var(--brand-900);
    border-radius: 10px;
    padding: 10px 12px;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.9rem;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
}

/* Advanced filters are active. The panel is collapsed by default, so without
   this there is nothing on screen explaining a reduced result count.

   A SOLID accent fill, not the pale accent-soft used for hover elsewhere: an
   active state has to be distinguishable from a mouse-over on the same button,
   and a filled control reads as "on" at a glance. */
.toggle-advanced.has-filters {
    background: var(--accent);
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
}

.toggle-advanced.has-filters:hover {
    filter: brightness(0.94);
}

/* Count of active fields, so the badge says how much is filtered, not just
   that something is. */
.advanced-count {
    position: absolute;
    top: -6px;
    right: -6px;
    min-width: 18px;
    height: 18px;
    padding: 0 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 999px;
    background: var(--brand-900);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1;
}



/* Narrow viewports: keep title/back on one row, drop the search block below.
   Raised from 900px to 1024px when the "In Stock Only" chip joined the centre
   block — search (484px) + toggle (40px) + chip no longer fit beside the title
   and back button below roughly 1024px.

   This block MUST stay after the .product-search-wrap-inline base rule above —
   a media query adds no specificity, so when it sat earlier in the file the
   later min-width:484px won and the search box overflowed small screens. */
@media (max-width: 1024px) {
    .product-header-inline {
        grid-template-columns: 1fr auto;
        grid-template-areas:
            "left right"
            "center center";
        row-gap: 12px;
    }

    .product-header-left {
        grid-area: left;
    }

    .product-header-right {
        grid-area: right;
    }

    .product-header-center {
        grid-area: center;
        justify-self: stretch;
    }

    .product-search-wrap-inline {
        min-width: 0;
        max-width: none;
    }
}

.product-advanced-panel {
    display: none;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 18px;
    box-shadow: var(--shadow);
    margin-bottom: 24px;
}

.product-advanced-panel.active {
    display: block;
}

.advanced-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 12px;
}

.advanced-grid input,
.advanced-grid select {
    width: 100%;
    border: 1px solid var(--border);
    background: #fff;
    border-radius: 10px;
    padding: 10px 12px;
    color: var(--text);
}

.advanced-actions {
    margin-top: 14px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    flex-wrap: wrap;
    gap: 10px;
}

/* margin-right:auto pushes the hint to the left edge while the buttons stay
   right-aligned, without needing a second wrapper element. */
.advanced-hint {
    margin-right: auto;
    color: var(--muted);
    font-size: 0.8rem;
}

.btn-primary,
.btn-secondary,
.btn-outline {
    border: none;
    border-radius: 10px;
    padding: 10px 14px;
    font-weight: 600;
    cursor: pointer;
}

.btn-primary {
    background: var(--brand-900);
    color: #fff;
}

.btn-secondary {
    background: var(--accent-soft);
    color: var(--accent);
}

.btn-outline {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--brand-900);
}

/* Whole chip is the label, so the text is a click target too, not just the box. */
.stock-toggle {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    flex: 0 0 auto;
    /* Height matches .toggle-advanced sitting next to it. */
    height: 40px;
    margin: 0;
    padding: 0 14px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: #fff;
    color: var(--brand-900);
    font-size: 0.85rem;
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    transition: all 0.2s ease;
}

.stock-toggle:hover {
    border-color: var(--accent);
    color: var(--accent);
}

.stock-toggle input {
    width: 16px;
    height: 16px;
    margin: 0;
    cursor: pointer;
    accent-color: var(--accent);
}

/* ---------------------------------------------------------------------------
   Clear button interaction states.

   The site theme ships Bootstrap's .btn-* palette, and its state rules are far
   more specific than our flat ones:

       .btn-secondary                                          -> 0,1,0  (ours)
       .btn-secondary:hover                                    -> 0,2,0
       .btn-secondary:not(:disabled):not(.disabled):active     -> 0,4,0

   so clicking Clear handed the button to the theme: dark grey fill, white text
   and a focus ring - and it stayed that way, because a clicked button keeps
   focus until something else takes it.

   Scoped to .btn-secondary inside the advanced panel only. Apply and the detail
   page buttons are deliberately left on their existing behaviour.
   --------------------------------------------------------------------------- */
.advanced-actions .btn-secondary,
.advanced-actions .btn-secondary:hover,
.advanced-actions .btn-secondary:focus,
.advanced-actions .btn-secondary:active {
    background: var(--accent-soft) !important;
    border-color: var(--accent-soft) !important;
    color: var(--accent) !important;
}

/* Suppress the theme's focus ring for pointer clicks only - keyboard users
   still get a visible ring below. */
.advanced-actions .btn-secondary:focus {
    outline: none !important;
    box-shadow: none !important;
}

.advanced-actions .btn-secondary:focus-visible {
    outline: 2px solid var(--accent) !important;
    outline-offset: 2px !important;
}

/* auto-fill, not auto-fit: auto-fit collapses the unused tracks, so a single
   search result would stretch across the whole row. auto-fill keeps the empty
   tracks in place, holding every card at one column width no matter how few
   results come back. */
.product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 20px;
}

.product-card {
    background: var(--card-bg);
    border: 1px solid var(--border);
    border-radius: 18px;
    overflow: hidden;
    box-shadow: 0 10px 28px rgba(15, 23, 42, 0.04);
    transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.product-card:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow);
}

/* An <a> wrapping the slides — display:block is required or the width/height
   below would not apply to an inline element. */
.product-card-image {
    display: block;
    position: relative;
    overflow: hidden;
    width: 100%;
    height: 220px;
    background: linear-gradient(135deg, #f6f8fa, #edf2f7);
    cursor: pointer;
}

.product-card-image img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Slides are stacked and pushed sideways rather than shown/hidden, so the card
   never flashes an empty box between images.

   Every idle slide parks off the right edge, the incoming one travels to 0 and
   the outgoing one carries on out to the left — so the strip always reads as
   moving right-to-left. .no-anim is how a spent slide is returned to the right
   without being seen crossing the card backwards. */
.product-card-image .card-slide {
    position: absolute;
    inset: 0;
    transform: translateX(100%);
    transition: transform 0.6s ease;
}

.product-card-image .card-slide.is-active {
    transform: translateX(0);
    z-index: 1;
}

.product-card-image .card-slide.is-leaving {
    transform: translateX(-100%);
    z-index: 1;
}

.product-card-image .card-slide.no-anim {
    transition: none;
}

/* Item code chip over the picture. Absolutely positioned and inside the fixed
   height image box, so it costs no layout: the card is exactly the size it was
   before. z-index clears the slides, which raise themselves while moving. */
.product-card-sku {
    position: absolute;
    top: 8px;
    right: 8px;
    z-index: 2;
    max-width: calc(100% - 16px);
    padding: 3px 7px;
    border-radius: 6px;
    background: rgba(15, 23, 42, 0.62);
    color: #fff;
    font-size: 0.68rem;
    font-weight: 700;
    line-height: 1.3;
    letter-spacing: 0.02em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    /* The whole picture is a link to the detail page - the chip must not eat
       the click. */
    pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
    .product-card-image .card-slide {
        transition: none;
    }
}

.product-card-body {
    padding: 10px 12px 12px;
}

/* Clamped to two lines rather than given a min-height: a min-height reserves
   the second line even for one-line names, which is where the dead space came
   from. The clamp caps tall names without padding short ones. */
.product-card-name {
    margin: 0 0 6px;
    font-size: 0.90rem;
    font-weight: 700;
    line-height: 1.35;
    color: var(--brand-900);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Item group: a quiet classification line under the name, deliberately smaller
   and lighter than the name so it reads as a label rather than competing. */
.product-card-group {
    margin: 0 0 6px;
    color: var(--muted);
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.3;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Price and stock share one row directly under the name. */
.product-card-meta {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
}

.product-card-price {
    margin: 0;
    color: var(--accent);
    font-weight: 800;
    font-size: 1rem;
    white-space: nowrap;
}

.product-card-stock {
    margin: 0;
    color: var(--muted);
    font-size: 0.82rem;
    white-space: nowrap;
}

.product-card-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    /* Tighter than the old 8px: three buttons now share a 240px card. */
    gap: 6px;
    margin-top: 10px;
}

/* Shared action-button appearance for the card footer (View / Save / Share) and
   for the detail page and popup action rows, so the two cannot drift apart.
   font-family is explicit because these render as both <button> and <a>, and a
   button does not inherit the page font. */
.card-detail-btn,
.card-watch-btn,
.card-share-btn,
.create-quotation-btn,
.detail-actions > button,
.detail-actions > a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: #e8e8e8;
    color: var(--brand-900);
    font-family: inherit;
    font-weight: 600;
    font-size: 0.85rem;
    line-height: 1.4;
    text-decoration: none;
    cursor: pointer;
    transition: all 0.2s ease;
}

/* Card footer: the three buttons split the row evenly, and min-width lets them
   shrink rather than the widest label forcing the row wider than the card. */
.card-detail-btn,
.card-watch-btn,
.card-share-btn {
    flex: 1;
    min-width: 0;
    padding: 6px 8px;
    white-space: nowrap;
}

/* Detail rows: size to the label instead, since these sit in a free-flowing
   row and carry longer text such as "View Full Page". */
.detail-actions > button,
.detail-actions > a {
    flex: 0 0 auto;
    padding: 8px 16px;
}

/* Hover highlight, and the saved state, declared together so the "Saved"
   button is highlighted with exactly the hover background the other buttons
   use — one rule, no chance of the two drifting apart. */
.card-detail-btn:hover,
.card-watch-btn:hover,
.card-share-btn:hover,
.create-quotation-btn:hover,
.detail-actions > button:hover,
.detail-actions > a:hover,
.btn-load-more:hover,
.card-watch-btn.is-saved,
.detail-actions [data-watch-item].is-saved {
    background: var(--accent-soft);
    border-color: var(--accent);
    color: var(--accent);
}

/* Share popover. Fixed positioning, placed by JS from the button's rect, so it
   escapes the card's overflow:hidden and stays inside the viewport. */
.share-menu {
    position: fixed;
    z-index: 2100;
    display: none;
    min-width: 190px;
    padding: 6px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 12px;
    box-shadow: var(--shadow);
}

.share-menu.is-open {
    display: block;
}

.share-menu a,
.share-menu button {
    display: flex;
    align-items: center;
    gap: 10px;
    width: 100%;
    padding: 8px 10px;
    border: none;
    border-radius: 8px;
    background: transparent;
    color: var(--text);
    font-size: 0.88rem;
    font-weight: 500;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}

.share-menu a:hover,
.share-menu button:hover {
    background: var(--brand-100);
}

.share-menu .share-ico {
    width: 18px;
    text-align: center;
    flex: 0 0 auto;
}

/* QR dialog. z-index sits above the product modal (2000) and the share menu
   (2100), since it can be opened from inside either. */
.qr-modal {
    position: fixed;
    inset: 0;
    z-index: 2200;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba(15, 23, 42, 0.6);
}

.qr-modal.is-open {
    display: flex;
}

.qr-modal-panel {
    position: relative;
    width: min(92vw, 400px);
    max-height: 90vh;
    overflow: auto;
    padding: 24px;
    background: #fff;
    border-radius: 18px;
    box-shadow: 0 25px 60px rgba(15, 23, 42, 0.28);
    text-align: center;
}

.qr-modal-close {
    position: absolute;
    top: 10px;
    right: 12px;
    width: 30px;
    height: 30px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: transparent;
    color: var(--muted);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}

.qr-modal-close:hover {
    background: var(--brand-100);
    color: var(--brand-900);
}

.qr-modal-title {
    margin: 0 0 6px;
    font-size: 0.8rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--muted);
}

.qr-modal-name {
    margin-bottom: 16px;
    font-weight: 700;
    color: var(--brand-900);
    overflow-wrap: anywhere;
}

.qr-modal-canvas {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 200px;
}

/* The canvas is drawn at an integer module scale; capping by width keeps it
   responsive without resampling it blurry on small screens. */
.qr-modal-canvas canvas {
    max-width: 100%;
    height: auto;
    border: 1px solid var(--border);
    border-radius: 12px;
}

.qr-modal-status {
    color: var(--muted);
    font-size: 0.9rem;
}

.qr-modal-url {
    margin: 14px 0 16px;
    font-size: 0.78rem;
    color: var(--muted);
    overflow-wrap: anywhere;
}

.qr-modal-actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 8px;
}

/* Full-screen image viewer. Above the product modal (2000), share menu (2100)
   and QR dialog (2200), since it opens from the carousel inside them. */
/* overflow:hidden because the zoomed image scales past the viewport and would
   otherwise add scrollbars to the page behind it. */
.image-lightbox {
    position: fixed;
    inset: 0;
    z-index: 2300;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 32px;
    overflow: hidden;
    background: rgba(9, 12, 18, 0.92);
}

.image-lightbox.is-open {
    display: flex;
}

/* contain, not cover: the whole picture has to be visible at full size. */
.image-lightbox-img {
    max-width: 100%;
    max-height: 100%;
    width: auto;
    height: auto;
    object-fit: contain;
    border-radius: 6px;
    cursor: zoom-in;
    transition: transform 0.25s ease;
}

/* Scaled about the cursor, which JS keeps updated via transform-origin. No
   transition while zoomed, or panning would lag a frame behind the pointer. */
.image-lightbox-img.is-zoomed {
    transform: scale(2.4);
    cursor: zoom-out;
    transition: none;
}

.image-lightbox-hint {
    position: absolute;
    z-index: 2;
    bottom: 18px;
    left: 50%;
    transform: translateX(-50%);
    padding: 6px 14px;
    border-radius: 20px;
    background: rgba(255, 255, 255, 0.14);
    color: rgba(255, 255, 255, 0.85);
    font-size: 0.8rem;
    pointer-events: none;
}

/* The hint has served its purpose once the image is magnified. */
.image-lightbox.is-zoomed .image-lightbox-hint {
    opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
    .image-lightbox-img {
        transition: none;
    }
}

/* Above the image: once scaled, it paints over this corner otherwise. */
.image-lightbox-close {
    position: absolute;
    z-index: 2;
    top: 18px;
    right: 22px;
    width: 44px;
    height: 44px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(255, 255, 255, 0.16);
    color: #fff;
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s ease;
}

.image-lightbox-close:hover {
    background: rgba(255, 255, 255, 0.32);
}

/* Signals that the picture opens full screen. */
.detail-carousel img {
    cursor: zoom-in;
}

/* Hover magnifier. The lens sits over the picture, below the carousel controls
   (which are z-index auto inside the same stacking context, so keep this low). */
.zoom-lens {
    position: absolute;
    top: 0;
    left: 0;
    display: none;
    border: 1px solid rgba(255, 255, 255, 0.85);
    background: rgba(255, 255, 255, 0.22);
    box-shadow: 0 0 0 9999px rgba(15, 23, 42, 0.18);
    pointer-events: none;
    z-index: 2;
}

.zoom-lens.is-visible {
    display: block;
}

/* Fixed, and positioned from the image's rect, so neither the image box's
   overflow:hidden nor the grid column can clip it. */
.zoom-panel {
    position: fixed;
    display: none;
    overflow: hidden;
    z-index: 1900;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 24px 55px rgba(15, 23, 42, 0.22);
    pointer-events: none;
}

.zoom-panel.is-visible {
    display: block;
}

/* object-fit:cover matches the crop of the source image, so the magnified view
   shows the same framing, only larger.

   max-width/max-height must be cleared: the site theme ships a global
   `img{max-width:100%;height:auto}`, which capped this image to the panel width
   and left the translated view showing blank panel. An inline width does not
   override a max-width, so it has to be reset here. */
.zoom-panel-img {
    position: absolute;
    top: 0;
    left: 0;
    max-width: none;
    max-height: none;
    min-width: 0;
    min-height: 0;
    object-fit: cover;
    will-change: transform;
}

@media (max-width: 640px) {
    .image-lightbox {
        padding: 16px;
    }

    .image-lightbox-close {
        top: 10px;
        right: 12px;
        width: 38px;
        height: 38px;
        font-size: 22px;
    }
}

/* Card action icons. Fixed box so the two buttons stay the same height
   regardless of each source PNG's aspect ratio. */
.card-btn-icon {
    display: block;
    width: 20px;
    height: 20px;
    object-fit: contain;
    opacity: 0.85;
    transition: opacity 0.2s ease, transform 0.2s ease;
}

.card-detail-btn:hover .card-btn-icon,
.card-watch-btn:hover .card-btn-icon {
    opacity: 1;
    transform: scale(1.08);
}

.card-watch-btn.is-saved .card-btn-icon {
    opacity: 1;
}

.product-detail-layout {
    display: grid;
    grid-template-columns: 1.2fr 0.8fr;
    gap: 28px;
    padding: 20px 0 36px;
}

/* Holds the carousel and, beneath it, the description block. align-self:start
   keeps it from stretching to match a taller spec panel. */
.detail-image-box {
    display: flex;
    flex-direction: column;
    align-self: start;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 22px;
    overflow: hidden;
    box-shadow: var(--shadow);
}

/* The box itself is unpadded so the carousel can bleed to its edges; the
   description needs its own padding and a rule to separate it. */
.detail-image-box .detail-section {
    margin-top: 0;
    padding: 18px 20px 20px;
    border-top: 1px solid var(--border);
}

/* Fixed height, because the images below are taken out of flow so they can be
   stacked and slid across; nothing is left to hold the box open. */
.detail-carousel {
    position: relative;
    overflow: hidden;
    background: linear-gradient(135deg, #f8fafc, #edf2f7);
    height: 460px;
}

/* Same right-to-left travel as the cards, but directional: .pos-right and
   .pos-left name which side a slide is parked on, so Prev can run the move the
   other way round instead of always pulling from the right. */
.detail-carousel img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transform: translateX(100%);
    transition: transform 0.6s ease;
}

.detail-carousel img.pos-right {
    transform: translateX(100%);
}

.detail-carousel img.pos-left {
    transform: translateX(-100%);
}

.detail-carousel img.is-active {
    transform: translateX(0);
    z-index: 1;
}

.detail-carousel img.no-anim {
    transition: none;
}

@media (prefers-reduced-motion: reduce) {
    .detail-carousel img {
        transition: none;
    }
}

.carousel-controls {
    position: absolute;
    inset: auto 16px 16px 16px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
}

.carousel-btn {
    border: none;
    background: rgba(17, 24, 39, 0.5);
    color: #fff;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    cursor: pointer;
    font-size: 20px;
}

.carousel-dots {
    display: flex;
    align-items: center;
    gap: 6px;
    background: rgba(15, 23, 42, 0.18);
    border-radius: 999px;
    padding: 6px 10px;
}

.carousel-dot {
    width: 8px;
    height: 8px;
    background: rgba(255,255,255,0.7);
    border-radius: 50%;
    display: inline-block;
}

.carousel-dot.active {
    background: white;
    width: 18px;
    border-radius: 99px;
}

.detail-panel {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 22px;
    padding: 20px 20px 16px;
    box-shadow: var(--shadow);
}

.detail-panel h2 {
    margin: 0 0 10px;
    font-size: clamp(1.8rem, 2vw, 2.4rem);
    color: var(--brand-900);
}

/* Body-sized, matching the price on the list cards. It was 1.8rem, nearly as
   large as the product title above it. Colour and weight are unchanged. */
.detail-price {
    font-size: 1rem;
    font-weight: 800;
    color: var(--accent);
    margin-bottom: 12px;
}

.detail-meta {
    display: flex;
    gap: 8px;
    flex-wrap: wrap;
    margin-bottom: 18px;
}

/* Filled with the same accent used for the hover/saved highlight, white text on
   top. The previous pale-on-pale pairing (--brand-100 behind --brand-700) sat
   at roughly 1.3:1 contrast, which is why the tags read as washed out. */
.detail-meta span {
    background: var(--accent);
    color: #fff;
    border-radius: 999px;
    padding: 6px 10px;
    font-size: 0.8rem;
    font-weight: 600;
}

.detail-actions {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 18px;
    flex-wrap: wrap;
}


.detail-section {
    margin-top: 18px;
}

.detail-section h3 {
    margin: 0 0 10px;
    font-size: 1rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--muted);
}

.detail-specs {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px 18px;
    padding-top: 10px;
}

.detail-spec {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 12px;
    border-radius: 12px;
    background: #f8fafc;
    border: 1px solid #edf2f7;
}

/* These are display captions, not form labels — a <label> with no associated
   control fails accessibility checks, so they are spans. */
.detail-spec .detail-spec-label {
    font-size: 0.7rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
}

.detail-spec span {
    color: var(--text);
    line-height: 1.5;
}

.detail-description {
    color: var(--text);
    line-height: 1.7;
}

/* Catalog loading overlay.

   pointer-events:none is deliberate — search runs as you type, so this appears
   on every query. Blocking input would make the field feel broken; the user has
   to stay able to keep typing and scrolling while results are fetched.

   z-index sits above the page but below the share menu (2100), QR dialog (2200)
   and lightbox (2300). */
.catalog-loading {
    position: fixed;
    inset: 0;
    z-index: 1800;
    display: none;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 10px;
    background: rgba(240, 242, 246, 0.82);
    pointer-events: none;
}

.catalog-loading.is-active {
    display: flex;
}

/* Fixed box: the source GIF is 475x480, far larger than it should render. */
.catalog-loading-img {
    width: 72px;
    height: 72px;
    object-fit: contain;
}

.catalog-loading-text {
    color: var(--muted);
    font-size: 0.9rem;
    font-weight: 600;
}

.empty-state {
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 18px;
    padding: 28px;
    text-align: center;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.04);
    color: var(--muted);
}

/* The "no results" message is injected into the grid, so it has to span every
   column — otherwise auto-fill confines it to a single 240px track. */
.product-grid .empty-state {
    grid-column: 1 / -1;
}

.watch-later-drawer {
    position: fixed;
    right: 20px;
    bottom: 90px;
    width: min(360px, calc(100vw - 24px));
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 18px;
    box-shadow: var(--shadow);
    display: none;
    z-index: 1500;
    overflow: hidden;
}

.watch-later-drawer.active {
    display: block;
}

.watch-later-header {
    padding: 16px 18px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.watch-later-list {
    max-height: 360px;
    overflow: auto;
    padding: 12px 14px 16px;
}

.watch-later-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    padding: 10px 0;
    border-bottom: 1px solid #eef2f7;
}

.watch-later-item:last-child {
    border-bottom: none;
}

/* min-width:0 lets a long product name wrap inside the row instead of pushing
   the Remove button out of the drawer. */
.watch-later-item-link {
    display: flex;
    align-items: center;
    gap: 10px;
    min-width: 0;
    color: inherit;
    text-decoration: none;
}

.watch-later-item-thumb {
    flex: 0 0 auto;
    width: 40px;
    height: 40px;
    border-radius: 8px;
    border: 1px solid var(--border);
    object-fit: cover;
    background: #f6f8fa;
}

.watch-later-item-text {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.watch-later-item-code {
    color: var(--brand-900);
    font-weight: 700;
    font-size: 0.85rem;
}

.watch-later-item-name {
    color: var(--muted);
    font-weight: 500;
    font-size: 0.82rem;
    line-height: 1.4;
    overflow-wrap: anywhere;
}

.watch-later-item-link:hover .watch-later-item-code {
    color: var(--accent);
}

.watch-later-item-link:hover .watch-later-item-name {
    color: var(--brand-900);
}

/* Action row at the bottom of the Saved Products drawer. Only rendered for a
   logged-in Desk user, so it is absent entirely for guests. */
.watch-later-footer {
    padding: 12px 14px 14px;
    border-top: 1px solid var(--border);
}

/* Same look as the card actions; only the drawer layout differs. */
.create-quotation-btn {
    width: 100%;
    padding: 9px 12px;
}

.create-quotation-btn:disabled {
    opacity: 0.6;
    cursor: default;
}

.watch-later-note {
    margin-top: 8px;
    font-size: 0.8rem;
    line-height: 1.4;
    color: var(--muted);
}

.watch-later-note.is-warn {
    color: var(--accent);
}

.watch-later-note.is-error {
    color: #b3261e;
}

.watch-later-item button {
    border: none;
    background: transparent;
    color: var(--muted);
    cursor: pointer;
    flex: 0 0 auto;
    white-space: nowrap;
}

/* Carries the same colours as an active card action button (the hover/saved
   state shared by View / Save / Share), so the floating control and the card
   controls read as one family. */
.floating-watch-later {
    position: fixed;
    right: 22px;
    bottom: 22px;
    z-index: 1400;
    border: 1px solid var(--accent);
    background: var(--accent-soft);
    color: var(--accent);
    border-radius: 50px;
    padding: 14px 18px;
    box-shadow: 0 18px 35px rgba(43, 68, 87, 0.28);
    font-weight: 700;
    cursor: pointer;
}

/* Sits directly above the Watch Later pill (22px + its ~50px height + gap)
   so the two fixed controls never overlap. */
.scroll-top-btn {
    position: fixed;
    right: 22px;
    bottom: 84px;
    z-index: 1400;
    width: 44px;
    height: 44px;
    display: none;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 50%;
    background: #fff;
    color: var(--brand-900);
    font-size: 1.15rem;
    line-height: 1;
    cursor: pointer;
    box-shadow: 0 10px 24px rgba(15, 23, 42, 0.16);
    transition: background 0.2s ease, transform 0.2s ease;
}

.scroll-top-btn.is-visible {
    display: flex;
}

.scroll-top-btn:hover {
    background: var(--brand-100);
    transform: translateY(-2px);
}

.floating-watch-later .count-badge {
    display: inline-flex;
    width: 24px;
    height: 24px;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    background: var(--accent);
    color: #fff;
    margin-left: 8px;
    font-size: 0.78rem;
}

@media (max-width: 860px) {
    .product-detail-layout {
        grid-template-columns: 1fr;
    }

    .detail-carousel {
        height: 360px;
    }

    .detail-specs {
        grid-template-columns: 1fr;
    }
}

@media (max-width: 640px) {
    .product-header-block,
    .product-topbar,
    .product-controls {
        flex-direction: column;
        align-items: stretch;
    }

    .product-page {
        padding-top: 20px;
    }

    .product-card-image {
        height: 200px;
    }

    .floating-watch-later {
        right: 14px;
        bottom: 14px;
    }

    .scroll-top-btn {
        right: 14px;
        bottom: 72px;
        width: 40px;
        height: 40px;
    }
}

.pagination-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    margin-top: 32px;
    padding: 20px;
    background: #fff;
    border: 1px solid var(--border);
    border-radius: 14px;
    box-shadow: 0 2px 8px rgba(15, 23, 42, 0.04);
}

.pagination-info {
    font-size: 0.95rem;
    color: var(--text);
    font-weight: 500;
}

.pagination-controls {
    display: flex;
    align-items: center;
    gap: 12px;
}

.items-per-page-select {
    border: 1px solid var(--border);
    background: #fff;
    border-radius: 8px;
    padding: 8px 12px;
    font-size: 0.9rem;
    color: var(--text);
    cursor: pointer;
}

.btn-load-more {
    border: 1px solid var(--border);
    background: #fff0e3;
    color: #000000;
    border-radius: 8px;
    padding: 10px 16px;
    font-weight: 600;
    cursor: pointer;
    font-size: 0.9rem;
    transition: all 0.2s ease;
}
/* 
.btn-load-more:hover {
    background: #ff8d21;
    border-color: #954800;
    transform: translateY(-1px);
} */

@media (max-width: 640px) {
    .pagination-footer {
        flex-direction: column;
        align-items: stretch;
    }

    .pagination-controls {
        flex-direction: column;
    }

    .items-per-page-select,
    .btn-load-more {
        width: 100%;
    }
}

/* --------------------------------------------------------------------------
   Small-screen refinements. Appended last on purpose: media queries add no
   specificity, so an override only wins if it comes after the rule it targets.
   -------------------------------------------------------------------------- */

@media (max-width: 860px) {
    .detail-carousel {
        height: 360px;
    }
}

@media (max-width: 640px) {
    .product-page {
        padding-left: 12px;
        padding-right: 12px;
    }

    .product-header-inline {
        padding: 12px;
    }

    /* Full row for the field; the toggle and In Stock chip wrap underneath. */
    .product-search-wrap-inline {
        flex: 1 1 100%;
    }

    .product-title-inline {
        font-size: 1.15rem;
    }

    .detail-carousel {
        height: 260px;
    }

    .detail-panel {
        padding: 16px 14px 14px;
    }

    /* clamp()'s floor is 1.8rem, which is oversized for a long product name on
       a phone. */
    .detail-panel h2 {
        font-size: 1.35rem;
    }

    .detail-image-box .detail-section {
        padding: 14px 16px 16px;
    }

    /* Line up with the floating button, which moves to right:14px here. */
    .watch-later-drawer {
        right: 14px;
        bottom: 78px;
    }

    /* "Showing 20 of 1,239 items" beside a select and a button does not fit on
       one row at phone widths — stack them and let the controls fill it. */
    .pagination-footer {
        flex-direction: column;
        align-items: stretch;
        gap: 12px;
        padding: 14px;
    }

    .pagination-controls {
        flex-direction: column;
        align-items: stretch;
    }

    .pagination-info {
        text-align: center;
    }

    .items-per-page-select,
    .btn-load-more {
        width: 100%;
    }

    /* Long unbroken product names must wrap rather than widen the card. */
    .product-card-name,
    .detail-panel h2 {
        overflow-wrap: anywhere;
    }
}
