/*
 * shop.css — Session 13
 * Shop and collection page styles. Enqueued on is_shop() + is_product_category().
 * Uses --silure- design tokens exclusively. No hardcoded colours. No border-radius.
 *
 * "was -0.03em, see file header" markers (Session 30 close-out, sitewide
 * letter-spacing sweep): this file predates the site's -0.01em heading
 * standard and still declared the old -0.03em value on a couple of
 * selectors. Live-confirmed neither actually renders at -0.03em today
 * (a later, more specific rule already wins for both), so this was
 * dead code, not a live bug -- corrected anyway rather than left as a
 * live landmine one future edit away from becoming visible again.
 *
 * "was Italiana, see file header" markers (Session 30 close-out,
 * Italiana removal sweep): CLAUDE.md permanently bans this font
 * ("Italiana removed — replaced by Mulish 700"). These two selectors
 * still declared it. Correction to the -0.03em note directly above --
 * .silure-shop-title is NOT dead, live-confirmed via a real fetch of
 * /shop/: the H1 there genuinely carries this exact class. Neither
 * property was ever actually rendering its declared value though (a
 * separate sitewide Elementor h1 rule already won for both font-family
 * and letter-spacing on the real page) -- corrected here so the
 * source itself is right, not just saved by a coincidental override
 * elsewhere staying in place.
 */

/* ─────────────────────────────────────────────────────────────────────
   PAGE WRAPPER
   ───────────────────────────────────────────────────────────────────── */

.silure-shop-wrap {
    max-width: 1440px;
    margin: 0 auto;
    padding: 0;
}

/* Strip WooCommerce's default breadcrumb margin on shop pages */
.woocommerce .woocommerce-breadcrumb {
    padding: 16px 80px 0;
    font-family: 'Mulish', sans-serif;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--silure-muted, #7A7672);
    margin: 0;
}

.woocommerce .woocommerce-breadcrumb a {
    color: inherit;
    text-decoration: none;
}

.woocommerce .woocommerce-breadcrumb a:hover {
    color: var(--silure-primary, #050505);
}

/* ─────────────────────────────────────────────────────────────────────
   BREADCRUMB (Session 97; font values corrected Session 101) — matches
   .silure-product-breadcrumb's real, RENDERED styling for sitewide
   visual consistency; kept as a separate rule rather than sharing that
   class so the two pages stay independently editable without cross-
   page risk.
   Session 101: confirmed via live getComputedStyle() that Session 97
   had copied product.css's own (Mulish/400/1.5px) copy of that rule —
   but product.css's copy is dead code, silently shadowed by
   silure-woocommerce.css's own later-loaded, equal-specificity
   .silure-product-breadcrumb rule (DM Mono/300/0.5px), which is what
   actually renders on every product page (silure-woocommerce.css
   always loads wherever product.css does — is_woocommerce() is a
   superset of is_product()). Corrected here to match what's real, and
   product.css's stale copy corrected too — see its own comment. */

/* Session 108 — shared-width wrapper so the breadcrumb and the H1 line
   up on their LEFT edge as one visually-centered unit, rather than each
   independently centering on its own (different) text width — see the
   full reasoning in archive-product.php's own comment on this markup.
   width: fit-content sizes this to whichever child is wider (almost
   always the breadcrumb); both children are plain block elements with
   no explicit width of their own, so they each stretch to fill this
   exact width, giving them identical getBoundingClientRect().left/
   .right regardless of how much shorter one's own text is. */
.silure-shop-heading-group {
    width: fit-content;
    /* Session 113 — real requirement: a bit more space above this whole
       section, between the site header and where it begins. Checked
       every real stylesheet first (Session 111's own lesson) — no
       competing rule anywhere touches this class or `.silure-shop-wrap`
       any more, so this margin-top is the only thing controlling that
       gap; nothing else to reconcile. Measured real baseline before
       this change: this element's own box already sat flush against
       the header (0px box-to-box), and the only existing whitespace —
       a real, measured 20px — came entirely from
       `.silure-shop-breadcrumb`'s own top padding (unchanged, still
       20px, still doing exactly what it did before). Adding 24px here
       stacks on top of that unchanged 20px, for a new real total of
       44px from the header to the breadcrumb's own visible text —
       "a little more," not a redesign of the existing spacing. Same
       0 auto horizontal centering as before — this is a top-margin-only
       change. Applies at every width uniformly (no breakpoint override
       needed) since nothing else redeclares this property anywhere. */
    margin: 24px auto 0;
}

.silure-shop-breadcrumb {
    display: flex;
    align-items: center;
    gap: 8px;
    /* Session 108 — explicit, not just the default: this sits inside
       .silure-shop-heading-group now, which centers the PAIR as a
       whole — each child staying left-aligned within that shared
       width is what makes their edges actually line up. */
    text-align: left;
    padding: 20px 80px 0;
    font-family: 'DM Mono', monospace;
    font-size: 10px;
    font-weight: 300;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: var(--silure-muted, #7A7672);
}

.silure-shop-breadcrumb a {
    /* Session 101: was `color: inherit` — harmless in isolation, but
       real live measurement showed it losing a same-specificity tie to
       Elementor's sitewide `.elementor-kit-5 a` default link color,
       rendering ink (#444) instead of the intended muted tone. Hard-
       coded explicitly, matching .silure-product-breadcrumb a's own
       already-explicit (and already-winning) color in
       silure-woocommerce.css, so this doesn't depend on cascade order
       against a global rule outside this file's control. */
    color: var(--silure-muted, #7A7672);
    text-decoration: none;
    transition: color 0.15s;
}

.silure-shop-breadcrumb a:hover {
    color: var(--silure-primary, #050505);
}

/* ─────────────────────────────────────────────────────────────────────
   PAGE HEADER
   ───────────────────────────────────────────────────────────────────── */

.silure-shop-header {
    /* Session 108 — see .silure-shop-heading-group's own comment: this
       stays left-aligned within that shared-width wrapper, which is
       what makes it line up with the breadcrumb above it. */
    text-align: left;
    /* Session 109 — top padding only (was 48px): this is the entire
       source of the visual gap between the breadcrumb above and the
       heading below (they're adjacent siblings with no margin of
       their own, so .silure-shop-breadcrumb's real bottom edge and
       .silure-shop-header's real top edge already touch at 0px — the
       48px lived entirely in this one value). Reduced to a real,
       measured 10px; same story at the two mobile breakpoints below,
       each has its own copy of this same top-padding value. */
    padding: 10px 80px 32px;
}

.silure-shop-title {
    font-family: 'Mulish', sans-serif; /* was Italiana, see file header */
    font-size: clamp(36px, 6vw, 72px);
    /* Session 112 — was 400: Session 111 corrected this to 400 after
       finding a stray, accidental font-weight:700 !important elsewhere
       silently overriding this rule (see that session's own entry).
       Manish has since seen the corrected 400 live and prefers it
       bolder as a genuine design choice — 700 here now is that
       deliberate choice, made in this, the correct/intended
       stylesheet, not a reversion of the old bug. */
    font-weight: 700;
    letter-spacing: -0.01em; /* was -0.03em, see file header */
    line-height: 0.88;
    text-transform: uppercase;
    color: var(--silure-primary, #050505);
    margin: 0;
}

.silure-shop-count {
    font-family: 'Mulish', sans-serif;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--silure-muted, #7A7672);
    white-space: nowrap;
}

/* ─────────────────────────────────────────────────────────────────────
   SINGLE FILTER ROW — tabs LEFT, count+sort+purity+price RIGHT
   Replaces the former separate .silure-cat-tabs row + .silure-filter-bar-wrap
   ───────────────────────────────────────────────────────────────────── */

/* Session 94 — real bug: this wrap had zero vertical padding, so the
   only thing separating the filter row from the product grid below was
   whatever incidental height the row's own tallest child happened to
   have — no deliberate gap at all, unlike the 44px `.silure-cat-tiles`
   already puts *above* this row (its own bottom margin). Matched to
   the exact same 44px value so both sides of this row use one
   consistent, deliberate gap rather than an inconsistent mix. Can't go
   on `.silure-products-grid` itself — that's pinned to `margin: 0
   !important` deliberately (full-bleed grid), so the gap has to come
   from this side. */
.silure-filter-bar-wrap {
    /* Session 136 — horizontal gutter moved onto .silure-filter-row
       itself (exact mockup spec: "row padding 20px 40px"); this wrap
       keeps only its own pre-existing, unrelated 44px bottom spacer
       (matching .silure-cat-tiles' own bottom margin, Session 94). */
    padding: 0 0 44px;
    background: var(--silure-bg, #FBF9F6);
}

/* Outer row. Session 93 — real, deliberately DIFFERENT structure between
   desktop and mobile, not just a reflow of the same one:
   - Desktop: ONE row — tabs left, sort/price/count group right,
     pushed to opposite ends via justify-content: space-between.
   - Mobile (≤768px, see the media query further down): TWO stacked
     rows — tabs centered on their own line, sort/price/count centered
     on their own line below. Both are genuinely separate layouts, not
     the same flex settings reflowing; each is set explicitly in its
     own block below rather than one trying to serve both. */
.silure-filter-row {
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
    flex-wrap: nowrap;
    /* Session 136 — exact mockup spec: "row padding: 20px 40px". */
    padding: 20px 40px;
}

/* LEFT (desktop): category tabs, left-aligned, gap between tabs instead
   of the old per-tab margin-right (Session 93 — matches the confirmed
   spec exactly: "display:flex, gap:32px between tabs"). Mobile override
   (width:100%, centered, tighter gap) lives in the media query below. */
.silure-cat-tabs {
    display: flex;
    align-items: center;
    gap: 32px;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}

.silure-cat-tabs::-webkit-scrollbar {
    display: none;
}

.silure-cat-tab {
    flex-shrink: 0;
    font-family: 'DM Mono', monospace;
    font-size: 10px;
    font-weight: 300;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--silure-muted, #7A7672);
    text-decoration: none;
    padding: 20px 0;
    border-bottom: 2px solid transparent;
    transition: color 0.2s ease, border-color 0.2s ease;
    white-space: nowrap;
}

.silure-cat-tab:hover {
    color: var(--silure-primary, #050505);
}

.silure-cat-tab.is-active {
    color: var(--silure-primary, #050505);
    border-bottom-color: var(--silure-primary, #050505);
}

/* ── LEFT group: Category, Price, Filters (Session 136 exact spec) ────
   "Gap between left-group items: 24px" — Category/Price/Filters, in
   that exact order, per the approved mockup. */
.silure-filter-left {
    display: flex;
    align-items: center;
    gap: 24px;
    min-width: 0;
}

.silure-jump-wrap {
    position: relative;
    flex-shrink: 0;
}

/* No border, no background — a plain muted text trigger. Session 136:
   "Font-size: 12px, letter-spacing: 0.5px — same as existing Sort/Price
   labels exactly" (was 10px/2px).
   Real bug, caught live via getComputedStyle() (not shipped assuming
   correct — traced to its actual source in the real, loaded CSS, not
   guessed): TWO separate sitewide rules target every `<button>`/
   `[type=button]` on this site, and this trigger never accounted for
   either. (1) hello-elementor/reset.css's own `[type=button],
   [type=submit],button{...font-size:1rem;padding:.5rem 1rem;
   display:inline-block;...}` loads AFTER shop.css and ties this
   trigger's own bare-class specificity (0,1,0 vs. its own [type=button]
   0,1,0) — a genuine tie, decided by load order, which reset.css wins.
   (2) silure-core.php's own deliberate, documented sitewide "KILL HELLO
   ELEMENTOR BUTTON PINK PERMANENTLY" rule forces `color: inherit
   !important` on every real button/[type=button] EXCEPT two explicitly
   named exclusions — nothing here is excluded, and no non-!important
   rule can ever beat an !important one regardless of specificity.
   Fix, matching how every OTHER control on this exact row already
   avoids this trap (Sort's "SORT:"/"NEWEST" spans, Price's own span):
   the button itself carries ONLY structural properties reset.css
   doesn't contest at a tie (display:flex beats inline-block outright —
   flex vs. inline-block isn't a value tie, it's a real distinct
   declaration each side makes, and flex is what's actually needed);
   real typography — including color — moves onto a real child element
   (.silure-jump-trigger-text / .silure-jump-icon below), which the
   !important rule never touches at all (it only ever selects
   button/[type=...] elements themselves, never their descendants). */
.silure-jump-wrap .silure-jump-trigger {
    display: flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
}

.silure-jump-trigger-text {
    font-family: 'DM Mono', monospace;
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: #8a8378;
    transition: color 0.2s ease;
}

.silure-jump-trigger:hover .silure-jump-trigger-text {
    color: var(--silure-primary, #050505);
}

/* Session 136 exact spec: 9x9px, viewBox "0 0 10 10", path
   "M1 3L5 7L9 3", stroke-width 1.3, no fill — stroke:currentColor on
   the SVG needs its OWN explicit `color` here (not inherited from the
   button, per the comment above) to resolve to the intended muted
   #8a8378 — verified live via getComputedStyle after this exact fix,
   not assumed. Replaces the old 24x24 chevron. */
.silure-jump-icon {
    width: 9px;
    height: 9px;
    flex-shrink: 0;
    color: #8a8378;
    transition: transform 0.2s ease, color 0.2s ease;
}

.silure-jump-trigger:hover .silure-jump-icon {
    color: var(--silure-primary, #050505);
}

.silure-jump-trigger[aria-expanded="true"] .silure-jump-icon {
    transform: rotate(180deg);
}

/* Filters trigger carries TWO icons — the same small chevron Category
   uses (desktop) and, mobile only, its own adjustments-lines icon (per
   the approved mockup's mobile-only spec below). Only one is ever
   visible at a time; see the ≤768px block further down for the swap.
   Session 138 — real bug, caught live via getComputedStyle(): this
   icon's own markup carries real width="15" height="15" attributes,
   but SVG presentation attributes lose to a CSS `width`/`height`
   declaration regardless of specificity — and the only CSS rule that
   was actually applying was the shared `.silure-jump-icon` base
   (9x9px, sized for the desktop chevron), which this class never
   overrode. Computed size was silently 9x9, not the real 15x15 this
   icon needs to match Sort's own (newly changed) icon exactly, per
   explicit instruction. Fixed by declaring the real size here. */
.silure-jump-icon--adjust {
    display: none;
    width: 15px;
    height: 15px;
}

/* Session 136 — Category's own standalone floating panel
   (.silure-jump-panel/.silure-jump-col*) is REMOVED outright, not just
   hidden: clicking Category now opens the Filters drawer below,
   scrolled to its own Category section, so this project no longer
   maintains two separate UI mechanisms for one concept. Confirmed no
   other file references these classes before removing the rules. */

/* ── Filters drawer content (Session 136, exact mockup spec) ──────────
   Explicit requirement, unchanged from the original build: no divider
   lines/underlines anywhere in this drawer's content, titles, or group
   headers — every separation below is spacing (margin/padding) only,
   never a border. Font-family split is deliberate and exact per the
   approved mockup: section titles are Mulish (matching every other
   drawer's own title convention — cart/search/wishlist), option rows
   stay DM Mono (matching SORT:/PRICE:/CATEGORY) — both confirmed live
   via getComputedStyle() after deploy, not just eyeballed. */
/* Real bug, caught live via getComputedStyle(): this is a real <h2>,
   and Elementor's own global Kit heading typography (em-based
   letter-spacing, recomputes per element font-size) was silently
   winning specifically on letter-spacing (computed -0.15px instead of
   the declared 1px) despite every OTHER property here correctly
   applying — !important here matches the exact same, already-
   established mechanism every other drawer's own title already uses
   in this file (.silure-wl-title/.silure-search-title/etc., see the
   SILURE TYPOGRAPHY SYSTEM block in header-footer-drawers.css) to
   beat this exact class of override, applied to every property here
   for consistency/robustness rather than leaving it fragile for just
   the one property that happened to lose this time.
   Session 138 — size AND weight brought in line with the real Account
   drawer's own title (.silure-acct-drawer-title), checked live via
   getComputedStyle(): 20px/700, not this drawer's own earlier
   15px/800. Letter-spacing (1px) and color (#050505) already matched
   Account's real values exactly, so those are untouched. */
.silure-filters-title {
    font-family: 'Mulish', sans-serif !important;
    font-weight: 700 !important;
    font-size: 20px !important;
    letter-spacing: 1px !important;
    text-transform: uppercase !important;
    color: var(--silure-primary, #050505) !important;
    margin: 0 !important;
}

/* Each section: padding 18px 0 — vertical spacing only, no divider
   lines between sections (exact mockup spec). Replaces the old
   margin-bottom approach; consecutive sections' own top+bottom padding
   is what creates the visible gap between them. */
.silure-filter-group {
    padding: 18px 0;
}

/* Native <details> marker removed in both engines — replaced by the
   plain chevron below, no default triangle, no border anywhere. */
.silure-filter-group summary {
    list-style: none;
    cursor: pointer;
}
.silure-filter-group summary::-webkit-details-marker {
    display: none;
}

/* Section title — exact mockup spec: Mulish 700, 12px, letter-spacing
   1px, ink, margin-bottom 12px (replaces the old DM Mono/padding
   approach; this is the "Category"/"Price"/"Availability"/etc. label
   itself, not the drawer's own H2 title above). */
.silure-filter-group__heading {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-family: 'Mulish', sans-serif;
    font-weight: 700;
    font-size: 12px;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--silure-primary, #050505);
    margin-bottom: 12px;
}

.silure-filter-group__heading::after {
    content: '';
    width: 8px;
    height: 8px;
    flex-shrink: 0;
    border-right: 1.5px solid var(--silure-muted, #7A7672);
    border-bottom: 1.5px solid var(--silure-muted, #7A7672);
    transform: rotate(45deg);
    transition: transform 0.2s ease;
    margin-left: 12px;
}

.silure-filter-group[open] > summary.silure-filter-group__heading::after {
    transform: rotate(-135deg);
}

/* No group-level gap/padding-top here any more — each option row now
   carries its own "padding: 6px 0" (below), and the heading's own
   margin-bottom:12px is the only spacing between title and options,
   avoiding double-spacing. */
.silure-filter-group__body {
    display: flex;
    flex-direction: column;
}

/* Each filter option — DM Mono, muted at rest, ink when its own radio
   is checked (no separate parent/child color distinction any more —
   indentation alone still marks a child row).
   Session 138 — font-size and letter-spacing brought in line with the
   real Account drawer's own body content (.silure-acct-drawer-text),
   checked live via getComputedStyle(): 13px/0.3px, not this drawer's
   own earlier 11px/unset (which had been silently inheriting an
   incidental 0.3px from elsewhere — now declared explicitly here so it
   can never drift). */
.silure-filter-option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
    font-family: 'DM Mono', monospace;
    font-size: 13px;
    font-weight: 300;
    letter-spacing: 0.3px;
    color: #8a8378;
    cursor: pointer;
    transition: color 0.15s ease;
}

.silure-filter-option:has(input:checked) {
    color: var(--silure-primary, #050505);
}

/* Custom radio — exact mockup spec: 14px circle, 1.3px solid muted
   border; checked state shows a solid ink dot inset ~3px from the
   circle's own edge. appearance:none + background-clip:content-box is
   the standard cross-browser technique for this (no native browser
   styling involved at any point, not just visually overridden).
   Real bug, caught live via getComputedStyle() — mobile-ux.css's own
   sitewide `input[type="radio"]{min-width:44px;min-height:44px}`
   (WCAG 2.1 SC 2.5.5 touch-target rule, deliberate and correct
   everywhere else) was silently winning over this rule's own
   `width/height:14px`: min-width/min-height are a real constraint on
   the resolved box size, not something a plain `width` declaration
   contests on its own — only explicitly re-declaring min-width/
   min-height here (at this selector's own higher 0,2,1 specificity,
   vs. the sitewide rule's 0,1,1) actually overrides it. Scoped to only
   this control, not a sitewide accessibility rollback: the real
   clickable/touchable area stays exactly as large as before, since
   the native <label> wrapping this input spans the ENTIRE option row
   (padding: 6px 0, full drawer width) — clicking anywhere in that row,
   not just the visual 14px dot, still activates the radio. */
.silure-filter-option input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    width: 14px;
    height: 14px;
    min-width: 14px;
    min-height: 14px;
    box-sizing: border-box;
    border-radius: 50%;
    border: 1.3px solid #8a8378;
    background-color: transparent;
    margin: 0;
    flex-shrink: 0;
    cursor: pointer;
}

.silure-filter-option input[type="radio"]:checked {
    padding: 1.7px; /* + the 1.3px border = ~3px total inset, per spec */
    background-color: var(--silure-primary, #050505);
    background-clip: content-box;
}

.silure-filter-option--child {
    padding-left: 24px;
}

.silure-filter-price-slider {
    position: relative;
    width: 100%;
    height: 14px;
    margin-bottom: 12px;
}

.silure-filter-price-values {
    display: flex;
    justify-content: space-between;
    font-family: 'DM Mono', monospace;
    font-size: 12px;
}

/* Apply button — centered, padding 14px, real live "VIEW [N] PRODUCTS".
   Real bug, caught live via getComputedStyle() — this is a real
   `<button type="button">`, subject to the exact same sitewide tie
   diagnosed on .silure-jump-trigger above: reset.css's own
   `[type=button],[type=submit],button{...}` (loads after shop.css,
   ties this bare class at equal 0,1,0 specificity, wins the tie —
   confirmed live as literally every one of its own properties:
   display:inline-block, padding:.5rem 1rem, font-size:1rem,
   background-color:transparent, border, text-align, all winning over
   this rule's own declarations) — scoped under its real parent
   (.silure-filters-footer, 0,2,0) beats that tie outright.
   Session 139 — color/background/border REMOVED from here entirely:
   this button is now a real member of silure-core.php's own sitewide
   "SITEWIDE BUTTON HOVER STANDARD" rule (the same mechanism every
   other primary CTA sitewide already uses — dark fill/white text base,
   light fill/dark text hover, both real `!important` values, not an
   opacity fade), which now supplies those properties completely,
   including the real, correct hover colors that were missing entirely
   under the old Session 138 approach (that only excluded this button
   from the KILL PINK rule without ever giving it a real replacement —
   real bug, real pink hover flash, found and fixed this session). This
   rule keeps only what that one doesn't touch: layout and typography.
   Font-size/weight/letter-spacing still match the real Account
   drawer's own primary button (.silure-acct-btn-primary): 13px/300/
   0.5px, checked live via getComputedStyle(). */
.silure-filters-footer .silure-filters-apply {
    display: block;
    width: 100%;
    padding: 14px;
    font-family: 'DM Mono', monospace;
    font-size: 13px;
    font-weight: 300;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    text-align: center;
    cursor: pointer;
}

.silure-products-grid {
    transition: opacity 0.2s ease;
}

/* RIGHT (desktop): sort, piece count — in that DOM order, piece count
   deliberately last/rightmost and least prominent. Session 136: gap
   matched to the left group's own 24px per the approved mockup (was
   28px); Price no longer lives in this group at all (moved left).
   `.silure-filters-form` stays `display: contents` so the <form>
   element (still needed for silure-filters.js's own FormData()/change-
   listener logic — completely untouched) doesn't add an extra box to
   this layout; its own two real children now are .silure-filter-left
   and .silure-filter-right themselves (Session 136 — see
   archive-product.php's own comment on this), so this contents-trick
   passes straight through an extra level with no effect on layout. */
.silure-filter-right {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 24px;
    flex-wrap: nowrap;
}

.silure-filters-form {
    display: contents;
}

/* Session 93 — shared label/value styling, applied identically across
   all three pairs (piece count, sort, price) per Manish's explicit
   flag that inconsistent weight/color between them was a real bug. */
.silure-label-muted {
    color: #8a8378;
    font-weight: 300;
}

.silure-label-ink {
    color: #050505;
    font-weight: 600;
}

/* Piece count */
.silure-filter-right .silure-shop-count {
    font-family: 'DM Mono', monospace;
    font-size: 12px;
    letter-spacing: 2px;
    text-transform: uppercase;
    white-space: nowrap;
}

.silure-filter-item {
    display: flex;
    align-items: center;
}

/* ── Sort — icon-led trigger (Session 92, confirmed "Option C") ──────── */

/* Session 99 used to pin this (and .silure-filter-item--price, below)
   to align-self:flex-start so their first lines shared one y position
   with each other, at the cost of both sitting ~13.5px above the row's
   true center (piece count was deliberately left centered instead, per
   that session's own scope). Session 121 — Manish's real, confirmed
   measurement + mockup: the category tabs on the left and this whole
   right-hand group need to share one true vertical CENTER as two
   groups, "regardless of each side's own internal element heights" —
   exactly what align-self:flex-start was preventing for 2 of these 3
   items. Removed here and on .silure-filter-item--price so all three
   (sort, price, count) inherit .silure-filter-right's own
   align-items:center consistently, matching piece count's
   already-correct behavior. Real, disclosed trade-off: SORT:'s and
   PRICE:'s own first lines no longer share a y position with each
   other the way Session 99 specifically arranged — that was solving a
   narrower, different complaint than this one, and the two goals are
   mutually exclusive here (price is a genuinely taller, two-row
   control; centering it against sort's one row can't also line up
   their tops). */
.silure-sort-control {
    position: relative;
}

/* Session 93 — real bug found while verifying text consistency: Hello
   Elementor's reset.css has `[type=button],[type=submit],button{
   font-size:1rem }`. The attribute selector `[type=button]` matches
   this element (it's a real `<button type="button">`) with the exact
   same specificity as a single class (0,1,0) — a tie that reset.css
   won purely on load order (it's enqueued after shop.css), computing
   to 16px instead of the 12px declared here. Scoping under the parent
   `.silure-sort-control` (0,2,0) beats the tie cleanly without needing
   `!important`. Confirmed via getComputedStyle() before and after. */
.silure-sort-control .silure-sort-trigger {
    display: flex;
    align-items: center;
    gap: 8px;
    background: none;
    border: none;
    padding: 0;
    margin: 0;
    cursor: pointer;
    font-family: 'DM Mono', monospace;
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.5px;
    color: #050505;
}

/* Session 138 — real bug found while investigating: Sort had NO hover
   treatment at all (no rule ever existed for it), while Category/
   Filters' own muted text/icon transition to ink on hover — a genuine,
   confirmed inconsistency across three otherwise-matching triggers on
   the same row, not a subjective feel. Fixed by giving Sort's OWN
   muted portion ("SORT:" and the icon — "NEWEST" stays ink regardless,
   since it's already showing the active/selected state, a third
   condition Category/Filters don't have) the exact same resting color,
   hover color, and 0.2s ease timing Category/Filters already use —
   scoped to this control specifically so it never leaks onto
   .silure-label-muted's other, unrelated sitewide uses (piece count). */
.silure-sort-control .silure-label-muted {
    transition: color 0.2s ease;
}

.silure-sort-control .silure-sort-trigger:hover .silure-label-muted,
.silure-sort-control .silure-sort-trigger:hover .silure-sort-icon {
    color: var(--silure-primary, #050505);
}

/* Icon color — explicit, resting muted (matches Category/Filters' own
   resting icon color exactly), stroke:currentColor in the markup now
   resolves from THIS, not a hardcoded "black" as before. */
.silure-sort-icon {
    width: 15px;
    height: 15px;
    flex-shrink: 0;
    color: #8a8378;
    transition: color 0.2s ease;
}

.silure-sort-menu {
    position: absolute;
    top: 100%;
    left: 50%;
    transform: translateX(-50%);
    margin-top: 12px;
    background: #FBF9F6;
    border: 1px solid var(--silure-border, #E8E4DF);
    list-style: none;
    padding: 4px 0;
    min-width: 190px;
    z-index: 20;
}

.silure-sort-menu[hidden] {
    display: none;
}

.silure-sort-menu li {
    padding: 10px 18px;
    font-family: 'DM Mono', monospace;
    font-size: 11px;
    font-weight: 300;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: #8a8378;
    white-space: nowrap;
    cursor: pointer;
    transition: color 0.15s ease, background 0.15s ease;
}

.silure-sort-menu li:hover,
.silure-sort-menu li[aria-selected="true"] {
    color: #050505;
    background: rgba(5, 5, 5, 0.04);
}

/* Native select stays in the DOM (real form field, real keyboard/
   assistive-tech access) but is visually hidden — the icon+text trigger
   above is the only thing sighted mouse/touch users see. */
.silure-visually-hidden-select {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* ── Price — dual-handle range slider (Session 92) ────────────────────── */

/* Session 138 — real bug, caught live via a screenshot showing this
   floating, visibly rendered near the piece count despite its real
   `hidden` attribute: the exact same specificity tie Session 134
   already diagnosed and fixed for `.silure-jump-panel[hidden]` —
   `.silure-filter-item`'s own `display:flex` (a single class, 0,1,0)
   ties the browser's default `[hidden]{display:none}` UA rule at
   equal specificity, and an author stylesheet loading after the UA
   sheet wins that tie regardless of which one "should" apply. This
   explicit, higher-specificity override is what actually keeps
   `hidden` authoritative, exactly like that established fix. */
.silure-filter-item--price[hidden] {
    display: none;
}

.silure-filter-item--price {
    position: relative;
    /* Session 126 — containing block for .silure-price-labels below,
       which is now pulled out of flow (position:absolute). Real bug
       found via direct measurement: Session 121's align-self:flex-start
       removal correctly centered THIS WHOLE BOX against
       .silure-filter-right's row (confirmed 0px diff) — but this box is
       the tallest item in that row (71px: 44px heading-row + 10px gap +
       17px price-labels), so centering the BOX still leaves its own
       "PRICE:" label sitting at the box's top, 13.5px above the row's
       true center, not at the center itself. .silure-sort-control has
       no such second row, so its own label lands at true center for
       free — the two were never actually held to the same standard.
       Removing price-labels from flow shrinks this box's own in-flow
       height to 44px, matching .silure-sort-control exactly, so both
       inherit .silure-filter-right's align-items:center identically.
       Verified via getBoundingClientRect(): .silure-price-heading now
       reaches 0.000px diff from the tabs/sort trigger, at 1440/1024/
       768/375px. */
    flex-direction: column;
    align-items: flex-start;
    /* Session 121 — align-self:flex-start removed; see
       .silure-sort-control's own comment above for the full reason. */
}

/* Session 99 — real bug: "PRICE" was stacked above the slider (the
   parent's own flex-direction:column applied to everything, heading
   included), inconsistent with "SORT:" sitting inline beside its own
   value in the control right next to this one. This row scopes just
   the label+slider pair to their own inline flex line — the min/max
   value labels below are untouched, still their own row beneath it.
   `min-height: 44px` matches the real cause of a residual ~14px text
   offset found while verifying: `.silure-sort-trigger` is a real
   `<button>`, which the sitewide WCAG 2.1 AA touch-target rule
   (mobile-ux.css) correctly gives `min-height: 44px` — its own
   icon+text content sits vertically centered *within* that 44px, not
   flush at its top, while this plain `<div>` had no matching height
   and centered within its own much shorter natural content height.
   Matching the same 44px here (a div, not a real control, so no
   accessibility implication either way) centers both by the same
   amount, aligning "SORT:" and "PRICE:" at the same text baseline
   instead of just aligning their outer boxes. */
.silure-price-heading-row {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 44px;
}

.silure-price-slider {
    position: relative;
    width: 220px;
    height: 14px;
}

.silure-price-track {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: 2px;
    background: #e5e0d8;
    transform: translateY(-50%);
}

.silure-price-track-fill {
    position: absolute;
    top: 50%;
    height: 2px;
    background: #050505;
    transform: translateY(-50%);
}

.silure-price-range {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 14px;
    margin: 0;
    background: transparent;
    -webkit-appearance: none;
    appearance: none;
    pointer-events: none; /* track itself is inert — only the thumb (below) is interactive */
}

.silure-price-range::-webkit-slider-runnable-track {
    background: transparent;
    border: none;
}

.silure-price-range::-moz-range-track {
    background: transparent;
    border: none;
}

.silure-price-range::-webkit-slider-thumb {
    -webkit-appearance: none;
    pointer-events: auto;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #050505;
    border: none;
    cursor: pointer;
}

.silure-price-range::-moz-range-thumb {
    pointer-events: auto;
    width: 14px;
    height: 14px;
    border-radius: 50%;
    background: #050505;
    border: none;
    cursor: pointer;
}

/* Max handle sits above min in stacking order — the more commonly
   dragged of the two, and in practice their clickable areas are only
   the 14px thumbs (pointer-events disabled on the track), so overlap
   only matters in the rare case both handles land on the same value. */
.silure-price-range--min {
    z-index: 2;
}

.silure-price-range--max {
    z-index: 3;
}

/* Session 100 — real bug, confirmed by measured coordinates, not
   assumed: this used to be `display:flex; justify-content:space-
   between`, which only ever coincidentally matched the handles when
   they sat at the exact 0%/100% extremes (the untouched default
   state) — dragging either handle moved it while the label stayed
   fixed at the track's own edge. Separately, Session 99's inline
   "PRICE:" repositioning shifted the slider itself to the right
   within its row, but this row was never given the same shift,
   leaving it a real, measured ~56px to the left of where the slider
   actually renders — present even at the default state. Both are
   fixed together in JS (silure-shop.js's price-slider render()), by
   positioning each label from the real, currently-measured handle
   center (getBoundingClientRect()) converted into this row's own
   local coordinate space — correct regardless of any offset between
   this row and the slider, and correct at any width (220px desktop,
   110px mobile) or handle position, not just the default one. A fixed
   height plus a non-static `position` (originally relative, now
   absolute — see Session 126's comment directly below) make this a
   plain positioning box for the two absolutely-positioned label spans
   below either way. */
.silure-price-labels {
    /* Session 126 — was position:relative + flex `gap:10px` on the
       parent for spacing beneath .silure-price-heading-row. Now
       position:absolute (see .silure-filter-item--price's own comment
       above for why) so this row's height no longer contributes to the
       parent's in-flow height. `position:relative` still worked fine as
       a containing block for this element's own child spans below
       (silure-shop.js's positionLabel()) — position:absolute is
       equally valid for that, so nothing about the child-span
       positioning changes. `left:0` is just this element's own static
       baseline; silure-shop.js's alignLabelsRowToSlider() already
       overwrites it every render() with a real, measured margin-left
       correcting for the slider's actual left edge — untouched by this
       change, verified still correct after it (that function measures
       from .silure-filter-item--price and the slider's own rects
       directly, neither of which moved). margin-top replaces the old
       flex gap to preserve the original 10px visual spacing. */
    position: absolute;
    top: 100%;
    left: 0;
    margin-top: 10px;
    width: 220px;
    height: 17px;
    font-family: 'DM Mono', monospace;
    font-size: 12px;
}

.silure-price-labels span {
    position: absolute;
    top: 0;
    white-space: nowrap;
}

/* "PRICE:" — exact mockup spec: muted color explicitly (was relying on
   the separate .silure-label-muted class before Session 136 moved this
   into its own inline group; now set directly so it can never silently
   inherit an ambient color from wherever this control is relocated to). */
.silure-price-heading {
    font-family: 'DM Mono', monospace;
    font-size: 12px;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    color: #8a8378;
    white-space: nowrap;
}

/* Session 92 — the old `.silure-filter-item` (padding/margin-divider
   spacing model), `.silure-filter-label`, `.silure-filter-select`
   (native dropdown arrow), `.silure-filter-item--price`/`.silure-price-
   inputs`/`.silure-price-input`/`.silure-price-sep` (Min/Max text
   inputs) that used to live here were removed outright, not left dead.
   All of it styled markup the redesign above replaced (icon-led sort
   trigger, range-slider price control, one `gap: 48px`-centered row) —
   confirmed unused anywhere else first (grepped archive-product.php,
   every theme JS file, and silure-core.php). Leaving it in place would
   have silently fought the new rules on every shared property
   (`.silure-filter-item`'s own `display`/`align-items` in particular),
   the exact "a rule in another block I didn't check" pattern that
   caused the Session 91 divider bug — not repeating it here. */

/* ─────────────────────────────────────────────────────────────────────
   PRODUCTS GRID WRAPPER
   ───────────────────────────────────────────────────────────────────── */

.silure-products-grid {
    /* Session 119 — Manish's final call, superseding the entire
       Session 116-118 padding-value discussion: the grid must be
       genuinely edge-to-edge at every screen width, matching
       production's real, current (pre-Session-111) behavior exactly
       — confirmed live on silure.in/shop: .silure-products-grid sits
       at x:0, width:100% of the true viewport, at 1440/1710/1920px
       alike, independent of any wrap cap. Production achieves this
       because its .silure-shop-wrap is itself uncapped there; staging's
       wrap stays capped at 1440px/centered (Session 111's fix, still
       correct — .silure-shop-header/.silure-shop-breadcrumb/
       .silure-filter-bar-wrap all still rely on it and are explicitly
       NOT part of this change). So the grid alone breaks out of its
       capped parent to the full viewport width via the standard
       full-bleed technique below, rather than uncapping the shared
       wrap — same real visual result for the grid, zero effect on
       every other section inside the same wrap. */
    padding: 0;
    background: var(--silure-bg, #FBF9F6);
    width: 100vw;
    position: relative;
    left: 50%;
    margin-left: -50vw;
    margin-right: -50vw;
}

/* ─────────────────────────────────────────────────────────────────────
   WC PRODUCT GRID — 3 col / 2 tablet / 1 mobile
   1px border-gap via grid background
   ───────────────────────────────────────────────────────────────────── */

.silure-products-grid .products,
.woocommerce .silure-products-grid ul.products {
    display: grid !important;
    grid-template-columns: repeat(4, 1fr) !important;
    gap: 16px !important;
    background: var(--silure-bg, #FBF9F6) !important;
    padding: 0 !important;
    margin: 0 !important;
    list-style: none !important;
    width: 100% !important;
    float: none !important;
}

/* Override WooCommerce's inline float and width styles */
.silure-products-grid .products::before,
.silure-products-grid .products::after {
    display: none !important;
}

.silure-products-grid .products li.product,
.woocommerce .silure-products-grid ul.products li.product {
    background: var(--silure-bg, #FBF9F6) !important;
    margin: 0 !important;
    padding: 0 !important;
    width: auto !important;
    float: none !important;
    clear: none !important;
    display: flex !important;
    flex-direction: column !important;
    border-right: none !important;
    border-bottom: none !important;
    border-left: none !important;
    border-top: none !important;
}

/* ─────────────────────────────────────────────────────────────────────
   PRODUCT CARD — .silure-product-card
   ───────────────────────────────────────────────────────────────────── */

.silure-product-card {
    display: flex;
    flex-direction: column;
}

/* Image area: 3:4 aspect ratio */
.silure-card-image-wrap {
    position: relative;
    overflow: hidden;
    background: var(--silure-surface, #F3F0EB);
    flex-shrink: 0;
}

.silure-card-image-link {
    display: block;
    text-decoration: none;
}

.silure-card-image {
    position: relative;
    aspect-ratio: 3 / 4;
    overflow: hidden;
}

/* Primary product image */
.silure-card-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.6s ease;
}

.silure-card-img--primary {
    z-index: 1;
    transition: transform 0.6s ease, opacity 0.4s ease;
}

/* Second hover image — 0 opacity by default */
.silure-card-img--hover {
    z-index: 2;
    opacity: 0;
    transition: opacity 0.45s ease;
}

.silure-product-card:hover .silure-card-img--hover {
    opacity: 1;
}

.silure-product-card:hover .silure-card-img--primary {
    transform: scale(1.02);
}

/* ── Tags: NEW and SOLD ───────────────────────────────────────────── */

.silure-tag {
    position: absolute;
    top: 12px;
    left: 12px;
    z-index: 10;
    font-family: 'Mulish', sans-serif;
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 2px;
    text-transform: uppercase;
    line-height: 1;
    padding: 5px 8px;
    pointer-events: none;
}

.silure-tag--new {
    background: var(--silure-bg, #FBF9F6);
    color: var(--silure-primary, #050505);
    border: 1px solid var(--silure-border, #E8E4DF);
}

.silure-tag--sold {
    background: var(--silure-bg-dark, #050505);
    color: var(--silure-on-dark, #FBF9F6);
}

/* ── Hover actions: wishlist + add ───────────────────────────────── */

.silure-card-actions {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    z-index: 5;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 12px;
    background: linear-gradient(to top, rgba(5,5,5,0.22) 0%, transparent 100%);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.3s ease, transform 0.3s ease;
    pointer-events: none;
}

.silure-product-card:hover .silure-card-actions {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Wishlist heart inside card actions — override silure-api.css absolute positioning */
.silure-card-actions .silure-heart,
.silure-card-actions .silure-card-heart {
    position: static !important;
    top: auto !important;
    right: auto !important;
    width: 30px;
    height: 30px;
    background: rgba(251, 249, 246, 0.9);
    flex-shrink: 0;
}

/* Add (+) button */
.silure-card-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-family: 'Mulish', sans-serif;
    font-size: 16px;
    font-weight: 300;
    line-height: 1;
    color: var(--silure-primary, #050505);
    background: var(--silure-on-dark, #FBF9F6);
    border: none;
    /* Session 58 — sitewide border standard: border matches this
       button's own text colour. Text stays --silure-primary in both
       states here (only the background tints on hover), so one
       currentColor declaration covers both; box-shadow (not `border`)
       keeps this exact 30x30 so the card grid never reflows. */
    box-shadow: inset 0 0 0 1px currentColor;
    width: 30px;
    height: 30px;
    text-decoration: none;
    cursor: pointer;
    transition: background 0.2s ease;
    flex-shrink: 0;
}

.silure-card-add:hover {
    background: var(--silure-surface, #F3F0EB);
    color: var(--silure-primary, #050505);
}

.silure-card-add--sold {
    font-family: 'Mulish', sans-serif;
    font-size: 8px;
    font-weight: 400;
    letter-spacing: 1.5px;
    text-transform: uppercase;
    color: var(--silure-muted, #7A7672);
    background: rgba(251, 249, 246, 0.8);
    cursor: default;
}

/* ── Card info ───────────────────────────────────────────────────── */

.silure-card-info {
    padding: 14px 10px 20px 16px;
    display: flex;
    flex-direction: column;
    gap: 5px;
    flex-grow: 1;
    background: var(--silure-bg, #FBF9F6);
}

.silure-card-name {
    font-family: 'Mulish', sans-serif;
    font-size: 13px;
    font-weight: 300;
    letter-spacing: 0.3px;
    line-height: 1.5;
    color: var(--silure-primary, #050505);
    text-decoration: none;
    display: block;
}

.silure-card-name:hover {
    color: var(--silure-muted, #7A7672);
}

.silure-card-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    flex-wrap: wrap;
}

.silure-card-purity {
    font-family: 'Mulish', sans-serif;
    font-size: 11px;
    font-weight: 400;
    letter-spacing: 1px;
    text-transform: uppercase;
    color: var(--silure-muted, #7A7672);
}

.silure-card-price {
    font-family: 'Mulish', sans-serif;
    font-size: 12px;
    font-weight: 400;
    letter-spacing: 0.3px;
    color: var(--silure-text, #444444);
}

/* WooCommerce price HTML elements */
.silure-card-price .woocommerce-Price-amount,
.silure-card-price .amount {
    font-family: inherit;
    font-size: inherit;
    font-weight: inherit;
    color: inherit;
}

/* ─────────────────────────────────────────────────────────────────────
   PROMO CARD (Session 42, rebuilt Session 43) — full-width dark card
   (every 9th product slot). Two confirmed layouts now — Carousel and
   Marquee, both matched to the confirmed reference (promo-concepts.html)
   precisely, not a rough approximation — picked randomly per slab
   alongside the existing random category pick. Rendered as <li>
   spanning all columns — same structural slot since Session 42.
   ───────────────────────────────────────────────────────────────────── */

li.silure-promo-card {
    grid-column: 1 / -1 !important;
    background: var(--silure-bg-dark, #050505) !important;
}

.silure-promo-card__eyebrow {
    /* DM Mono, not Mulish — matches the reference exactly (its
       .promo-eyebrow has no font-family override, inheriting the
       reference document's own DM Mono body default). */
    font-family: 'DM Mono', monospace;
    font-size: 10px;
    font-weight: 300;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: rgba(251, 249, 246, 0.5);
    margin: 0 0 12px;
}

/* Real bug fixed here since Session 42, not just patched: this heading
   is a bare <h3>, and Elementor's active kit (elementor-kit-5, see
   MASTER_CONTEXT.md Known Issue #8) ships its own `.elementor-kit-5 h3
   { color: var(--e-global-color-primary) }` rule — which resolves to
   #050505, i.e. this exact section's own background colour. That
   rule's specificity (one class + one element type) is HIGHER than a
   single class selector, so it silently wins over a single-class
   headline rule regardless of load order — confirmed live via the
   browser's own matched-rules inspection, not guessed. The compound
   selector below (two classes) beats the kit's rule on specificity
   alone, and !important is added on top as the same defensive
   belt-and-braces this codebase already uses elsewhere against
   Elementor kit bleed-through (see the silure_kill_pink_late()
   pattern). */
.silure-promo-card .silure-promo-card__headline {
    font-family: 'Mulish', sans-serif;
    font-size: 24px;
    font-weight: 800;
    line-height: 1.3;
    color: var(--silure-on-dark, #FBF9F6) !important;
    margin: 0 0 12px;
}

.silure-promo-card__text {
    /* DM Mono, not Mulish — matches the reference's .promo-copy
       (no font-family override there either, same inheritance). */
    font-family: 'DM Mono', monospace;
    font-size: 13px;
    font-weight: 300;
    line-height: 1.6;
    color: rgba(251, 249, 246, 0.6);
    margin: 0 0 24px;
}

/* Solid cream-on-dark CTA — the confirmed reference's own .promo-cta
   treatment exactly (background:cream, text:ink, no border, no
   radius), deliberately not the ghost-outline-on-dark style the
   retired single-message band used. Manish confirmed this exact
   button twice via the reference file; it's the standard for this
   component specifically, same "solid, no border, radius 0" concept
   as .silure-btn-primary sitewide, just colour-inverted because this
   sits on a dark background where the literal black-bg/cream-text
   version would go invisible the same way the headline once did. */
.silure-promo-card__cta {
    display: inline-block;
    position: relative; /* Session 57 — anchors the shine sweep below */
    overflow: hidden;
    font-family: 'DM Mono', monospace;
    font-size: 12px;
    font-weight: 300;
    letter-spacing: 0.5px;
    text-transform: uppercase;
    background: var(--silure-on-dark, #FBF9F6);
    color: var(--silure-bg-dark, #050505);
    border: none;
    /* Session 58 — sitewide border standard, applied to its natural-
       default-state case: this button is light-filled by default (not
       a hover-inverted state), so its dark text colour is what the
       border matches here, at rest — same currentColor technique as
       every other fixed button, still zero layout impact alongside the
       shine's overflow: hidden above. */
    box-shadow: inset 0 0 0 1px currentColor;
    border-radius: 0;
    padding: 13px 28px;
    text-decoration: none;
    transition: opacity 0.25s ease;
}

.silure-promo-card__cta:hover {
    opacity: 0.85;
}

/* Silver traveling shine (Session 57) — one of exactly 3 confirmed CTAs
   sitewide (the other two: homepage's "Explore Collection" in
   homepage.css, quick-checkout's landing-page Pay Now in
   quick-checkout.css — same block, duplicated per page-scoped
   stylesheet rather than a sitewide file, matching this project's
   existing convention). This button sits on a light cream background
   (--silure-on-dark, confusingly named but literally #FBF9F6 here) —
   the other two are dark buttons, where a plain bright-white sweep pops
   on its own. On a near-white button a plain white streak would be all
   but invisible, so the gradient's peak uses a muted silver-gray
   instead (via --silure-shine-color), while keeping the same
   dark-light-dark "glint" structure as the other two so it still reads
   as one consistent effect, not a different animation. Deliberately
   transform: translateX() for the sweep, never `left` — GPU-composited,
   not a per-frame layout recalc. Text is wrapped in its own <span>
   (see silure_render_promo_card_carousel()/_marquee() in
   silure-core.php) so it can stay above the shine layer via z-index,
   since z-index has no effect on a bare text node. */
@keyframes silure-shine-sweep {
    0%   { transform: translateX(-160%); }
    55%  { transform: translateX(160%); }
    100% { transform: translateX(160%); }
}
.silure-promo-card__cta::before {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    width: 55%;
    height: 100%;
    background: linear-gradient(
        100deg,
        transparent 35%,
        rgba(0, 0, 0, 0.08) 42%,
        var(--silure-shine-color, rgba(120, 120, 120, 0.4)) 50%,
        rgba(0, 0, 0, 0.08) 58%,
        transparent 65%
    );
    transform: translateX(-160%);
    animation: silure-shine-sweep 3.4s ease-in-out infinite;
    animation-delay: 1s;
    will-change: transform;
    pointer-events: none;
    z-index: 0;
}
.silure-promo-card__cta > * {
    position: relative;
    z-index: 1;
}

/* ── Concept 1: Carousel ── */

li.silure-promo-card.silure-promo-card--carousel {
    display: flex !important;
    align-items: stretch;
    text-align: left;
    padding: 0 !important;
    /* Session 107 — was a fixed height: 420px here; removed. The image
       box below is now a genuine square (aspect-ratio: 1/1, driven by
       its own resolved width), so the row's real height is however
       tall that square naturally comes out to at the current
       viewport — align-items: stretch then matches the content column
       to it, exactly like before, just no longer pinned to one
       hardcoded number. Confirmed with Manish, intentional: this makes
       the card noticeably taller than the old fixed 420px/260px/220px
       values (real measurements in MASTER_CONTEXT.md's own Session 107
       entry). */
}

/* Session 107 — alternating layout: image-on-right, content-on-left,
   applied on top of the base row above. Deliberately kept LOWER
   specificity (2 classes, no element) than the mobile stacking rule
   below (li.silure-promo-card.silure-promo-card--carousel, 2 classes +
   1 element) so it never fights that rule for control of
   flex-direction — on mobile the column layout always wins regardless
   of which side this card would have shown on desktop, with no
   separate mobile override needed here. Which side a given carousel
   card gets is decided in PHP (silure_render_promo_card()) via a
   session cookie mirroring the repeat-prevention one from Session 105
   — see that function's own comment for why a cookie, not
   sessionStorage, and why it lives in the one function both injection
   points already share. */
.silure-promo-card--carousel.silure-promo-card--carousel-right {
    flex-direction: row-reverse;
}

.silure-promo-carousel-images {
    position: relative;
    width: 55%;
    /* Session 107 — replaces the fixed `height: 100%` (which relied on
       the card's own now-removed fixed height): a real aspect-ratio
       square instead, matching the real 1024x1024 source photos
       exactly, at every width, with no separate breakpoint-specific
       height value needed (this replaces the mobile block's old
       height:260px/220px values too — see there). */
    aspect-ratio: 1 / 1;
    overflow: hidden;
    flex-shrink: 0;
    background: var(--silure-surface, #F3F0EB);
}

/* Session 121 — each image is a real product link now (silure-core.php's
   silure_render_promo_card_carousel()); this keeps the <a> wrapper out
   of the box model entirely so the <img> inside stays the actual
   position:absolute, opacity-crossfading element exactly as before —
   no other rule below, and no JS, needed to change. */
.silure-promo-carousel-link {
    display: contents;
}

.silure-promo-carousel-images img {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    /* Session 107 — replaces Session 106's object-fit:contain
       (letterboxed landscape box) with the final, confirmed design:
       the BOX is now a genuine square, matching the real 1024x1024
       source photos' own ratio exactly, so `cover` displays the
       complete image with no meaningful cropping and no letterbox
       bars are needed at all. */
    object-fit: cover;
    opacity: 0;
    transition: opacity 0.8s ease;
    /* Session 122 — real, reproduced bug: all N images stack in this
       exact same box (position:absolute, identical top/left/width/
       height), and by default every one of them is still hit-testable
       for clicks regardless of its own opacity — only the LAST one in
       DOM order ever actually receives a click, no matter which is
       visually active, since browsers resolve overlapping same-
       stacking-context hit tests by paint order, not opacity.
       Confirmed live via elementFromPoint() at each image's own real
       screen position: every one of them resolved to the same, last,
       always-wrong <a>. Session 120/121's own <a href> wrapping fix
       made this newly clickable-but-wrong — before that, nothing was
       clickable at all, so the bug had no click to go to yet. Turning
       hit-testing off here for every image except the active one (just
       below) means only the one actually on screen can ever be
       clicked, and it changes instantly with the class (not animated),
       so there's never a moment where two images are both clickable
       mid-crossfade. */
    pointer-events: none;
}

.silure-promo-carousel-images img.is-active {
    opacity: 1;
    pointer-events: auto;
}

.silure-promo-carousel-dots {
    position: absolute;
    bottom: 16px;
    left: 50%;
    transform: translateX(-50%);
    display: flex;
    gap: 6px;
    z-index: 2;
}

.silure-promo-carousel-dots span {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: rgba(251, 249, 246, 0.4);
    transition: background 0.3s;
}

.silure-promo-carousel-dots span.is-active {
    background: var(--silure-on-dark, #FBF9F6);
}

.silure-promo-carousel-content {
    width: 45%;
    padding: 36px 32px;
    display: flex;
    flex-direction: column;
    justify-content: center;
}

/* Session 106 — real bug: this container has no explicit align-items,
   so its default (stretch) was stretching the CTA <a> to the full
   column width — display:inline-block only shrinks to fit outside a
   flex context, and this button sits inside one. Scoped to the
   carousel context specifically (marquee's own CTA row is a plain
   block, not flex, so this selector would be a no-op there anyway —
   marquee was already confirmed fine, untouched). flex-start keeps it
   left-aligned, matching the left-aligned text above it, at its own
   natural (fit-content) width instead of stretched. */
.silure-promo-carousel-content .silure-promo-card__cta {
    align-self: flex-start;
}

/* ── Concept 2: Marquee ── */

li.silure-promo-card.silure-promo-card--marquee {
    display: block !important;
    padding: 40px 0 !important;
    text-align: center;
}

.silure-promo-marquee-top {
    padding: 0 32px 24px;
    text-align: center;
}

.silure-promo-marquee-track-wrap {
    /* Session 123 — split from a single `overflow: hidden`: horizontal
       clipping is what actually makes the scrolling illusion work and
       stays required; vertical was only ever hidden as a side effect,
       and the new hover pop's box-shadow (below) needs to be able to
       bleed slightly above/below the strip instead of getting clipped
       flat by it. No ancestor of this wrapper clips overflow itself
       (checked), so letting y overflow through here doesn't get cut
       off further up either. */
    overflow-x: hidden;
    overflow-y: visible;
    width: 100%;
}

/* Session 120 — hovering anywhere over the visible strip pauses the
   scroll; resuming is just animation-play-state going back to
   'running', which continues from wherever the animation already was
   (no jump/reset) — no JS state needed for pause or resume. */
.silure-promo-marquee-track-wrap:hover .silure-promo-marquee-track {
    animation-play-state: paused;
}

.silure-promo-marquee-track {
    display: flex;
    gap: 16px;
    width: max-content;
    /* Real bug found live: this track's computed max-width came back
       100% (traced live via the browser's own ancestor-chain
       inspection to something cascading from a sitewide layout rule —
       every ancestor up to <body> reports the same clamped width),
       silently capping the track at its wrapper's clipped width
       regardless of the wider `width` above or an explicit inline
       pixel width set by JS. Since the entire point of this element is
       to be WIDER than its wrapper (that's what makes the scroll loop
       work), max-width needs an explicit override here rather than
       depending on `width` to win on its own. */
    max-width: none !important;
    animation: silure-promo-marquee-scroll 18s linear infinite;
}

/* Session 120 — sizing moved from the bare <img> onto this new <a>
   wrapper (each photo is now a real product link — see
   silure_render_promo_card_marquee() in silure-core.php); the anchor
   is the flex item now.
   Session 123 — overflow:hidden removed (was clipping the hover-zoom
   to this same box, matching the sitewide contained-zoom convention
   from Session 115). Manish's explicit, different ask this round:
   a real "lift toward the viewer" pop, not a zoom-in-place — that
   needs the image free to grow and cast a shadow beyond its own box
   into the gap between cards, which a clipping wrapper would cut off
   flat. position:relative + z-index on hover keeps the popped card
   painting above its flex neighbours instead of partly behind them. */
.silure-promo-marquee-track a {
    display: block;
    width: 180px;
    height: 220px;
    flex-shrink: 0;
    position: relative;
}

.silure-promo-marquee-track a:hover {
    z-index: 2;
}

.silure-promo-marquee-track img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0.92;
    display: block;
    /* Session 123 — was a flat scale(1.03) matching the sitewide
       contained-zoom convention; a more pronounced, genuinely
       dimensional pop instead — a larger scale, a real elevated
       drop-shadow (blurred and offset, not just a border), and a
       tasteful perspective tilt so the card reads as lifting toward
       the viewer rather than just growing.
       Session 124 — unified to one 0.35s duration (was 0.45s) so the
       pop and the new dim-siblings effect below read as one motion,
       per Manish's approved mockup; added `filter` to the list for
       that same dim effect. */
    transform-style: preserve-3d;
    transition: transform 0.35s ease, box-shadow 0.35s ease, opacity 0.35s ease, filter 0.35s ease;
}

/* Session 124 — dim-siblings, approved mockup: hovering ANY card in
   the row dims every image in the track (including ones not yet
   scrolled into view — the selector is plain and class-based, so it
   automatically covers however many clones Session 123's width-fix
   produced, not a fixed count). `:hover` bubbles up to the track
   itself whenever the pointer is over it or any descendant, so this
   fires the instant any card is entered and clears the instant the
   pointer leaves the WHOLE row — exactly "resume together when
   leaving the row," not per-card. Pure CSS `filter`, not an actual
   overlay element, specifically so there's nothing new in the DOM to
   intercept clicks or interfere with the pause rule above — dimming a
   pixel's brightness never touches pointer-events. */
.silure-promo-marquee-track:hover img {
    filter: brightness(0.35);
}

/* Session 124 — the hovered card's own image overrides the dim above
   back to full brightness and gets the (now bigger) pop; specificity
   is naturally higher than the dim rule (one more element in the
   selector: `a:hover img` vs `:hover img`), so this wins without
   needing !important, regardless of source order. */
.silure-promo-marquee-track a:hover img {
    opacity: 1;
    filter: brightness(1);
    /* Session 124 — scale bumped from 1.06 to 1.16 and the tilt/shadow
       scaled up to match, per Manish's approved mockup ("noticeably
       bigger, more confident pop... still look intentional at the
       larger size"). */
    transform: perspective(700px) scale(1.16) rotateX(5deg) rotateY(-5deg);
    box-shadow:
        0 36px 64px -18px rgba(0, 0, 0, 0.55),
        0 16px 28px -10px rgba(0, 0, 0, 0.4);
}

@keyframes silure-promo-marquee-scroll {
    from { transform: translateX(0); }
    /* Session 120 — was a flat -50%. With flex `gap` counted only
       BETWEEN items (never before the first or after the last), the
       doubled track's total width always has one MORE gap than two
       clean "N images + N gaps" halves would, so -50% of the total is
       permanently short by half a gap (8px at this 16px gap) — a
       structural stutter at the loop point, confirmed live, not a
       measurement fluke. --silure-marquee-shift is the exact repeat
       distance instead, computed per-track by initPromoMarquees()
       (silure-shop.js) from the real rendered widths; -50% stays only
       as a same-tab fallback for the instant before that JS runs. */
    to   { transform: translateX(var(--silure-marquee-shift, -50%)); }
}

.silure-promo-marquee-cta-row {
    text-align: center;
    margin-top: 28px;
}

/* ─────────────────────────────────────────────────────────────────────
   LOAD MORE
   ───────────────────────────────────────────────────────────────────── */

.silure-load-more-wrap {
    padding: 56px 80px;
    text-align: center;
    background: var(--silure-bg, #FBF9F6);
    /* Session 123 — border-top removed. Session 122 already found and
       removed a dead duplicate copy of this rule (silure-woocommerce.css,
       0.5px/wrong padding) that was the more visible half of Manish's
       original complaint; this file's own 1px divider was left in place
       pending his confirmation on whether he wanted it gone too — he
       does. Removed. */
}

.silure-load-more {
    display: inline-block;
    font-family: 'Mulish', sans-serif;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--silure-primary, #050505);
    background: transparent;
    border: 1px solid var(--silure-primary, #050505);
    padding: 14px 56px;
    cursor: pointer;
    transition: background 0.25s ease, color 0.25s ease;
    line-height: 1.7em;
}

.silure-load-more:hover {
    background: var(--silure-primary, #050505);
    color: var(--silure-on-dark, #FBF9F6);
}

.silure-load-more.is-loading {
    opacity: 0.5;
    cursor: wait;
    pointer-events: none;
}

/* ─────────────────────────────────────────────────────────────────────
   EMPTY STATE
   ───────────────────────────────────────────────────────────────────── */

.silure-empty-state {
    padding: 96px 80px;
    text-align: center;
    background: var(--silure-bg, #FBF9F6);
}

.silure-empty-state__text {
    font-family: 'Mulish', sans-serif;
    font-size: 15px;
    font-weight: 300;
    letter-spacing: 0.5px;
    line-height: 1.7em;
    color: var(--silure-muted, #7A7672);
    margin: 0 0 28px;
}

/* Shared ghost button for empty state CTAs */
.silure-btn-outline {
    display: inline-block;
    font-family: 'Mulish', sans-serif;
    font-size: 10px;
    font-weight: 400;
    letter-spacing: 2px;
    text-transform: uppercase;
    color: var(--silure-primary, #050505);
    /* Session 58 — sitewide border standard found a real bug here: this
       border was hardcoded to --silure-primary (dark) in both states,
       so on hover — when the fill inverts to dark and text becomes
       cream — the dark border sat on a now-dark fill (invisible) and
       no longer matched the new cream text at all. currentColor tracks
       both states correctly; border (not box-shadow) kept deliberately,
       since a real border already existed here at rest — switching to
       box-shadow would shrink the box by 2px, an unrelated regression
       this fix isn't meant to introduce. */
    border: 1px solid currentColor;
    padding: 12px 36px;
    text-decoration: none;
    transition: background 0.25s ease, color 0.25s ease;
    line-height: 1.7em;
}

.silure-btn-outline:hover {
    background: var(--silure-primary, #050505);
    color: var(--silure-on-dark, #FBF9F6);
}

/* SOLD product image dimming lives entirely in sold-badge.css now
   (Session 39) — this rule targeted .silure-card-img--primary, a class
   that doesn't exist anywhere in the real card markup (content-product.php
   uses .silure-img-main/.silure-img-hover), so it never actually
   applied. Removed as part of consolidating sold-out image treatment
   down to one real, live implementation instead of leaving a second,
   dead one beside it. */

/* ─────────────────────────────────────────────────────────────────────
   SHOP GRID — MEJURI-STYLE CARD LAYOUT
   Inherits carousel image/button CSS from silure-core.php; overrides
   for 4-col grid context (aspect ratio, card info row, + icon, sale price).
   ───────────────────────────────────────────────────────────────────── */

/* B — Image: 364/433 Mejuri ratio, fills wrapper absolutely */
.silure-products-grid .silure-card-img-wrap {
    aspect-ratio: 364 / 433;
    position: relative;
    overflow: hidden;
}
.silure-products-grid .silure-card-img-wrap .silure-img-main,
.silure-products-grid .silure-card-img-wrap .silure-img-hover {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    aspect-ratio: auto !important;
    object-fit: cover;
    object-position: center top;
}
.silure-products-grid .silure-product-card:hover .silure-img-hover,
.silure-products-grid .silure-card-link:hover .silure-img-hover {
    opacity: 1;
}

/* C — Card info: name+price left, + icon right (same row as carousel) */
.silure-products-grid .silure-card-info {
    display: flex !important;
    align-items: flex-start !important;
    justify-content: space-between !important;
    gap: 8px !important;
    padding: 12px 10px 14px !important;
    flex-grow: 0 !important;
    flex-direction: row !important;
}
.silure-products-grid .silure-card-info__text {
    display: flex;
    flex-direction: column;
    gap: 3px;
    flex: 1;
    min-width: 0;
    text-decoration: none;
    color: inherit;
    justify-content: space-between;
}
.silure-products-grid ul.products li.product .silure-card-title {
    padding: 0 !important;
    margin: 0 0 2px !important;
}

/* E — Sale price: del 30% smaller, tight baseline gap */
.silure-products-grid .silure-card-price {
    display: flex !important;
    align-items: baseline !important;
    gap: 4px !important;
    flex-wrap: wrap !important;
}
.silure-products-grid .silure-card-price del .woocommerce-Price-amount,
.silure-card-price del .woocommerce-Price-amount {
    font-size: 13px !important;
    color: var(--silure-muted, #7A7672) !important;
    text-decoration: line-through !important;
}
.silure-products-grid .silure-card-price ins {
    text-decoration: none !important;
    color: #050505 !important;
}

/* C — Fix actions: reset absolute overlay, become static flex-shrink:0 right col */
.silure-products-grid .silure-card-actions {
    position: static !important;
    flex-shrink: 0 !important;
    display: flex !important;
    align-items: center !important;
    background: none !important;
    opacity: 1 !important;
    transform: none !important;
    pointer-events: auto !important;
    padding: 0 !important;
    bottom: auto !important;
    left: auto !important;
    right: auto !important;
    z-index: auto !important;
    justify-content: flex-end !important;
    /* Session 44 — .silure-card-info uses align-items:flex-start, so the
       title and this actions box share a top offset. The title's own
       line-box (11px/1.4 = 15.4px) is much shorter than the "+" button's
       fixed 36px box, leaving the button's center 10.3px below the
       title's text center by default: (36/2) - (11*1.4/2) = 18 - 7.7 =
       10.3px. Pull it up by that exact amount so centers match. Same
       value used in the homepage New In section (same font-size/
       line-height/button-size there) — see .elementor-element-newin001
       .silure-card-actions above. */
    margin-top: -10.3px !important;
}

/* D — +/cart icon: 36x36 circle, same as carousel */
.silure-products-grid .silure-card-actions .add_to_cart_button {
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 36px !important;
    height: 36px !important;
    border-radius: 50% !important;
    background: transparent !important;
    border: none !important;
    cursor: pointer !important;
    transition: background 0.2s ease !important;
    padding: 0 !important;
    text-decoration: none !important;
    flex-shrink: 0 !important;
}
.silure-products-grid .silure-card-actions .add_to_cart_button:hover {
    background: var(--silure-surface, #F3F0EB) !important;
    border-color: transparent !important;
}
.silure-products-grid .silure-card-actions .add_to_cart_button svg {
    display: none !important;
}
.silure-products-grid .silure-card-actions .add_to_cart_button::before {
    content: "+";
    font-family: "DM Mono", monospace;
    font-weight: 300;
    font-size: 20px;
    line-height: 1;
    color: #050505;
    display: block;
}
/* In-cart state: hide "+", show SVG bag icon */
.silure-products-grid .silure-card-actions .add_to_cart_button.silure-in-cart::before,
.silure-products-grid .silure-card-actions .add_to_cart_button.added::before {
    content: "" !important;
}
.silure-products-grid .silure-card-actions .add_to_cart_button.silure-in-cart svg,
.silure-products-grid .silure-card-actions .add_to_cart_button.added svg {
    display: block !important;
    width: 16px !important;
    height: 16px !important;
    /* Same root cause as the sitewide "kill pink" pattern: hello-elementor's
       own reset.css sets a bare `a { color: #c36 }` (#cc3366), and nothing
       here ever gave .add_to_cart_button its own real `color` -- the "+"
       pseudo-element masked this by setting its own explicit color, but the
       SVG's stroke="currentColor" inherits the anchor's real (pink) computed
       color directly. Set explicitly here rather than on the base selector,
       matching this block's own existing scope. */
    color: #050505 !important;
}
/* Hide WC's injected "View Cart" link */
.silure-products-grid a.added_to_cart.wc-forward {
    display: none !important;
}
/* WC button display override — higher specificity beats silure-core.php .woocommerce ul.products li.product .button { display:none } */
.woocommerce .silure-products-grid ul.products li.product .button {
    display: flex !important;
}

/* A — Mobile: 2-column grid */
@media (max-width: 768px) {
    .silure-products-grid .products,
    .woocommerce .silure-products-grid ul.products {
        grid-template-columns: repeat(2, 1fr) !important;
        gap: 8px !important;
    }
}

/* ─────────────────────────────────────────────────────────────────────
   RESPONSIVE
   ───────────────────────────────────────────────────────────────────── */

@media (max-width: 1024px) {
    /* Session 110 — real requirement: centered stays desktop-only;
       tablet and mobile go back to left-aligned. 1024px is the first
       real breakpoint where this exact component's own CSS already
       diverges from pure desktop (the padding rule right below,
       unchanged), so it's the natural, already-established switch
       point for this pair of elements specifically — not a new
       threshold invented for this change. Removing the auto margin
       (width: fit-content stays exactly as it is) is enough on its
       own: with no auto margin, a block-level element just sits flush
       against its own containing block's left edge by default, same
       as every other stacked section on this page at these widths
       (category tiles, filter bar, grid) — nothing else needs
       touching, and because max-width:1024px is a superset of the
       narrower <768px/<480px breakpoints below, this one declaration
       already covers all of "tablet and mobile" without needing to be
       repeated in either of those blocks. The 10px vertical gap
       (Session 109, .silure-shop-header's own padding-top) is
       untouched by this — only horizontal alignment changes here. */
    .silure-shop-heading-group {
        /* Session 113 — was a bare `margin: 0` (all four sides,
           correctly killing the auto-centering for left-align, but as
           a side effect also zeroing out the base rule's new
           margin-top). Kept left/right/bottom at 0 (still no
           auto-centering) but carried the same 24px top value forward
           here explicitly, so tablet/mobile get the same real top-gap
           increase as desktop, not a silently-cancelled one. */
        margin: 24px 0 0;
    }

    .silure-shop-header,
    .silure-shop-breadcrumb,
    .silure-filter-bar-wrap,
    .silure-load-more-wrap,
    .silure-empty-state {
        padding-left: 40px;
        padding-right: 40px;
    }
    /* Session 119 — .silure-products-grid removed from this list: it's
       unconditionally edge-to-edge now (base rule), no per-breakpoint
       padding value applies to it any more at all. */

    .silure-promo-carousel-content {
        padding: 28px 24px;
    }

    .silure-promo-marquee-top {
        padding: 0 24px 20px;
    }
}

@media (max-width: 768px) {
    .silure-shop-header {
        /* Session 109 — top padding only (was 32px), same 10px gap as
           desktop; see the base rule's own comment. */
        padding: 10px 24px 24px;
    }

    .silure-shop-breadcrumb {
        padding-left: 24px;
        padding-right: 24px;
    }

    .silure-filter-bar-wrap,
    .silure-load-more-wrap,
    .silure-empty-state {
        padding-left: 24px;
        padding-right: 24px;
    }

    /* Session 118 set the grid to 0 here specifically; Session 119
       made that the unconditional base rule for every width instead
       (see .silure-products-grid's own comment), so this breakpoint-
       specific copy is redundant now, not a separate value — removed
       rather than left duplicated. */

    /* Carousel's 55/45 side-by-side split doesn't work below ~700px —
       stacks image-over-content instead, same content, same images,
       just a necessary mobile adaptation the (desktop-only) reference
       didn't need to cover. */
    li.silure-promo-card.silure-promo-card--carousel {
        flex-direction: column;
        height: auto;
    }

    .silure-promo-carousel-images {
        width: 100%;
        /* Session 107 — was height:260px; removed. aspect-ratio:1/1
           (base rule, unconditional, still applies here) now drives
           height from this 100%-of-card width instead, same as
           desktop — no separate mobile height value needed any more. */
    }

    .silure-promo-carousel-content {
        width: 100%;
        padding: 28px 24px;
    }

    /* Session 106 — real bug: the flex-start fix above keeps the CTA
       left-aligned at every width, which reads fine on desktop
       (matching the left-aligned text beside it) but looks wrong once
       this stacks to a full-width column on mobile — the button sits
       flush against the left edge instead of centered under the
       (still left-aligned, untouched) text above it. Overrides just
       this one element's cross-axis alignment for mobile; the
       eyebrow/headline/text stay exactly as they are. */
    .silure-promo-carousel-content .silure-promo-card__cta {
        align-self: center;
    }

    /* Session 120 — was on the bare img; sizing lives on the <a>
       wrapper now (see the base rule's own comment). */
    .silure-promo-marquee-track a {
        width: 140px;
        height: 170px;
    }

    /* Session 136 — exact mockup spec: mobile is a genuinely SIMPLIFIED
       single row, not the desktop row's own left/right groups shrunk
       down. Left side shows ONLY the Filters trigger; right side
       is unchanged, exactly Sort + piece count as they already
       appeared on mobile before this pass.
       Session 138 — the standalone Price control's own mobile-only
       `display:none` here is gone: it's now permanently hidden at every
       width via a real `hidden` attribute in the markup (Manish removed
       it from the standalone row entirely, real Price control lives
       only in the Filters drawer now), so this rule would have been
       dead weight duplicating what the HTML attribute already does
       everywhere, not just here. */
    .silure-filter-row {
        flex-direction: row;
        justify-content: space-between;
        gap: 0;
    }

    .silure-filter-left {
        gap: 0;
    }

    /* Category trigger: hidden outright on mobile, not just visually
       de-emphasized — remains a fully real, working element at desktop/
       tablet widths, and remains reachable on mobile via the Filters
       drawer's own Category section. */
    .silure-jump-wrap--category {
        display: none;
    }

    /* Filters trigger swaps its icon here: the small chevron (desktop)
       hides, the adjustments-lines icon (matching Sort's own) shows —
       per the approved mockup's explicit mobile-only icon spec. */
    .silure-jump-icon--chevron {
        display: none;
    }
    .silure-jump-icon--adjust {
        display: block;
    }

    #silure-filters-trigger .silure-jump-trigger-text {
        font-size: 11px;
    }

    .silure-filter-right {
        width: auto;
        justify-content: flex-end;
        gap: 18px;
    }

    .silure-filter-right .silure-shop-count,
    .silure-sort-control .silure-sort-trigger {
        font-size: 11px;
    }
}

@media (max-width: 480px) {
    .silure-shop-header {
        /* Session 109 — top padding only (was 24px), same 10px gap as
           desktop; see the base rule's own comment. */
        padding: 10px 16px 20px;
    }

    .silure-shop-breadcrumb {
        padding-left: 16px;
        padding-right: 16px;
    }

    .silure-filter-bar-wrap,
    .silure-load-more-wrap,
    .silure-empty-state {
        padding-left: 16px;
        padding-right: 16px;
    }

    /* Session 119 — same removal as the ≤768px block; the grid's
       unconditional base rule now covers every width, including this
       one. */

    .silure-card-actions {
        opacity: 1;
        transform: none;
        pointer-events: auto;
    }

    /* Session 107 — was a .silure-promo-carousel-images { height:220px }
       override here; removed. aspect-ratio:1/1 (base rule) already
       makes it a real square at this width too, matching everywhere
       else — a fixed height here would have broken that specifically
       below 480px. */

    .silure-promo-carousel-content {
        padding: 24px 20px;
    }

    .silure-promo-marquee-top {
        padding: 0 16px 16px;
    }

    /* Session 120 — see the ≤768px block's own comment. */
    .silure-promo-marquee-track a {
        width: 110px;
        height: 135px;
    }
}
