/* Marcellus — self-hosted. Was fetched from fonts.googleapis.com on every page
   load, which cost a DNS lookup and a TLS handshake with two extra domains
   before any text could be drawn. Both weights of the file are the same 400;
   Marcellus ships in one weight only. */
@font-face {
    font-family: 'Marcellus';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/marcellus-normal-400-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Marcellus';
    font-style: normal;
    font-weight: 400;
    font-display: swap;
    src: url('../fonts/marcellus-normal-400-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ═══════════════════════════════════════════════════════
   SELF-HOSTED FONTS — inlined from fonts.css (removes an
   @import render-blocking round trip). fonts.css is kept as the
   canonical copy; regenerate this block from it when editing fonts.
   ═══════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════
   SELF-HOSTED FONTS
   ═══════════════════════════════════════════════════════════
   Previously an @import from fonts.googleapis.com, which made the
   browser resolve DNS and negotiate TLS with TWO more domains
   (googleapis for the CSS, then gstatic for the files) before a
   single word of the page could render. Serving them from this
   origin removes both round trips.

   Latin and Latin-Extended only — the Cyrillic, Greek, Vietnamese,
   maths and symbol subsets Google also ships are never used by an
   English-language shop, and unicode-range means the browser only
   downloads a file when a character in its range actually appears.

   Montserrat is a VARIABLE font: one file covers its whole 100-900
   weight axis rather than a separate download per weight. Marcellus
   ships a single 400 and has no axis, which is why every heading
   weight in this file resolves to 400 — see --font-heading-weight.
   ═══════════════════════════════════════════════════════════ */

@font-face {
    font-family: 'Montserrat';
    font-style: italic;
    font-weight: 100 900;
    font-display: swap;
    src: url('../fonts/montserrat-italic-100900-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Montserrat';
    font-style: italic;
    font-weight: 100 900;
    font-display: swap;
    src: url('../fonts/montserrat-italic-100900-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('../fonts/montserrat-normal-100900-latin-ext.woff2') format('woff2');
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: 'Montserrat';
    font-style: normal;
    font-weight: 100 900;
    font-display: swap;
    src: url('../fonts/montserrat-normal-100900-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}



/* ============================================================
   Dievon — Design System
   Brand: deep burgundy + elegant gold + soft ivory
   ============================================================ */
:root {
    /* ── Backgrounds ─────────────────────────────────────── */
    --bg-main:             #fdfbf9; /* warm soft ivory */
    --bg-surface:          #ffffff;
    --bg-surface-soft:     #f8f5f0;
    --bg-hero:             #f5eee8;

    /* ── Brand Colors ─────────────────────────────────────── */
    --color-primary:       #511126; /* deep burgundy / plum */
    --color-primary-dark:  #3a0c1b;
    --color-primary-light: #e8d8de; /* soft blush */
    --color-primary-bg:    #f9f6f7;

    --color-secondary:     #C59B4B; /* elegant gold */
    --color-secondary-bg:  #fbf8f3;

    --color-accent:        #D4AF37; /* rich gold — decoration, and text on DARK grounds */
    --color-accent-bg:     #fcfaf6;
    /* Gold dark enough to read as TEXT on a light ground.
       ────────────────────────────────────────────────────────────────────────
       #D4AF37 on white measures 2.09:1 against a 4.5:1 requirement, so gold
       text on the pale pages — the "NEW" pill, editorial labels, category card
       links, the luxury badge — is a WCAG AA failure wherever it appears.

       It cannot be fixed by darkening --color-accent itself: that token is also
       gold text on the dark footer (8.98:1) and on maroon (6.85:1), both of
       which are comfortably passing today and would drop to 3.73:1 and 2.85:1
       if the value moved. Changing one token fixes the light pages and breaks
       the dark ones — measured, not assumed.

       So the two cases get two tokens. This is the one to reach for when gold
       sits on white or cream; --color-accent stays exactly as it is everywhere
       else, and the brand colour is unchanged for every decorative use. */
    --color-accent-ink:    #8a6a12; /* 5.06:1 on white, 4.90:1 on cream */

    --color-danger:        #a94442;
    --color-success:       #2e7d32;

    /* ── Text ─────────────────────────────────────────────── */
    --text-primary:   #3a0c1b; /* dark burgundy for text */
    --text-secondary: #511126;
    /* 4.21:1 on the cream ground, against a 4.5:1 requirement — a fail by a
       small margin, and this token carries breadcrumbs, the struck-through MRP,
       the "From" prefix, card categories and the "Select Size" caption. Nudged
       to 5.24:1. Verified as body text on light grounds only; no dark-background
       rule uses it. */
    --text-muted:     #7a674f;
    --text-on-primary: #ffffff;

    /* ── Layout ───────────────────────────────────────────── */
    /* The page gutter. .container and the homepage hero's text panel both read
       it, so a heading in a contained section and the hero headline above it
       start on the same pixel. Stepped down per breakpoint in responsive.css. */
    --dv-gutter: 4.5rem;

    /* ── Borders ──────────────────────────────────────────── */
    --border-color:  rgba(81, 17, 38, 0.1);
    --border-strong: rgba(81, 17, 38, 0.25);
    --border-light:  rgba(81, 17, 38, 0.05);

    /* ── Gradients ────────────────────────────────────────── */
    --grad-primary:  linear-gradient(135deg, #511126 0%, #3a0c1b 100%);
    --grad-coral:    linear-gradient(135deg, #78213e 0%, #511126 100%);
    --grad-mint:     linear-gradient(135deg, #C59B4B 0%, #D4AF37 100%);
    --grad-golden:   linear-gradient(135deg, #D4AF37 0%, #e8ca6b 100%);
    --grad-hero:     linear-gradient(135deg, #f5eee8 0%, #ffffff 100%);
    --grad-warm:     linear-gradient(135deg, #f8f5f0 0%, #f5eee8 100%);
    --grad-text:     linear-gradient(135deg, #511126, #C59B4B);

    /* ── Typography ───────────────────────────────────────── */
    /* Marcellus — a low-contrast humanist serif with bracketed serifs. Quieter
       and warmer than a Didone; it does not echo the DIEVON wordmark the way
       Bodoni Moda did, which was the reason for the earlier switch, but this is
       the face the brand wants and it is now served from assets/fonts rather
       than fetched from Google.

       Fallbacks are serifs of a similar colour, so a browser that cannot load
       the file degrades to something related rather than to Times. */
    --font-heading: 'Marcellus', 'Cormorant Garamond', Georgia, 'Times New Roman', serif;

    /* Both 400, and they have to be.
       Marcellus ships ONE weight. Bodoni Moda was a variable font with a
       400-900 axis, so 500 and 600 were real cuts drawn by the designer; ask
       Marcellus for them and the browser SYNTHESISES a bold instead — it
       smears each glyph sideways, which thickens the hairlines unevenly and
       makes the serifs blunt. That is the exact failure this comment used to
       warn about for GFS Didot.
       If headings need more presence, use size or letter-spacing, not weight. */
    --font-heading-weight: 400;
    --font-heading-weight-strong: 400;
    --font-body:    'Montserrat', sans-serif;

    /* ── Shadows ──────────────────────────────────────────── */
    --shadow-xs: 0 1px 2px rgba(81, 17, 38, 0.03);
    --shadow-sm: 0 4px 12px rgba(81, 17, 38, 0.05);
    --shadow-md: 0 8px 24px rgba(81, 17, 38, 0.08);
    --shadow-lg: 0 16px 40px rgba(81, 17, 38, 0.12);

    /* ── Radius ───────────────────────────────────────────────
       There isn't one, by decision: nothing on this site has rounded
       corners, so every shape reads as part of the same set. The five
       tokens that used to live here (0/0/2/4/4) are gone rather than
       zeroed — a token named --radius-lg that evaluates to 0 is an
       invitation to "just put it back to 4", which is how the site
       ended up with six different radii in the first place. Square is
       not a default here, it is the house style. */

    /* ── Transitions ──────────────────────────────────────── */
    --transition:        all 0.3s ease;
    --transition-bounce: all 0.4s cubic-bezier(0.25, 1, 0.5, 1);
    --transition-slow:   all 0.6s cubic-bezier(0.25, 1, 0.5, 1);
}

/* ═══════════════════════════════════════════════════════════
   UNIFORM SITE-WIDE MASTER SECTION SPACING (.section-space)
   ═══════════════════════════════════════════════════════════ */
/* The value lives in assets/css/responsive.css, which loads after this file
   and therefore wins. A second copy here said 40px while that one said 60px —
   two numbers for one idea, and only one of them had any effect. */
.section-mb { margin-bottom: 50px !important; }
.section-mb-sm { margin-bottom: 30px !important; }
.section-mb-lg { margin-bottom: 80px !important; }
/* THE container. Single source of truth — the responsive padding ladder for
   this rule lives at the bottom of responsive.css and nowhere else.
   The !important flags that used to be here were removed: they were only
   needed to beat a second, conflicting .container rule further down this file
   (max-width 1240px / padding 24px), and that rule has been deleted. Keeping
   them also silently killed every responsive padding step, which is why a
   1000px-wide screen was still losing 112px to desktop padding. */
.container {
    width: 100%;
    max-width: 1440px;
    margin: 0 auto;
    /* 4.5rem = 72px, taken from the design comp: measured at 43-46px on an
       864px render, and 864 is exactly 1440 x 0.6, so the scaling is exact
       rather than estimated. That put the intended gutter at ~74px across five
       separate content rows; 4.5rem is the round number inside that. Content
       width at 1440 is therefore 1296px.

       Read from --dv-gutter rather than written here, because .container is no
       longer the only thing that needs it: the homepage hero is full bleed, so
       its text panel is not inside a container, and its headline still has to
       start on the same pixel as every section heading below it. One variable
       means the two cannot drift — which is the whole reason the product card
       and the journal card were unified earlier in this branch.

       The step-down ladder lives in responsive.css and moves the variable, not
       this rule — see the note there. */
    padding: 0 var(--dv-gutter);
    box-sizing: border-box;
}

/* ── Reset ─────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* overflow-x uses `clip` (with `hidden` as the legacy fallback) so that html never
   becomes a scroll container — `hidden` would break position:sticky site-wide.
   See the matching note in responsive.css. */
html { scroll-behavior: smooth; overflow-x: hidden; overflow-x: clip; }

/* ── Focus ─────────────────────────────────────────────────────────────
   The ring is back.

   This block used to be a blanket reset:

       button:focus, a:focus, input:focus, … {
           outline: none !important;
           box-shadow: 0 2px 6px rgba(0,0,0,0.03) !important;
       }

   That removed every focus indicator on the shop and replaced it with a
   shadow at three per cent opacity — invisible in practice. Someone working
   through checkout on a keyboard had no way to tell which field or button
   they were on, all the way to Place Order. It is a WCAG 2.4.7 failure and
   the first rule of the design skill's accessibility category: never remove
   the outline without a replacement.

   The `!important` also made it unfixable from anywhere else in this file,
   so every later attempt at a focus style was dead on arrival.

   What replaces it keeps the original intent — no ring when you CLICK
   something — because :focus-visible only matches keyboard and equivalent
   input. Mouse users see nothing; keyboard users see the ring.

   Two layers, because no single colour works on both grounds: burgundy
   reads 12.6:1 on the pale pages, and the white gap around it is what makes
   the ring visible against the near-black footer, where burgundy alone
   would disappear. */
button:focus-visible,
a:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible,
summary:focus-visible {
    outline: 2px solid var(--color-primary, #511126);
    outline-offset: 2px;
    box-shadow: 0 0 0 2px #fff;
}

/* A mouse click leaves no ring — the reason the reset existed at all. */
button:focus:not(:focus-visible),
a:focus:not(:focus-visible),
select:focus:not(:focus-visible) {
    outline: none;
}

/* ── Tap highlight ───────────────────────────────────────────
   iOS and Android paint a translucent grey/blue rectangle over any element the
   moment it is tapped. It is not a focus indicator and it respects none of the
   styling below it — over a product photo or a rounded pill it reads as a
   rendering fault rather than feedback, which is exactly how it looked on the
   card grid.

   Removing it costs nothing: every interactive control here already has its own
   :hover / :active / .is-active treatment, and keyboard focus rings are outlines
   set per component, so they are untouched by this. */
html {
    -webkit-tap-highlight-color: transparent;
    -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

/* ── Autofilled fields ───────────────────────────────────────
   Chrome overrides an input's background with its own pale yellow (or blue in
   dark mode) once it autofills it, and ignores `background` to do so. On the
   checkout and login forms that produced a row of yellow boxes among white
   ones. The huge inset shadow is the documented way to repaint it, and
   -webkit-text-fill-color keeps the typed value the right colour. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
textarea:-webkit-autofill,
select:-webkit-autofill {
    -webkit-box-shadow: 0 0 0 1000px var(--bg-surface) inset !important;
    box-shadow: 0 0 0 1000px var(--bg-surface) inset !important;
    -webkit-text-fill-color: var(--text-primary) !important;
    caret-color: var(--text-primary);
    transition: background-color 9999s ease-in-out 0s;
}

.navbar-luxury {
    position: fixed !important;
    top: 0 !important;
    left: 0 !important;
    right: 0 !important;
    width: 100% !important;
    z-index: 1000 !important;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-light);
    box-shadow: 0 4px 15px rgba(0,0,0,0.06);
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}

/* ══ High-End Ultra-Slim Luxury Sticky Header ════════════ */
.logo img {
    width: 100%;
    transition: height 0.25s ease !important;
}

.main-header-bar {
    /* This is the rule that decides the header height. .main-header-bar is
       declared twice in this file — the other one (further down, with the
       background and border) also sets a padding, but without !important, so
       it never applies. Change the value here, not there. */
    padding: 20px 0 !important;
    transition: padding 0.25s ease !important;
}

/* ══ Compact Luxury Currency Selector ═════════════════════ */
.header-brand-group {
    display: flex;
    align-items: center;
    gap: 12px;
}

.currency-selector-wrapper {
    display: inline-flex;
    align-items: center;
}

#currencySelector, #countrySelector {
    /* Quieter than it was. It sits in a row of thin outline icons, and a filled,
       heavily bordered pill pulled the eye straight to the least important
       control there. background-COLOR, not the shorthand — the shorthand would
       wipe the arrow set further down this same block. */
    background-color: rgba(197, 155, 75, 0.07) !important;
    border: 1px solid rgba(197, 155, 75, 0.3) !important;
    font-family: var(--font-body), sans-serif !important;
    font-size: 12px !important;
    /* 700 was heavier than every label beside it. */
    font-weight: 700 !important;
    letter-spacing: 0.04em !important;
    color: var(--text-primary) !important;
    cursor: pointer !important;
    outline: none !important;
    /* Sized for the two-letter label it now carries. It kept the padding from
       when this read "IN R INR", so "UK" floated in an oval of empty space. */
    padding: 5px 22px 5px 11px !important;
    height: 32px !important;
    /* A hard ceiling, so a long country name can never widen this control again
       and shove the icon row under the centred search bar. The labels are short
       by design (see includes/header.php) — this is the belt to that braces. */
    max-width: 62px !important;
    text-overflow: ellipsis !important;
    line-height: 1 !important;
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='8' height='5' viewBox='0 0 10 6'%3E%3Cpath fill='%23C59B4B' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E") !important;
    background-repeat: no-repeat !important;
    background-position: right 8px center !important;
    /* Only the two things that actually change on hover. `all` also animated
       background-image, which is what made the arrow appear to slide away
       rather than simply be replaced. */
    transition: background-color 0.2s ease, border-color 0.2s ease !important;
    box-shadow: 0 2px 6px rgba(0,0,0,0.03) !important;
}

#currencySelector:hover,
#currencySelector:focus,
#countrySelector:hover,
#countrySelector:focus {
    /* background-COLOR, never the shorthand.
       ─────────────────────────────────────────────────────────────
       `background: #ffffff` resets background-image along with the colour, and
       the dropdown arrow IS a background-image (the inline SVG a few rules up).
       So the arrow vanished the instant the pointer touched the control and
       came back when it left — "going somewhere and coming".

       Exactly the same mistake sat in the mobile rule in responsive.css. The
       shorthand is the trap: it looks like it only sets a colour. */
    background-color: #ffffff !important;
    border-color: var(--color-primary) !important;
    box-shadow: 0 4px 14px rgba(197, 155, 75, 0.2) !important;
    color: var(--color-accent) !important;
    /* No translateY. A control that lifts on hover is fine on a card; in a row
       of static icons it reads as a wobble, and this one sits inches from the
       cart button. */
}

#currencySelector option, #countrySelector option {
    background: #ffffff;
    color: #1e293b;
    font-weight: 600;
    padding: 8px 12px;
}

.show-mobile-only {
    display: none !important;
}

/* ══ Mobile Bottom Navigation Dock ═════════════════════════ */
.mobile-bottom-dock {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100%;
    background: rgba(255, 255, 255, 0.98);
    backdrop-filter: blur(16px);
    -webkit-backdrop-filter: blur(16px);
    border-top: 1px solid var(--border-light);
    z-index: 998;
    box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.06);
    padding: 6px 0 calc(6px + env(safe-area-inset-bottom));
}

.mobile-dock-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 3px;
    color: var(--text-secondary);
    text-decoration: none;
    background: none;
    border: none;
    padding: 4px 0;
    cursor: pointer;
    font-family: var(--font-body), sans-serif;
    font-size: 11px;
    font-weight: 500;
    transition: var(--transition);
}

.mobile-dock-item i {
    font-size: 20px;
    color: var(--text-primary);
    transition: var(--transition);
}

.mobile-dock-item span {
    font-size: 11px;
    line-height: 1.2;
    color: var(--text-secondary);
}

.mobile-dock-item.active,
.mobile-dock-item:hover {
    color: var(--color-primary);
}

.mobile-dock-item.active i,
.mobile-dock-item:hover i {
    color: var(--color-primary);
}

.mobile-dock-whatsapp i {
    color: #25d366 !important;
    font-size: 22px;
}

.navbar-luxury {
    /* background: rgba(255, 255, 255, 0.94) !important; */
    background: rgba(255, 255, 255, 0.8) !important;
    /* backdrop-filter: blur(16px) saturate(180%) !important; */
    backdrop-filter: blur(5px) !important;
    /* -webkit-backdrop-filter: blur(16px) saturate(180%) !important; */
    -webkit-backdrop-filter: blur(5px) !important;
    border-bottom: 1px solid rgba(197, 155, 75, 0.18) !important;
    transition: background 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease !important;
}

/* Ultra-Slim Sticky Mode */
.navbar-luxury.header-compact {
    /* background: rgba(255, 255, 255, 0.98) !important; */
    background: rgba(255, 255, 255, 0.8) !important;
    border-bottom: 1px solid rgba(197, 155, 75, 0.35) !important;
    box-shadow: 0 8px 25px rgba(20, 4, 8, 0.08) !important;
}



.navbar-luxury.header-compact .category-nav-bar {
    padding: 2px 0 !important;
}

.main-nav-links a {
    position: relative;
    padding-bottom: 4px;
}

.main-nav-links a::after {
    content: '';
    position: absolute;
    bottom: 0;
    left: 50%;
    width: 0;
    height: 2px;
    background: linear-gradient(90deg, #C59B4B, #E6C687);
    transition: width 0.3s ease, left 0.3s ease;
}

.main-nav-links a:hover::after {
    width: 100%;
    left: 0;
}

body:not(.admin-wrapper) {
    background-color: var(--bg-main);
    color: var(--text-primary);
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.65;
    max-width: 100%;
    padding-top: var(--header-height, 145px) !important;
}

body.admin-wrapper {
    padding-top: 56px !important;
}

a { color: inherit; text-decoration: none; transition: var(--transition); }
img { max-width: 100%; height: auto; display: block; }
button { font-family: inherit; border: none; outline: none; background: none; cursor: pointer; transition: var(--transition); }

h1, h2, h3, h4, h5 {
    font-family: var(--font-heading);
    color: var(--text-primary);
    /* Reads from the variable instead of a hardcoded 700.
       Marcellus ships a single 400 weight, so asking for 700 made every heading
       on the site a browser-synthesised bold — glyphs smeared sideways, serifs
       blunted, hairlines thickened unevenly. Going through the variable means
       one line decides it, and a future font with real bold cuts only needs
       --font-heading-weight-strong changed. */
    font-weight: var(--font-heading-weight, 400);
    line-height: 1.2;
    /* Was -0.02em. Negative tracking pulls the letters together, which suits a
       tightly-fitted sans but works against a serif with generous sidebearings:
       squeezing Marcellus collides its bracketed serifs. The logo wordmark is
       spaced open, not tight, so normal matches it. */
    letter-spacing: normal;
}

/* ── Scrollbar ─────────────────────────────────────────────── */
::-webkit-scrollbar { width: 6px; }
::-webkit-scrollbar-track { background: var(--bg-main); }
::-webkit-scrollbar-thumb { background: var(--color-primary-light); }
::-webkit-scrollbar-thumb:hover { background: var(--color-primary); }

/* ── Utilities ─────────────────────────────────────────────── */

.gradient-text {
    background: var(--grad-text);
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    display: inline-block;
}




/* Cart button */
.cart-btn {
    position: relative;
    width: 42px; height: 42px;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-surface);
    border: 1.5px solid var(--border-color);
    font-size: 18px;
    color: var(--text-secondary);
    cursor: pointer;
    transition: var(--transition);
    box-shadow: var(--shadow-xs);
}
.cart-btn:hover {
    background: var(--color-primary-bg);
    border-color: var(--color-primary);
    color: var(--color-primary);
    box-shadow: var(--shadow-glow);
    transform: scale(1.05);
}
.cart-badge {
    position: absolute;
    top: -4px; right: -4px;
    background: var(--grad-primary);
    color: white;
    font-size: 10px; font-weight: 700;
    width: 19px; height: 19px;
    display: flex; align-items: center; justify-content: center;
    border: 2px solid var(--bg-main);
    font-family: var(--font-heading);
    transition: var(--transition-bounce);
}
.cart-badge.bump { transform: scale(1.5); }

/* ── Buttons ───────────────────────────────────────────────── */
.btn-primary {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 13px 26px;
    font-size: 14px; font-weight: 700;
    background: var(--grad-primary);
    color: white;
    border: none; cursor: pointer;
    box-shadow: 0 4px 18px rgba(255, 77, 109, 0.35);
    transition: var(--transition);
    font-family: var(--font-heading);
    letter-spacing: 0.01em;
    white-space: nowrap;
}
.btn-primary:hover {
    transform: translateY(-2px);
    box-shadow: 0 8px 28px rgba(255, 77, 109, 0.45);
}
.btn-primary:active { transform: translateY(0); }

.btn-secondary {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 12px 24px;
    font-size: 14px; font-weight: 600;
    background: var(--bg-surface);
    color: var(--text-primary);
    border: 1.5px solid var(--border-light);
    cursor: pointer; transition: var(--transition);
    font-family: var(--font-heading);
    box-shadow: var(--shadow-xs);
    white-space: nowrap;
}
.btn-secondary:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
    background: var(--color-primary-bg);
    transform: translateY(-1px);
    box-shadow: var(--shadow-sm);
}

.btn-danger {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 15px;
    font-size: 12px; font-weight: 600;
    background: #fff5f5; color: var(--color-danger);
    border: 1px solid #fecaca; cursor: pointer; transition: var(--transition);
}
.btn-danger:hover { background: #fee2e2; border-color: var(--color-danger); }

.btn-outline {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 8px 15px;
    font-size: 12px; font-weight: 600;
    background: transparent; color: var(--color-primary);
    border: 1px solid var(--border-strong); cursor: pointer; transition: var(--transition);
}
.btn-outline:hover { background: var(--color-secondary-bg); border-color: var(--color-secondary); }

/* ── Hero ──────────────────────────────────────────────────── */
.hero {
    position: relative;
    overflow: hidden;
    padding: 0;
    background: var(--bg-surface);
}

/* Colourful top band */
.hero-band {
    background: var(--grad-hero);
    padding: 70px 0 120px;
    position: relative;
    overflow: hidden;
}
.hero-band::before {
    content: '';
    position: absolute;
    bottom: -2px; left: 0; right: 0;
    height: 80px;
    background: var(--bg-surface);
}
/* Decorative circles */
.hero-band::after {
    content: '';
    position: absolute;
    top: -60px; right: -60px;
    width: 280px; height: 280px;
    background: rgba(255,255,255,0.08);
    pointer-events: none;
}

.hero-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 48px;
    align-items: center;
    position: relative;
    z-index: 1;
}

.hero-eyebrow {
    display: inline-flex; align-items: center; gap: 8px;
    background: rgba(255,255,255,0.2);
    backdrop-filter: blur(8px);
    border: 1px solid rgba(255,255,255,0.35);
    padding: 6px 16px;
    font-size: 12px; font-weight: 700;
    color: rgba(255,255,255,0.95);
    margin-bottom: 20px;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}

.hero-title {
    font-size: 54px;
    color: #ffffff;
    margin-bottom: 18px;
    text-shadow: 0 2px 20px rgba(0,0,0,0.1);
}
.hero-subtitle {
    font-size: 16px;
    color: rgba(255,255,255,0.85);
    margin-bottom: 32px;
    max-width: 480px;
    line-height: 1.7;
}
.hero-buttons { display: flex; gap: 14px; flex-wrap: wrap; }
.btn-hero-primary {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 14px 28px;
    font-size: 15px; font-weight: 700;
    background: white; color: var(--color-primary);
    border: none; cursor: pointer;
    box-shadow: 0 4px 20px rgba(0,0,0,0.15);
    transition: var(--transition);
    font-family: var(--font-heading);
}
.btn-hero-primary:hover { transform: translateY(-2px); box-shadow: 0 8px 30px rgba(0,0,0,0.2); }

.btn-hero-secondary {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 14px 24px;
    font-size: 15px; font-weight: 600;
    background: rgba(255,255,255,0.18);
    backdrop-filter: blur(8px);
    color: white; border: 1.5px solid rgba(255,255,255,0.4);
    cursor: pointer; transition: var(--transition);
    font-family: var(--font-heading);
}
.btn-hero-secondary:hover { background: rgba(255,255,255,0.28); transform: translateY(-2px); }

/* Hero visual (right side) */
.hero-visual {
    display: flex; align-items: center; justify-content: center;
    position: relative; height: 340px;
}
.hero-card {
    background: white;
    padding: 28px;
    width: 290px;
    text-align: center;
    box-shadow: 0 20px 60px rgba(0,0,0,0.18);
    position: relative; z-index: 2;
    animation: floatingCard 5s infinite ease-in-out;
}
.hero-card-emoji { font-size: 72px; margin-bottom: 10px; display: block; }
.hero-card h3 { font-size: 18px; color: var(--text-primary); margin-bottom: 4px; }
.hero-card p  { font-size: 13px; color: var(--text-muted); margin-bottom: 14px; }
.hero-card-footer { display: flex; justify-content: space-between; align-items: center; }
.hero-bubble {
    position: absolute;
    background: rgba(255,255,255,0.15);
    animation: floatBubble 6s infinite ease-in-out;
}

/* Stats strip */
.hero-stats {
    display: flex; gap: 0;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-light);
    padding: 20px 0;
}
.hero-stat {
    flex: 1;
    text-align: center;
    padding: 0 24px;
    border-right: 1px solid var(--border-light);
}
.hero-stat:last-child { border-right: none; }
.hero-stat-value { font-size: 26px; font-weight: 800; font-family: var(--font-heading); color: var(--color-primary); }
.hero-stat-label { font-size: 12px; color: var(--text-muted); margin-top: 2px; }

/* ── Section Helpers ───────────────────────────────────────── */
.section-label {
    display: inline-block;
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.12em;
    color: var(--color-primary);
    background: var(--color-primary-bg);
    border: 1px solid rgba(255,77,109,0.2);
    padding: 5px 14px;
    margin-bottom: 14px;
}
.section-header { text-align: center; margin-bottom: 45px; position: relative; }
.section-title  { font-family: var(--font-heading); font-size: 32px; font-weight: var(--font-heading-weight); text-transform: uppercase; letter-spacing: 0.12em; margin-bottom: 12px; color: var(--text-primary); }
.section-header::after {
    content: '';
    display: block;
    width: 50px;
    height: 2px;
    background: linear-gradient(90deg, var(--color-accent), var(--color-primary));
    margin: 14px auto 0;
}
.section-subtitle { font-size: 14px; color: var(--text-secondary); max-width: 480px; margin: 0 auto; line-height: 1.6; }


/* ── Products Section ──────────────────────────────────────── */
.products-section { background: var(--bg-main); }

/* Category Tabs */
.category-tabs { display: flex; justify-content: center; gap: 8px; margin-bottom: 40px; flex-wrap: wrap; }
.cat-tab {
    padding: 9px 20px;
    font-size: 13px; font-weight: 600;
    color: var(--text-secondary);
    background: var(--bg-surface);
    border: 1.5px solid var(--border-light);
    cursor: pointer; transition: var(--transition);
    box-shadow: var(--shadow-xs);
}
.cat-tab:hover { border-color: var(--color-primary); color: var(--color-primary); background: var(--color-primary-bg); }
.cat-tab.active {
    background: var(--grad-primary);
    color: white; border-color: transparent;
    box-shadow: 0 4px 14px rgba(255,77,109,0.30);
}

/* Products Grid */
.products-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 24px; }

/* ── Product Card (shared design: shop, home, wishlist, related) ──── */
.product-card {
    display: flex;
    flex-direction: column;
    position: relative;
    border: 1px solid var(--border-light);
    background: var(--bg-surface);
    padding-bottom: 25px;
    /* Keeps a hover-scaled photograph inside the card's own box. */
    overflow: hidden;
    transition: all 0.5s ease 0s !important;
}
.product-card:hover {
    transform: translateY(-6px);
    box-shadow: var(--shadow-md);
    border-color: var(--border-color);
}

.card-img-container {
    position: relative;
    overflow: hidden;
    /*
     * aspect-ratio, not a fixed height.
     *
     * A fixed 320px against a ~268px card is a ratio of 0.84, while most of the
     * catalogue is shot at 896x1200 — 3:4, or 0.75. object-fit:cover then had to
     * crop about 11% off the top and bottom of every one of those photos, which
     * on a full-length garment is the hem and the model's head.
     *
     * 3/4 matches the dominant format, so those images now show essentially
     * uncropped, and every card still shares one shape so the grid stays even.
     * The box also scales with the column instead of staying 320px at any width.
     */
    aspect-ratio: 3 / 4;
    height: auto;
    border-bottom: 1px solid var(--border-light);
}
.card-img-link { display: block; height: 100%; }
.card-img { width: 100%; height: 100%; object-fit: cover; transition: opacity 0.3s ease, transform 1.2s cubic-bezier(0.22, 1, 0.36, 1); }
.card-img-fallback {
    width: 100%; height: 100%;
    background: var(--bg-surface-soft);
    display: flex; align-items: center; justify-content: center;
    font-size: 60px;
}

/* ── Scroll-reveal fade-in (site-wide utility, see footer.php) ──────── */
.reveal-on-scroll {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity 0.6s ease, transform 0.6s ease;
}
.reveal-on-scroll.revealed {
    opacity: 1;
    transform: translateY(0);
}
@media (prefers-reduced-motion: reduce) {
    .reveal-on-scroll { opacity: 1; transform: none; transition: none; }
    .quick-view-modal, .quick-view-content, .product-lightbox-modal, .product-lightbox-container-wrapper {
        transition: none;
    }
}

.quick-view-btn {
    position: absolute;
    bottom: 15px; left: 50%;
    transform: translateX(-50%);
    background: rgba(255,255,255,0.95);
    border: 1px solid var(--border-strong);
    padding: 10px 20px;
    font-size: 11px; text-transform: uppercase; font-weight: 500;
    cursor: pointer;
    display: none;
    white-space: nowrap;
    color: var(--text-primary);
    transition: all 0.3s ease;
    z-index: 5;
}

.product-card-cta { padding: 8px 24px; font-size: 10px; width: 100%; justify-content: center; margin-top: auto; }
.product-card-actions { display: flex; flex-direction: column; gap: 8px; width: 100%; margin-top: auto; }
.product-card-actions .btn-luxury, .product-card-actions .btn-luxury-outline { width: 100%; padding: 10px; font-size: 11px; }
.product-card-sizes { font-size: 10px; color: var(--text-muted); text-transform: uppercase; margin-bottom: 8px; display: block; }

/* ── Cart Sidebar ──────────────────────────────────────────── */
.cart-overlay {
    position: fixed; inset: 0;
    background: rgba(26, 10, 20, 0.4);
    backdrop-filter: blur(3px);
    z-index: 99998 !important;
    opacity: 0; pointer-events: none;
    transition: opacity 0.35s ease;
}
.cart-overlay.open { opacity: 1; pointer-events: all; }

.cart-sidebar {
    position: fixed;
    top: 0; right: 0;
    width: 420px; max-width: 100vw;
    height: 100vh; height: 100dvh;
    background: var(--bg-surface);
    border-left: 1px solid var(--border-light);
    z-index: 99999 !important;
    display: flex; flex-direction: column;
    transform: translateX(100%);
    transition: transform 0.38s cubic-bezier(0.4, 0, 0.2, 1);
    box-shadow: -8px 0 40px rgba(26,10,20,0.12);
    box-sizing: border-box;
}
.cart-sidebar.open { transform: translateX(0); }

.cart-header {
    padding: 20px 24px;
    border-bottom: 1px solid var(--border-light);
    display: flex; align-items: center; justify-content: space-between;
    background: var(--bg-main);
}
.cart-header h2 { font-size: 19px; display: flex; align-items: center; gap: 10px; color: var(--text-primary); font-family: var(--font-heading); font-weight: var(--font-heading-weight); text-transform: uppercase; letter-spacing: 0.05em; margin: 0; }

.btn-close-cart {
    width: 34px; height: 34px;
    display: flex; align-items: center; justify-content: center;
    background: var(--bg-surface); border: 1px solid var(--border-light);
    font-size: 16px; color: var(--text-muted); cursor: pointer; transition: var(--transition);
}
.btn-close-cart:hover { background: #fff5f5; border-color: var(--color-danger); color: var(--color-danger); }

.cart-items { flex: 1; overflow-y: auto; padding: 16px 20px; background: var(--bg-surface); }
.cart-empty { text-align: center; padding: 60px 20px; }
.cart-empty-icon { font-size: 60px; margin-bottom: 14px; opacity: 0.35; }
.cart-empty p { font-size: 15px; color: var(--text-secondary); }

.cart-item {
    display: flex; align-items: center; gap: 12px;
    padding: 16px 0; border-bottom: 1px solid var(--border-light);
}
.cart-item:last-child { border-bottom: none; }
.cart-item-img {
    width: 65px; height: 75px; object-fit: cover;
    border: 1px solid var(--border-light); flex-shrink: 0;
}
.cart-item-info { flex: 1; min-width: 0; }
.cart-item-name {
    font-size: 13px; font-weight: 600; margin-bottom: 4px; color: var(--text-primary);
    line-height: 1.35; word-break: break-word; white-space: normal;
}
.cart-item-price { font-size: 13px; color: var(--color-primary); font-weight: 700; }

/* The stepper's circle-button look lived here. It is now one pill, defined once
   at the end of this file — leaving a superseded copy behind is the same
   two-definitions problem the unification was for. */

.btn-remove-item { color: var(--text-muted); font-size: 14px; padding: 4px 6px; cursor: pointer; transition: var(--transition); background: none; border: none; flex-shrink: 0; }
.btn-remove-item:hover { color: var(--color-danger); }

.cart-footer {
    padding: 20px 24px 24px;
    border-top: 1px solid var(--border-light);
    background: var(--bg-main);
    display: flex;
    flex-direction: column;
    gap: 14px;
    box-sizing: border-box;
    width: 100%;
}
.cart-total-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.cart-total-label  { font-size: 14px; color: var(--text-secondary); }
.cart-total-amount { font-size: 26px; font-weight: 400; font-family: var(--font-heading); color: var(--color-primary); }
.cart-footer .btn-luxury,
.cart-footer .btn-luxury-outline {
    width: 100% !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    height: 48px !important;
    padding: 0 16px !important;
    font-size: 12px !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    letter-spacing: 0.08em !important;
    box-sizing: border-box !important;
    margin: 0 0 4px 0 !important;
    text-decoration: none !important;
}

.btn-clear-cart   { width: 100%; padding: 8px; font-size: 12px; font-weight: 600; color: var(--text-muted); background: none; border: none; cursor: pointer; transition: var(--transition); text-align: center; margin-top: 4px; }
.btn-clear-cart:hover { color: var(--color-danger); }


/* ── Toast ─────────────────────────────────────────────────── */
.toast {
    position: fixed; bottom: 28px; left: 50%;
    transform: translateX(-50%) translateY(80px);
    background: white;
    border: 1px solid var(--border-light);
    border-left: 4px solid var(--color-primary);
    padding: 14px 22px;
    display: flex; align-items: center; gap: 12px;
    min-width: 270px; max-width: 360px;
    z-index: 400;
    opacity: 0;
    box-shadow: var(--shadow-md);
    transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast-icon { font-size: 22px; flex-shrink: 0; }
.toast-text { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.toast-sub  { font-size: 12px; color: var(--text-secondary); margin-top: 2px; }

/* ── Alerts ────────────────────────────────────────────────── */
.alert {
    display: flex; align-items: flex-start; gap: 12px;
    padding: 14px 18px;
    font-size: 14px; font-weight: 500; margin-bottom: 20px;
}
.alert i { margin-top: 1px; flex-shrink: 0; }
.alert-danger  { background: #fff5f5; border: 1px solid #fecaca; color: #b91c1c; }
.alert-success { background: var(--color-secondary-bg); border: 1px solid rgba(0,201,167,0.3); color: #059669; }
.alert-info    { background: var(--color-primary-bg); border: 1px solid rgba(255,77,109,0.25); color: var(--color-primary-dark); }

/* ── Form Controls ─────────────────────────────────────────── */
.form-group  { margin-bottom: 20px; }
.form-label  {
    display: block;
    font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--text-secondary); margin-bottom: 8px;
}
.form-control {
    width: 100%; padding: 13px 16px;
    background: var(--bg-main);
    border: 1.5px solid var(--border-light);
    color: var(--text-primary);
    font-family: var(--font-body); font-size: 14px;
    transition: var(--transition);
}
.form-control:focus {
    outline: none;
    border-color: var(--color-primary);
    background: white;
    box-shadow: 0 0 0 3px rgba(255,77,109,0.10);
}
.form-control::placeholder { color: var(--text-muted); }
textarea.form-control { resize: vertical; min-height: 100px; }
select.form-control { appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='%23b8a0b0' d='M5 8l5 5 5-5z'/%3E%3C/svg%3E"); background-repeat: no-repeat; background-position: right 12px center; background-size: 20px; padding-right: 36px; }

/* ── Checkout Page ─────────────────────────────────────────── */
.checkout-page { padding: 56px 0 80px; background: var(--bg-main); }
.checkout-grid { display: grid; grid-template-columns: 1.1fr 0.9fr; gap: 32px; align-items: start; }
.section-card { padding: 30px; background: var(--bg-surface); border: 1.5px solid var(--border-light); box-shadow: var(--shadow-sm); }
.section-card h2 { font-size: 20px; margin-bottom: 22px; display: flex; align-items: center; gap: 10px; }

.order-summary-item {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 0; border-bottom: 1px solid var(--border-light);
}
.order-summary-item:last-of-type { border-bottom: none; }
.osi-emoji  { font-size: 28px; width: 40px; text-align: center; }
.osi-info   { flex: 1; }
.osi-name   { font-size: 14px; font-weight: 600; color: var(--text-primary); }
.osi-qty    { font-size: 12px; color: var(--text-muted); }
.osi-price  { font-size: 15px; font-weight: 700; color: var(--color-primary); }

.summary-divider     { border: none; border-top: 1px solid var(--border-light); margin: 18px 0; }
.summary-total-row   { display: flex; justify-content: space-between; align-items: center; }
.summary-total-label { font-size: 15px; color: var(--text-secondary); }
.summary-total-price { font-size: 28px; font-weight: 800; font-family: var(--font-heading); color: var(--color-primary); }

/* ── Order Confirmation ────────────────────────────────────── */
.confirmation-page      { padding: 80px 0; text-align: center; background: var(--bg-main); }
.confirmation-icon      { font-size: 88px; margin-bottom: 12px; animation: bounceIn 0.8s cubic-bezier(0.34,1.56,0.64,1); }
.confirmation-title     { font-size: 42px; margin-bottom: 12px; }
.confirmation-subtitle  { font-size: 16px; color: var(--text-secondary); margin: 0 auto 32px; max-width: 480px; line-height: 1.6; }

.order-code-box {
    display: inline-block;
    background: var(--color-primary-bg);
    border: 2px dashed rgba(255,77,109,0.35);
    padding: 16px 36px; margin-bottom: 32px;
}
.order-code-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-muted); margin-bottom: 6px; }
.order-code-value { font-size: 28px; font-weight: 800; font-family: var(--font-heading); color: var(--color-primary); letter-spacing: 3px; }

.confirmation-details { max-width: 540px; margin: 0 auto 36px; padding: 28px; background: var(--bg-surface); border: 1.5px solid var(--border-light); box-shadow: var(--shadow-sm); text-align: left; }
.conf-row         { display: flex; gap: 12px; padding: 10px 0; border-bottom: 1px solid var(--border-light); }
.conf-row:last-child { border-bottom: none; }
.conf-label       { font-size: 13px; color: var(--text-muted); width: 120px; flex-shrink: 0; padding-top: 2px; }
.conf-value       { font-size: 14px; font-weight: 600; flex: 1; color: var(--text-primary); }

/* ── Status Badges ─────────────────────────────────────────── */
.status-badge {
    display: inline-block; padding: 4px 12px;
    font-size: 11px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.05em;
}
.status-pending    { background: #fef9c3; color: #a16207; border: 1px solid #fde68a; }
.status-processing { background: var(--color-secondary-bg); color: #047857; border: 1px solid rgba(0,201,167,0.3); }
.status-delivered  { background: #d1fae5; color: #065f46; border: 1px solid #6ee7b7; }
.status-cancelled  { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }

/* ── Admin Styles ──────────────────────────────────────────── */
.admin-wrapper { min-height: 100vh; display: flex; flex-direction: column; background: var(--bg-main); }
.admin-content { flex: 1; padding: 36px 0 60px; }

/* Tabs */
.admin-tabs { display: flex; gap: 4px; border-bottom: 2px solid var(--border-light); margin-bottom: 28px; }
.admin-tab {
    padding: 11px 22px; font-size: 14px; font-weight: 600; cursor: pointer;
    color: var(--text-secondary); border-bottom: 2px solid transparent; margin-bottom: -2px;
    transition: var(--transition);
    display: flex; align-items: center; gap: 8px;
}
.admin-tab:hover { color: var(--text-primary); background: var(--color-primary-bg); }
.admin-tab.active { color: var(--color-primary); border-bottom-color: var(--color-primary); background: var(--color-primary-bg); }

/* Stats */
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 16px; margin-bottom: 28px; }
.stat-card {
    padding: 22px 24px;
    background: var(--bg-surface);
    border: 1.5px solid var(--border-light);
    box-shadow: var(--shadow-sm);
    position: relative; overflow: hidden;
}
.stat-card::before {
    content: '';
    position: absolute; top: 0; left: 0; right: 0; height: 3px;
    background: var(--grad-primary);
}
.stat-card-icon { font-size: 26px; margin-bottom: 10px; }
.stat-label     { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin-bottom: 6px; }
.stat-value     { font-size: 30px; font-weight: 800; font-family: var(--font-heading); color: var(--color-primary); }

/* Data Table */
.table-wrapper { overflow-x: auto; }
.data-table    { width: 100%; border-collapse: collapse; }
.data-table th {
    padding: 12px 16px; text-align: left;
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--text-muted); border-bottom: 1.5px solid var(--border-light);
    background: var(--bg-main); white-space: nowrap;
}
.data-table td { padding: 14px 16px; border-bottom: 1px solid var(--border-light); font-size: 14px; vertical-align: middle; }
.data-table tr:hover td { background: var(--bg-main); }
.data-table tr:last-child td { border-bottom: none; }

/* Order items pill */
.items-pill {
    display: inline-flex; align-items: center; gap: 4px;
    background: var(--color-primary-bg); border: 1px solid rgba(255,77,109,0.15);
    padding: 3px 10px; font-size: 12px; margin: 2px;
    color: var(--text-primary);
}

/* Order detail expand */
.order-detail-row      { display: none; }
.order-detail-row td   { padding: 0; }
.order-detail-inner { padding: 20px 24px; background: var(--bg-main); margin: 6px; border: 1px solid var(--border-light); }
.order-detail-grid     { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 16px; }
.order-detail-field label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-muted); display: block; margin-bottom: 4px; }
.order-detail-field p  { font-size: 14px; font-weight: 600; color: var(--text-primary); }

/* Status select */
.status-select {
    padding: 8px 12px;
    background: var(--bg-surface); border: 1.5px solid var(--border-light);
    color: var(--text-primary); font-size: 13px; font-family: var(--font-body); cursor: pointer;
}
.status-select:focus { outline: none; border-color: var(--color-primary); }

/* Action group */
.action-group { display: flex; gap: 8px; align-items: center; }

/* Btn small */
.btn-sm { padding: 7px 14px; font-size: 12px; font-weight: 600; cursor: pointer; transition: var(--transition); display: inline-flex; align-items: center; gap: 5px; }
.btn-sm-primary { background: var(--grad-primary); color: white; border: none; box-shadow: 0 2px 8px rgba(255,77,109,0.25); }
.btn-sm-primary:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(255,77,109,0.35); }
.footer-enhanced { border-top: 1px solid var(--border-light); font-family: var(--font-body); }
.footer-top { display: grid; grid-template-columns: 2fr 1fr 1fr 1.5fr; gap: 40px; margin-bottom: 20px; }
.btn-sm-outline { background: transparent; border: 1px solid var(--border-light); color: var(--text-secondary); }
.btn-sm-outline:hover { border-color: var(--color-primary); color: var(--color-primary); background: var(--color-primary-bg); }
.btn-sm-danger  { background: #fff5f5; border: 1px solid #fecaca; color: var(--color-danger); }
.btn-sm-danger:hover  { background: #fee2e2; border-color: var(--color-danger); }

/* Admin page header */
.admin-page-header   { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 28px; flex-wrap: wrap; gap: 16px; }
.admin-page-title    { font-size: 28px; color: var(--text-primary); }
.admin-page-subtitle { font-size: 14px; color: var(--text-secondary); margin-top: 4px; }

/* Admin glass panel  */
.glass-panel {
    background: var(--bg-surface);
    border: 1.5px solid var(--border-light);
    box-shadow: var(--shadow-sm);
}
.glass-panel:hover { border-color: var(--border-color); box-shadow: var(--shadow-card); }

/* Form section */
.form-section   { padding: 28px; margin-bottom: 20px; }
.form-section h3 { font-size: 16px; margin-bottom: 20px; display: flex; align-items: center; gap: 8px; color: var(--color-primary); }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }

/* ── Login Page ────────────────────────────────────────────── */
.login-page     { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 40px 24px; background: var(--bg-main); }
.login-card     { width: 100%; max-width: 420px; padding: 44px; text-align: center; }
.login-icon     { font-size: 52px; margin-bottom: 14px; }
.login-title    { font-size: 28px; margin-bottom: 6px; }
.login-subtitle { font-size: 14px; color: var(--text-secondary); margin-bottom: 30px; }
.login-form     { text-align: left; }

/* ── Footer ────────────────────────────────────────────────── */
.footer { background: var(--text-primary); padding: 32px 0; margin-top: auto; }
.footer-inner  { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 16px; }
.footer-logo   { font-size: 20px; font-weight: 800; font-family: var(--font-heading); color: white; }
.footer-logo span { color: var(--color-primary-light); }
.footer-copy   { font-size: 13px; color: rgba(255,255,255,0.45); }
.footer-links  { display: flex; gap: 20px; }
.footer-links a { font-size: 13px; color: rgba(255,255,255,0.55); }
.footer-links a:hover { color: var(--color-primary-light); }

/* ── Expand btn (admin) ────────────────────────────────────── */
.expand-btn { cursor: pointer; background: none; border: none; color: var(--color-secondary); font-size: 13px; display: flex; align-items: center; gap: 4px; }
.expand-btn:hover { color: var(--color-primary); }

/* Product form extras */
.emoji-btn {
    font-size: 22px; padding: 8px;
    background: var(--bg-main); border: 1.5px solid var(--border-light);
    cursor: pointer; transition: var(--transition);
    display: inline-flex; align-items: center; justify-content: center;
    width: 44px; height: 44px;
}
.emoji-btn:hover, .emoji-btn.selected {
    background: var(--color-primary-bg); border-color: var(--color-primary); transform: scale(1.1);
}
.preview-card { padding: 28px; display: flex; flex-direction: column; align-items: center; text-align: center; }
.preview-img-wrap {
    width: 140px; height: 140px;
    display: flex; align-items: center; justify-content: center;
    margin-bottom: 14px; transition: var(--transition);
    background: var(--bg-main); border: 2px dashed var(--border-strong);
    overflow: hidden;
}
.preview-img-wrap img { width: 100%; height: 100%; object-fit: cover; }

/* ── Product Accordions ──────────────────────────────────── */
.product-accordion {
    border-bottom: 1px solid var(--border-light);
}
/* .product-detail-static-title rides on this selector deliberately.
   ────────────────────────────────────────────────────────────────────────────
   Design & Fit is a plain heading rather than a button, but it sits in the same
   stack as five accordion headers and has to be indistinguishable from them —
   the only difference a shopper should notice is that it has no +/- to press.

   It began as its own rule with its own numbers and had already drifted: 15px
   against 14px, and 0.9px of tracking against 0.7px. Small enough that nobody
   would name it, big enough that the stack looked subtly uneven. Sharing one
   declaration is the only arrangement in which they cannot drift again. */
.product-accordion-header,
.product-detail-static-title {
    /* These headers are real <button> elements, so the first block strips the
       styling a browser gives a button by default. width:100% is the one that
       matters visually: a button shrink-wraps its text, and without it the
       flex row has nothing to spread, so justify-content:space-between stops
       pushing the +/- icon out to the right edge. */
    width: 100%;
    background: none;
    border: none;
    -webkit-appearance: none;
    appearance: none;
    text-align: left;

    padding: 18px 0;
    font-family: var(--font-heading);
    font-size: 14px;
    text-transform: uppercase;
    font-weight: 500;
    display: flex;
    justify-content: space-between;
    align-items: center;
    cursor: pointer;
    color: var(--text-primary);
    letter-spacing: 0.05em;
    transition: color 0.3s ease;

    /* An <h3> carries a browser margin a <button> does not, and it is not a
       control — so it must not offer a pointer that promises a click. */
    margin: 0;
}
.product-detail-static-title { cursor: default; }
.product-accordion-header:hover {
    color: var(--color-secondary);
}
/* !important is load-bearing here, not habit. The global reset near the top of
   this file (button:focus-visible { outline: none !important }) would otherwise
   erase this ring, leaving these buttons keyboard-focusable but with nothing on
   screen to show where focus is — worse than the old divs, which at least could
   not be tabbed to at all. A negative offset draws the ring just inside the
   box, so a full-width header cannot bleed the outline past its container. */
.product-accordion-header:focus-visible {
    outline: 2px solid var(--color-secondary) !important;
    outline-offset: -2px;
}
.product-accordion-content {
    max-height: 0;
    overflow: hidden;
    transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.6;
    padding: 0;
}

/* In-cart counter badge on product cards */
.in-cart-indicator {
    margin-top: 8px;
    font-size: 12px; font-weight: 700;
    color: var(--color-secondary);
    background: var(--color-secondary-bg);
    border: 1px solid rgba(0,201,167,0.3);
    padding: 3px 10px;
    display: none;
    align-items: center;
    gap: 4px;
    width: fit-content;
}
.in-cart-indicator.visible { display: inline-flex; }

/* Remove the old emoji wrap ::before gradient hack – product-card::before remains the top accent line */

/* ── "View Cart" navbar button ───────────────────────────── */
.btn-view-cart {
    display: inline-flex; align-items: center; gap: 8px;
    padding: 10px 18px;
    font-size: 13px; font-weight: 700;
    background: var(--bg-surface);
    border: 1.5px solid var(--border-color);
    color: var(--text-primary);
    cursor: pointer; transition: var(--transition);
    font-family: var(--font-heading);
    box-shadow: var(--shadow-xs);
    position: relative;
}
.btn-view-cart:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
    background: var(--color-primary-bg);
    box-shadow: var(--shadow-glow);
    transform: translateY(-1px);
}
.btn-view-cart .cart-badge {
    position: relative;
    top: auto; right: auto;
    background: var(--grad-primary);
    color: white; font-size: 10px; font-weight: 700;
    min-width: 20px; height: 20px;
    padding: 0 5px;
    display: inline-flex; align-items: center; justify-content: center;
    border: none;
    font-family: var(--font-heading);
    transition: var(--transition-bounce);
}
.btn-view-cart .cart-badge.bump { transform: scale(1.4); }

/* ── Product image upload (admin form) ───────────────────── */
.image-upload-box {
    border: 2px dashed var(--border-strong);
    padding: 28px 20px;
    text-align: center;
    cursor: pointer;
    transition: var(--transition);
    background: var(--bg-main);
    position: relative;
}
.image-upload-box:hover, .image-upload-box.dragover {
    border-color: var(--color-primary);
    background: var(--color-primary-bg);
}
.image-upload-box input[type="file"] {
    position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%;
}
.image-upload-icon { font-size: 36px; margin-bottom: 10px; opacity: 0.5; }
.image-upload-label { font-size: 13px; color: var(--text-secondary); }
.image-upload-label strong { color: var(--color-primary); }
.image-current-thumb {
    width: 100%; max-width: 240px;
    margin: 12px auto 0; display: block;
    object-fit: cover; border: 1.5px solid var(--border-light);
}

/* ── Gallery Page ─────────────────────────────────────────── */
.gallery-page { padding: 64px 0 80px; background: var(--bg-main); }
.gallery-grid {
    columns: 3;
    column-gap: 18px;
    gap: 18px;
}
.gallery-item {
    break-inside: avoid;
    margin-bottom: 18px;
    overflow: hidden;
    cursor: pointer;
    position: relative;
    display: block;
    transition: var(--transition);
    border: 1.5px solid var(--border-light);
    box-shadow: var(--shadow-xs);
}
.gallery-item:hover { transform: translateY(-4px); box-shadow: var(--shadow-card); border-color: var(--border-color); }
.gallery-item img {
    width: 100%; height: auto; display: block;
    transition: transform 0.45s cubic-bezier(0.4,0,0.2,1);
}
.gallery-item:hover img { transform: scale(1.04); }
.gallery-item-overlay {
    position: absolute; inset: 0;
    background: linear-gradient(to top, rgba(26,10,20,0.65) 0%, transparent 60%);
    opacity: 0; transition: opacity 0.3s ease;
    display: flex; align-items: flex-end; padding: 14px;
}
.gallery-item:hover .gallery-item-overlay { opacity: 1; }
.gallery-item-caption {
    font-size: 13px; font-weight: 600; color: white;
    text-shadow: 0 1px 4px rgba(0,0,0,0.4);
}
.gallery-empty {
    text-align: center; padding: 80px 20px; color: var(--text-muted);
}
.gallery-empty-icon { font-size: 64px; opacity: 0.25; margin-bottom: 14px; }

/* Lightbox */
.lightbox-overlay {
    position: fixed; inset: 0;
    background: rgba(10,0,15,0.92);
    z-index: 1000;
    display: flex; align-items: center; justify-content: center;
    opacity: 0; pointer-events: none;
    transition: opacity 0.3s ease;
    padding: 20px;
}
.lightbox-overlay.open { opacity: 1; pointer-events: all; }
.lightbox-inner {
    position: relative;
    max-width: 90vw; max-height: 90vh;
    animation: lightboxIn 0.35s cubic-bezier(0.34,1.56,0.64,1);
}
.lightbox-inner img {
    max-width: 90vw; max-height: 85vh;
    object-fit: contain;
    display: block; box-shadow: 0 24px 60px rgba(0,0,0,0.5);
}
.lightbox-caption {
    text-align: center; color: rgba(255,255,255,0.8);
    font-size: 14px; margin-top: 14px; font-weight: 500;
}
.lightbox-close {
    position: absolute; top: -16px; right: -16px;
    width: 38px; height: 38px;
    background: white; color: var(--text-primary);
    display: flex; align-items: center; justify-content: center;
    font-size: 18px; cursor: pointer; border: none;
    box-shadow: 0 4px 14px rgba(0,0,0,0.2);
    transition: var(--transition);
}
.lightbox-close:hover { background: var(--color-primary); color: white; transform: scale(1.1); }
@keyframes lightboxIn {
    from { transform: scale(0.85); opacity: 0; }
    to   { transform: scale(1); opacity: 1; }
}

/* Gallery admin grid */
.admin-gallery-grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
    gap: 16px;
}
.admin-gallery-item {
    overflow: hidden;
    border: 1.5px solid var(--border-light);
    position: relative; background: var(--bg-main);
    box-shadow: var(--shadow-xs);
}
.admin-gallery-item img { width: 100%; height: 130px; object-fit: cover; display: block; }
.admin-gallery-item-meta { padding: 8px 10px; }
.admin-gallery-item-caption { font-size: 12px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.admin-gallery-del {
    position: absolute; top: 6px; right: 6px;
    width: 28px; height: 28px;
    background: rgba(239,68,68,0.9); color: white;
    display: flex; align-items: center; justify-content: center;
    font-size: 12px; cursor: pointer; border: none;
    opacity: 0; transition: var(--transition);
    box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.admin-gallery-item:hover .admin-gallery-del { opacity: 1; }

/* ── About / Contact Page ─────────────────────────────────── */
.about-page { padding: 0; background: var(--bg-main); }

.about-hero {
    background: var(--grad-hero);
    padding: 80px 0 100px;
    position: relative; overflow: hidden;
    text-align: center;
}
.about-hero::before {
    content: '';
    position: absolute; bottom: -2px; left: 0; right: 0;
    height: 60px; background: var(--bg-main);
}
.about-hero-eyebrow {
    display: inline-flex; align-items: center; gap: 8px;
    background: rgba(255,255,255,0.2); backdrop-filter: blur(8px);
    border: 1px solid rgba(255,255,255,0.35);
    padding: 6px 18px;
    font-size: 12px; font-weight: 700;
    color: rgba(255,255,255,0.95); margin-bottom: 20px;
    text-transform: uppercase; letter-spacing: 0.08em;
}
.about-hero h1 { color: white; font-size: 50px; margin-bottom: 16px; position: relative; z-index: 1; }
.about-hero p  { color: rgba(255,255,255,0.85); font-size: 17px; max-width: 560px; margin: 0 auto; line-height: 1.7; position: relative; z-index: 1; }

.about-story-section { }
.about-story-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 56px; align-items: center; }
.about-story-img {
    overflow: hidden;
    box-shadow: var(--shadow-lg);
    aspect-ratio: 4/3;
    background: var(--bg-surface-soft);
    display: flex; align-items: center; justify-content: center;
    font-size: 100px;
}
.about-story-img img { width: 100%; height: 100%; object-fit: cover; }
.about-story-content .section-label { margin-bottom: 14px; }
.about-story-content h2 { font-size: 36px; margin-bottom: 18px; }
.about-story-content p { font-size: 15px; color: var(--text-secondary); line-height: 1.75; margin-bottom: 16px; }

/* Contact & Map */
.contact-map-section { background: var(--bg-surface); }
.contact-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; }
.map-container {
    overflow: hidden;
    border: 1.5px solid var(--border-light);
    box-shadow: var(--shadow-sm);
    height: 380px;
}
.map-container iframe { width: 100%; height: 100%; border: none; display: block; }

/* Social Buttons */
.social-links-section { background: var(--bg-main); }
.social-buttons-grid { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; margin-top: 28px; }
.social-btn {
    display: inline-flex; align-items: center; gap: 10px;
    padding: 13px 26px;
    font-size: 14px; font-weight: 700;
    font-family: var(--font-heading);
    text-decoration: none; transition: var(--transition);
    border: 2px solid transparent;
    box-shadow: var(--shadow-sm);
}
.social-btn:hover { transform: translateY(-3px); box-shadow: var(--shadow-md); }
.social-btn-instagram { background: linear-gradient(135deg,#f09433,#e6683c,#dc2743,#cc2366,#bc1888); color: white; }
.social-btn-whatsapp  { background: #25D366; color: white; }
.social-btn-tiktok    { background: #000000; color: white; border-color: #333; }
.social-btn-facebook  { background: #1877F2; color: white; }

/* ── New Home / Landing Page ─────────────────────────────── */
.landing-hero {
    background: var(--grad-hero);
    padding: 90px 0 130px;
    position: relative; overflow: hidden;
    text-align: center;
}
.landing-hero::after {
    content: '';
    position: absolute; bottom: -2px; left: 0; right: 0;
    height: 70px; background: var(--bg-main);
}
.landing-hero-inner { position: relative; z-index: 1; }
.landing-eyebrow {
    display: inline-flex; align-items: center; gap: 8px;
    background: rgba(255,255,255,0.2); backdrop-filter: blur(8px);
    border: 1px solid rgba(255,255,255,0.35);
    padding: 6px 18px;
    font-size: 12px; font-weight: 700;
    color: rgba(255,255,255,0.95); margin-bottom: 24px;
    text-transform: uppercase; letter-spacing: 0.08em;
    display: inline-block;
}
.landing-title { font-size: 68px; color: white; margin-bottom: 20px; text-shadow: 0 2px 20px rgba(0,0,0,0.12); line-height: 1.08; }
.landing-subtitle { font-size: 18px; color: rgba(255,255,255,0.85); margin-bottom: 36px; max-width: 560px; margin-left: auto; margin-right: auto; line-height: 1.7; }
.landing-cta-group { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; }


/* Story section */
.story-section {
    background: var(--bg-surface);
    position: relative; overflow: hidden;
}
.story-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 60px; align-items: center; }
.story-img-block {
    overflow: hidden;
    height: 440px;
    background: var(--bg-surface-soft);
    display: flex; align-items: center; justify-content: center;
    font-size: 120px;
    box-shadow: var(--shadow-lg);
}
.story-img-block img { width: 100%; height: 100%; object-fit: cover; }
.story-content .section-label { margin-bottom: 16px; }
.story-content h2 { font-size: 40px; margin-bottom: 20px; }
.story-content p { font-size: 15px; color: var(--text-secondary); line-height: 1.75; margin-bottom: 14px; }
.story-highlights { display: flex; gap: 24px; margin-top: 28px; flex-wrap: wrap; }
.story-highlight {
    display: flex; align-items: center; gap: 10px;
    background: var(--color-primary-bg);
    border: 1px solid rgba(255,77,109,0.15);
    padding: 12px 18px;
    flex: 1; min-width: 140px;
}
.story-highlight-icon { font-size: 24px; }
.story-highlight-text { font-size: 13px; font-weight: 600; color: var(--text-primary); }

/* Landing CTA Section */
.landing-cta-section {
    background: var(--grad-primary);
    text-align: center;
    position: relative; overflow: hidden;
}
.landing-cta-section::before {
    content: '';
    position: absolute; top: -60px; right: -60px;
    width: 240px; height: 240px;
    background: rgba(255,255,255,0.08);
}
.landing-cta-section h2 { font-size: 38px; color: white; margin-bottom: 14px; position: relative; z-index: 1; }
.landing-cta-section p  { font-size: 16px; color: rgba(255,255,255,0.85); margin-bottom: 32px; position: relative; z-index: 1; }
.btn-cta-white {
    display: inline-flex; align-items: center; gap: 10px;
    padding: 16px 36px;
    font-size: 16px; font-weight: 700;
    background: white; color: var(--color-primary);
    border: none; cursor: pointer;
    box-shadow: 0 6px 24px rgba(0,0,0,0.15);
    transition: var(--transition);
    font-family: var(--font-heading);
    position: relative; z-index: 1;
}
.btn-cta-white:hover { transform: translateY(-3px); box-shadow: 0 12px 36px rgba(0,0,0,0.20); }

/* ── Enhanced Footer ─────────────────────────────────────── */
.footer-enhanced {
    background: var(--text-primary);
    padding: 48px 0 28px;
    margin-top: auto;
}
.footer-top {
    display: grid;
    grid-template-columns: 2fr 1fr 1fr 1fr;
    gap: 40px;
    padding-bottom: 36px;
    border-bottom: 1px solid rgba(255,255,255,0.1);
}
.footer-brand p {
    font-size: 13px; color: rgba(255,255,255,0.5);
    line-height: 1.7; margin-top: 12px; max-width: 240px;
}
.footer-col h4 {
    font-size: 12px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.1em;
    color: rgba(255,255,255,0.4); margin-bottom: 14px;
}
.footer-col ul { list-style: none; }
.footer-col ul li { margin-bottom: 8px; }
.footer-col ul li a { font-size: 13px; color: rgba(255,255,255,0.6); transition: var(--transition); }
.footer-col ul li a:hover { color: var(--color-primary-light); }
.footer-social { display: flex; gap: 10px; margin-top: 16px; }
.footer-social a {
    width: 36px; height: 36px;
    background: rgba(255,255,255,0.08);
    border: 1px solid rgba(255,255,255,0.12);
    display: flex; align-items: center; justify-content: center;
    color: rgba(255,255,255,0.6); font-size: 15px;
    transition: var(--transition);
}
.footer-social a:hover { background: var(--color-primary); border-color: var(--color-primary); color: white; }
.footer-bottom-bar {
    display: flex; justify-content: space-between; align-items: center;
    padding-top: 24px; flex-wrap: wrap; gap: 12px;
}
.footer-bottom-bar .footer-logo { font-size: 20px; font-weight: 800; font-family: var(--font-heading); color: white; }
.footer-bottom-bar .footer-logo span { color: var(--color-primary-light); }
.footer-copy-text { font-size: 12px; color: rgba(255,255,255,0.35); }

/* Country and currency, in words, at the foot of the page.
   ───────────────────────────────────────────────────────────────────────────
   The header's picker is a globe with no label, so this is the only place the
   active country is actually stated. Both controls call the same
   changeCountry(), so they write one cookie and cannot disagree.

   Styled as the footer's own type rather than as a form control: the native
   chrome is stripped and the site's chevron put back, the same treatment the
   header's picker had while it was text. The label is dimmer than the value,
   so the country reads as the answer and "Shipping to" as the question. */
.footer-country {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
}
.footer-country label {
    color: rgba(255, 255, 255, 0.35);
    letter-spacing: 0.02em;
    margin: 0;
}
/* What is actually seen: the country, the currency, and the site's chevron. */
.footer-country-value {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding-bottom: 3px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.18);
    color: rgba(255, 255, 255, 0.78);
    transition: color 0.2s ease, border-color 0.2s ease;
    white-space: nowrap;
    pointer-events: none;   /* the select over it takes the click */
}
.footer-country-value svg {
    width: 10px;
    height: 10px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2.2;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.7;
}
.footer-country:hover .footer-country-value {
    color: #ffffff;
    border-bottom-color: rgba(255, 255, 255, 0.45);
}
/* The real control, invisible over the top: native keyboard handling, native
   screen-reader semantics, native wheel on iOS. opacity, not display — a
   hidden select cannot be focused. */
.footer-country select {
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    -webkit-appearance: none;
    appearance: none;
    background: transparent;
    border: 0;
    padding: 0;
    margin: 0;
    opacity: 0;
    cursor: pointer;
    font-size: 16px;   /* iOS zooms the page on a smaller focused control */
}
.footer-country:focus-within .footer-country-value {
    color: #ffffff;
    outline: 2px solid var(--color-secondary);
    outline-offset: 3px;
}
.footer-country select:focus-visible { outline: none; }
/* Drawn by the operating system, so it cannot take the footer's palette — it
   can at least not be light-on-light. */
.footer-country select option { color: #3a0c1b; background: #ffffff; }

/* ── Cart item image ─────────────────────────────────────── */
.cart-item-img {
    width: 50px; height: 50px;
    object-fit: cover; flex-shrink: 0;
    border: 1px solid var(--border-light);
    background: var(--bg-main);
}
.cart-item-img-placeholder {
    width: 50px; height: 50px;
    display: flex; align-items: center; justify-content: center;
    font-size: 26px; flex-shrink: 0;
    background: var(--color-primary-bg);
    border: 1px solid rgba(255,77,109,0.12);
}

/* ── Admin gallery upload ─────────────────────────────────── */
.gallery-upload-zone {
    border: 2px dashed var(--border-strong);
    padding: 36px 20px;
    text-align: center;
    cursor: pointer;
    transition: var(--transition);
    background: var(--bg-main);
    position: relative;
    margin-bottom: 24px;
}
.gallery-upload-zone:hover { border-color: var(--color-primary); background: var(--color-primary-bg); }
.gallery-upload-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }

/* ── Categories admin list ───────────────────────────────── */
.cat-list-item {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 18px; border-bottom: 1px solid var(--border-light);
    transition: var(--transition);
}
.cat-list-item:last-child { border-bottom: none; }
.cat-list-item:hover { background: var(--bg-main); }
.cat-name { font-size: 15px; font-weight: 600; color: var(--text-primary); }

/* ── Product Variant Picker Modal ────────────────────────── */
.variant-picker-overlay {
    position: fixed; inset: 0; z-index: 500;
    background: rgba(26, 10, 20, 0.55);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
    opacity: 0; pointer-events: none;
    transition: opacity 0.28s ease;
}
.variant-picker-overlay.open { opacity: 1; pointer-events: all; }

.variant-picker-card {
    background: var(--bg-surface);
    padding: 32px 28px 28px;
    max-width: 400px; width: 100%;
    box-shadow: var(--shadow-lg);
    transform: translateY(20px) scale(0.96);
    transition: transform 0.32s cubic-bezier(0.34,1.56,0.64,1), opacity 0.28s ease;
    position: relative;
}
.variant-picker-overlay.open .variant-picker-card {
    transform: translateY(0) scale(1);
}

.variant-picker-img {
    width: 72px; height: 72px;
    object-fit: cover;
    margin: 0 auto 14px;
    display: block;
    border: 2px solid var(--border-light);
}
.variant-picker-emoji {
    font-size: 52px;
    text-align: center;
    margin-bottom: 10px;
    display: block;
}
.variant-picker-title {
    font-family: var(--font-heading);
    font-size: 20px; font-weight: 800;
    color: var(--text-primary);
    text-align: center;
    margin-bottom: 6px;
}
.variant-picker-label {
    font-size: 13px;
    color: var(--text-muted);
    text-align: center;
    margin-bottom: 20px;
    font-weight: 500;
}
.variant-pills {
    display: flex; flex-wrap: wrap;
    gap: 10px;
    justify-content: center;
    margin-bottom: 22px;
}
.variant-pill {
    display: flex; flex-direction: column;
    align-items: center; justify-content: center;
    padding: 12px 20px;
    border: 2px solid var(--border-color);
    background: var(--bg-main);
    cursor: pointer;
    transition: var(--transition-bounce);
    min-width: 110px;
    gap: 4px;
}
.variant-pill:hover {
    border-color: var(--color-primary);
    background: var(--color-primary-bg);
    transform: translateY(-2px);
    box-shadow: var(--shadow-card);
}
.variant-pill.selected {
    border-color: var(--color-primary);
    background: var(--grad-primary);
}
.variant-pill.selected .variant-pill-name,
.variant-pill.selected .variant-pill-price { color: white; }
.variant-pill-name {
    font-size: 15px; font-weight: 700;
    font-family: var(--font-heading);
    color: var(--text-primary);
}
.variant-pill-price {
    font-size: 17px; font-weight: 800;
    font-family: var(--font-heading);
    color: var(--color-primary);
}
.variant-pill:hover .variant-pill-price { color: var(--color-primary); }
.variant-pill.selected .variant-pill-price { color: white; }

.variant-picker-close {
    position: absolute; top: 14px; right: 14px;
    width: 32px; height: 32px;
    background: var(--bg-main); border: 1px solid var(--border-light);
    display: flex; align-items: center; justify-content: center;
    font-size: 14px; color: var(--text-muted); cursor: pointer;
    transition: var(--transition);
}
.variant-picker-close:hover { background: #fff5f5; color: var(--color-danger); border-color: var(--color-danger); }

/* Price range display on product card */
.product-price-range {
    font-size: 13px; font-weight: 600; color: var(--text-muted);
    margin-top: 2px;
    font-family: var(--font-heading);
}
.product-price-from {
    font-size: 12px; color: var(--text-muted); font-weight: 500;
    font-family: var(--font-body);
    margin-bottom: 2px;
}

/* ── Nut Allergy Tag ─────────────────────────────────────── */
.allergy-tag {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: #fef3c7;
    border: 1px solid #f59e0b;
    color: #92400e;
    font-size: 11px;
    font-weight: 700;
    padding: 3px 9px;
    margin-bottom: 10px;
    letter-spacing: 0.02em;
}
.allergy-tag i { font-size: 10px; color: #d97706; }

/* ── Animations ────────────────────────────────────────────── */
@keyframes floatingCard {
    0%, 100% { transform: translateY(0)   rotate(1deg); }
    50%       { transform: translateY(-14px) rotate(1deg); }
}
@keyframes floatBubble {
    0%, 100% { transform: translate(0, 0); }
    50%       { transform: translate(8px, -12px); }
}
@keyframes bounceIn {
    0%   { transform: scale(0); opacity: 0; }
    60%  { transform: scale(1.15); opacity: 1; }
    100% { transform: scale(1); }
}

/* ══════════════════════════════════════════════════════════
   RESPONSIVE — Mobile-first, modern standards
   Breakpoints: 1100 | 960 | 768 | 640 | 480 | 380
══════════════════════════════════════════════════════════ */

/* ── Hamburger button (always rendered, hidden on desktop) ── */
.nav-hamburger {
    display: none !important;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: 4px;
    width: 22px; 
    height: 22px;
    padding: 0;
    background: transparent !important;
    border: none !important;
    cursor: pointer;
    z-index: 250;
    transition: var(--transition);
    flex-shrink: 0;
}
.nav-hamburger:hover { background: transparent !important; }
.nav-hamburger span {
    display: block;
    width: 20px; 
    height: 2px;
    background: var(--text-primary);
    transition: var(--transition);
}
.nav-hamburger span:nth-child(2) {
    width: 14px;
}
.nav-hamburger.open span:nth-child(1) { transform: translateY(6px) rotate(45deg); width: 20px; }
.nav-hamburger.open span:nth-child(2) { opacity: 0; transform: scaleX(0); }
.nav-hamburger.open span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); width: 20px; }

/* ── Mobile drawer ─────────────────────────────────────── */
/* ── Mobile drawer (Full Screen Overlay) ───────────────── */
.mobile-drawer {
    display: none;
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    width: 100vw !important;
    height: 100vh !important;
    height: 100dvh !important;
    z-index: 999999 !important;
    background: #ffffff !important;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s ease;
}
.mobile-drawer.open {
    display: block !important;
    opacity: 1 !important;
    pointer-events: all !important;
}
.mobile-nav-panel {
    position: fixed !important;
    top: 0 !important; left: 0 !important; right: 0 !important; bottom: 0 !important;
    width: 100vw !important; max-width: 100vw !important;
    height: 100vh !important; height: 100dvh !important;
    background: #ffffff !important;
    padding: 0 !important;
    display: flex; flex-direction: column;
    transform: translateX(-100%);
    transition: transform 0.35s cubic-bezier(0.4,0,0.2,1);
    overflow-y: auto;
    z-index: 1000000 !important;
}
.mobile-drawer.open .mobile-nav-panel { transform: translateX(0) !important; }


/* ═══════════════════════════════════════════════════════════
   SHARED LUXURY UTILITIES  (consolidated from header.php)
   ═══════════════════════════════════════════════════════════ */

.luxury-hero {
    position: relative;
    background: #eae7e1;
    padding: 120px 0 80px;
    text-align: center;
    border-bottom: 1px solid var(--border-light);
    margin-bottom: 40px;
}
.luxury-hero-eyebrow { font-size: 13px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--color-secondary); margin-bottom: 15px; font-weight: 500; }
.luxury-hero h1 { font-size: 48px; font-weight: var(--font-heading-weight); line-height: 1.2; color: var(--text-primary); margin-bottom: 15px; }
.luxury-hero p { font-size: 16px; max-width: 600px; margin: 0 auto; color: var(--text-secondary); font-family: var(--font-heading); font-style: italic; }

/* ── Product page hero ──────────────────────────────────────────────────────
   The product page is the one page where this band is NOT the page heading:
   the garment's real <h1> lives in the buy box below. So its name is a <p>
   carrying the h1's typography rather than a second <h1> competing with it,
   and it needs these two lines to escape the italic serif `.luxury-hero p`
   treatment meant for a subtitle.

   Hidden below 992px, where it was 207px of an 844px screen — a blurred copy
   of the photograph that appears sharp immediately beneath it, above a
   breadcrumb repeating the category and a title repeating the name. Removing
   it takes Add to Bag from 1,531px to 1,312px on a 390px phone. */
.luxury-hero .product-hero-name {
    max-width: 670px; margin: 0 auto 15px;
    font-family: var(--font-heading); font-style: normal;
    font-size: 48px; font-weight: var(--font-heading-weight); line-height: 1.2;
    color: var(--text-primary);
}
@media (max-width: 991px) {
    .luxury-hero.product-hero { display: none; }
}

/* ── Sticky buy bar, product page, phones and tablets ───────────────────────
   Shown only while the real Add to Bag button is off screen, so a shopper
   never sees two identical buttons at once. `hidden` is on the element in the
   markup and JS clears it, which means a browser that never runs the script
   shows nothing rather than a bar the page cannot control.

   It REPLACES .mobile-bottom-dock here rather than stacking above it — see
   .has-pdp-buy-bar below. The class goes on <body> from JS, so a failed script
   leaves the dock exactly where it was. */
.pdp-buy-bar {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 9400;
    display: flex; align-items: center; gap: 14px;
    padding: 10px 16px;
    /* The home-indicator strip on a modern iPhone sits inside the viewport, so
       without this the button's lower third is under it and taps land nowhere. */
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
    background: var(--bg-surface);
    border-top: 1px solid var(--border-strong);
    box-shadow: 0 -4px 18px rgba(20, 10, 14, 0.10);
    transform: translateY(101%);
    transition: transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}
.pdp-buy-bar.is-visible { transform: translateY(0); }
/* The compare tray also pins to the bottom and outranks this one. When it is
   up, sit above it instead of underneath. */
.pdp-buy-bar.is-above-compare { bottom: 78px; }
.pdp-buy-bar-price { display: flex; flex-direction: column; line-height: 1.2; }
.pdp-buy-bar-price-label {
    font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
    color: var(--text-muted); font-weight: 600;
}
.pdp-buy-bar-price-amount { font-size: 17px; font-weight: 700; color: var(--color-primary); }
.pdp-buy-bar-btn {
    flex: 1; min-height: 48px; padding: 13px 18px;
    font-size: 12px; letter-spacing: 0.1em; white-space: nowrap;
}
/* The swap. Scoped to the viewports where the dock exists at all. */
@media (max-width: 991px) {
    body.has-pdp-buy-bar .mobile-bottom-dock { display: none; }
}
@media (min-width: 992px) {
    /* Desktop keeps the buy box in a column beside a pinned gallery — the real
       button is a short scroll away and a fixed bar would be noise. */
    .pdp-buy-bar { display: none; }
}
@media (prefers-reduced-motion: reduce) {
    .pdp-buy-bar { transition: none; }
}

/* ── Luxury Hero with background photo (set --hero-bg-image per page) ──
   Usage: <section class="luxury-hero has-bg-image" style="--hero-bg-image:url('...')"> */
.luxury-hero.has-bg-image {
    background-image: linear-gradient(rgba(20, 10, 14, 0.45), rgba(20, 10, 14, 0.45)), var(--hero-bg-image);
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    padding: 170px 0 110px;
    border-bottom: none;
}
.luxury-hero.has-bg-image .luxury-hero-eyebrow { color: var(--color-accent); }
/* Was 300 — over a photo, a light Didone at small sizes almost disappears.
   The stronger step keeps the hairlines readable against a busy image. */
.luxury-hero.has-bg-image h1 { color: #ffffff; font-weight: var(--font-heading-weight-strong); max-width: 720px; margin-left: auto; margin-right: auto;}
.luxury-hero.has-bg-image p { color: rgba(255, 255, 255, 0.85); }
.editorial-section { }
.editorial-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 40px; align-items: center; }
.editorial-img img { width: 100%; height: auto; border: 1px solid var(--border-light); }
.editorial-content { padding-right: 40px; }
.editorial-label { font-size: 12px; letter-spacing: 0.15em; text-transform: uppercase; color: var(--color-accent-ink); font-weight: 600; display: block; margin-bottom: 10px; }
.editorial-title { font-family: var(--font-heading); font-size: 36px; font-weight: var(--font-heading-weight); margin-bottom: 20px; line-height: 1.3; }
.editorial-text { color: var(--text-secondary); margin-bottom: 30px; font-size: 15px; line-height: 1.8; }
.btn-luxury {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--color-primary);
    color: #ffffff !important;
    padding: 12px 30px;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    border: 1.5px solid var(--color-primary);
    cursor: pointer;
    transition: all 0.25s ease;
    text-decoration: none;
}
.btn-luxury:hover {
    background: var(--color-primary-dark, #3a0c1b) !important;
    color: #ffffff !important;
    border-color: var(--color-primary-dark, #3a0c1b) !important;
    transform: translateY(-1px);
    box-shadow: 0 4px 15px rgba(58, 12, 27, 0.25);
}

.btn-luxury-outline {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: transparent;
    color: var(--color-primary) !important;
    padding: 12px 30px;
    font-size: 13px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    border: 1.5px solid var(--color-primary);
    cursor: pointer;
    transition: all 0.25s ease;
    text-decoration: none;
}
.btn-luxury-outline:hover {
    background: var(--color-primary) !important;
    color: #ffffff !important;
    border-color: var(--color-primary) !important;
    transform: translateY(-1px);
    box-shadow: 0 4px 15px rgba(81, 17, 38, 0.2);
}
.has-bg-image .btn-luxury-outline { color: #ffffff !important; border-color: #ffffff; }
.has-bg-image .btn-luxury-outline:hover { background: #ffffff !important; color: var(--text-primary) !important; border-color: #ffffff !important; }
.page-container { max-width: 1200px; margin: 0 auto; padding: 40px 20px; }
.form-luxury-group { margin-bottom: 20px; }
.form-luxury-group label { display: block; font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px; font-weight: 600; color: var(--text-primary); }
.form-luxury-input { width: 100%; padding: 12px; border: 1px solid var(--border-strong); background: var(--bg-surface); font-family: inherit; font-size: 14px; color: var(--text-primary); transition: var(--transition); }
.form-luxury-input:focus { border-color: var(--color-primary); outline: none; }
.badge-luxury { position: absolute; top: 15px; left: 15px; background: var(--color-accent-ink); color: var(--text-on-primary); font-size: 10px; text-transform: uppercase; letter-spacing: 0.1em; padding: 4px 10px; font-weight: 600; z-index: 10; }
.badge-luxury.badge-sale { background: var(--color-danger); color: #ffffff; }

/* ═══════════════════════════════════════════════════════════
   MEGA MENU  (consolidated from header.php)
   ═══════════════════════════════════════════════════════════ */

.nav-links li { position: relative; }
.mega-menu-trigger { cursor: pointer; }
.mega-menu-dropdown {
    position: absolute; top: 100%; left: 50%; transform: translate(-50%, 20px);
    width: 850px; max-width: 1400px; background: var(--bg-surface);
    border: 1px solid var(--border-light); box-shadow: var(--shadow-lg); z-index: 100;
    display: grid; grid-template-columns: 3fr 1fr; gap: 40px; padding: 40px;
    opacity: 0; visibility: hidden; transition: all 0.3s cubic-bezier(0.25, 1, 0.5, 1); text-align: left;
}
.nav-links li:hover .mega-menu-dropdown { opacity: 1; visibility: visible; transform: translate(-50%, 0); }
.mega-menu-col h4 { font-family: var(--font-heading); font-size: 18px; font-weight: 500; text-transform: none; letter-spacing: 0.05em; color: var(--color-accent); margin-bottom: 20px; border-bottom: 1px solid var(--border-light); padding-bottom: 8px; }
.mega-menu-col ul { list-style: none; padding: 0; margin: 0; }
.mega-menu-col ul li { margin-bottom: 12px; }
.mega-menu-col a { color: var(--text-muted); text-decoration: none; transition: var(--transition); font-size: 13px; font-weight: 400; text-transform: none; display: inline-block; }
.mega-menu-col a:hover { color: var(--color-primary); transform: translateX(3px); }
.mega-menu-flat-list { columns: 5; column-gap: 30px; margin: 0; padding: 0; list-style: none; }
.mega-menu-flat-list li { break-inside: avoid; margin-bottom: 12px; }
.mega-menu-flat-list a { color: var(--text-muted); text-decoration: none; transition: var(--transition); font-size: 14px; }
.mega-menu-flat-list a:hover { color: var(--color-primary); padding-left: 5px; }
.mega-menu-promo { border: 1px solid var(--border-light); padding: 15px; background: var(--bg-surface-soft); text-align: center; }
.mega-menu-promo img { width: 100%; height: 150px; object-fit: cover; margin-bottom: 15px; }
.mega-menu-promo h5 { font-family: var(--font-heading); font-size: 16px; margin-bottom: 5px; font-weight: 500; }
.mega-menu-promo a { font-size: 11px; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; color: var(--color-accent); }
.logo span { color: var(--color-accent) !important; }

/* ═══════════════════════════════════════════════════════════
   SEARCH OVERLAY  (consolidated from header.php)
   ═══════════════════════════════════════════════════════════ */

.search-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100vh; background: rgba(255,255,255,0.98); z-index: 9999; display: none; opacity: 0; transition: opacity 0.3s ease; }
.search-overlay.active { display: flex; flex-direction: column; opacity: 1; }
.search-header { padding: 40px; display: flex; justify-content: flex-end; }
.search-close { background: none; border: none; font-size: 24px; cursor: pointer; color: var(--text-primary); }
.search-container { max-width: 800px; margin: 0 auto; width: 100%; padding: 0 20px; }
.search-input-wrapper { position: relative; border-bottom: 2px solid var(--border-strong); display: flex; align-items: center; }
.search-input { width: 100%; border: none; background: transparent; font-size: 32px; font-weight: 300; padding: 20px 0; color: var(--text-primary); outline: none; font-family: var(--font-heading); }
.search-input::placeholder { color: var(--text-muted); }
.voice-search-btn { background: none; border: none; font-size: 20px; color: var(--text-muted); cursor: pointer; padding: 10px; transition: color 0.2s; }
.voice-search-btn:hover { color: var(--color-primary); }
.search-results-area { display: grid; grid-template-columns: 1fr 2fr; gap: 40px; margin-top: 40px; }
/* Was ".search-suggestions h4". The search panel's labels are navigation
   furniture, not document sections, and they sat before every page's <h1> — a
   screen-reader user browsing by heading met them before the page's own title.
   They are now <div>s, so this selector matches the class instead.
   font-family, font-weight and line-height are stated explicitly because they
   used to arrive from the global "h1, h2, h3, h4, h5" rule, which a <div> no
   longer matches; the values are the ones the browser actually computed before
   the change, so the panel renders identically. */
.search-suggestions .search-suggest-title { font-family: var(--font-heading); font-weight: 700; line-height: 1.2; font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin: 0 0 15px; }
.search-suggestions ul { list-style: none; padding: 0; margin-bottom: 30px; }
.search-suggestions li { margin-bottom: 10px; }
.search-suggestions a { color: var(--text-secondary); text-decoration: none; font-size: 15px; transition: color 0.2s; }
.search-suggestions a:hover { color: var(--color-primary); }
/* "Products" was a bare <h4> with no rule of its own — every visible property
   came from the global "h1, h2, h3, h4, h5" rule and the h4 default size. It is
   a <div> now (a search-panel label is not a document section), so the values
   the browser previously computed are written out here: 15px/700/1.2 in the
   heading face, text-primary, no margins. */
.instant-results .instant-results-title { font-family: var(--font-heading); font-size: 15px; font-weight: 700; line-height: 1.2; color: var(--text-primary); margin: 0; }
.instant-results-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.search-product-card { display: flex; gap: 15px; text-decoration: none; color: var(--text-primary); }
.search-product-card img { width: 80px; height: 100px; object-fit: cover; border: 1px solid var(--border-light); }
.search-product-details { display: flex; flex-direction: column; justify-content: center; }
.search-product-title { font-size: 14px; font-weight: 500; margin-bottom: 5px; }
.search-product-price { font-size: 13px; color: var(--text-secondary); }

/* ═══════════════════════════════════════════════════════════
   FOOTER  (consolidated from footer.php)
   ═══════════════════════════════════════════════════════════ */

.footer-newsletter-form { display: flex; gap: 0; width: 100%; }
.footer-newsletter-form input { flex: 1; min-width: 0; }
.footer-bottom-bar { border-top: 1px solid rgba(255,255,255,0.1); padding-top: 30px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: #888888; }

/* ═══════════════════════════════════════════════════════════
   HOME PAGE GRIDS  (consolidated from home.php)
   ═══════════════════════════════════════════════════════════ */

.zoom-box:hover .zoom-img { transform: scale(1.04); }
.hide-scrollbar::-webkit-scrollbar { display: none; }
.hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }
.newsletter-input-wrap { position: relative; width: 100%; max-width: 400px; margin: 0 auto 30px; }
.animated-input { width: 100%; border: none !important; border-bottom: 2px solid var(--border-strong) !important; background: transparent !important; padding: 12px 0 !important; font-size: 14px !important; outline: none !important; transition: all 0.3s ease !important; text-align: center !important; color: var(--text-primary) !important; }
.animated-label { position: absolute; left: 50%; transform: translateX(-50%) translateY(12px); pointer-events: none; transition: all 0.3s ease; font-size: 12px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.1em; font-weight: 600; }
.animated-input:focus ~ .animated-label,
.animated-input:valid ~ .animated-label { transform: translateX(-50%) translateY(-15px); font-size: 10px; color: var(--color-accent); font-weight: 700; }
.animated-input:focus { border-bottom-color: var(--color-primary) !important; }
/* Four across, not three. Same tiles, one fewer row — the collections were
   costing a whole extra screen of scroll to show the same number of ways in.
   Stepped down by width rather than left to auto-fill, so this grid and the
   size grid in the admin form both wrap at counts someone chose. */
/* Dievon Collections — a full row when there are many, a centred one when few.
   ────────────────────────────────────────────────────────────────────────────
   This was repeat(5, 1fr): five equal columns whatever the shop actually has.
   With two collections the cards filled columns one and two and the remaining
   three sat empty, so the row hugged the left edge with a third of the section
   blank beside it — which reads as something having failed to load rather than
   as a deliberate row of two.

   auto-fit collapses the tracks nothing occupies, and once they are collapsed
   there is spare room for justify-content to centre what is left. The track
   size is the width a column WOULD have had at the full count, so five
   collections still fill the row exactly as before — nothing changes until
   there are too few to fill it, which is precisely when centring is wanted.

   The count and the gap are the only things a breakpoint changes now; the
   formula stays put and cannot drift out of step with them. */
/* ══════════════════════════════════════════════════════════════════════════
   DIEVON COLLECTIONS — an editorial mosaic that is whole at any count
   ──────────────────────────────────────────────────────────────────────────
   This was five equal tiles across, capped at five, becoming a swipeable rail
   on a phone. It is now a mosaic: one large tile leading, the rest arranged
   around it, nothing scrolling in any direction at any width.

   THE PROBLEM WORTH KNOWING ABOUT. A mosaic is a composition of a KNOWN number
   of tiles. The reference design has six. Give the same rules two — which is
   what this shop has today — and you get a tall feature beside one small tile
   with two thirds of the row empty: not a sparse layout, a broken one.

   So the arrangement is chosen from the count, which the markup writes onto the
   grid as data-count. Every count from one upward has a composition that fills
   its rows completely. Six is the largest — the composition IS the limit, and
   a seventh collection is reached through the button under the grid rather
   than by making the section taller. The shop can add its third, fourth, fifth
   and sixth collection without anyone touching this file.

   Eighteen columns because that is the smallest number that divides cleanly
   into the reference: the feature takes 6, the three tiles beside it take 4
   each, and the two beneath take 6 each. Twelve cannot do thirds and halves of
   the same remainder; eighteen can.
   ══════════════════════════════════════════════════════════════════════════ */
.home-categories-grid {
    --cat-gap: 24px;
    display: grid;
    grid-template-columns: repeat(18, minmax(0, 1fr));
    /* An explicit row height, so the feature spanning two rows is exactly two
       rows plus the gap between them and its lower edge lines up with the tile
       beside it. Rows sized by content could not guarantee that. */
    grid-auto-rows: clamp(200px, 24vw, 340px);
    gap: var(--cat-gap);
}

/* The tile. aspect-ratio is cleared because the grid row decides the height
   here; .category-card keeps its 3/4 everywhere else it is used. */
.home-categories-grid > .category-card {
    aspect-ratio: auto;
    height: 100%;
    min-width: 0;
    box-shadow: 0 1px 2px rgba(58, 12, 27, 0.04);
    transition: box-shadow 0.45s ease, transform 0.45s ease;
}

/* ── The full arrangement: six ─────────────────────────────────────────────
   Feature 6 wide and 2 tall; three tiles of 4 beside it; two of 6 beneath.
   Six is also the maximum — pages/home.php slices to it, and a seventh
   collection is reached through the button below rather than by growing the
   section. There is deliberately no rule for a seventh tile: if one appeared
   it would be a bug in the slice, and a silent extra row would hide it. */
.home-categories-grid > .category-card              { grid-column: span 4; }
.home-categories-grid > .category-card:nth-child(1) { grid-column: span 6; grid-row: span 2; }
.home-categories-grid > .category-card:nth-child(5),
.home-categories-grid > .category-card:nth-child(6) { grid-column: span 6; }

/* ── One collection ── A single tile is a banner, not a mosaic. */
.home-categories-grid[data-count="1"] { grid-auto-rows: auto; }
.home-categories-grid[data-count="1"] > .category-card:nth-child(1) {
    grid-column: span 18;
    grid-row: auto;
    aspect-ratio: 21 / 9;
    height: auto;
}

/* ── Two ── The shop's position today. Two equal portrait tiles read as a
   deliberate pair; a feature-plus-one reads as a layout missing four tiles. */
.home-categories-grid[data-count="2"] { grid-auto-rows: auto; }
.home-categories-grid[data-count="2"] > .category-card,
.home-categories-grid[data-count="2"] > .category-card:nth-child(1) {
    grid-column: span 9;
    grid-row: auto;
    aspect-ratio: 4 / 5;
    height: auto;
}

/* ── Three ── Feature leading, two full-width tiles stacked beside it. */
.home-categories-grid[data-count="3"] > .category-card              { grid-column: span 12; }
.home-categories-grid[data-count="3"] > .category-card:nth-child(1) { grid-column: span 6; grid-row: span 2; }

/* ── Four ── Two beside the feature, one spanning the width under them. */
.home-categories-grid[data-count="4"] > .category-card:nth-child(1) { grid-column: span 6; grid-row: span 2; }
.home-categories-grid[data-count="4"] > .category-card:nth-child(2),
.home-categories-grid[data-count="4"] > .category-card:nth-child(3) { grid-column: span 6; }
.home-categories-grid[data-count="4"] > .category-card:nth-child(4) { grid-column: span 12; }

/* ── Five ── Two and two beside the feature; both rows exactly full. */
.home-categories-grid[data-count="5"] > .category-card              { grid-column: span 6; }
.home-categories-grid[data-count="5"] > .category-card:nth-child(1) { grid-column: span 6; grid-row: span 2; }

/* ── Hover, on the tile rather than on the photograph ──────────────────────
   All four effects hang off .category-card:hover, so they fire wherever the
   cursor is on the tile. Hooking the zoom to the image and the underline to the
   link meant the underline only moved if you happened to be over those two
   words. Slow on purpose: 0.9s on the image, which at 1.06 scale is a drift
   rather than a jump. */
.home-categories-grid > .category-card .zoom-img,
.home-categories-grid > .category-card img {
    transition: transform 0.9s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.home-categories-grid > .category-card:hover .zoom-img,
.home-categories-grid > .category-card:focus-visible .zoom-img {
    transform: scale(1.06);
}
.home-categories-grid > .category-card:hover,
.home-categories-grid > .category-card:focus-visible {
    box-shadow: 0 18px 40px -18px rgba(58, 12, 27, 0.34);
}
/* The scrim deepens rather than appears, and it reaches higher up the frame so
   the name stays readable as the picture brightens under the zoom. */
.home-categories-grid > .category-card .category-card-overlay {
    background: linear-gradient(to top, rgba(0,0,0,0.62) 0%, rgba(0,0,0,0.12) 42%, rgba(0,0,0,0) 68%);
    transition: background 0.55s ease;
}
.home-categories-grid > .category-card:hover .category-card-overlay,
.home-categories-grid > .category-card:focus-visible .category-card-overlay {
    background: linear-gradient(to top, rgba(0,0,0,0.76) 0%, rgba(0,0,0,0.30) 48%, rgba(0,0,0,0.06) 78%);
}

/* The gold rule under the link. The site's shared underline already animates
   from scaleX(0) on the link's own hover; here it rests at a short stub — the
   gold line the reference shows sitting under every collection name — and
   draws out to full width when the TILE is hovered. Same component, same
   easing, one behaviour added rather than a second underline invented.

   --color-accent, not --color-accent-ink: this sits on a darkened photograph,
   and style.css:~120 records that the ink token is the one for gold on pale
   grounds while --color-accent is the one for gold on dark. */
.home-categories-grid > .category-card .category-card-link { color: var(--color-accent); }
.home-categories-grid > .category-card .category-card-link::after {
    transform: scaleX(0.4);
    transition: transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.home-categories-grid > .category-card:hover .category-card-link::after,
.home-categories-grid > .category-card:focus-visible .category-card-link::after {
    transform: scaleX(1);
}

/* Keyboard focus has to be visible on a tile whose whole surface is the link. */
.home-categories-grid > .category-card:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
    .home-categories-grid > .category-card,
    .home-categories-grid > .category-card .zoom-img,
    .home-categories-grid > .category-card img,
    .home-categories-grid > .category-card .category-card-overlay,
    .home-categories-grid > .category-card .category-card-link::after {
        transition: none;
    }
    .home-categories-grid > .category-card:hover .zoom-img,
    .home-categories-grid > .category-card:focus-visible .zoom-img { transform: none; }
}

/* View All — only rendered when collections were actually held back, so the
   button never appears promising more than the row already shows. */
.home-categories-more { text-align: center; margin-top: 38px; }
.home-categories-more .btn-luxury-outline { padding: 13px 34px; font-size: 12px; letter-spacing: 0.12em; }
.home-best-sellers-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px; }
.home-blog-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 30px; }
.home-benefits-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px; text-align: center; }
.home-section-header { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 50px; }
.home-section-header-btns { display: flex; gap: 10px; }

/* ═══════════════════════════════════════════════════════════
   SHOP PAGE  (consolidated from shop.php)
   ═══════════════════════════════════════════════════════════ */

@keyframes spinner-spin { to { transform: rotate(360deg); } }
.wishlist-btn-active { color: var(--color-primary) !important; }
.products-grid.list-view { display: flex !important; flex-direction: column !important; gap: 20px !important; }
.products-grid.list-view .product-card { flex-direction: row !important; height: 250px; align-items: center; text-align: left; }
.products-grid.list-view .product-card > div:nth-child(3) { width: 200px; height: 100% !important; border-bottom: none !important; border-right: 1px solid var(--border-light); }
.products-grid.list-view .product-card > div:nth-child(4) { padding: 30px !important; text-align: left !important; align-items: flex-start !important; }
.products-grid.list-view .product-card .btn-luxury { width: auto !important; }
.filter-body { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; display: flex; flex-direction: column; gap: 12px; }
.filter-body.open { max-height: 500px; margin-top: 15px; padding-bottom: 5px; }
.shop-layout { display: grid; grid-template-columns: 260px 1fr; gap: 40px; }
.shop-sidebar { border-right: 1px solid var(--border-light); padding-right: 20px; }
.filter-accordion { border-bottom: 1px solid var(--border-light); padding: 15px 0; }
/* These headers are real <button> elements. The leading declarations undo the
   styling a browser gives a button by default; width:100% is the one doing
   visible work, since a button shrink-wraps its text and the flex row would
   then have no free space for justify-content to push the arrow to the right. */
.filter-header { width: 100%; background: none; border: none; -webkit-appearance: none; appearance: none; text-align: left; font-family: var(--font-heading); font-size: 15px; text-transform: uppercase; font-weight: 500; display: flex; justify-content: space-between; align-items: center; cursor: pointer; color: var(--text-primary); letter-spacing: 0.05em; }
/* !important is required, not stylistic: the global reset near the top of this
   file (button:focus-visible { outline: none !important }) would otherwise wipe
   this ring, leaving the filters tabbable but with no visible focus at all. */
.filter-header:focus-visible { outline: 2px solid var(--color-secondary) !important; outline-offset: -2px; }
.filter-toggle-icon { transition: transform 0.3s ease; font-size: 12px; color: var(--text-muted); }
.filter-header.open .filter-toggle-icon { transform: rotate(180deg); }
.card-img-container:hover .quick-view-btn { display: block !important; }
.quick-view-btn:hover { background: var(--color-primary) !important; color: white !important; }
.quick-view-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000; display: none; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.3s ease; }
.quick-view-modal.is-visible { opacity: 1; }
.quick-view-content { background: var(--bg-main); width: 900px; max-width: 90%; height: 500px; max-height: 90vh; display: flex; position: relative; overflow: hidden; transform: scale(0.94); transition: transform 0.3s ease; }
.quick-view-modal.is-visible .quick-view-content { transform: scale(1); }
.quick-view-img { width: 50%; height: 100%; object-fit: cover; }
.quick-view-details { width: 50%; padding: 40px; overflow-y: auto; }
.quick-view-close { position: absolute; top: 15px; right: 15px; font-size: 24px; color: var(--text-muted); cursor: pointer; border: none; background: transparent; }

/* ═══════════════════════════════════════════════════════════
   PRODUCT PAGE  (consolidated from product.php)
   ═══════════════════════════════════════════════════════════ */

.selected-pill { background: var(--color-primary) !important; color: var(--text-on-primary) !important; }
.product-detail-container { width: 100%; max-width: 1240px; margin: 0 auto; padding: 0 20px; box-sizing: border-box; }
/* Flexbox, not CSS Grid — position:sticky on a grid item with implicit
   (auto) row sizing is unreliable in some Chrome versions; the identical
   sticky rule on a flex child is far more consistently supported. */
.product-layout { display: flex; gap: 40px; width: 100%; box-sizing: border-box; align-items: flex-start; }
.product-layout > .product-image-section,
.product-layout > .product-details-col { flex: 1 1 0; min-width: 0; }
.product-details-col { display: flex; flex-direction: column; width: 100%; box-sizing: border-box; min-width: 0; }

/* ── Compact Purchase Benefits Row ─────────────────────────── */
.product-benefits-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; margin: 4px 0 18px; padding: 16px 4px; border-top: 1px solid var(--border-light); border-bottom: 1px solid var(--border-light); }
/* .benefit-item is TWO different components sharing one name.
   ─────────────────────────────────────────────────────────────────────────────
   The compact strip inside the product buy box (Free Shipping / Dispatched /
   Returns / COD) and the four editorial brand promises on the homepage both
   call their tiles .benefit-item, and all three blocks of rules for it were
   written unscoped — so each component silently styled the other:

     · the homepage tiles were picking up this block's font-size: 11px and
       --text-secondary from the product strip;
     · the product strip was picking up the homepage's padding: 10px, and its
       hover lift — translateY(-5px) on a static row of facts that is not a
       link and cannot be clicked.

   They are not the same component and should not be merged: one is four
   site-wide promises with an icon, a heading and a sentence; the other is four
   facts about the item being bought, one of which only appears when COD is
   available. Scoping each set to its own container is the fix — the names can
   stay, because they can no longer reach each other. */
.product-benefits-row .benefit-item { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 6px; font-size: 11px; color: var(--text-secondary); line-height: 1.35; }
.product-benefits-row .benefit-item i { font-size: 18px; color: var(--color-primary); }
.product-benefits-row .benefit-item small { color: var(--text-muted); font-size: 10px; }
@media (max-width: 480px) {
    .product-benefits-row { grid-template-columns: repeat(2, 1fr); gap: 16px 8px; }
}

/* ── Secure Payments Badge Row ──────────────────────────────── */
/* The three children of .footer-bottom-bar opt out of the scroll reveal.
   ───────────────────────────────────────────────────────────────────────────
   The site-wide reveal script claims every direct child of a listed container,
   .footer-bottom-bar among them. At the very foot of a long page the observer
   is unreliable — a browser that restores a scroll position straight to the
   bottom, or a short page where the bar never crosses the threshold, leaves
   these at opacity 0 permanently. Two of the three were already exempted for
   that reason; the country picker is the third and needs it most, since a
   setting a shopper cannot see is worse than one that simply fades in late. */
.footer-secure-payments.reveal-on-scroll { opacity: 1 !important; transform: none !important; }
.footer-bottom.reveal-on-scroll { opacity: 1 !important; transform: none !important; }
.footer-country.reveal-on-scroll { opacity: 1 !important; transform: none !important; }
.product-secure-payments { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px; margin-bottom: 18px; padding: 12px 14px; background: var(--bg-surface-soft); border: 1px solid var(--border-light); }
.secure-payments-label { font-size: 12px; font-weight: 700; color: var(--text-primary); display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.secure-payments-label i { color: #10b981; }
.secure-payments-logos { display: flex; align-items: center; gap: 12px; }
.secure-payments-logos i { font-size: 26px; color: var(--text-secondary); }
.secure-payments-logos img { height: 18px; width: auto; display: block; }
.footer-secure-payments {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px 16px;
    margin: 0;
}
.footer-secure-payments .secure-payments-label {
    font-size: 11px;
    font-weight: 500;
    color: rgba(255, 255, 255, 0.45);
    display: flex;
    align-items: center;
    gap: 6px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    white-space: nowrap;
}
.footer-secure-payments .secure-payments-label i {
    color: #10b981;
    font-size: 11px;
}
.footer-secure-payments .secure-payments-logos {
    display: flex;
    align-items: center;
    gap: 10px;
}
.footer-secure-payments .secure-payments-logos i {
    font-size: 18px;
    color: rgba(255, 255, 255, 0.45);
    transition: color 0.3s ease;
}
.footer-secure-payments .secure-payments-logos i:hover {
    color: #ffffff;
}
.footer-secure-payments .secure-payments-logos img {
    height: 13px;
    width: auto;
    display: block;
    opacity: 0.45;
    filter: brightness(0) invert(1);
    transition: opacity 0.3s ease;
}
.footer-secure-payments .secure-payments-logos img:hover {
    opacity: 1;
}

/* .product-buy-box is intentionally NOT sticky. Only the gallery
   (.product-image-section) pins while the buy box and the detail content scroll
   normally past it. This rule previously declared position:sticky here too, but it
   never actually took effect because overflow-x:hidden on html/body made them scroll
   containers and silently disabled every position:sticky on the site. Once that was
   corrected to overflow-x:clip the rule activated and pinned the buy box as well,
   which is not the intended behaviour — so it has been removed. */
.product-breadcrumbs { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin-bottom: 15px; }
.product-breadcrumbs a { color: var(--text-muted); text-decoration: none; }
.product-breadcrumbs span { color: var(--text-primary); }
.product-title-heading { font-family: var(--font-heading); font-size: 32px; font-weight: var(--font-heading-weight); text-transform: uppercase; line-height: 1.3; color: var(--text-primary); margin-bottom: 12px; word-break: break-word; }
.product-price-heading { font-size: 22px; font-weight: 600; color: var(--color-primary); margin-bottom: 20px; }
.product-description-text { font-size: 14px; line-height: 1.8; color: var(--text-secondary); margin-bottom: 25px; word-break: break-word; }

/* ── Description clamp (phones and tablets only) ────────────────────────────
   A real Dievon description runs three paragraphs. Rendered in full on a
   390px screen that is ~450px of body copy sitting between the price and the
   size picker, and it pushed Add to Bag down to 1,827px — past two entire
   screens before a shopper can buy. Desktop keeps the full text: there it sits
   in a column beside a pinned gallery and costs nothing.

   The class is added by JS, and only after measuring that the text really
   overflows, so a one-line description is left alone. Never applied by default:
   if the script fails the shopper reads a long description rather than a
   permanently truncated one. */
.product-description-text.is-clamped {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
    margin-bottom: 8px;
}
.desc-read-more {
    display: inline-flex; align-items: center; gap: 7px;
    background: none; border: 0; padding: 6px 0;
    /* 44px of touch target without 44px of visible chrome — the row reads as a
       text link and still meets the minimum tap size. */
    min-height: 44px;
    font-family: inherit; font-size: 12px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.08em;
    color: var(--color-accent-ink); cursor: pointer;
    margin-bottom: 20px;
}
/* Without this the button is visible on every product, at every width.
   `hidden` hides an element by way of `display: none` in the BROWSER's default
   stylesheet, and any author `display` beats it — so the `display: inline-flex`
   above silently cancelled the attribute. The JS was measuring correctly and
   setting hidden; the stylesheet ignored it, and a three-line description still
   carried a "Read more" that expanded nothing. */
.desc-read-more[hidden] { display: none; }

.desc-read-more i { font-size: 10px; transition: transform 0.2s ease; }
.desc-read-more[aria-expanded="true"] i { transform: rotate(180deg); }
.desc-read-more:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }
@media (prefers-reduced-motion: reduce) {
    .desc-read-more i { transition: none; }
}


/* ═══════════════════════════════════════════════════════════
   HOME PAGE HERO SLIDER  (consolidated from home.php)
   ═══════════════════════════════════════════════════════════ */

/* .hero-slider-section { position: relative; overflow: hidden; height: 80vh; background: #000; } */
.hero-slider-section { position: relative; overflow: hidden; height: 100vh;
    background: #000; max-height: calc(100vh - 138px); }
.category-box-height { height: 450px; overflow: hidden; }
.home-new-arrivals-item { flex: 0 0 280px; scroll-snap-align: start; }
.home-best-sellers-img { height: 300px; overflow: hidden; }
.slide-content h2 { font-size: 56px; font-family: var(--font-heading); font-weight: var(--font-heading-weight); text-transform: uppercase; line-height: 1.1; margin-bottom: 20px; color: #ffffff; }

/* ═══════════════════════════════════════════════════════════
   ADMIN SIDEBAR & LAYOUT  (consolidated from admin header)
   ═══════════════════════════════════════════════════════════ */

.expand-btn { cursor: pointer; background: none; border: none; color: var(--color-secondary); font-size: 13px; display: flex; align-items: center; gap: 4px; }
.expand-btn:hover { color: var(--color-primary); }

body.admin-wrapper {
    background-color: #f9fafb;
    display: flex;
    min-height: 100vh;
    margin: 0;
    font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}
.admin-sidebar {
    width: 260px;
    background: #ffffff;
    border-right: 1px solid #e5e7eb;
    display: flex;
    flex-direction: column;
    position: fixed;
    height: 100vh;
    z-index: 50;
}
.admin-sidebar-logo {
    padding: 24px;
    font-size: 20px;
    font-weight: 700;
    color: #111827;
    border-bottom: 1px solid #e5e7eb;
    text-decoration: none;
    display: flex;
    align-items: center;
    gap: 12px;
}
.admin-sidebar-nav {
    padding: 16px;
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.admin-sidebar-nav a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 12px 16px;
    color: #4b5563;
    text-decoration: none;
    font-size: 14px;
    font-weight: 500;
    transition: all 0.2s;
}
.admin-sidebar-nav a:hover { background: #f3f4f6; color: #111827; }
.admin-sidebar-nav a.active { background: #f3f4f6; color: #111827; font-weight: 600; }
.admin-sidebar-nav a i { font-size: 16px; width: 20px; text-align: center; }
.admin-main { flex: 1; margin-left: 260px; display: flex; flex-direction: column; min-width: 0; }
.admin-topbar {
    height: 64px;
    background: #ffffff;
    border-bottom: 1px solid #e5e7eb;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    padding: 0 32px;
    position: sticky;
    top: 0;
    z-index: 40;
}
.admin-content { padding: 32px; max-width: 1200px; width: 100%; margin: 0 auto; }
body.admin-wrapper .navbar { display: none !important; }
body.admin-wrapper .admin-tabs { display: none !important; }

body.admin-wrapper .stats-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 24px;
    margin-bottom: 32px;
}
body.admin-wrapper .stat-card {
    background: #ffffff;
    border: 1px solid #e5e7eb;
    padding: 24px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05);
    display: flex;
    flex-direction: column;
    justify-content: center;
}
body.admin-wrapper .stat-card .stat-label { font-size: 13px; color: #6b7280; text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; margin-bottom: 8px; }
body.admin-wrapper .stat-card .stat-value { font-size: 28px; font-weight: 700; color: #111827; }
body.admin-wrapper .stat-card-icon { display: none; }
body.admin-wrapper .admin-page-header { margin-bottom: 32px; border-bottom: 1px solid #e5e7eb; padding-bottom: 24px; }
body.admin-wrapper .admin-page-title { font-size: 24px; font-weight: 700; color: #111827; }

body.admin-wrapper .glass-panel {
    background: #ffffff !important;
    border: 1px solid #e5e7eb !important;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05) !important;
    backdrop-filter: none !important;
    padding: 24px !important;
}
body.admin-wrapper .form-control {
    background: #ffffff !important;
    border: 1px solid #d1d5db !important;
    color: #111827 !important;
    box-shadow: 0 1px 2px rgba(0,0,0,0.05) !important;
}
body.admin-wrapper .form-control:focus {
    border-color: #6366f1 !important;
    box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.2) !important;
    outline: none;
}
body.admin-wrapper .form-label { color: #374151 !important; font-weight: 500 !important; }
/* The admin's own stylesheet owns its buttons.
   ────────────────────────────────────────────────────────────────────────────
   This repainted every admin .btn-primary near-black (#111827) with six
   !important declarations, from the STOREFRONT sheet — so nothing in
   admin/assets/css/style.css could ever take effect, and Save came out dark navy
   while Delete beside it kept the admin styling. One button overridden and the
   other not is the whole reason the pair looked mismatched.

   Removed rather than re-coloured: the admin sheet already defines these buttons
   properly, and two files styling one control is what caused this. The selector
   was scoped to body.admin-wrapper, which only admin/includes/header.php sets,
   so the shop is untouched either way. */

body.admin-wrapper .product-form-grid {
    display: grid;
    grid-template-columns: 1fr 300px;
    gap: 28px;
    align-items: start;
}

/* ═══════════════════════════════════════════════════════════
   ABOUT PAGE GRIDS
   ═══════════════════════════════════════════════════════════ */

.about-narrative-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 60px;
    align-items: center;
}
.about-values-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 40px;
}

/* ═══════════════════════════════════════════════════════════
   CONTACT PAGE GRIDS
   ═══════════════════════════════════════════════════════════ */

.contact-layout-grid {
    display: grid;
    grid-template-columns: 1.2fr 0.8fr;
    gap: 60px;
    align-items: start;
}
.contact-form-row {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 20px;
}

.blog-list-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 30px;
}

/* ═══════════════════════════════════════════════════════════
   HERO SLIDER & HOME COMPONENTS
   ═══════════════════════════════════════════════════════════ */

/* Any number of slides, not exactly three.
   The container was width:300% with each slide at 33.333% of it — arithmetic
   that only works for three. Add a fourth banner and four slides at 33.333%
   overflow the container, so flex shrinks every one of them to 25% and the
   viewport shows two half slides side by side instead of one whole one.
   The owner hit exactly that on adding a fourth banner.

   Now the container is one viewport wide and each slide takes all of it
   (flex: 0 0 100%, and flex-shrink 0 so a fifth slide cannot squeeze the
   others). The JS translates by index * 100%, which is one viewport per step
   for any count. */
.slider-container { display: flex; width: 100%; height: 100%; transition: transform 0.8s cubic-bezier(0.25, 1, 0.5, 1); }
.slide { flex: 0 0 100%; width: 100%; height: 100%; position: relative; background-size: cover; background-position: center; }
/* Second copy of the seed-photograph paths -- this file declares a number of
   selectors twice, and BOTH copies fetched, so removing only the later one
   left the 404s exactly where they were. See the note at the .slide-media
   rules further down. */
.slide-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.3); z-index: 1; }
/* Content raised from bottom: 15% so the block no longer crowds the slider
   dots at the very bottom (which it hid on shorter screens) — the empty band
   it left up top is also smaller as a result. */
.slide-content { position: absolute; left: 10%; bottom: 26%; z-index: 2; color: #ffffff; max-width: 600px; text-align: left; }
.slide-content h1,
.slide-content h2 {
    font-family: var(--font-heading) !important;
    font-size: clamp(32px, 5vw, 56px) !important;
    font-weight: var(--font-heading-weight) !important;
    margin-bottom: 15px !important;
    text-transform: uppercase !important;
    color: #ffffff !important;
    text-shadow: 0 2px 12px rgba(0, 0, 0, 0.5) !important;
}
.btn-hero-explore {
    margin-top: 20px !important;
    display: inline-flex !important;
    background: #ffffff !important;
    color: #111111 !important;
    border-color: #ffffff !important;
    padding: 9px 24px !important;
    font-size: 11px !important;
    font-weight: 700 !important;
    text-transform: uppercase !important;
    letter-spacing: 0.12em !important;
    transition: var(--transition);
}
.btn-hero-explore:hover {
    background: var(--color-primary) !important;
    color: #ffffff !important;
    border-color: var(--color-primary) !important;
}
/* 50px here, and never below 44 at any breakpoint — see the mobile rule in
   responsive.css. These render 40x40 on a phone, under the size a finger needs,
   on the control that moves the shop's main photography. */
/* A slim dark pill behind the dots: over a bright photograph the outline dots
   used to vanish into the picture. The backdrop guarantees they read on every
   slide. */

.section-title-wrapper { text-align: center; margin-bottom: 50px; }
.section-title-wrapper::after {
    content: '';
    display: block;
    width: 50px;
    height: 2px;
    background: linear-gradient(90deg, var(--color-accent), var(--color-primary));
    margin: 14px auto 0;
}


/* height:450px used to sit here before aspect-ratio, and was already dead —
   the height:auto later in the same rule overrode it. Removed so the box has
   one source of truth: 3/4, derived from the column width. */
.category-card { position: relative; overflow: hidden; display: block; border: 1px solid var(--border-light); aspect-ratio: 3 / 4; }
.category-card img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s ease; }
.category-card-overlay { position: absolute; inset: 0; background: linear-gradient(to top, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0) 50%); display: flex; align-items: flex-end; padding: 30px; z-index: 2; }
.category-card-overlay h3 { font-family: var(--font-heading); font-size: 26px; font-weight: var(--font-heading-weight); text-transform: uppercase; color: white; margin-bottom: 5px; }
.category-card-link { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--color-accent-ink); font-weight: 600; }

.new-arrivals-section { background: var(--bg-surface); font-family: var(--font-body); position: relative; }
.btn-carousel-arrow { width: 44px; height: 44px; border: 1px solid var(--border-strong); display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-primary); background: transparent; transition: var(--transition); }
.btn-carousel-arrow:hover { background: var(--color-primary); color: white; }
/* NOTE: this block is overridden by the identical selector later in this file (search
   ".new-arrivals-carousel"). Kept in sync so whichever one wins behaves the same.

   padding-bottom is 0, not the 20px it carried. That 20px was reserving room
   for a horizontal scrollbar that .hide-scrollbar removes, under cards with no
   shadow, on a rail whose overflow-y computes to visible — so it protected
   nothing and simply pushed New Arrivals 20px lower than Trending, which sits
   directly beneath it and is built from the same cards. Two sections that look
   the same should not sit at two different heights. padding-top stays: the
   cards lift on hover and want the room. */
.new-arrivals-carousel { display: flex; gap: 24px; overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scroll-snap-type: x proximity; padding-top: 12px; padding-bottom: 0; }

.product-card-details { padding: 20px 20px 0; text-align: center; display: flex; flex-direction: column; align-items: center; flex: 1; }
.product-card-category { font-size: 11px; color: var(--text-muted); text-transform: uppercase; display: block; margin-bottom: 6px; letter-spacing: 0.1em; }
.product-card-title { font-family: var(--font-heading); font-size: 18px; font-weight: var(--font-heading-weight); margin-bottom: 10px; min-height: 48px; line-height: 1.4; }
.product-card-title a { color: var(--text-primary); text-decoration: none; transition: color 0.2s ease; }
.product-card-title a:hover { color: var(--color-primary); }
.product-card-price { font-size: 16px; font-weight: 700; color: var(--color-primary); margin-bottom: 12px; }
.price-mrp-strike { display: block; font-size: 0.8em; font-weight: 400; color: var(--text-muted); text-decoration: line-through; margin-bottom: 2px; }

.trending-section { background: var(--bg-main); font-family: var(--font-body); }
.tab-toggle-container { text-align: center; margin-bottom: 50px; }
.tab-toggle-wrapper { display: inline-flex; border: 1px solid var(--border-strong); padding: 5px; background: var(--bg-surface); gap: 5px; }
/* Replaces the inline style="display:none" the tab grids used to carry, so the
   toggle is a class swap rather than a written-in style attribute. */
.is-hidden { display: none !important; }

/* Floating circular wishlist buttons on product cards */
.product-card-wishlist-btn {
    position: absolute;
    top: 15px;
    right: 15px;
    z-index: 10;
    width: 36px;
    height: 36px;
    background: rgba(255, 255, 255, 0.9);
    border: 1px solid rgba(0, 0, 0, 0.05);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    color: var(--text-primary);
    box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
    transition: all 0.3s ease;
}
.product-card-wishlist-btn i {
    font-size: 16px;
    transition: transform 0.2s ease;
}
.product-card-wishlist-btn:hover {
    background: #ffffff;
    transform: scale(1.08);
    box-shadow: 0 6px 15px rgba(0, 0, 0, 0.12);
}
.product-card-wishlist-btn:hover i {
    transform: scale(1.1);
}


.occasion-bar-section { background: var(--bg-surface); text-align: center; border-bottom: 1px solid var(--border-light); font-family: var(--font-body); }
.occasion-bar-title { font-size: 10px; text-transform: uppercase; letter-spacing: 0.15em; font-weight: 600; color: var(--color-accent); display: block; margin-bottom: 18px; }
.occasion-bar-pills { display: flex; justify-content: center; gap: 15px; flex-wrap: wrap; }
.pill-btn { padding: 10px 24px !important; font-size: 11px !important; font-weight: 600 !important; }

.occasion-banners-section { font-family: var(--font-body); }
.occasion-banner { padding: 140px 0; position: relative; background-size: cover; background-position: center; }
.banner-wedding { background-image: url('../../uploads/gallery/lookbook_1.webp'); }
.banner-office { background-image: url('../../uploads/gallery/lookbook_2.webp'); border-top: 1px solid var(--border-light); }
.occasion-banner-overlay { position: absolute; inset: 0; background: rgba(0,0,0,0.35); z-index: 1; }
.occasion-banner-content { position: relative; z-index: 2; color: white; text-align: center; max-width: 600px; margin: 0 auto; }
.occasion-banner-eyebrow { font-size: 11px; text-transform: uppercase; letter-spacing: 0.25em; color: var(--color-accent); font-weight: 600; display: block; margin-bottom: 15px; }
.occasion-banner-content h2 { font-family: var(--font-heading); font-size: 44px; font-weight: var(--font-heading-weight); text-transform: uppercase; color: white; margin-bottom: 15px; }
.occasion-banner-content p { font-size: 14px; font-style: italic; color: #f5f4f0; margin-bottom: 25px; font-family: var(--font-heading); line-height: 1.6; }
.btn-banner { background: white !important; color: black !important; border-color: white !important; }

/* The .video-banner-* rules were removed with the Atelier Film section they
   styled (pages/home.php section 7). The clip was hosted on Vimeo, the link
   had expired to a 403, and the band rendered empty for every visitor. No
   markup carries these class names any more. */

/* Atelier Film lightbox (homepage "Play Full Film") */
.film-modal {
    position: fixed;
    inset: 0;
    background: rgba(10, 4, 8, 0.9);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 99999;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.3s ease;
    padding: 20px;
}
.film-modal.open { opacity: 1; pointer-events: all; }
.film-modal-inner {
    position: relative;
    width: min(960px, 100%);
    background: #000;
    overflow: hidden;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.6);
    aspect-ratio: 16 / 9;
}
.film-modal-inner video { width: 100%; height: 100%; display: block; object-fit: contain; }
.film-modal-close {
    position: absolute;
    top: 12px;
    right: 12px;
    width: 40px;
    height: 40px;
    background: rgba(255, 255, 255, 0.92);
    color: #3a0c1b;
    font-size: 20px;
    line-height: 1;
    border: none;
    cursor: pointer;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: transform 0.2s ease;
}
.film-modal-close:hover { transform: scale(1.1); }

.reviews-section { background: var(--bg-surface-soft); font-family: var(--font-body); border-top: 1px solid var(--border-light); border-bottom: 1px solid var(--border-light); text-align: center; overflow: hidden; position: relative; }
.reviews-container { max-width: 800px; margin: 0 auto; padding: 0 20px; }
.reviews-slider-wrap { position: relative; height: 180px; }
.reviews-slider { display: flex; width: 300%; height: 100%; transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1); }
.review-slide { width: 33.333%; padding: 0 20px; display: flex; flex-direction: column; align-items: center; }
.review-stars { color: var(--color-accent); margin-bottom: 15px; font-size: 16px; }
.review-quote { font-family: var(--font-heading); font-size: 22px; font-style: italic; color: var(--text-primary); margin-bottom: 15px; line-height: 1.5; font-weight: var(--font-heading-weight); }
.review-author { font-size: 11px; text-transform: uppercase; font-weight: 700; color: var(--text-secondary); letter-spacing: 0.1em; font-style: normal; }
.review-dots-container { display: flex; justify-content: center; gap: 10px; margin-top: 20px; }
.review-dot { width: 6px; height: 6px; background: var(--text-muted); cursor: pointer; transition: 0.3s; opacity: 0.5; }
.review-dot.active { background: var(--color-primary); opacity: 1; }


.newsletter-section { background: var(--bg-main); font-family: var(--font-body); border-bottom: 1px solid var(--border-light); text-align: center; }
.newsletter-container { max-width: 600px; margin: 0 auto; padding: 0 20px; }
.newsletter-subtitle { color: var(--text-secondary); font-size: 14px; margin-bottom: 40px; font-style: italic; font-family: var(--font-heading); }
.btn-newsletter { padding: 12px 40px !important; font-size: 12px !important; font-weight: 600 !important; letter-spacing: 0.05em !important; }

.blog-card { background: var(--bg-surface); border: 1px solid var(--border-light); }
/* .blog-card-cover / .blog-img-1..3 lived here: three fixed background images
   for the home page's hand-written Journal cards. Those cards now read the real
   posts and carry a real <img class="blog-card-img"> inside .blog-card-media,
   exactly as pages/blog.php does, so a card can no longer show a picture that
   belongs to a different article than the one it links to. */
.blog-card-content { padding: 30px; }
.blog-card-tag { font-size: 11px; text-transform: uppercase; color: var(--color-accent); font-weight: 600; }
.blog-card-title { font-family: var(--font-heading); font-size: 22px; font-weight: var(--font-heading-weight); margin: 12px 0; }
.blog-card-title a { color: var(--text-primary); text-decoration: none; }
.blog-card-text { color: var(--text-secondary); font-size: 13px; line-height: 1.6; margin-bottom: 20px; }
.blog-card-link { font-size: 11px; text-transform: uppercase; letter-spacing: 0.15em; font-weight: 700; color: var(--text-primary); text-decoration: none; }
.blog-card-link i { margin-left: 5px; }

.benefits-section { background: var(--bg-surface); border-bottom: 1px solid var(--border-light); font-family: var(--font-body); }
/* Scoped for the reason set out beside .product-benefits-row above. The flex
   column is restated here rather than inherited from that block: these tiles
   were relying on it, and the moment it stops leaking across they need their
   own. Same computed result as before for the homepage — the change is that the
   product strip no longer gets any of it. */
.home-benefits-grid .benefit-item { display: flex; flex-direction: column; align-items: center; text-align: center; padding: 10px; }
.home-benefits-grid .benefit-icon { font-size: 24px; color: var(--color-accent); margin-bottom: 12px; }
.home-benefits-grid .benefit-title { font-family: var(--font-heading); font-size: 16px; font-weight: 500; text-transform: uppercase; margin-bottom: 6px; }
.home-benefits-grid .benefit-text { font-size: 12px; color: var(--text-secondary); }

/* Product Page Clean Components */
.badge-product-page { top: 25px; left: 25px; z-index: 10; }

.product-details-col { padding-top: 10px; }
.product-breadcrumbs { font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin-bottom: 20px; }
.product-breadcrumbs a { color: var(--text-muted); text-decoration: none; }
.product-breadcrumbs span { color: var(--text-primary); }
.product-title-heading { font-family: var(--font-heading); font-size: 40px; font-weight: var(--font-heading-weight); text-transform: uppercase; line-height: 1.2; color: var(--text-primary); margin-bottom: 15px; }
.product-price-heading { font-size: 20px; font-weight: 500; color: var(--color-secondary); margin-bottom: 30px; }
.product-description-text { font-size: 14px; line-height: 1.8; color: var(--text-secondary); margin-bottom: 35px; }

.delivery-checker-box { margin-bottom: 35px; border: 1px dashed var(--border-strong); padding: 15px; }
.delivery-checker-label { display: block; font-size: 11px; text-transform: uppercase; font-weight: 600; letter-spacing: 0.1em; color: var(--text-primary); margin-bottom: 10px; }
.delivery-checker-input-wrap { display: flex; gap: 10px; }
.delivery-checker-input-wrap input { flex: 1; padding: 10px; border: 1px solid var(--border-light); font-size: 13px; background: var(--bg-surface); }
.delivery-checker-input-wrap button { padding: 10px 20px; font-size: 12px; font-weight: 500; }
.delivery-status-msg { font-size: 12px; margin-top: 10px; display: none; }

/* ── Discount Pricing (MRP strike-through + % off) ─────────────────── */
.price-current-amount { color: var(--color-primary); }
.price-mrp-amount { font-size: 0.65em; font-weight: 400; color: var(--text-muted); text-decoration: line-through; margin-left: 10px; }
.price-off-badge { font-size: 0.55em; font-weight: 700; color: var(--color-success); margin-left: 8px; text-transform: uppercase; letter-spacing: 0.03em; }

/* ── Inline Delivery Estimate ───────────────────────────────────────── */
.delivery-estimate-row { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-primary); margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px dashed var(--border-light); }
.delivery-estimate-row i { color: var(--color-primary); }

/* ── Shop with Confidence ───────────────────────────────────────────── */
/* .shop-confidence-box / -title went with the always-open box; the list now
   lives inside a product accordion, which supplies its own heading and frame. */
.shop-confidence-list { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; }
.shop-confidence-list li { font-size: 12px; color: var(--text-secondary); display: flex; align-items: center; gap: 8px; }
.shop-confidence-list i { color: var(--color-primary); width: 14px; text-align: center; }
@media (max-width: 480px) {
    .shop-confidence-list { grid-template-columns: 1fr; }
}

/* ── Offers & Coupons ───────────────────────────────────────────────── */
.coupon-list { display: flex; flex-direction: column; gap: 12px; }
.coupon-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding: 10px 0; border-bottom: 1px solid var(--border-light); }
.coupon-row:last-child { border-bottom: none; }
.coupon-code-chip { display: flex; align-items: center; gap: 8px; border: 1px dashed var(--border-strong); padding: 6px 10px; }
.coupon-code-text { font-size: 12px; font-weight: 700; letter-spacing: 0.05em; color: var(--color-primary); }
.coupon-copy-btn { background: none; border: none; cursor: pointer; color: var(--text-muted); font-size: 12px; padding: 0; }
.coupon-copy-btn:hover { color: var(--color-primary); }
.coupon-desc { font-size: 12px; color: var(--text-secondary); flex: 1; min-width: 180px; }
.coupon-desc-sub { color: var(--text-muted); margin-top: 2px; }

.specifications-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; }

/* ── Product Details tabs (About + one per component) ──────────────── */
.product-details-tabs { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 20px; }
.product-details-tab-btn {
    padding: 10px 20px;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    background: var(--bg-surface);
    border: 1px solid var(--border-light);
    cursor: pointer;
    transition: all 0.2s ease;
}
.product-details-tab-btn:hover { border-color: var(--border-strong); color: var(--text-primary); }
.product-details-tab-btn.active { color: var(--text-primary); font-weight: 700; border-color: var(--color-primary); }
/* !important is load-bearing here, same as .product-accordion-header:focus-visible
   above — the global reset near the top of this file erases any non-!important
   outline on a button, which left these tabs keyboard-focusable with nothing on
   screen to show where focus is. The 10px flex gap on .product-details-tabs
   leaves room for a positive offset, so the ring sits outside the box. */
.product-details-tab-btn:focus-visible { outline: 2px solid var(--color-primary) !important; outline-offset: 2px; }
.btn-reset-filters { width: 100%; padding: 10px; font-size: 11px; justify-content: center; margin-top: 20px; }
.filters-sorting-bar { display: flex; justify-content: space-between; align-items: center; padding-bottom: 20px; border-bottom: 1px solid var(--border-strong); margin-bottom: 30px; }
.view-controls { display: flex; gap: 10px; }
.view-controls .view-btn { background: none; border: none; font-size: 18px; color: var(--text-muted); cursor: pointer; }
.view-controls .view-btn.active { color: var(--text-primary); }
.sorting-controls { display: flex; align-items: center; gap: 10px; }
.sorting-controls label { font-size: 11px; text-transform: uppercase; font-weight: 600; letter-spacing: 0.05em; color: var(--text-secondary); }
.sorting-controls select { border: 1px solid var(--border-strong); background: var(--bg-surface); padding: 8px 16px; font-family: inherit; font-size: 12px; outline: none; cursor: pointer; text-transform: uppercase; letter-spacing: 0.05em; }

.scroll-loader-wrap { text-align: center; padding: 60px 0 20px; display: flex; justify-content: center; align-items: center; flex-direction: column; }
.scroll-loader-wrap .loader-spinner { width: 24px; height: 24px; border: 2px solid var(--border-strong); border-top-color: var(--color-primary); animation: spinner-spin 0.8s linear infinite; display: none; }
.scroll-loader-wrap p { font-size: 12px; text-transform: uppercase; letter-spacing: 0.1em; color: var(--text-muted); margin-top: 10px; font-weight: 600; }

.cart-page-layout { display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 50px; align-items: start; }
.cart-action-btns { margin-top: 30px; display: flex; justify-content: space-between; }

/* Checkout Page Clean Components */
/* Was `background: #eae7e1 !important` — a shorthand, so it also reset
   background-image, and with !important it beat .luxury-hero.has-bg-image.
   That made a hero photo impossible on checkout. Only the spacing is kept. */
.checkout-hero { margin-bottom: 40px !important; }
.checkout-main-section { background: var(--bg-main); font-family: var(--font-body); }
.checkout-grid { display: grid; grid-template-columns: 1.2fr 0.8fr; gap: 50px; align-items: start; }
.checkout-form-panel { background: var(--bg-surface); border: 1px solid var(--border-light); padding: 40px; }
.checkout-alert { margin-bottom: 24px; padding: 15px; background: #fdf2f2; color: #a94442; border: 1px solid #f5c6cb; font-size: 13px; }
.step-nav-header { display: flex; gap: 20px; margin-bottom: 45px; border-bottom: 1px solid var(--border-light); padding-bottom: 15px; justify-content: space-between; }
.step-nav-tab { font-size: 12px; text-transform: uppercase; font-weight: 700; letter-spacing: 0.1em; color: var(--text-muted); border-bottom: 2px solid transparent; padding-bottom: 15px; flex: 1; text-align: center; }
.step-nav-tab.active-tab { color: var(--color-accent); border-bottom-color: var(--color-accent); }

.checkout-step-title { font-family: var(--font-heading); font-size: 24px; font-weight: var(--font-heading-weight); text-transform: uppercase; margin-bottom: 25px; }
.checkout-step-subtitle { font-family: var(--font-heading); font-size: 20px; font-weight: var(--font-heading-weight); text-transform: uppercase; margin-bottom: 20px; border-top: 1px solid var(--border-light); padding-top: 30px; }

.express-shipping-box { border: 1px solid var(--color-accent); padding: 25px; background: var(--bg-surface-soft); margin-bottom: 35px; display: flex; justify-content: space-between; align-items: center; }
.express-shipping-box h3 { font-family: var(--font-heading); font-size: 18px; font-weight: 500; margin-bottom: 5px; }
.express-shipping-box h3 i { color: var(--color-accent); margin-right: 8px; }
.express-shipping-box p { font-size: 13px; color: var(--text-secondary); margin: 0; }

/* Main Header Bar */
/* padding intentionally not set here — the earlier .main-header-bar rule uses
   !important, so a value here would be dead code. */
.main-header-bar { background: transparent; border-bottom: 1px solid var(--border-light); width: 100%; }
.main-header-container { display: flex; align-items: center; justify-content: space-between; flex-wrap: nowrap; gap: 30px; width: 100%; }
.header-brand-group { display: flex; align-items: center; flex-shrink: 0; }
.logo img {
    width: 100%;
    object-fit: contain;
    display: block;
}



/* Center Search Wrap */
/* 460px, not 580px. At 580 the centred box crowded the logo and the icons on
   anything narrower than a wide desktop — see the width calculation in the
   min-width:992px block near the end of this file, which is what actually
   stops them colliding. */
.header-search-wrap { flex: 1; max-width: 460px; position: relative; display: flex; align-items: center; background: #f9f9f9; border: 1px solid var(--border-strong); padding: 5px 6px 5px 18px; cursor: pointer; transition: all 0.2s ease; }
.header-search-wrap:hover { border-color: var(--color-primary); background: #ffffff; box-shadow: 0 2px 10px rgba(0,0,0,0.05); }
.header-search-wrap .search-icon { color: var(--text-muted); font-size: 14px; margin-right: 10px; }
/* min-width: 0 — an <input> carries an intrinsic minimum width from its `size`
   attribute (about 20 characters), and a flex item will not shrink below its
   intrinsic minimum. So in this row the input refused to give ground and its
   text was clipped dead against the Image Search button instead.

   padding-right keeps a gap so the last letter never touches the button, and
   text-overflow trades a hard cut mid-word for an ellipsis, which reads as
   "there is more" rather than as a rendering fault. */
.header-search-wrap input { border: none; background: transparent; width: 100%; min-width: 0; padding-right: 10px; text-overflow: ellipsis; font-size: 13px; color: var(--text-primary); outline: none; cursor: pointer; }

/* flex: 0 0 auto — the button keeps its natural width and gives none of it up.
   It already refuses to wrap, so without this a narrow row would squeeze its
   padding away and push the label out past its own rounded edge. */
.header-image-search-btn { flex: 0 0 auto; background: var(--bg-surface-soft); border: 1px solid var(--border-light); padding: 6px 14px; font-size: 11px; font-weight: 600; color: var(--text-secondary); display: flex; align-items: center; gap: 6px; cursor: pointer; transition: background 0.2s; white-space: nowrap; }
.header-image-search-btn:hover { background: var(--border-light); color: var(--text-primary); }

/* Right Action Icons */
.nav-actions { display: flex; align-items: center; gap: 8px; }
.nav-action-item { display: flex; flex-direction: column; align-items: center; text-decoration: none; color: var(--text-primary); font-size: 17px; position: relative; cursor: pointer; background: none; border: none; transition: color 0.2s; }
.nav-action-item:hover { color: var(--color-accent); }
.nav-action-item span { font-size: 10px; font-weight: 600; text-transform: uppercase; margin-top: 3px; color: var(--text-secondary); letter-spacing: 0.05em; }
.action-badge { position: absolute; top: -6px; right: -8px; background: var(--color-primary); color: #ffffff; font-size: 10px; font-weight: 700; width: 18px; height: 18px; display: flex; align-items: center; justify-content: center; }

/* Bottom Category Navigation Bar */
/* ── Women / Men switch ──────────────────────────────────────
   Rendered by includes/header.php only when both audiences have live stock, so
   these rules are inert on a single-audience shop. Two real links, not a
   toggle widget: /men is an address a crawler and a shared link both reach. */
/* Transparent so the header's own frosted background shows through as one
   continuous surface. It was opaque white, which painted a solid band across an
   otherwise translucent header and made this strip read as a separate element
   sitting on top of it. */
.audience-switch-bar { background: transparent; border-bottom: 1px solid var(--border-light); }
.audience-switch { display: flex; justify-content: center; gap: 4px; }
.audience-switch-link {
    display: inline-block;
    padding: 9px 26px;
    font-family: var(--font-heading);
    font-size: 12px;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-decoration: none;
    color: var(--text-secondary);
    /* Transparent border on the inactive side too, so switching sides does not
       shift the bar's height by two pixels. */
    border-bottom: 2px solid transparent;
    transition: color 0.2s ease, border-color 0.2s ease;
}
.audience-switch-link:hover { color: var(--color-primary); }
.audience-switch-link.is-active {
    color: var(--color-primary);
    border-bottom-color: var(--color-primary);
    font-weight: 600;
}
/* !important: the blanket button/link focus reset near the top of this file
   erases outlines with !important, which would leave this keyboard-focusable
   but with nothing on screen to show where focus is. */
.audience-switch-link:focus-visible {
    outline: 2px solid var(--color-secondary) !important;
    outline-offset: -2px;
}

/* Mobile drawer version of the same switch. Two equal halves rather than tabs,
   because a thumb needs a target, not a text link. */
.mobile-audience-switch {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
    padding: 14px 20px 0;
}
.mobile-audience-link {
    display: block;
    padding: 12px 8px;
    text-align: center;
    text-decoration: none;
    font-family: var(--font-heading);
    font-size: 12px;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-secondary);
    border: 1px solid var(--border-strong);
    background: transparent;
}
.mobile-audience-link.is-active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--text-on-primary, #ffffff);
    font-weight: 600;
}
.mobile-audience-link:focus-visible {
    outline: 2px solid var(--color-secondary) !important;
    outline-offset: 2px;
}

.bottom-header-bar { background: transparent; padding: 12px 0; position: relative !important; overflow: visible !important; }
.bottom-header-container,
.nav-links,
.navbar-luxury { overflow: visible !important; }
.bottom-header-container { display: flex; justify-content: center; }
/* Last-resort net behind the PHP cap in includes/header.php.
   The row had no wrapping and no max width, so a long collection name pushed the
   bar past the page edge with nothing to catch it. The cap keeps the count sane;
   this keeps an unusually long NAME from overflowing anyway — it drops to a
   second line instead of off the screen. row-gap is smaller than column-gap so a
   wrapped second line sits close rather than opening a hole in the header. */
.nav-links {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    justify-content: center;
    column-gap: 32px;
    row-gap: 10px;
    max-width: 100%;
    list-style: none;
    margin: 0;
    padding: 0;
}
/* A single collection name never eats the whole bar. */
.nav-links > li > .nav-link-item { max-width: 22ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-link-item { font-size: 12px; font-weight: 600; letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-primary); text-decoration: none; transition: color 0.2s ease; position: relative; padding: 6px 0; display: flex; align-items: center; gap: 6px; }
.nav-link-item:hover,
.nav-link-item.active { color: var(--color-accent) !important; font-weight: 700 !important; }
.nav-link-item.active::after {
    content: '';
    position: absolute;
    bottom: -2px;
    left: 0;
    width: 100%;
    height: 2px;
    background-color: var(--color-accent);
}
.nav-highlight-new { color: var(--color-accent); font-weight: 700; }
.nav-arrow { font-size: 10px; transition: transform 0.2s; }

/* Mega Menu Dropdown */
.has-mega-menu { position: static !important; }
.mega-dropdown {
    position: absolute !important;
    top: 100% !important;
    left: 0 !important;
    right: 0 !important;
    width: 100% !important;
    background: #ffffff !important;
    border-top: 1px solid var(--border-light) !important;
    border-bottom: 3px solid var(--color-primary) !important;
    box-shadow: 0 20px 45px rgba(0,0,0,0.12) !important;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transform: translateY(8px);
    transition: opacity 0.2s ease, visibility 0.2s ease, transform 0.2s ease !important;
    z-index: 9999 !important;
    padding: 35px 0 !important;
}

/* Hover Bridge: Prevents gap closing when cursor moves to dropdown */
.mega-dropdown::before {
    content: '';
    position: absolute;
    top: -15px;
    left: 0;
    right: 0;
    height: 18px;
    background: transparent;
}

.has-mega-menu:hover .mega-dropdown,
.has-mega-menu:focus-within .mega-dropdown,
.mega-dropdown:hover {
    opacity: 1 !important;
    visibility: visible !important;
    pointer-events: auto !important;
    transform: translateY(0) !important;
}

.has-mega-menu:hover .nav-arrow { transform: rotate(180deg) !important; }
.mega-dropdown-container { display: grid; grid-template-columns: 220px 220px 1fr; gap: 40px; align-items: start; }
/* line-height is stated here now because .mega-heading was an <h5> and inherited
   1.2 from the global heading rule; it is a <div> since these mega-menu column
   labels are navigation, not document sections. Same computed value as before. */
.mega-column .mega-heading { font-family: var(--font-heading); font-size: 13px; font-weight: 700; line-height: 1.2; text-transform: uppercase; letter-spacing: 0.12em; color: var(--color-accent); margin-bottom: 16px; border-bottom: 1px solid var(--border-light); padding-bottom: 6px; }
.mega-column ul { list-style: none; padding: 0; margin: 0; }
.mega-column ul li { margin-bottom: 10px; }
.mega-column ul li a { font-size: 13px; color: var(--text-secondary); text-decoration: none; font-weight: 500; transition: color 0.2s ease, padding-left 0.2s ease; display: inline-block; }
.mega-column ul li a:hover { color: var(--text-primary); font-weight: 700; padding-left: 4px; }

.mega-promo-column { width: 100%; }
.mega-promo-card { position: relative; height: 210px; overflow: hidden; display: flex; align-items: flex-end; }
.mega-promo-card img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.6s ease; }
.mega-promo-card:hover img { transform: scale(1.04); }
.mega-promo-overlay { position: absolute; inset: 0; background: linear-gradient(180deg, rgba(0,0,0,0.1) 0%, rgba(0,0,0,0.7) 100%); padding: 24px; display: flex; flex-direction: column; justify-content: flex-end; color: #ffffff; }
/* Was ".mega-promo-overlay h4" — the promo card's title, one per mega menu, so
   five of them appeared before every page's <h1>. Now a <div>; line-height is
   explicit for the same reason as the rules above. */
.mega-promo-overlay .mega-promo-title { font-family: var(--font-heading); font-size: 20px; font-weight: var(--font-heading-weight); line-height: 1.2; margin: 0 0 8px; color: #ffffff; }
.btn-mega-shop { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; color: #ffffff; text-decoration: none; display: inline-flex; align-items: center; gap: 8px; }
.btn-mega-shop:hover { color: var(--color-accent); }

/* ═══════════════════════════════════════════════════════════
   SHOP PAGE PRICE RANGE FILTER DESIGN FIX
   ═══════════════════════════════════════════════════════════ */
.sidebar-filters-title { font-family: var(--font-heading); font-size: 18px; font-weight: var(--font-heading-weight); text-transform: uppercase; margin-bottom: 20px; border-bottom: 2px solid var(--text-primary); padding-bottom: 8px; display: inline-block; }
.filter-checkbox-label { display: flex; align-items: center; gap: 10px; font-size: 13px; color: var(--text-secondary); cursor: pointer; padding: 3px 0; }
.filter-checkbox-label input[type="checkbox"] { accent-color: var(--color-primary); width: 16px; height: 16px; }
.price-range-container { display: flex; flex-direction: column; gap: 10px; padding-top: 4px; }
.price-range-inputs { display: flex; align-items: center; gap: 8px; }
.price-input-box { display: flex; align-items: center; background: var(--bg-surface); border: 1px solid var(--border-strong); padding: 6px 10px; flex: 1; min-width: 0; }
.price-input-box span { font-size: 12px; color: var(--text-muted); margin-right: 4px; font-weight: 600; }
.price-input-box input { border: none; background: transparent; width: 100%; font-size: 13px; color: var(--text-primary); outline: none; min-width: 0; }
.btn-apply-price { width: 100%; background: var(--text-primary); color: #ffffff; border: none; padding: 8px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; cursor: pointer; transition: background 0.2s; margin-top: 4px; }
.btn-apply-price:hover { background: var(--color-accent); }

/* ═══════════════════════════════════════════════════════════
   COMPACT SUGGESTED PIECES & RECENTLY VIEWED CARDS (ASPECT RATIO 3:4)
   ═══════════════════════════════════════════════════════════ */
.compact-suggested-section { background: var(--bg-surface); border-top: 1px solid var(--border-light); }
/* Same 4-up grid and gap as .home-best-sellers-grid, since it now holds the
   identical .product-card component. */
.related-compact-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 30px; }
.compact-product-card { border: 1px solid var(--border-light); background: #ffffff; text-align: center; overflow: hidden; transition: transform 0.2s ease, box-shadow 0.2s ease; }
.compact-product-card:hover { transform: translateY(-4px); box-shadow: 0 8px 24px rgba(0,0,0,0.06); }
.compact-card-img-wrap { aspect-ratio: 3 / 4; height: auto; max-height: 340px; overflow: hidden; background: var(--bg-surface-soft); border-bottom: 1px solid var(--border-light); }
.compact-card-img-wrap img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s ease; }
.compact-product-card:hover .compact-card-img-wrap img { transform: scale(1.05); }
.compact-card-details { padding: 14px 12px; }
.compact-card-cat { font-size: 10px; color: var(--text-muted); text-transform: uppercase; display: block; margin-bottom: 4px; font-weight: 500; }
.compact-card-name { font-family: var(--font-heading); font-size: 14px; font-weight: 500; margin-bottom: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.compact-card-name a { color: var(--text-primary); text-decoration: none; }
.compact-card-price { font-size: 13px; font-weight: 600; color: var(--color-primary); }
.recently-viewed-empty { text-align: center; font-size: 13px; color: var(--text-muted); display: none; padding: 20px 0; }

/* Product Page Action Buttons */
.product-variants-wrap { margin: 20px 0; }
.size-select-header-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.size-select-header-row .variant-select-label { margin: 0; }
.variant-select-label { font-size: 11px; text-transform: uppercase; font-weight: 700; letter-spacing: 0.08em; color: var(--text-muted); display: block; margin-bottom: 8px; }

/* .product-trust-badges / .trust-badge-item removed with the strip they styled.
   Both its claims — dispatch and the return window — now appear once each in
   .product-benefits-row, which is where every other delivery promise already
   was. Nothing else in the shop referenced these classes. */

.product-section-header-row { display: flex; justify-content: space-between; align-items: flex-end; border-bottom: 1px solid var(--border-strong); padding-bottom: 15px; margin-bottom: 40px; flex-wrap: wrap; gap: 20px; }
.product-section-header-row h2 { font-family: var(--font-heading); font-size: 28px; font-weight: var(--font-heading-weight); text-transform: uppercase; margin: 0; }

.product-modal-card { background: var(--bg-surface); border: 1px solid var(--border-strong); width: 100%; max-width: 520px; overflow: hidden; position: relative; box-shadow: var(--shadow-lg); }
.product-modal-card-header { padding: 20px 24px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; }
.product-modal-card-header h3 { font-family: var(--font-heading); font-size: 20px; color: var(--text-primary); margin: 0; }
.product-modal-close-btn { background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-muted); }
.product-modal-card-body { padding: 24px; }
.product-modal-hint { font-size: 13px; color: var(--text-muted); margin-bottom: 16px; }
.product-modal-form-content { max-width: 600px; width: 90%; height: auto; flex-direction: column; padding: 40px; }
.product-modal-form-title { font-family: var(--font-heading); font-size: 24px; margin-bottom: 20px; text-transform: uppercase; }
.product-modal-form-msg { margin-bottom: 16px; padding: 12px 14px; font-size: 13px; }
.form-luxury-textarea-tall { height: 120px; }
.product-modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(4px); z-index: 9999; align-items: center; justify-content: center; padding: 20px; }
.variant-pills-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.variant-pill-btn { padding: 8px 16px; border: 1px solid var(--border-strong); background: var(--bg-surface); font-size: 12px; font-weight: 500; cursor: pointer; transition: all 0.2s; color: var(--text-primary); }
.variant-pill-btn.selected-pill, .variant-pill-btn:hover { background: var(--text-primary); color: #ffffff; border-color: var(--text-primary); }

.size-guide-trigger-btn { background: none; border: none; color: var(--color-primary); font-size: 12px; font-weight: 700; cursor: pointer; text-decoration: underline; letter-spacing: 0.04em; padding: 0; }

/* ── Colour Selector (Biba-style image swatches) ───────────── */
.product-colors-wrap { margin: 20px 0 24px; }
.color-select-label { font-size: 11px; text-transform: uppercase; font-weight: 700; letter-spacing: 0.08em; color: var(--text-muted); display: block; margin-bottom: 12px; }
.color-select-label strong { color: var(--text-primary); text-transform: none; letter-spacing: 0; font-weight: 700; }
.color-swatch-row { display: flex; flex-wrap: wrap; gap: 16px; }
.color-swatch-btn { background: none; border: none; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 0; font-family: inherit; }
/* Portrait, not a circle.
   ────────────────────────────────────────────────────────────────────────
   These photographs are shot 3:4 like every other product image on the shop.
   Squeezed into a 60px circle with object-fit: cover, a swatch kept the
   middle band of the frame and discarded the rest — so it showed the fabric
   but not the garment, which is the one thing a colour swatch is for.
   66x88 is the same 3:4 the product cards and the collection tiles use, so a
   shopper reads the colour AND recognises the piece. The 2px radius matches
   .category-card-media rather than introducing a third corner on one page. */
.color-swatch-ring { width: 66px; height: 88px; border: 2px solid transparent; padding: 3px; display: flex; align-items: center; justify-content: center; overflow: hidden; transition: border-color 0.2s, box-shadow 0.2s; background: var(--bg-surface-soft); }
.color-swatch-btn:hover .color-swatch-ring { border-color: var(--border-strong); }
.color-swatch-btn.color-swatch-selected .color-swatch-ring,
.color-swatch-btn:focus-visible .color-swatch-ring { border-color: var(--color-primary); box-shadow: 0 0 0 2px #fff, 0 0 0 3px var(--color-primary); }
.color-swatch-ring img { width: 100%; height: 100%; object-fit: cover; }
.color-swatch-fallback-emoji { font-size: 24px; }
.color-swatch-name { font-size: 11px; color: var(--text-secondary); font-weight: 500; }
.color-swatch-btn.color-swatch-selected .color-swatch-name { color: var(--color-primary); font-weight: 700; }
.color-swatch-btn:focus-visible { outline: none; }

/* ── Size Ladder (numeric + letter Biba-style buttons) ─────── */
.size-ladder-grid { display: flex; flex-wrap: wrap; gap: 10px; }
.size-pill-btn { position: relative; min-width: 68px; padding: 9px 12px; border: 1px solid var(--border-strong); background: var(--bg-surface); font-size: 12px; font-weight: 600; cursor: pointer; transition: all 0.2s; color: var(--text-primary); text-align: center; }
.size-pill-btn:hover:not(.size-pill-disabled) { border-color: var(--color-primary); }
.size-pill-btn.size-pill-selected { background: var(--color-primary); color: #ffffff; border-color: var(--color-primary); }
.size-pill-btn.size-pill-disabled { color: var(--text-muted); background: var(--bg-surface-soft); cursor: not-allowed; text-decoration: line-through; opacity: 0.55; }
.size-pill-low-stock { display: block; font-size: 9px; font-weight: 700; color: var(--color-danger); margin-top: 2px; text-transform: uppercase; letter-spacing: 0.02em; }
.size-pill-btn.size-pill-selected .size-pill-low-stock { color: #ffe4e4; }
.size-select-validation-msg { color: var(--color-danger); font-size: 12px; font-weight: 600; margin: 10px 0 0; }
.size-guide-empty-msg { color: var(--text-muted); font-size: 13px; margin: 0; }

/* ── Dievon Size Guide Modal ────────────────────────────────── */
.dievon-sg-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(4px); z-index: 9999; display: flex; align-items: center; justify-content: center; padding: 20px; opacity: 0; transition: opacity 0.3s ease; }
.dievon-sg-overlay.is-visible { opacity: 1; }
.dievon-sg-modal { background: var(--bg-surface); border: 1px solid var(--border-strong); width: 100%; max-width: 1120px; max-height: 94vh; overflow-y: auto; position: relative; box-shadow: var(--shadow-lg); transform: scale(0.95) translateY(10px); transition: transform 0.3s ease; }
.dievon-sg-overlay.is-visible .dievon-sg-modal { transform: scale(1) translateY(0); }
.dievon-sg-header { position: sticky; top: 0; background: var(--bg-surface); z-index: 2; padding: 22px 28px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; }
.dievon-sg-header-text { display: flex; flex-direction: column; gap: 4px; }
.dievon-sg-eyebrow { font-size: 11px; font-weight: 600; letter-spacing: 0.12em; text-transform: uppercase; color: var(--color-accent); }
.dievon-sg-title { font-family: var(--font-heading); font-size: 22px; letter-spacing: 0.06em; color: var(--text-primary); margin: 0; }
.dievon-sg-close { background: none; border: none; font-size: 26px; line-height: 1; cursor: pointer; color: var(--text-muted); padding: 4px 8px; }
.dievon-sg-close:hover, .dievon-sg-close:focus-visible { color: var(--color-primary); background: var(--bg-surface-soft); outline: none; }
.dievon-sg-intro { padding: 18px 28px 0; margin: 0; font-size: 13px; line-height: 1.6; color: var(--text-secondary); }
.dievon-sg-controls { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 14px; padding: 16px 28px 0; }
.dievon-sg-tabs { display: flex; gap: 4px; border: 1px solid var(--border-strong); overflow: hidden; }
.dievon-sg-tab { padding: 9px 16px; background: var(--bg-surface); border: none; font-size: 11px; font-weight: 700; letter-spacing: 0.05em; cursor: pointer; color: var(--text-secondary); }
.dievon-sg-tab.active { background: var(--color-primary); color: #fff; }
.dievon-sg-unit-toggle { display: flex; gap: 4px; border: 1px solid var(--border-strong); overflow: hidden; }
.dievon-sg-unit-btn { padding: 9px 14px; background: var(--bg-surface); border: none; font-size: 11px; font-weight: 700; letter-spacing: 0.05em; cursor: pointer; color: var(--text-secondary); }
.dievon-sg-unit-btn.active { background: var(--color-secondary); color: #fff; }
.dievon-sg-body { padding: 20px 28px 28px; }
.dievon-sg-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; border: 1px solid var(--border-light); }
.dievon-sg-table { width: 100%; min-width: 480px; border-collapse: collapse; font-size: 13px; text-align: center; }
.dievon-sg-table thead { background: var(--color-primary); }
.dievon-sg-table thead th { color: #fff; font-weight: 600; text-transform: uppercase; font-size: 11px; letter-spacing: 0.05em; }
.dievon-sg-table th, .dievon-sg-table td { padding: 11px 10px; border: 1px solid var(--border-light); }
.dievon-sg-table tbody tr:nth-child(even) { background: var(--bg-surface-soft); }
.dievon-sg-table tbody tr:hover { background: rgba(81, 17, 38, 0.06); }
.dievon-sg-size-cell { font-weight: 700; color: var(--color-primary); }
.dievon-sg-empty-row { color: var(--text-muted); padding: 18px !important; }
.dievon-sg-illustration-row { display: flex; gap: 32px; margin-top: 26px; flex-wrap: wrap; align-items: flex-start; }
.dievon-sg-illustration { flex: 0 0 auto; width: 220px; color: var(--color-primary); background: var(--bg-surface-soft); border: 1px solid var(--border-light); padding: 18px; box-sizing: border-box; }
.dievon-sg-illustration img, .dievon-sg-illustration-svg { width: 100%; height: auto; display: block; }
.dievon-sg-photo-stage { position: relative; width: 100%; overflow: hidden; }
.dievon-sg-photo-img { display: block; width: 100%; height: auto; }
.dievon-sg-photo-line { position: absolute; left: 50%; transform: translateX(-50%); height: 0; border-top: 2px dashed var(--color-accent); }
.dievon-sg-photo-line span { position: absolute; left: 100%; top: -9px; margin-left: 6px; background: rgba(255,255,255,0.92); color: var(--color-primary); font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; padding: 2px 6px; white-space: nowrap; }
.dievon-sg-photo-length { position: absolute; left: 4%; width: 0; border-left: 2px dashed var(--color-accent); }
.dievon-sg-photo-length span { position: absolute; left: 0; top: 100%; margin-top: 6px; background: rgba(255,255,255,0.92); color: var(--color-primary); font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; padding: 2px 6px; white-space: nowrap; }
.dievon-sg-instructions { flex: 1; min-width: 240px; }
.dievon-sg-instructions h4 { margin: 0 0 10px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--text-primary); }
.dievon-sg-instructions p { font-size: 13px; color: var(--text-secondary); margin: 0 0 8px; line-height: 1.6; }
.dievon-sg-tip { margin-top: 16px; padding: 12px 14px; background: var(--bg-surface-soft); border-left: 3px solid var(--color-accent); font-size: 12px; line-height: 1.6; color: var(--text-secondary); }
.dievon-sg-tip strong { color: var(--text-primary); }
.dievon-sg-cta { margin-top: 24px; padding-top: 20px; border-top: 1px solid var(--border-light); display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; }
.dievon-sg-cta p { margin: 0; font-size: 13px; color: var(--text-secondary); }
body.dievon-sg-open { overflow: hidden; }
@media (prefers-reduced-motion: reduce) {
    .dievon-sg-overlay, .dievon-sg-modal { transition: none; }
}
@media (max-width: 640px) {
    .dievon-sg-modal { max-height: 92vh; }
    .dievon-sg-header, .dievon-sg-body { padding-left: 18px; padding-right: 18px; }
    .dievon-sg-illustration { width: 120px; }
}

/* justify-content pushes the wishlist button to the right edge.
   The row is already the same 580px as the Add to Bag / Buy Now row beneath it,
   but the stepper and the heart huddled at the left and left roughly half of it
   empty — so the block read as two small controls floating above two large ones
   rather than as one panel. Aligning the heart with the right edge of Buy Now
   below gives the four controls a shared rectangle. */
.product-quantity-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; margin-top: 20px; }
.product-qty-controls { height: 50px; box-sizing: border-box; border: 1px solid var(--border-strong); padding: 0 14px; gap: 16px; }
.product-qty-controls .qty-btn { width: 30px; height: 30px; font-size: 15px; }
.product-qty-controls .qty-value { font-size: 15px; min-width: 22px; }

.product-action-buttons-group { display: flex; align-items: center; gap: 14px; margin: 28px 0; flex-wrap: wrap; }
.btn-add-bag { flex: 2; height: 50px; justify-content: center; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; display: flex; align-items: center; gap: 10px; min-width: 180px; }
.btn-buy-now { flex: 1; height: 50px; justify-content: center; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; display: flex; align-items: center; min-width: 140px; }
.btn-wishlist-action { width: 50px; height: 50px; border: 1px solid var(--border-strong); background: var(--bg-surface); font-size: 20px; color: var(--text-primary); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s; }
.btn-wishlist-action:hover { border-color: var(--color-danger); color: var(--color-danger); }
.btn-wishlist-action.active { background: var(--color-primary); border-color: var(--color-primary); }
.btn-wishlist-action.active .wishlist-btn-active { color: #ffffff !important; }

/* ═══════════════════════════════════════════════════════════
   PRODUCT REVIEWS & Q&A SECTIONS
   ═══════════════════════════════════════════════════════════ */
.product-reviews-section { background: var(--bg-surface); border-top: 1px solid var(--border-light); border-bottom: 1px solid var(--border-light); font-family: var(--font-body); }
.product-reviews-container { max-width: 1240px; margin: 0 auto; padding: 0 20px; box-sizing: border-box; }
.reviews-header-bar { display: flex; justify-content: space-between; align-items: flex-end; border-bottom: 1px solid var(--border-strong); padding-bottom: 20px; margin-bottom: 40px; flex-wrap: wrap; gap: 20px; }
.reviews-title { font-family: var(--font-heading); font-size: 28px; font-weight: var(--font-heading-weight); text-transform: uppercase; color: var(--text-primary); margin: 0; }
.reviews-rating-summary { font-size: 14px; color: var(--text-muted); margin-top: 8px; display: flex; align-items: center; gap: 6px; }
.reviews-rating-summary i { color: #fbbf24; }
.reviews-rating-summary span { font-weight: 600; color: var(--text-primary); margin-left: 6px; }
.btn-write-review { padding: 10px 24px; font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.08em; }

.reviews-list-grid { display: flex; flex-direction: column; gap: 24px; }
.review-card-item { background: #ffffff; border: 1px solid var(--border-light); padding: 24px; box-shadow: 0 2px 10px rgba(0,0,0,0.02); transition: transform 0.2s ease, box-shadow 0.2s ease; }
.review-card-item:hover { box-shadow: 0 6px 20px rgba(0,0,0,0.05); }
.review-card-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; flex-wrap: wrap; gap: 10px; }
.review-buyer-name { font-size: 15px; color: var(--text-primary); font-weight: 600; }
.review-verified-tag { font-size: 12px; color: #10b981; margin-left: 12px; font-weight: 600; display: inline-flex; align-items: center; gap: 4px; }
.review-date { font-size: 12px; color: var(--text-muted); font-weight: 500; }
.review-card-stars { margin-bottom: 12px; font-size: 13px; color: #fbbf24; display: flex; gap: 4px; }
.review-card-title { font-family: var(--font-heading); font-size: 18px; font-weight: 500; margin-bottom: 10px; color: var(--text-primary); }
.review-card-text { font-size: 14px; color: var(--text-secondary); line-height: 1.7; margin: 0; }

.product-qa-section { background: var(--bg-surface-soft); border-bottom: 1px solid var(--border-light); font-family: var(--font-body); }


/* ═══════════════════════════════════════════════════════════
   FOOTER ENHANCED DARK STYLING & NEWSLETTER FIX
   ═══════════════════════════════════════════════════════════ */
.footer-enhanced { background: #111111; color: #ffffff; padding: 80px 0 40px; font-family: var(--font-body); border-top: 1px solid #222222; }
.footer-top { display: grid; grid-template-columns: 2fr 1fr 1fr 2fr; gap: 50px; margin-bottom: 60px; }
.footer-brand img { max-width: 170px; max-height: 75px; width: auto; margin-bottom: 20px; filter: brightness(0) invert(1); }
.footer-brand p { font-size: 13px; color: #a0a0a0; line-height: 1.7; margin-bottom: 24px; max-width: 340px; }
.footer-social { display: flex; gap: 16px; }
.footer-social a { width: 36px; height: 36px; background: #222222; color: #ffffff; display: flex; align-items: center; justify-content: center; font-size: 15px; text-decoration: none; transition: background 0.3s, transform 0.3s; }
.footer-social a:hover { background: var(--color-accent); transform: translateY(-3px); }

.footer-col h4 { font-family: var(--font-heading); font-size: 15px; font-weight: 500; text-transform: uppercase; letter-spacing: 0.12em; color: #ffffff; margin-bottom: 24px; position: relative; padding-bottom: 8px; }
.footer-col h4::after { content: ''; position: absolute; bottom: 0; left: 0; width: 30px; height: 1px; background: var(--color-accent); }
.footer-col p { font-size: 13px; color: #a0a0a0; line-height: 1.6; margin-bottom: 20px; }
.footer-col ul { list-style: none; padding: 0; margin: 0; }
.footer-col ul li { margin-bottom: 12px; }
.footer-col ul li a { color: #a0a0a0; font-size: 13px; text-decoration: none; transition: color 0.2s; }
.footer-col ul li a:hover { color: #ffffff; padding-left: 4px; }

.footer-newsletter-form { display: flex; gap: 0; border: 1px solid #333333; overflow: hidden; margin-top: 16px; background: #1a1a1a; }
.footer-newsletter-form input { flex: 1; padding: 12px 16px; border: none; background: transparent; color: #ffffff; font-size: 12px; outline: none; }
.footer-newsletter-form input::placeholder { color: #777777; }
.footer-newsletter-form button { background: var(--color-primary); color: #ffffff; border: none; padding: 12px 24px; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; cursor: pointer; transition: background 0.2s; }
.footer-newsletter-form button:hover { background: var(--color-accent); }

.footer-bottom-bar { border-top: 1px solid #222222; padding-top: 30px; display: flex; justify-content: space-between; align-items: center; font-size: 12px; color: #777777; flex-wrap: wrap; gap: 15px; }
.footer-logo-text { font-family: var(--font-heading); font-size: 18px; color: #ffffff; letter-spacing: 0.15em; text-transform: uppercase; }

/* ═══════════════════════════════════════════════════════════
   ACTION BADGES & ADMIN LOGIN LUXURY PORTAL & HONEYPOT
   ═══════════════════════════════════════════════════════════ */
/* ═══════════════════════════════════════════════════════════
   STICKY FLOATING VIEW CART BAR (SITE-WIDE)
   ═══════════════════════════════════════════════════════════ */
.sticky-view-cart-bar {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%) translateY(100px);
    z-index: 99999;
    background: #111111;
    color: #ffffff;
    padding: 12px 24px;
    box-shadow: 0 10px 30px rgba(0,0,0,0.35);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
    opacity: 0;
    pointer-events: none;
    transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275), opacity 0.3s ease;
    max-width: 90vw;
    width: 480px;
}
.sticky-view-cart-bar.show {
    transform: translateX(-50%) translateY(0);
    opacity: 1;
    pointer-events: auto;
}
.sticky-cart-info {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 13px;
    font-weight: 500;
}
.sticky-cart-info i {
    color: #e6c687;
    font-size: 16px;
}
.sticky-cart-total {
    color: #ffffff;
    font-weight: 700;
    margin-left: 5px;
}
.btn-sticky-cart {
    background: var(--color-primary);
    color: #ffffff !important;
    border: none;
    padding: 8px 18px;
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 8px;
    transition: var(--transition);
}
.btn-sticky-cart:hover {
    background: #ffffff;
    color: var(--color-primary) !important;
}

/* In Bag Product Button State & Master Button Active State (Deep Burgundy Brand Colors) */
.btn-add-bag.in-bag-active,
.step-nav-tab.active-tab,
.acc-tab-btn.active,
.variant-pill-btn.active,
.variant-pill.active {
    background: var(--color-primary) !important;
    color: #ffffff !important;
    border-color: var(--color-primary) !important;
    box-shadow: 0 4px 15px rgba(81, 17, 38, 0.25) !important;
}

/* Product Lightbox Modal */
.product-lightbox-modal {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background: rgba(10, 3, 5, 0.98);
    backdrop-filter: blur(8px);
    z-index: 999999;
    display: none;
    align-items: center;
    justify-content: center;
    box-sizing: border-box;
    opacity: 0;
    transition: opacity 0.3s ease;
}

.product-lightbox-modal.is-visible { opacity: 1; }

.product-lightbox-container-wrapper {
    display: flex;
    width: 92%;
    height: 90%;
    max-width: 1400px;
    align-items: center;
    justify-content: center;
    position: relative;
    gap: 40px;
    transform: scale(0.95);
    transition: transform 0.3s ease;
}

.product-lightbox-modal.is-visible .product-lightbox-container-wrapper { transform: scale(1); }

/* Vertical Thumbnails List on Left */
.product-lightbox-thumbnails {
    display: flex;
    flex-direction: column;
    gap: 12px;
    max-height: 80vh;
    overflow-y: auto;
    width: 80px;
    flex-shrink: 0;
    scrollbar-width: none;
}
.product-lightbox-thumbnails::-webkit-scrollbar {
    display: none;
}
.product-lightbox-thumb-item {
    width: 70px;
    height: 94px;
    overflow: hidden;
    cursor: pointer;
    border: 2px solid transparent;
    opacity: 0.5;
    transition: all 0.3s ease;
    background: #000;
}
.product-lightbox-thumb-item img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.product-lightbox-thumb-item.active {
    border-color: var(--color-primary);
    opacity: 1;
    transform: scale(1.05);
}
.product-lightbox-thumb-item:hover {
    opacity: 1;
}

/* Main Image Area in Center */
.product-lightbox-main-area {
    display: flex;
    flex-grow: 1;
    height: 100%;
    align-items: center;
    justify-content: center;
    position: relative;
    max-width: calc(100% - 120px);
}
.product-lightbox-content {
    max-width: 100%;
    max-height: 85vh;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.product-lightbox-content img {
    max-width: 100%;
    max-height: 85vh;
    object-fit: contain;
    box-shadow: 0 10px 40px rgba(0,0,0,0.5);
    cursor: zoom-in;
    touch-action: none;
    transform-origin: center center;
    transition: transform 0.2s ease-out, opacity 0.15s ease;
    will-change: transform;
}
.product-lightbox-content img.is-swapping { opacity: 0; }
.product-lightbox-content img.lb-zoomed {
    cursor: grab;
}
.product-lightbox-content img.lb-panning {
    cursor: grabbing;
    transition: none;
}

/* Navigation Controls inside Main Area */
.product-lightbox-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.15);
    color: #ffffff;
    font-size: 20px;
    width: 46px;
    height: 46px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    z-index: 10;
}
.product-lightbox-nav:hover {
    background: rgba(255, 255, 255, 0.25);
    transform: translateY(-50%) scale(1.08);
}
.product-lightbox-nav.prev {
    left: 20px;
}
.product-lightbox-nav.next {
    right: 20px;
}

.product-lightbox-close {
    position: absolute;
    top: 25px;
    right: 30px;
    background: none;
    border: none;
    color: #fff;
    font-size: 36px;
    cursor: pointer;
    z-index: 1000000;
    transition: transform 0.2s ease;
}
.product-lightbox-close:hover {
    color: var(--color-primary);
    transform: scale(1.15);
}
.product-lightbox-counter {
    position: absolute;
    bottom: 20px;
    left: 50%;
    transform: translateX(-50%);
    color: rgba(255, 255, 255, 0.7);
    font-size: 14px;
    letter-spacing: 0.1em;
}


@media (max-width: 768px) {
    .product-lightbox-container-wrapper {
        width: 100%;
        height: 100%;
        flex-direction: column-reverse;
        gap: 0;
    }
    .product-lightbox-thumbnails {
        flex-direction: row;
        width: 100%;
        max-height: none;
        height: 74px;
        padding: 10px;
        overflow-x: auto;
        overflow-y: hidden;
        flex-shrink: 0;
    }
    .product-lightbox-thumb-item {
        width: 50px;
        height: 54px;
        flex-shrink: 0;
    }
    .product-lightbox-main-area {
        width: 100%;
        max-width: 100%;
        flex: 1;
        height: auto;
    }
    .product-lightbox-content,
    .product-lightbox-content img {
        max-height: 100%;
    }
    .product-lightbox-close {
        top: 12px; right: 12px; font-size: 24px;
        width: 44px; height: 44px;
        display: flex; align-items: center; justify-content: center;
        background: rgba(0, 0, 0, 0.55);
    }
    .product-lightbox-nav.prev { left: 10px; }
    .product-lightbox-nav.next { right: 10px; }
}

button:active,
.btn-luxury:active,
.btn-luxury-outline:active,
.btn-primary:active,
.btn-secondary:active,
.btn-outline:active,
.btn-sm-primary:active,
.btn-sm-outline:active {
    /* Pressed feedback without the colour flash: a blanket dark-maroon
       background (old #3a0c1b !important) made every button and btn-styled
       link snap to a flat dark box the instant it was clicked. Dimming with
       brightness keeps each button's own colours and just reads as a press. */
    filter: brightness(0.92);
    transform: scale(0.98);
}
/* Outline/ghost buttons are transparent, so a brightness filter alone would
   give no press feedback — a faint fill keeps the pressed state visible. */
.btn-outline:active,
.btn-sm-outline:active {
    background: rgba(0, 0, 0, 0.06);
}

.in-bag-status-banner {
    background: var(--bg-surface-soft);
    border: 1px solid var(--border-strong);
    color: var(--color-primary);
    padding: 10px 16px;
    margin-bottom: 20px;
    font-size: 13px;
    display: flex;
    align-items: center;
    justify-content: space-between;
}
.in-bag-view-link {
    color: var(--color-primary);
    font-weight: 700;
    text-decoration: underline;
}

/* ═══════════════════════════════════════════════════════════
   UNIVERSAL BUTTON ICON COLOR & HOVER / ACTIVE SYNC
   ═══════════════════════════════════════════════════════════ */
button i, button svg,
a.btn i, a.btn svg,
a.btn-luxury i, a.btn-luxury svg,
a.btn-luxury-outline i, a.btn-luxury-outline svg,
.btn-luxury i, .btn-luxury svg,
.btn-luxury-outline i, .btn-luxury-outline svg,
.btn-primary i, .btn-primary svg,
.btn-secondary i, .btn-secondary svg,
.btn-outline i, .btn-outline svg,
.btn-sm i, .btn-sm svg,
.btn-add-cart i, .btn-add-cart svg,
.nav-action-item i, .nav-action-item svg,
.variant-pill i, .variant-pill svg,
.variant-pill-btn i, .variant-pill-btn svg,
.step-nav-tab i, .step-nav-tab svg,
.cat-tab i, .cat-tab svg {
    color: inherit !important;
}

/* ══ Global FontAwesome Button Icon Spacing ══════════════════════ */
button svg,
a.btn i, a.btn svg,
.btn-luxury i, .btn-luxury svg,
.btn-luxury-outline i, .btn-luxury-outline svg,
.btn-primary i, .btn-primary svg,
.btn-secondary i, .btn-secondary svg,
.btn-outline i, .btn-outline svg,
.btn-sm i, .btn-sm svg,
.btn-add-bag i, .btn-buy-now i,
.acc-tab-btn i {
    margin-right: 8px !important;
}

button i.fa-arrow-right,
.btn-luxury i.fa-arrow-right, .btn-luxury-outline i.fa-arrow-right {
    margin-right: 0 !important;
    margin-left: 8px !important;
}

button:hover i, button:hover svg,
a.btn:hover i, a.btn:hover svg,
a.btn-luxury:hover i, a.btn-luxury:hover svg,
a.btn-luxury-outline:hover i, a.btn-luxury-outline:hover svg,
.btn-luxury:hover i, .btn-luxury:hover svg,
.btn-luxury-outline:hover i, .btn-luxury-outline:hover svg,
.btn-primary:hover i, .btn-primary:hover svg,
.btn-secondary:hover i, .btn-secondary:hover svg,
.btn-outline:hover i, .btn-outline:hover svg,
.btn-sm:hover i, .btn-sm:hover svg,
.btn-add-cart:hover i, .btn-add-cart:hover svg,
.nav-action-item:hover i, .nav-action-item:hover svg,
.variant-pill:hover i, .variant-pill:hover svg,
.variant-pill-btn:hover i, .variant-pill-btn:hover svg,
.variant-pill-btn.selected-pill i, .variant-pill-btn.selected-pill svg,
.step-nav-tab:hover i, .step-nav-tab:hover svg,
.step-nav-tab.active-tab i, .step-nav-tab.active-tab svg,
.acc-tab-btn:hover i, .acc-tab-btn:hover svg,
.acc-tab-btn.active i, .acc-tab-btn.active svg,
.cat-tab:hover i, .cat-tab:hover svg,
.cat-tab.active i, .cat-tab.active svg,
.in-bag-active i, .in-bag-active svg {
    /* The button:active i/svg force-to-inherit was paired with the old maroon
       pressed background (white icon on a dark box). That background is gone,
       so icons keep their own colour on press. */
    color: inherit !important;
}

.hp-input, input.hp-input {

    display: none !important;
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
    position: absolute !important;
    width: 0 !important;
    height: 0 !important;
    overflow: hidden !important;
    left: -9999px !important;
    top: -9999px !important;
    z-index: -9999 !important;
}

/* Smaller than the icon it belongs to, and lifted clear of it.
   ─────────────────────────────────────────────────────────────
   These were 18px circles sitting on 17px icons — the badge was physically
   larger than the thing it was counting, and at top:-6px/right:-8px it landed
   across the icon's own outline, so the two shapes merged into one dark blob.
   A count is an annotation; it should not outweigh what it annotates.

   15px, pulled further out, and given a white ring so it separates from the
   icon instead of blending into it. This is the LAST .action-badge rule in the
   file, so it is the one that wins — the earlier declaration around line 2851
   sets the same properties and is overridden here. */
.action-badge, .cart-badge, .wishlist-badge {
    background: var(--color-primary) !important;
    color: #ffffff !important;
    font-size: 9px;
    font-weight: 700;
    width: 15px;
    height: 15px;
    display: flex;
    align-items: center;
    justify-content: center;
    top: -7px;
    right: -9px;
    box-shadow: 0 0 0 2px #ffffff;
    line-height: 1;
}


.admin-login-body { background: #111111; min-height: 100vh; display: flex; align-items: center; justify-content: center; font-family: var(--font-body); margin: 0; padding: 20px; }
.admin-login-wrapper { width: 100%; max-width: 440px; }
.admin-login-card { background: #ffffff; border: 1px solid var(--border-strong); padding: 45px 35px; box-shadow: 0 20px 50px rgba(0,0,0,0.5); text-align: center; }
.admin-login-brand { margin-bottom: 30px; }
.admin-login-logo { font-family: var(--font-heading); font-size: 36px; font-weight: 300; letter-spacing: 0.15em; color: var(--color-primary); text-transform: uppercase; margin-bottom: 6px; }
.admin-login-tagline { font-size: 11px; text-transform: uppercase; letter-spacing: 0.12em; color: var(--text-muted); font-weight: 600; }
.admin-login-alert { background: #fdf2f2; color: var(--color-danger); border: 1px solid #f5c6cb; padding: 12px; font-size: 13px; margin-bottom: 24px; text-align: left; display: flex; align-items: center; gap: 8px; }
.admin-login-form { text-align: left; }
.admin-input-group { margin-bottom: 20px; }
.admin-input-group label { display: block; font-size: 11px; text-transform: uppercase; font-weight: 700; letter-spacing: 0.08em; color: var(--text-primary); margin-bottom: 8px; }
.admin-input-box { display: flex; align-items: center; background: var(--bg-surface-soft); border: 1px solid var(--border-strong); padding: 0 14px; height: 48px; position: relative; transition: border-color 0.2s; }
.admin-input-box:focus-within { border-color: var(--color-primary); background: #ffffff; }
.admin-input-box i { color: var(--text-muted); font-size: 14px; margin-right: 12px; }
.admin-input-box input { border: none; background: transparent; width: 100%; height: 100%; font-size: 14px; color: var(--text-primary); outline: none; }
.btn-pwd-eye { background: none; border: none; color: var(--text-muted); cursor: pointer; padding: 4px; font-size: 14px; transition: color 0.2s; }
.btn-pwd-eye:hover { color: var(--color-primary); }
.admin-login-forgot { display: inline-block; font-size: 12px; color: var(--text-muted); text-decoration: none; margin-top: 8px; text-align: right; width: 100%; transition: color 0.2s; }
.admin-login-forgot:hover { color: var(--color-primary); }
.btn-admin-submit { width: 100%; height: 50px; background: var(--color-primary); color: #ffffff !important; border: none; font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.1em; cursor: pointer; transition: background 0.2s; margin-top: 10px; display: flex; align-items: center; justify-content: center; gap: 10px; }
.btn-admin-submit:hover { background: var(--color-primary-dark); }
.admin-login-footer a:hover { color: var(--color-primary); }

/* ═══════════════════════════════════════════════════════════
   DIEVON SIGNATURE LUXURY ATELIER ADMIN THEME
   ═══════════════════════════════════════════════════════════ */
body.admin-wrapper {
    background: #FAF8F5;
    margin: 0;
    padding: 0;
    font-family: var(--font-body);
    color: #1A0A14;
}

.admin-global-topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 60px;
    background: #1C050A;
    border-bottom: 1px solid #3D0B13;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 24px;
    z-index: 500;
    box-shadow: 0 2px 10px rgba(0,0,0,0.15);
}

.admin-brand-logo {
    display: flex;
    align-items: center;
    gap: 10px;
    color: #ffffff;
    text-decoration: none;
    font-family: var(--font-heading);
    font-size: 18px;
    letter-spacing: 0.12em;
    text-transform: uppercase;
}

.admin-brand-logo small {
    font-size: 10px;
    color: var(--color-accent);
    letter-spacing: 0.15em;
    font-weight: 700;
}

.admin-topbar-actions {
    display: flex;
    align-items: center;
    gap: 16px;
}

.topbar-btn-shop {
    color: #E5C2C9;
    text-decoration: none;
    font-size: 12px;
    font-weight: 600;
    padding: 6px 14px;
    border: 1px solid #3D0B13;
    background: rgba(255,255,255,0.05);
    transition: all 0.2s ease;
}

.topbar-btn-shop:hover {
    background: var(--color-primary);
    color: #ffffff;
    border-color: var(--color-primary);
}

.admin-user-pill {
    font-size: 12px;
    font-weight: 600;
    color: #FAF7F2;
    background: rgba(255,255,255,0.08);
    padding: 6px 14px;
    display: flex;
    align-items: center;
    gap: 6px;
}

.topbar-btn-logout {
    color: #ffffff;
    background: var(--color-primary);
    text-decoration: none;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    padding: 6px 14px;
    letter-spacing: 0.05em;
    transition: background 0.2s ease;
}

.topbar-btn-logout:hover {
    background: #7a1525;
}

.admin-body-layout {
    display: flex;
    margin-top: 60px;
    min-height: calc(100vh - 60px);
}

.admin-sidebar {
    position: fixed;
    top: 60px;
    left: 0;
    bottom: 0;
    height: calc(100vh - 60px);
    /* dvh tracks the viewport as the browser's own bars slide in and out; vh is
       the tall one, measured as if they were never there. With vh alone the foot
       of this panel sits under the address bar whenever that bar is showing, so
       the last menu rows cannot be reached however far the list is dragged —
       which reads as a sidebar that has stopped scrolling. Declared second so a
       browser without dvh keeps the vh line above. */
    height: calc(100dvh - 60px);
    width: 250px;
    background: #2B080F;
    border-right: 1px solid #3D0B13;
    overflow-y: auto;
    /* Once the list reaches its end the drag was passing to the page behind it,
       which moves while the panel appears stuck. Keep the gesture in here. */
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    z-index: 400;
    box-sizing: border-box;
}

.admin-sidebar::-webkit-scrollbar {
    width: 5px;
}
.admin-sidebar::-webkit-scrollbar-thumb {
    background: rgba(255,255,255,0.15);
}

.admin-sidebar-nav {
    padding: 12px 10px 80px;
    display: flex;
    flex-direction: column;
    gap: 3px;
}

.sidebar-group-title {
    font-size: 9.5px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: #A37F87;
    padding: 16px 10px 4px;
}

.admin-sidebar-nav a {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 12px;
    color: #D6C2C7;
    text-decoration: none;
    font-size: 12.5px;
    font-weight: 500;
    transition: all 0.2s ease;
}

.admin-sidebar-nav a i {
    width: 16px;
    text-align: center;
    font-size: 13px;
    color: #8C6A72;
}

.admin-sidebar-nav a:hover {
    background: rgba(255, 255, 255, 0.08);
    color: #FFFFFF;
}

.admin-sidebar-nav a.active {
    background: #FAF7F2 !important;
    color: #3D0B13 !important;
    font-weight: 700 !important;
    box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}

.admin-sidebar-nav a.active i {
    color: var(--color-primary) !important;
}

.admin-main {
    margin-left: 250px;
    flex: 1;
    width: calc(100% - 250px);
    background: #FAF8F5;
    min-height: calc(100vh - 60px);
    box-sizing: border-box;
}

.admin-content {
    padding: 32px;
    max-width: 1300px;
}

.admin-page-header {
    margin-bottom: 28px;
}

.admin-page-title {
    font-family: var(--font-heading);
    font-size: 26px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #3D0B13;
    margin: 0;
}


/* Tables & Cards */
.admin-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
    text-align: left;
}

.admin-table th {
    background: #FAF7F2;
    padding: 14px 16px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #3D0B13;
    border-bottom: 1px solid #EFEBE4;
}

.admin-table td {
    padding: 16px;
    border-bottom: 1px solid #F5F0E8;
    color: #2D1A22;
    vertical-align: middle;
}





/* ═══════════════════════════════════════════════════════════
   NEW ARRIVALS CAROUSEL & PRODUCT CARDS
   ═══════════════════════════════════════════════════════════ */
.new-arrivals-section { background: var(--bg-surface); font-family: var(--font-body); position: relative; }
.home-section-header-btns { display: flex; gap: 10px; }
.btn-carousel-arrow { width: 44px; height: 44px; border: 1px solid var(--border-strong); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-primary); background: transparent; transition: all 0.2s; }
.btn-carousel-arrow:hover { background: var(--color-primary); color: #ffffff; }

/* overflow-y is set explicitly: with only overflow-x:auto the spec forces overflow-y to
   compute to `auto`, which makes this band a scroll container on BOTH axes and lets it
   swallow vertical page-scroll gestures. `proximity` (not `mandatory`) keeps the snap
   without the browser claiming every slightly-diagonal trackpad/touch swipe. Smooth
   scrolling is NOT set here on purpose — scrollCarousel() in pages/home.php passes
   behavior:'smooth' explicitly, so the arrows stay animated while user gestures do not.
   padding-top leaves room for the .product-card:hover lift (-6px) and its shadow. */
.new-arrivals-carousel { display: flex; gap: 24px; overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain; scroll-snap-type: x proximity; padding-top: 12px; padding-bottom: 0; }
.product-card-carousel { flex: 0 0 280px; width: 280px; scroll-snap-align: start; }

.hide-scrollbar::-webkit-scrollbar { display: none; }
.hide-scrollbar { -ms-overflow-style: none; scrollbar-width: none; }

/* ══ Member Auth Forms (560px clean card styling) ════════════════════ */
.auth-card-box.reveal-on-scroll {
    transform: none;
}
.auth-section {
    padding: 60px 0;
}
.auth-container {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 0 20px;
}
.auth-card-box {
    width: 100%;
    max-width: 560px;
    background: var(--bg-surface);
    border: 1px solid var(--border-strong);
    padding: 40px 38px;
    text-align: center;
    box-shadow: var(--shadow-sm);
    box-sizing: border-box;
}
.auth-title {
    font-family: var(--font-heading);
    font-size: 24px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    margin-bottom: 24px;
    margin-top: 4px;
    color: var(--text-primary);
}
.auth-form {
    text-align: left;
    margin-bottom: 24px;
}
.auth-form .form-luxury-group {
    margin-bottom: 16px;
}
.auth-form label {
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    margin-bottom: 6px;
    display: block;
    color: var(--text-primary);
}
.auth-form .form-luxury-input {
    width: 100%;
    padding: 12px 14px;
    font-size: 13.5px;
    box-sizing: border-box;
}
/* Was text-align:right with only the Forgot Password link in it. Now it also
   carries the "keep me signed in" tick, so the two sit at opposite ends and
   wrap onto separate lines on a narrow screen rather than colliding. */
.auth-forgot-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    text-align: right;
    margin-bottom: 8px;
}
.auth-remember {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
    font-size: 12px;
    color: var(--text-secondary);
    text-align: left;
    user-select: none;
}
.auth-remember input {
    width: 15px;
    height: 15px;
    accent-color: var(--color-primary);
    cursor: pointer;
    flex-shrink: 0;
}
.auth-remember-hint {
    font-size: 11px;
    color: var(--text-muted);
    margin-bottom: 22px;
}
.auth-forgot-row a {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
    font-weight: 600;
}
.auth-submit-btn {
    width: 100%;
    justify-content: center;
    height: 48px;
    font-size: 12px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    box-sizing: border-box;
}
.auth-divider {
    display: flex;
    align-items: center;
    margin-bottom: 22px;
}
.auth-divider hr {
    flex: 1;
    border: 0;
    border-top: 1px solid var(--border-light);
}
.auth-divider-text {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--text-muted);
    padding: 0 14px;
}
.auth-social-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 12px;
    margin-bottom: 28px;
}
.auth-social-btn {
    border: 1px solid var(--border-strong);
    padding: 12px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 16px;
    cursor: pointer;
    color: var(--text-primary);
    background: var(--bg-surface-soft);
    transition: var(--transition);
}
.auth-social-btn:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
}
.auth-footer-text {
    font-size: 13px;
    color: var(--text-secondary);
    margin: 0;
}
.auth-footer-text a {
    color: var(--color-primary);
    font-weight: 700;
    text-decoration: underline;
}
.auth-admin-link {
    margin-top: 25px;
    border-top: 1px solid var(--border-light);
    padding-top: 18px;
}
.auth-admin-link a {
    font-size: 11px;
    text-transform: uppercase;
    color: var(--text-muted);
    font-weight: 500;
    letter-spacing: 0.05em;
}

/* ═══════════════════════════════════════════════════════════
   MOBILE SHOP BOTTOM BAR & FILTER DRAWERS (REFINE & SORT BY)
   ═══════════════════════════════════════════════════════════ */
.mobile-shop-bottom-bar {
    display: none;
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    height: 64px;
    background: #ffffff;
    border-top: 1px solid #e2e8f0;
    z-index: 990;
    padding: 10px 16px;
    gap: 12px;
    align-items: center;
    justify-content: center;
    box-shadow: 0 -4px 20px rgba(0,0,0,0.08);
}

.mobile-filter-btn {
    flex: 1;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: 1px solid #e2e8f0;
    background: #FAF8F5;
    font-size: 14px;
    font-weight: 600;
    color: #2b080f;
    cursor: pointer;
    transition: all 0.2s ease;
    box-shadow: 0 2px 6px rgba(0,0,0,0.04);
}

.mobile-filter-btn:hover, .mobile-filter-btn:active {
    background: #2b080f;
    color: #ffffff;
    border-color: #2b080f;
}

/* Drawer Backdrop */
.mobile-drawer-backdrop {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(3px);
    z-index: 2000;
    display: none;
    opacity: 0;
    transition: opacity 0.3s ease;
}

.mobile-drawer-backdrop.active {
    display: block;
    opacity: 1;
}

/* Drawer Sheet */
.mobile-drawer-sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: -100%;
    max-height: 88vh;
    background: #ffffff;
    z-index: 2001;
    transition: bottom 0.35s cubic-bezier(0.16, 1, 0.3, 1);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    box-shadow: 0 -10px 40px rgba(0,0,0,0.25);
}

.mobile-drawer-sheet.open {
    bottom: 0;
}

.mobile-drawer-header {
    padding: 16px 20px;
    border-bottom: 1px solid #e2e8f0;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-weight: 700;
    font-size: 16px;
    color: #0f172a;
    background: #ffffff;
}

.mobile-drawer-close-btn {
    background: none;
    border: none;
    font-size: 20px;
    color: #64748b;
    cursor: pointer;
    padding: 4px;
}

/* Sort Drawer Items */
.sort-options-list {
    padding: 8px 0;
    max-height: 55vh;
    overflow-y: auto;
}

.sort-option-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 15px 24px;
    font-size: 14px;
    font-weight: 500;
    color: #334155;
    cursor: pointer;
    border-bottom: 1px solid #f1f5f9;
    transition: background 0.15s ease;
}

.sort-option-item:hover, .sort-option-item.selected {
    background: #fdf2f2;
    color: var(--color-primary);
    font-weight: 700;
}

/* Refine Drawer Layout */
.refine-drawer-layout {
    display: flex;
    flex: 1;
    overflow: hidden;
    min-height: 380px;
    max-height: 62vh;
}

.refine-tabs-col {
    width: 38%;
    background: #f8fafc;
    border-right: 1px solid #e2e8f0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
}

.refine-tab-btn {
    padding: 14px 16px;
    font-size: 13px;
    font-weight: 600;
    color: #475569;
    text-align: left;
    background: transparent;
    border: none;
    border-bottom: 1px solid #f1f5f9;
    cursor: pointer;
    font-family: var(--font-body);
    transition: all 0.2s ease;
}

.refine-tab-btn.active {
    background: #ffffff;
    color: #2b080f;
    font-weight: 700;
    border-left: 4px solid #2b080f;
}

.refine-content-col {
    width: 62%;
    padding: 16px;
    overflow-y: auto;
    background: #ffffff;
}

.refine-tab-pane {
    display: none;
}

.refine-tab-pane.active {
    display: block;
}

.refine-search-box {
    position: relative;
    margin-bottom: 14px;
}

.refine-search-box input {
    width: 100%;
    padding: 9px 34px 9px 12px;
    border: 1px solid #cbd5e1;
    font-size: 13px;
    outline: none;
}

.refine-search-box i {
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: #94a3b8;
    font-size: 13px;
}

.refine-checkbox-list {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.refine-checkbox-label {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 4px;
    font-size: 13px;
    color: #334155;
    cursor: pointer;
}

.refine-checkbox-label input[type="checkbox"] {
    width: 17px;
    height: 17px;
    accent-color: #2b080f;
    cursor: pointer;
}

.refine-bottom-actions {
    padding: 12px 20px;
    border-top: 1px solid #e2e8f0;
    display: flex;
    gap: 12px;
    background: #ffffff;
}

.btn-refine-close {
    flex: 1;
    height: 44px;
    border: 1px solid #cbd5e1;
    background: #ffffff;
    color: #334155;
    font-weight: 600;
    font-size: 14px;
    cursor: pointer;
}

.btn-refine-apply {
    flex: 1;
    height: 44px;
    border: none;
    background: #2b080f;
    color: #ffffff;
    font-weight: 700;
    font-size: 14px;
    cursor: pointer;
}

/* ══ Order Confirmation Page Styling ═════════════════════════════ */
.order-conf-hero {
    padding: 60px 0 40px;
    text-align: center;
}
.order-conf-icon-check {
    width: 70px;
    height: 70px;
    background: rgba(16, 185, 129, 0.1);
    border: 2px solid #10b981;
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 auto 20px;
    color: #10b981;
    font-size: 30px;
}
.order-conf-eyebrow {
    letter-spacing: 0.15em;
    color: var(--color-accent);
    font-weight: 600;
}
.order-conf-heading {
    font-family: var(--font-heading);
    font-size: 32px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase;
    margin: 10px 0;
}
.order-conf-desc {
    font-size: 14px;
    color: var(--text-muted);
    max-width: 600px;
    margin: 0 auto;
}
.order-conf-container {
    max-width: 850px;
    margin: 0 auto;
    padding: 0 20px;
}
.order-conf-banner {
    background: var(--bg-surface);
    border: 1px solid var(--border-light);
    padding: 25px 30px;
    margin-bottom: 30px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 15px;
    box-shadow: var(--shadow-sm);
}
.order-conf-label-sm {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text-muted);
    display: block;
    margin-bottom: 4px;
}
.order-conf-ref-code {
    font-family: var(--font-heading);
    font-size: 22px;
    font-weight: 700;
    color: var(--color-primary);
    letter-spacing: 0.05em;
}
.order-conf-status-badge {
    display: inline-block;
    padding: 6px 16px;
    background: rgba(212, 175, 55, 0.15);
    color: #b48c14;
    border: 1px solid rgba(212, 175, 55, 0.3);
    font-size: 12px;
    font-weight: 700;
    text-transform: uppercase;
}
.order-conf-date-val {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-primary);
}
.order-conf-collection-box {
    background: var(--bg-surface-soft);
    border: 1px solid var(--border-light);
    padding: 24px;
    margin-bottom: 30px;
    text-align: center;
}
.order-conf-panel {
    background: var(--bg-surface);
    border: 1px solid var(--border-light);
    padding: 30px;
    margin-bottom: 30px;
    box-shadow: var(--shadow-sm);
}
.order-conf-panel-title {
    font-family: var(--font-heading);
    font-size: 18px;
    font-weight: 500;
    text-transform: uppercase;
    margin: 0 0 20px;
    padding-bottom: 15px;
    border-bottom: 1px solid var(--border-light);
    color: var(--text-primary);
}
.order-conf-items-list {
    display: flex;
    flex-direction: column;
    gap: 15px;
    margin-bottom: 25px;
}
.order-conf-item-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 15px;
    padding-bottom: 15px;
    border-bottom: 1px dashed var(--border-light);
}
.order-conf-item-left {
    display: flex;
    align-items: center;
    gap: 12px;
}
.order-conf-item-emoji {
    font-size: 20px;
    min-width: 25px;
    text-align: center;
}
.order-conf-item-name {
    margin: 0;
    font-size: 14px;
    font-weight: 600;
    color: var(--text-primary);
}
.order-conf-item-sub {
    font-size: 12px;
    color: var(--text-muted);
    display: block;
    margin-top: 2px;
}
.order-conf-item-meta {
    font-size: 12px;
    color: var(--text-secondary);
    font-weight: 500;
}
.order-conf-item-price {
    font-size: 14px;
    font-weight: 700;
    color: var(--text-primary);
}
.order-conf-calc-wrap {
    display: flex;
    flex-direction: column;
    gap: 10px;
    font-size: 13px;
    color: var(--text-secondary);
    max-width: 320px;
    margin-left: auto;
}
.order-conf-calc-row {
    display: flex;
    justify-content: space-between;
}
.order-conf-calc-discount {
    display: flex;
    justify-content: space-between;
    color: #10b981;
}
.order-conf-calc-total {
    display: flex;
    justify-content: space-between;
    padding-top: 12px;
    border-top: 1px solid var(--border-strong);
    font-size: 16px;
    font-weight: 700;
    color: var(--text-primary);
}
.order-conf-total-val {
    color: var(--color-accent);
    font-family: var(--font-heading);
    font-size: 20px;
}
.order-conf-details-grid {
    background: var(--bg-surface-soft);
    border: 1px solid var(--border-light);
    padding: 25px 30px;
    margin-bottom: 35px;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 20px;
    font-size: 13px;
}
.order-conf-actions-row {
    display: flex;
    gap: 15px;
    justify-content: center;
    flex-wrap: wrap;
}

/* ═══════════════════════════════════════════════════════════
   LUXURY TWO-COLUMN PRODUCT GALLERY GRID
   ═══════════════════════════════════════════════════════════ */
.product-image-section {
    display: block;
    position: relative;
    width: 100%;
}

.sticky-sentinel {
    position: absolute;
    top: 0;
    left: 0;
    width: 1px;
    height: 1px;
    visibility: hidden;
    pointer-events: none;
}

@media (min-width: 992px) {
    .product-image-section {
        position: sticky;
        /* Sits a little higher than it did — the gap under the fixed header was
           16px, which left the gallery hanging low once pinned and wasted a
           strip of screen on a tall product photograph. */
        top: calc(var(--header-height, 90px) + 8px);
        align-self: flex-start;
        /* Sticky cannot animate its own pinning, so the softness comes from the
           lift below rather than from moving `top`. */
        transition: top 0.25s ease;
    }

    /* A soft "lift" that appears only once the gallery is truly pinned in
       place while scrolling — confirms the stuck state and makes it feel
       deliberate rather than just structurally positioned. */
    /* transform is written per scroll frame by the handler in pages/product.php,
       so it must NOT be transitioned here — a 0.35s ease on a value that changes
       every frame lags behind the scroll instead of following it. The fixed
       translateY(2px) that used to live here is gone for the same reason: an
       inline transform would have overridden it anyway. */
    .product-image-section .product-gallery-grid {
        /* opacity has to be repeated here. `transition` is a shorthand, so this
           more specific rule REPLACED the base one wholesale and silently
           removed the colour-swap fade on desktop — the exact screens where the
           gallery is sticky and the swatches are used. */
        transition: box-shadow 0.35s ease, opacity 0.22s ease;
        will-change: transform;
    }
}


.product-gallery-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
    width: 100%;
}

.gallery-grid-item {
    position: relative;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    background: #ffffff;
    border: 1px solid var(--border-light);
    cursor: zoom-in;
    transition: box-shadow 0.3s ease;
}


.gallery-grid-item picture { display: block; width: 100%; height: 100%; }
/* `cover` here is the DESKTOP framing and is deliberate — the two-column grid
   keeps the look it has always had. Phones letterbox instead; see the
   object-fit override in the max-width:768px block. */
.gallery-grid-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94), opacity 0.25s ease;
}

/* The transition belongs on the element, not on the swapping state.
   ────────────────────────────────────────────────────────────────────────
   It used to be declared inside .is-swapping — so it existed only while the
   class was applied. Fading OUT worked; the moment the class came off, the
   transition came off with it and opacity snapped straight back to 1. That
   is the blink: every colour change faded away and then jumped back.

   Whether it looked like a fade or a flash depended purely on how quickly
   the pictures arrived, which is why some colours seemed smooth and others
   did not. */
.product-gallery-grid { transition: opacity 0.22s ease; }
.product-gallery-grid.is-swapping { opacity: 0; }

.gallery-grid-item:hover .gallery-grid-img {
    transform: scale(1.03);
}

.gallery-grid-video, .gallery-grid-item iframe {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border: none;
    aspect-ratio: 3 / 4;
}

.product-gallery-grid > .gallery-grid-item:only-child {
    grid-column: span 2;
    max-width: 650px;
    margin: 0 auto;
}

.product-gallery-slide-counter {
    display: none;
    justify-content: center;
    gap: 6px;
    margin-top: 10px;
}

/* Hidden until the carousel claims it. The desktop gallery is a grid with
   every photograph already on screen, so there is nothing to count. */
.product-gallery-frame-count { display: none; }
.product-gallery-slide-counter .dot {
    width: 6px; height: 6px;
    background: var(--border-strong);
    transition: background 0.2s ease, transform 0.2s ease;
}
.product-gallery-slide-counter .dot.active {
    background: var(--color-primary);
    transform: scale(1.3);
}

.gallery-nav {
    display: none;
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    background: rgba(255, 255, 255, 0.85);
    border: 1px solid var(--border-light);
    color: var(--text-primary);
    font-size: 14px;
    width: 36px;
    height: 36px;
    cursor: pointer;
    align-items: center;
    justify-content: center;
    z-index: 5;
    box-shadow: 0 2px 8px rgba(0,0,0,0.12);
}
.gallery-nav-prev { left: 10px; }
.gallery-nav-next { right: 10px; }
.gallery-nav:active { background: #ffffff; transform: translateY(-50%) scale(0.95); }


@media (max-width: 768px) {
    .product-gallery-slider-wrap { position: relative; }
    .product-gallery-slider-wrap.has-multiple .gallery-nav {
        display: flex;
    }
    /* Swipeable one-at-a-time slider instead of every photo stacked full-width —
       stacking was pushing the buy box far down the page behind a wall of images. */
    .product-gallery-grid {
        display: flex;
        grid-template-columns: none;
        /* Paired with the slide width below: the gap is the breathing space
           between the current photograph and the one peeking in after it. */
        gap: 8px;
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
        -ms-overflow-style: none;
    }
    .product-gallery-grid::-webkit-scrollbar { display: none; }
    .gallery-grid-item {
        aspect-ratio: 3 / 4;
        /* A slide stops short of the full width so the NEXT photograph shows
           at the right edge. A slider that ends exactly at the screen edge
           looks like a single static picture — nothing tells the shopper there
           are four more. The peeking sliver is what says "swipe me", and it
           says it without an arrow or a caption.
           84px = 8px gap + 76px of the next photograph. This rule drives the
           fallback rail only. When Owl is running it sets each slide's width
           itself, from PEEK in initGalleryOwl() (pages/product.php) — Owl's
           autoWidth needs a pixel width before it measures, and by then it has
           reparented the slides out from under this rule. The two constants
           have to be changed together. */
        flex: 0 0 calc(100% - 84px);
        scroll-snap-align: start;
        border: none;
    }
    /* Show the whole garment, never a trimmed one.
       ────────────────────────────────────────────────────────────────
       The frame is a fixed 3/4, and `cover` fills it by cropping whatever
       does not fit — a square photograph lost a quarter of itself, and
       what it dropped was the top and bottom: the model's head and the
       hem. On a clothing shop those are the parts being sold.

       PHONES ONLY, by request. The desktop grid keeps `cover` and the
       white frame it has always had — see the base .gallery-grid-img
       rule. The two therefore frame an odd-shaped photograph
       differently, which is a deliberate choice, not an oversight.

       No-op for correctly sized photography: a 3/4 photograph in a 3/4
       frame renders identically under cover and contain.

       Video keeps `cover` (see .gallery-grid-video): clips are usually
       16/9, and contained in a 3/4 frame one would be mostly empty band.

       The soft surface tone rather than white, because on a phone the
       band is a large part of the screen and #ffffff read as a lit panel
       behind the garment instead of the page showing through. */
    .gallery-grid-item {
        background: var(--bg-surface-soft);
    }
    .gallery-grid-img {
        object-fit: cover;
    }

    .product-gallery-grid > .gallery-grid-item:only-child {
        grid-column: unset;
        max-width: none;
        margin: 0;
        /* Nothing to peek at when there is only one photograph, so take the
           full width back — otherwise the peek gap became 84px of dead
           background beside the only picture the product has. */
        flex: 0 0 100%;
    }
    .product-gallery-slide-counter {
        display: flex;
        position: absolute;
        left: 50%;
        bottom: 12px;
        transform: translateX(-50%);
        margin-top: 0;
        z-index: 5;
        padding: 5px 9px;
        background: rgba(0, 0, 0, 0.32);
    }
    .product-gallery-slide-counter .dot {
        background: rgba(255, 255, 255, 0.55);
    }
    .product-gallery-slide-counter .dot.active {
        background: #ffffff;
    }
}


/* ═══════════════════════════════════════════════════════════
   PRODUCT GALLERY — Owl Carousel on phones
   ───────────────────────────────────────────────────────────
   Owl is already vendored and loaded site-wide (includes/header.php),
   and every other rail in the shop rides it. The product gallery was
   the one hand-rolled slider left, so it behaved differently from the
   rest of the shop under the finger.

   Owl drives the gallery only below 768px. Above it the two-column grid
   is not a slider at all, so the carousel is destroyed rather than
   merely restyled — see initGalleryOwl() in pages/product.php.

   Everything here hangs off .is-owl, which that code adds only after
   Owl reports itself initialised. If jQuery or the plugin fails to
   load, the class never lands, the scroll-snap rail above stays in
   charge and the gallery still swipes. The enhancement is allowed to
   fail; the gallery is not.
   ═══════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

    /* Owl slides by transforming its own stage. The scroll-snap rail has
       to stand down while it does, or a single swipe moves the gallery
       twice — once by Owl's transform and once by the native scroll
       underneath it. */
    .product-gallery-slider-wrap.is-owl .product-gallery-grid {
        display: block;
        position: relative;
        overflow: visible;
        overflow-x: visible;
        gap: 0;
        scroll-snap-type: none;
    }
    .product-gallery-slider-wrap.is-owl .gallery-grid-item {
        flex: none;
        scroll-snap-align: none;
        /* Width is written inline per slide by initGalleryOwl(), measured
           from the container — Owl's autoWidth reads that measurement.
           A percentage cannot be used: inside .owl-stage it would resolve
           against the stage's total scroll width, not the screen. */
    }

    /* The hand-rolled arrows and dot strip step aside for Owl's own, but
       are left in the markup rather than removed: they are the fallback
       that runs when Owl never loads. */
    .product-gallery-slider-wrap.is-owl .gallery-nav,
    .product-gallery-slider-wrap.is-owl .product-gallery-slide-counter {
        display: none;
    }

    /* ── Owl's nav, wearing the arrows this shop already uses ─────────
       The !important is answering an !important, not decorating one. Every
       other slider on this site draws its own controls, so Owl's are killed
       site-wide further down this file ("Owl's own nav and dots, hidden").
       That rule is right and stays; the product gallery is the one place
       that genuinely uses Owl's pair, so it opts back in by name rather
       than by weakening the blanket rule for the hero slider, the occasion
       strip and the product rails as well. */
    .product-gallery-slider-wrap.is-owl .owl-nav {
        margin: 0;
        display: block !important;
    }
    /* Owl marks the whole control .disabled when every slide already fits,
       and the opt-in above would otherwise force it back into view. */
    .product-gallery-slider-wrap.is-owl .owl-nav.disabled,
    .product-gallery-slider-wrap.is-owl .owl-dots.disabled {
        display: none !important;
    }
    .product-gallery-slider-wrap.is-owl .owl-nav button.owl-prev,
    .product-gallery-slider-wrap.is-owl .owl-nav button.owl-next {
        position: absolute;
        /* Centred on the PHOTOGRAPH, not on the carousel — the dot strip
           sits below the stage and is part of the element's height, so a
           plain top:50% would hang the arrows low. */
        top: 0;
        bottom: 42px;
        height: 36px;
        margin: auto 0;
        display: flex;
        align-items: center;
        justify-content: center;
        width: 36px;
        padding: 0;
        border: 1px solid var(--border-light);
        /* Square, matching the .gallery-nav buttons these replaced — a
           browser's default button rounding would have crept in otherwise,
           and the shop's arrows are square everywhere else. */
        border-radius: 0;
        background: rgba(255, 255, 255, 0.85);
        color: var(--text-primary);
        font-size: 14px;
        z-index: 5;
        box-shadow: 0 2px 8px rgba(0,0,0,0.12);
        transition: background 0.2s ease, opacity 0.2s ease;
    }
    .product-gallery-slider-wrap.is-owl .owl-nav button.owl-prev { left: 10px; }
    /* Clear of the peeking photograph rather than on top of it: the sliver
       is the thing telling the shopper there is more, and an arrow parked
       over it hid the very cue it duplicates. */
    .product-gallery-slider-wrap.is-owl .owl-nav button.owl-next { right: 94px; }
    .product-gallery-slider-wrap.is-owl .owl-nav button:active {
        background: #ffffff;
        transform: scale(0.95);
    }
    /* Owl marks an end-stop button .disabled. Dimmed rather than hidden,
       so the control does not jump in and out of the layout mid-swipe. */
    .product-gallery-slider-wrap.is-owl .owl-nav button.disabled {
        opacity: 0.35;
        pointer-events: none;
    }

    /* ── "3 / 7" over the photograph ───────────────────────────────
       Bottom LEFT, not bottom right as in the reference: this gallery
       already carries the details (i) button in the right corner, and the
       reference had no such button to make room for. Two chips fighting
       for one corner is worse than a mirrored one.
       Inset past the peeking photograph so it reads as belonging to the
       picture it counts, rather than floating between two of them. */
    .product-gallery-slider-wrap.is-owl .product-gallery-frame-count {
        display: block;
        position: absolute;
        left: 12px;
        bottom: 54px;
        z-index: 4;
        padding: 5px 10px;
        font-size: 12px;
        letter-spacing: 0.06em;
        color: #ffffff;
        background: rgba(0, 0, 0, 0.42);
        /* Safari still needs the prefix, and this shop's traffic is largely
           iPhones — unprefixed, the chip lost its blur on exactly the
           browsers the gallery was rebuilt for. The tint alone keeps it
           legible where neither is supported. */
        -webkit-backdrop-filter: blur(4px);
        backdrop-filter: blur(4px);
        pointer-events: none;
    }
    /* The details button sits clear of the peeking photograph too — parked
       over the sliver it hid the very cue that says there is more. */
    .product-gallery-slider-wrap.is-owl .gallery-details-btn {
        right: 94px;
        bottom: 46px;
    }

    /* ── Owl's dots, below the photograph ──────────────────────────── */
    .product-gallery-slider-wrap.is-owl .owl-dots {
        display: flex !important;   /* see the note on .owl-nav above */
        justify-content: center;
        align-items: center;
        gap: 6px;
        padding: 14px 0 2px;
        line-height: 0;
    }
    .product-gallery-slider-wrap.is-owl .owl-dots button.owl-dot {
        width: 26px;
        height: 4px;
        padding: 0;
        border: none;
        border-radius: 2px;
        background: var(--border-strong);
        opacity: 0.45;
        transition: background 0.25s ease, opacity 0.25s ease;
    }
    .product-gallery-slider-wrap.is-owl .owl-dots button.owl-dot.active {
        background: var(--color-primary);
        opacity: 1;
    }
}


/* ═══════════════════════════════════════════════════════════
   SUPPORT TICKETS — photo attachment & advisor replies
   (classes rather than inline styles so the markup stays clean
   and these are themeable in one place)
   ═══════════════════════════════════════════════════════════ */
.ticket-list { margin-bottom: 25px; display: flex; flex-direction: column; gap: 10px; }
.ticket-item { border: 1px solid var(--border-light); padding: 14px; background: var(--bg-surface-soft); font-size: 13px; }
.ticket-item-head { display: flex; justify-content: space-between; gap: 10px; }
.ticket-item-status { font-weight: 700; color: var(--color-primary); white-space: nowrap; }
.ticket-item-msg { margin: 6px 0 0; color: var(--text-secondary); }
.ticket-item-photo { margin-top: 10px; max-width: 130px; border: 1px solid var(--border-light); }

.ticket-reply { margin-top: 12px; padding: 12px 14px; background: var(--bg-surface); border-left: 3px solid var(--color-primary); }
.ticket-reply-label { font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--color-primary); font-weight: 700; margin-bottom: 5px; }
.ticket-reply-body { color: var(--text-primary); }

.ticket-form-group { margin-bottom: 15px; }
.ticket-form-label { display: block; font-size: 11px; text-transform: uppercase; margin-bottom: 6px; }
.ticket-file-input { padding: 8px; }
.ticket-file-hint { display: block; color: var(--text-muted); font-size: 11px; margin-top: 5px; }
.ticket-photo-preview { display: none; margin-top: 10px; max-width: 160px; border: 1px solid var(--border-light); }
.ticket-photo-preview.is-visible { display: block; }
.ticket-submit-btn { padding: 10px 24px; font-size: 12px; }

/* ═══════════════════════════════════════════════════════════
   DIEVON DIALOG — branded replacement for window.alert/confirm
   Loaded on both the storefront and the admin panel so the whole
   product never shows the grey "localhost says…" browser dialog.
   ═══════════════════════════════════════════════════════════ */
.dv-dialog-overlay {
    position: fixed; inset: 0; z-index: 100000;
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
    background: rgba(28, 10, 16, 0.55);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    opacity: 0; visibility: hidden;
    transition: opacity 0.22s ease, visibility 0.22s ease;
}
.dv-dialog-overlay.is-open { opacity: 1; visibility: visible; }

.dv-dialog {
    width: 100%; max-width: 420px;
    background: var(--bg-surface, #fff);
    border: 1px solid var(--border-light, #eae4dc);
    box-shadow: 0 24px 60px rgba(40, 12, 24, 0.28);
    padding: 30px 28px 24px;
    text-align: center;
    transform: translateY(12px) scale(0.97);
    transition: transform 0.26s cubic-bezier(0.22, 1, 0.36, 1);
    max-height: 90vh; overflow-y: auto;
}
.dv-dialog-overlay.is-open .dv-dialog { transform: translateY(0) scale(1); }

.dv-dialog-icon {
    width: 54px; height: 54px; margin: 0 auto 16px;
    display: flex; align-items: center; justify-content: center;
    font-size: 24px; line-height: 1;
}
.dv-dialog--success .dv-dialog-icon { background: rgba(16,185,129,0.12); color: #10b981; }
.dv-dialog--error   .dv-dialog-icon { background: rgba(239,68,68,0.12);  color: #ef4444; }
.dv-dialog--warning .dv-dialog-icon { background: rgba(245,158,11,0.14); color: #f59e0b; }
.dv-dialog--confirm .dv-dialog-icon,
.dv-dialog--info    .dv-dialog-icon { background: rgba(81,17,38,0.10);   color: var(--color-primary, #511126); }

.dv-dialog-title {
    font-family: var(--font-heading, Georgia, serif);
    font-size: 20px; font-weight: 400; letter-spacing: 0.02em;
    color: var(--text-primary, #241017);
    margin: 0 0 8px;
}
.dv-dialog-message {
    font-size: 14px; line-height: 1.6;
    color: var(--text-secondary, #6b5b62);
    margin: 0 0 24px;
    white-space: pre-line;   /* keeps \n from the original alert() strings readable */
    word-break: break-word;
}
/* The text box in dievonPrompt(). Sits between the message and the buttons, so
   it carries the message's bottom margin and the message loses its own when the
   field is present — otherwise the two stack up and the dialog grows a gap. */
.dv-dialog-input {
    display: block; width: 100%; box-sizing: border-box;
    margin: 0 0 24px;
    padding: 11px 14px;
    font-family: var(--font-body, inherit);
    font-size: 15px;
    color: var(--text-primary, #3a0c1b);
    background: var(--bg-surface, #ffffff);
    border: 1px solid var(--border-strong, rgba(81,17,38,0.25));
    text-align: center;
}
.dv-dialog-input:focus {
    outline: 2px solid var(--color-accent, #D4AF37) !important;
    outline-offset: 1px !important;
    border-color: var(--color-primary, #511126);
}
.dv-dialog-message:has(+ .dv-dialog-input) { margin-bottom: 14px; }

.dv-dialog-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
.dv-dialog-btn {
    font-family: var(--font-body, inherit);
    font-size: 12px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.08em;
    padding: 12px 26px;
    cursor: pointer; border: 1px solid transparent;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
    min-width: 116px;
}
.dv-dialog-btn--primary { background: var(--color-primary, #511126); color: #fff; }
.dv-dialog-btn--primary:hover { background: var(--color-secondary, #6d1832); }
.dv-dialog-btn--ghost { background: transparent; color: var(--text-secondary, #6b5b62); border-color: var(--border-strong, #d8cec4); }
.dv-dialog-btn--ghost:hover { background: var(--bg-surface-soft, #faf8f5); color: var(--text-primary, #241017); }
.dv-dialog-btn--danger { background: #c0392b; color: #fff; }
.dv-dialog-btn--danger:hover { background: #a93226; }

body.dv-dialog-open { overflow: hidden; }

@media (max-width: 480px) {
    .dv-dialog { padding: 26px 20px 20px; }
    .dv-dialog-actions { flex-direction: column-reverse; }
    .dv-dialog-btn { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
    .dv-dialog-overlay, .dv-dialog { transition: none; }
}

/* Available offers — one quiet line under the price. Deliberately no border, no
   box and no chips: the codes should be findable without competing with the
   price or the Add to Bag button. The code itself is still click-to-copy. */
.product-offers-line {
    font-size: 12.5px;
    line-height: 1.7;
    color: var(--text-secondary);
    /* Its own line directly above .delivery-checker-box — no border or padding,
       so it reads as a standalone note rather than part of the box below it. */
    margin: 0 0 10px;
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px;
}
.product-offers-line > i { color: var(--color-accent); font-size: 11px; }
.offer-code {
    font-family: var(--font-body);
    font-size: 12.5px;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--color-primary);
    background: none;
    border: none;
    border-bottom: 1px dashed var(--color-primary);
    padding: 0;
    cursor: pointer;
}
.offer-code:hover { color: var(--color-secondary); border-bottom-style: solid; }
.offer-sep { color: var(--text-muted); opacity: 0.6; }

/* Site-wide "How to Measure" picture inside the size guide popup.
   Sits between the measurement table and the built-in SVG diagrams — additive,
   it does not replace them. Uploaded once in Admin → Size Guide. */
.dievon-sg-howto {
    margin: 24px 0 4px;
    padding-top: 22px;
    border-top: 1px solid var(--border-light);
    text-align: center;
}
.dievon-sg-howto-img {
    max-width: 100%;
    height: auto;
    display: block;
    margin: 0 auto;
}

/* ═══════════════════════════════════════════════════════════
   CHECKOUT — shipping zone detected from the postcode
   The customer no longer picks domestic vs international; the
   destination decides, and the other option is disabled so it
   cannot be selected by hand.
   ═══════════════════════════════════════════════════════════ */
.shipping-zone-note {
    font-size: 12.5px; line-height: 1.6; margin: 0 0 14px;
    padding: 10px 13px;
    background: var(--bg-surface-soft); border-left: 3px solid var(--border-strong);
    color: var(--text-secondary);
}
.shipping-zone-note--domestic { border-left-color: #10b981; background: rgba(16,185,129,0.07); }
.shipping-zone-note--intl     { border-left-color: #f59e0b; background: rgba(245,158,11,0.08); }
.shipping-option-disabled { opacity: 0.45; pointer-events: none; filter: grayscale(1); }

/* ═══════════════════════════════════════════════════════════
   QUICK VIEW — product preview without leaving the listing
   ═══════════════════════════════════════════════════════════ */
.qv-overlay { position: fixed; inset: 0; z-index: 99000; display: flex; align-items: center; justify-content: center;
    padding: 24px; background: rgba(28,10,16,.55); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    opacity: 0; visibility: hidden; transition: opacity .22s ease, visibility .22s ease; }
.qv-overlay.is-open { opacity: 1; visibility: visible; }

/* The modal itself must NOT scroll — .qv-body does. Otherwise the absolutely
   positioned close button scrolls away with the content and, on a phone where
   the panel is tall, there is no way left to dismiss the popup. */
.qv-modal { position: relative; width: 100%; max-width: 880px; display: flex; flex-direction: column;
    max-height: 88vh; max-height: 88dvh; overflow: hidden;
    background: var(--bg-surface); box-shadow: 0 24px 60px rgba(40,12,24,.3);
    transform: translateY(12px) scale(.97); transition: transform .26s cubic-bezier(.22,1,.36,1); }
.qv-overlay.is-open .qv-modal { transform: translateY(0) scale(1); }
.qv-close { position: absolute; top: 12px; right: 14px; z-index: 3; background: rgba(255,255,255,.92); border: none;
    font-size: 26px; line-height: 1; width: 34px; height: 34px; cursor: pointer; color: var(--text-muted);
    box-shadow: 0 2px 8px rgba(40,12,24,.18); }
.qv-close:hover { color: var(--color-primary); }
.qv-body { display: grid; grid-template-columns: 1fr 1fr; gap: 0; min-height: 340px;
    overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; }
.qv-loading { grid-column: 1 / -1; display: flex; align-items: center; justify-content: center;
    padding: 70px 20px; color: var(--text-muted); font-size: 14px; }

.qv-media { background: var(--bg-surface-soft); padding: 18px; display: flex; flex-direction: column;
    gap: 10px; align-items: center; justify-content: center; }
/* Catalogue images run from 0.75 (portrait) to 1.83 (ultra-wide). A fixed-height
   box with object-fit:cover cropped 55% off a portrait kurti — the model's head
   and the hem both disappeared.
   Intrinsic sizing (auto width AND auto height, bounded by max-*) is used rather
   than a fixed box with object-fit:contain: contain would fit the whole garment
   too, but it leaves letterbox bars whenever the cap overrides the ratio. Letting
   the element take the image's own shape means the frame always hugs the photo —
   nothing cropped and no bars, for every ratio in the catalogue. */
.qv-img { max-width: 100%; max-height: min(56vh, 520px); width: auto; height: auto;
    display: block; }
.qv-img-fallback { width: 100%; aspect-ratio: 4 / 5; max-height: min(56vh, 520px);
    display: flex; align-items: center; justify-content: center; font-size: 68px;
    background: var(--bg-surface); }
.qv-thumbs { display: flex; gap: 8px; flex-wrap: wrap; align-self: stretch; }
.qv-thumb { width: 54px; height: 54px; object-fit: cover; cursor: pointer;
    border: 2px solid transparent; opacity: .7; flex: 0 0 auto; }
.qv-thumb.is-active, .qv-thumb:hover { border-color: var(--color-primary); opacity: 1; }

.qv-info { padding: 30px 28px; display: flex; flex-direction: column; }
.qv-badge { align-self: flex-start; background: var(--color-accent); color: #fff; font-size: 10px; font-weight: 700;
    letter-spacing: .08em; text-transform: uppercase; padding: 3px 9px; margin-bottom: 9px; }
.qv-cat { font-size: 10.5px; letter-spacing: .14em; text-transform: uppercase; color: var(--text-muted); }
.qv-title { font-family: var(--font-heading); font-size: 23px; font-weight: var(--font-heading-weight); margin: 5px 0 12px; color: var(--text-primary); }
.qv-price-row { margin-bottom: 12px; }
.qv-price { font-size: 21px; font-weight: 700; color: var(--color-primary); }
.qv-mrp { text-decoration: line-through; color: var(--text-muted); font-size: 14px; margin-left: 7px; }
.qv-off { color: #10b981; font-size: 12px; font-weight: 700; margin-left: 5px; }
.qv-desc { font-size: 13px; line-height: 1.65; color: var(--text-secondary); margin: 0 0 16px; }

.qv-label { display: block; font-size: 10.5px; letter-spacing: .1em; text-transform: uppercase;
    color: var(--text-muted); margin-bottom: 7px; }
.qv-size-row { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 16px; }
.qv-size { min-width: 46px; padding: 8px 12px; font-size: 12px; font-weight: 600; cursor: pointer;
    background: transparent; border: 1px solid var(--border-strong); color: var(--text-primary); }
.qv-size:hover:not(.is-out) { border-color: var(--color-primary); }
.qv-size.is-selected { background: var(--color-primary); color: #fff; border-color: var(--color-primary); }
.qv-size.is-out { opacity: .35; cursor: not-allowed; text-decoration: line-through; }

.qv-attrs { display: grid; grid-template-columns: auto 1fr; gap: 4px 14px; font-size: 12.5px; margin: 0 0 18px; }
.qv-attrs dt { color: var(--text-muted); }
.qv-attrs dd { margin: 0; color: var(--text-primary); }

.qv-actions { display: flex; gap: 10px; margin-top: auto; flex-wrap: wrap; }
.qv-add { flex: 1; min-width: 150px; justify-content: center; }
.qv-full { display: flex; align-items: center; justify-content: center; padding: 0 18px; white-space: nowrap; }

body.qv-open { overflow: hidden; }

/* ── Quick view: responsive ladder ──────────────────────────
   Four steps rather than the single 760px break it had before, because the
   layout fails in three different ways: the two columns get too narrow first,
   then they have to stack, then the padding and type are too big for a phone.
   A short landscape phone needs the opposite treatment — columns back on. */

@media (max-width: 980px) {
    .qv-overlay { padding: 18px; }
    .qv-info    { padding: 26px 22px; }
    .qv-media   { padding: 14px; }
}

/* Stacked. The image now sits above the details, so it must not eat the whole
   screen before the shopper sees the price — hence the vh cap. */
@media (max-width: 760px) {
    .qv-body   { grid-template-columns: 1fr; min-height: 0; }
    .qv-img { max-height: 42vh; }
    .qv-img-fallback { max-height: 42vh; }
    .qv-info   { padding: 22px 20px 26px; }
    .qv-modal  { max-height: 92vh; max-height: 92dvh; }
    .qv-thumbs { flex-wrap: nowrap; overflow-x: auto; overscroll-behavior-x: contain;
                 -webkit-overflow-scrolling: touch; scrollbar-width: none; padding-bottom: 2px; }
    .qv-thumbs::-webkit-scrollbar { display: none; }
}

@media (max-width: 520px) {
    /* env() keeps the popup clear of a notch / home indicator. */
    .qv-overlay { padding: 10px calc(10px + env(safe-area-inset-right)) calc(10px + env(safe-area-inset-bottom)) calc(10px + env(safe-area-inset-left)); }
    .qv-modal   { max-height: 94vh; max-height: 94dvh; }
    .qv-media   { padding: 10px; }
    .qv-img, .qv-img-fallback { max-height: 38vh; }
    .qv-info    { padding: 18px 16px 22px; }
    .qv-title   { font-size: 20px; }
    .qv-price   { font-size: 19px; }
    .qv-desc    { font-size: 12.5px; }
    /* 44px is the minimum comfortable touch target. */
    .qv-close   { width: 40px; height: 40px; top: 8px; right: 8px; font-size: 28px; }
    .qv-size    { min-height: 40px; }
    .qv-actions { flex-direction: column; gap: 8px; }
    .qv-add, .qv-full { width: 100%; flex: 1 1 auto; min-width: 0; }
    .qv-full    { min-height: 44px; }
    .qv-attrs   { grid-template-columns: auto 1fr; font-size: 12px; }
}

/* Landscape phone: plenty of width, almost no height. Go back to two columns
   so the details are not pushed below a tall image on a 380px-high screen. */
@media (max-height: 560px) and (orientation: landscape) {
    .qv-overlay { padding: 8px; }
    .qv-modal   { max-height: 96vh; max-height: 96dvh; }
    .qv-body    { grid-template-columns: 1fr 1fr; min-height: 0; }
    .qv-img { max-height: 60vh; }
    .qv-img-fallback { aspect-ratio: auto; height: 100%; max-height: 60vh; }
    .qv-info    { padding: 16px 18px 20px; }
    .qv-title   { font-size: 18px; margin: 4px 0 8px; }
    .qv-desc    { margin-bottom: 10px; }
}

@media (prefers-reduced-motion: reduce) { .qv-overlay, .qv-modal { transition: none; } }

/* ═══════════════════════════════════════════════════════════
   COMPARE — pick up to 3 products, view side by side
   ═══════════════════════════════════════════════════════════ */
/* Sits BELOW the badge, not on top of it. Originally at top:12/left:12 with
   z-index 3, which put it under .badge-luxury (top:15/left:15, z-index 10) —
   a 93x24px overlap on a 96x27px control, so on any card carrying a badge the
   Compare pill was both invisible and unclickable. The badge is 25px tall from
   15px, so 48px clears it. Bottom-left is not free: .quick-view-btn is centred
   along the bottom and reveals on the same hover. */
.compare-toggle { position: absolute; top: 48px; left: 15px; z-index: 11; display: flex; align-items: center; gap: 5px;
    background: rgba(255,255,255,.94); border: 1px solid var(--border-light);
    padding: 4px 10px 4px 7px; font-size: 10.5px; font-weight: 600; letter-spacing: .04em;
    text-transform: uppercase; color: var(--text-secondary); cursor: pointer; opacity: 0; transition: opacity .2s ease; }
.product-card:hover .compare-toggle,
.compare-toggle:has(input:checked) { opacity: 1; }
/* Touch devices never fire :hover, so the reveal-on-hover rule above left the
   control permanently invisible on phones and tablets. Show it outright there. */
@media (hover: none), (max-width: 900px) {
    .compare-toggle { opacity: 1; }
}
/* The checkbox itself is never seen. product_card.php is the only place a
   .compare-toggle is emitted and it always sits inside an <article class="product-card">,
   where the rule further down takes the input out of flow at opacity 0 with
   pointer-events off — the arrow glyph beside it is the whole control. An
   accent-color and a cursor on a transparent, unclickable box styled nothing;
   they only read as if a bare checkbox were still on show somewhere. */
.compare-toggle:has(input:checked) { border-color: var(--color-primary); color: var(--color-primary); }

.cmp-bar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 9500; background: var(--bg-surface);
    border-top: 1px solid var(--border-strong); box-shadow: 0 -6px 24px rgba(40,12,24,.10);
    transform: translateY(100%); transition: transform .28s cubic-bezier(.22,1,.36,1); }
.cmp-bar.is-visible { transform: translateY(0); }
.cmp-bar-inner { max-width: 1280px; margin: 0 auto; padding: 12px 20px; display: flex; align-items: center;
    justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.cmp-bar-items { display: flex; gap: 8px; flex-wrap: wrap; flex: 1; min-width: 200px; }
.cmp-chip { display: inline-flex; align-items: center; gap: 7px; background: var(--bg-surface-soft);
    border: 1px solid var(--border-light); padding: 5px 8px 5px 13px; font-size: 12px; }
.cmp-chip button { background: none; border: none; font-size: 16px; line-height: 1; cursor: pointer;
    color: var(--text-muted); padding: 0 2px; }
.cmp-chip button:hover { color: #c0392b; }
.cmp-bar-actions { display: flex; align-items: center; gap: 12px; }
.cmp-count { font-size: 12px; color: var(--text-muted); white-space: nowrap; }
.cmp-open { padding: 10px 24px; font-size: 12px; }
.cmp-open:disabled { opacity: .45; cursor: not-allowed; }
.cmp-clear { background: none; border: none; font-size: 12px; color: var(--text-muted);
    text-decoration: underline; cursor: pointer; }

.cmp-overlay { position: fixed; inset: 0; z-index: 99500; display: flex; align-items: center; justify-content: center;
    padding: 24px; background: rgba(28,10,16,.55); opacity: 0; visibility: hidden;
    transition: opacity .22s ease, visibility .22s ease; }
.cmp-overlay.is-open { opacity: 1; visibility: visible; }
.cmp-modal { width: 100%; max-width: 960px; max-height: 88vh; display: flex; flex-direction: column;
    background: var(--bg-surface); box-shadow: 0 24px 60px rgba(40,12,24,.3); }
.cmp-head { display: flex; align-items: center; justify-content: space-between;
    padding: 18px 24px; border-bottom: 1px solid var(--border-light); }
.cmp-head h2 { font-family: var(--font-heading); font-size: 20px; font-weight: var(--font-heading-weight); margin: 0; }
.cmp-close { background: none; border: none; font-size: 28px; line-height: 1; cursor: pointer; color: var(--text-muted); }
.cmp-body { overflow: auto; padding: 0; }
.cmp-loading { padding: 60px 20px; text-align: center; color: var(--text-muted); font-size: 14px; }
.cmp-scroll { overflow-x: auto; }

.cmp-table { width: 100%; border-collapse: collapse; min-width: 520px; }
.cmp-table th, .cmp-table td { padding: 12px 14px; text-align: left; font-size: 13px;
    border-bottom: 1px solid var(--border-light); vertical-align: top; }
.cmp-table thead th { background: var(--bg-surface-soft); text-align: center; vertical-align: bottom; }
.cmp-rowlabel { width: 110px; font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase;
    color: var(--text-muted); font-weight: 700; background: var(--bg-surface-soft); }
.cmp-img { width: 100%; max-width: 130px; height: 150px; object-fit: cover; display: block; margin: 0 auto 8px; }
.cmp-img-fallback { height: 150px; display: flex; align-items: center; justify-content: center; font-size: 44px; }
.cmp-name { display: block; font-weight: 700; font-size: 13px; color: var(--text-primary); text-decoration: none; margin-bottom: 4px; }
.cmp-name:hover { color: var(--color-primary); }
.cmp-best { display: inline-block; background: rgba(16,185,129,.12); color: #0e8f68; font-size: 10px;
    font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: 2px 8px; margin-bottom: 6px; }
.cmp-remove { display: block; margin: 4px auto 0; background: none; border: none; font-size: 11px;
    color: var(--text-muted); text-decoration: underline; cursor: pointer; }
.cmp-remove:hover { color: #c0392b; }
.cmp-price { font-weight: 700; color: var(--color-primary); font-size: 15px; }
.cmp-mrp { color: var(--text-muted); font-size: 12px; }
.cmp-off { color: #10b981; font-size: 11px; font-style: normal; font-weight: 700; }

body.cmp-open { overflow: hidden; }

@media (max-width: 700px) {
    .cmp-bar-inner { flex-direction: column; align-items: stretch; }
    .cmp-bar-actions { justify-content: space-between; }
    .cmp-rowlabel { width: 84px; font-size: 9.5px; }
    .cmp-table th, .cmp-table td { padding: 9px 8px; font-size: 12px; }
}
@media (prefers-reduced-motion: reduce) { .cmp-bar, .cmp-overlay { transition: none; } }

/* ═══════════════════════════════════════════════════════════
   ABOUT PAGE
   Extracted from style="" attributes in pages/about.php.
   Values copied verbatim from the inline rules they replace.
   ═══════════════════════════════════════════════════════════ */
.about-narrative-grid.is-spaced { margin-bottom: 80px; }

.about-heading {
    font-family: var(--font-heading);
    font-size: 38px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase;
    margin-bottom: 25px;
    line-height: 1.3;
}
.about-copy {
    color: var(--text-secondary);
    font-size: 14px;
    line-height: 1.8;
    margin-bottom: 20px;
}
/* The closing paragraph of each column used a separate margin-bottom:0 inline
   rule; :last-child expresses the same thing without a second class. */
.about-copy:last-child { margin-bottom: 0; }

.about-figure-img {
    width: 100%;
    border: 1px solid var(--border-light);
    box-shadow: var(--shadow-lg);
}

.about-values-label   { margin-bottom: 20px; }
.about-values-heading {
    font-family: var(--font-heading);
    font-size: 36px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase;
    margin-bottom: 50px;
}
.about-value-icon {
    font-size: 32px;
    color: var(--color-accent);
    margin-bottom: 15px;
}
.about-value-title {
    font-family: var(--font-heading);
    font-size: 20px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase;
    margin-bottom: 10px;
}
.about-value-text {
    font-size: 13px;
    color: var(--text-secondary);
    line-height: 1.6;
}

/* ═══════════════════════════════════════════════════════════
   JOURNAL / BLOG LIST
   Extracted from style="" attributes in pages/blog.php.
   ═══════════════════════════════════════════════════════════ */
.blog-empty      { text-align: center; padding: 60px 20px; color: var(--text-muted); }
.blog-empty-icon { font-size: 48px; margin-bottom: 12px; opacity: 0.3; }
.blog-empty-text { font-size: 16px; }

.blog-card {
    background: var(--bg-surface);
    border: 1px solid var(--border-light);
    display: flex;
    flex-direction: column;
}
.blog-card-media { height: 100%; overflow: hidden; position: relative; }
.blog-card-img   { width: 100%; height: 100%; object-fit: cover; transition: var(--transition-slow); }
.blog-card-body  { padding: 30px; display: flex; flex-direction: column; flex: 1; }

.blog-card-meta {
    margin-bottom: 12px;
    font-size: 11px;
    text-transform: uppercase;
    font-weight: 600;
    letter-spacing: 0.05em;
}
.blog-card-category { color: var(--color-accent); }
.blog-card-date     { color: var(--text-muted); }

.blog-card-title {
    font-family: var(--font-heading);
    font-size: 24px;
    font-weight: var(--font-heading-weight);
    line-height: 1.3;
    margin-bottom: 15px;
    min-height: 62px;
}
.blog-card-title a { color: var(--text-primary); }

.blog-card-excerpt {
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1.7;
    margin-bottom: 25px;
    flex: 1;
}
.blog-card-link {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    font-weight: 700;
    color: var(--text-primary);
}
.blog-card-link i { margin-left: 5px; }

/* ═══════════════════════════════════════════════════════════
   CHECKOUT — collapsible order summary + minimal footer
   ═══════════════════════════════════════════════════════════ */

/* Desktop: the toggle is just a heading. No pointer, no chevron, panel open.
   Written open-by-default so that if the JS never runs, nothing is hidden. */
.summary-toggle {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    width: 100%; background: none; border: none; padding: 0; margin: 0;
    font: inherit; color: inherit; text-align: left; cursor: default;
}
.summary-toggle-right { display: flex; align-items: center; gap: 10px; }
.summary-peek { display: none; }
/* Scoped to the button rather than bare .summary-chevron: FontAwesome's
   all.min.css loads after this file and its `.fa-solid { display: inline-block }`
   has the same specificity, so a one-class rule here loses on source order. */
.summary-toggle .summary-chevron { display: none; }

/* The full marketing footer is suppressed on checkout; tighten what is left so
   the remaining bar does not keep the tall padding it needs under 4 columns. */
.footer-minimal .footer-bottom-bar { border-top: none; margin-top: 0; padding-top: 0; }
.footer-minimal .container { padding-top: 22px; padding-bottom: 22px; }

@media (max-width: 991px) {
    /* Phone / tablet: becomes a real control. Collapsed by default, total on show. */
    .summary-toggle  { cursor: pointer; }
    .summary-peek    { display: inline; font-size: 15px; font-weight: 700; color: var(--color-primary); }
    .summary-toggle .summary-chevron { display: inline-block; font-size: 12px;
                       color: var(--text-muted); transition: transform .22s ease; }
    .summary-collapsible.is-collapsed { display: none; }
    .summary-toggle[aria-expanded="true"] .summary-chevron { transform: rotate(180deg); }
    /* Collapsed, the card is a summary bar — the heading's bottom rule and the
       card's lower padding would otherwise leave a large empty gap. */
    .summary-collapsible.is-collapsed ~ * { display: none; }
}

/* ═══════════════════════════════════════════════════════════
   ORDER TRACKING LOOKUP
   Two fields now: the code alone used to be enough to print a
   customer's name, address and items to anyone holding it.
   ═══════════════════════════════════════════════════════════ */
.track-form {
    max-width: 420px; margin: 0 auto;
    display: flex; flex-direction: column; gap: 10px;
}
.track-input { text-align: center; font-size: 15px; }
.track-input-code { text-transform: uppercase; font-weight: 600; letter-spacing: 0.04em; }
.track-submit { padding: 12px 30px; font-size: 12px; justify-content: center; }
.track-hint {
    max-width: 420px; margin: 14px auto 0;
    font-size: 12px; line-height: 1.6; color: var(--text-muted); text-align: center;
}
.track-hint a { color: var(--color-accent); font-weight: 600; text-decoration: underline; }

/* ═══════════════════════════════════════════════════════════
   CHECKOUT SECTIONS
   Replaces the "1. / 2. / 3." step wizard. Everything is on the
   page at once, separated by rules rather than tabs, so a
   shopper can see and correct anything without clicking back
   and forth. Kept flat (no boxes) to avoid adding height —
   this page had a scroll problem before.
   ═══════════════════════════════════════════════════════════ */
.checkout-section + .checkout-section {
    margin-top: 26px;
    padding-top: 26px;
    border-top: 1px solid var(--border-light);
}
.checkout-section-title {
    font-family: var(--font-heading);
    font-size: 18px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--text-primary);
    margin: 0 0 16px;
}
/* The order button is the only action left on the page, so it gets the full
   width rather than sitting beside a "Back" that no longer exists. */
.checkout-section .btn-step-space-between {
    display: block;
    margin-top: 28px;
}
.checkout-section .btn-step-space-between .btn-luxury {
    width: 100%;
    justify-content: center;
}

@media (max-width: 640px) {
    .checkout-section + .checkout-section { margin-top: 20px; padding-top: 20px; }
    .checkout-section-title { font-size: 16px; margin-bottom: 13px; }
}

/* ═══════════════════════════════════════════════════════════
   CHECKOUT — one section open at a time
   Replaces both the 1/2/3 tab wizard and the dimmed-locked
   variant. Collapsing is done here rather than by removing the
   markup, so every field keeps its value and still submits.
   ═══════════════════════════════════════════════════════════ */

/* Hide everything in a collapsed section except its heading and summary line. */
.checkout-section.is-complete > *:not(.checkout-section-title):not(.section-summary),
.checkout-section.is-upcoming > *:not(.checkout-section-title):not(.section-summary) {
    display: none !important;
}

.checkout-section-title {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
}

/* A section not yet reached is a dead title — no Edit, nothing to act on. */
.checkout-section.is-upcoming .checkout-section-title { color: var(--text-muted); }
.checkout-section.is-upcoming .section-edit,
.checkout-section.is-open     .section-edit { display: none; }

/* Completed sections get a tick and a way back in. */
.checkout-section.is-complete .section-name::after {
    content: '\2713';
    margin-left: 8px;
    color: #1E6B37;
    font-size: 14px;
}
.section-edit {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-primary);
    text-decoration: underline;
    cursor: pointer;
    white-space: nowrap;
}
.section-edit:hover { color: var(--color-accent); }

.section-summary {
    font-size: 12.5px;
    line-height: 1.5;
    color: var(--text-secondary);
    margin: -6px 0 0;
}
.checkout-section.is-open .section-summary,
.checkout-section.is-upcoming .section-summary { display: none; }

.section-advance { margin-top: 22px; }
.section-advance-btn { width: 100%; justify-content: center; padding: 13px 28px; font-size: 12px; }

@media (max-width: 640px) {
    .section-advance { margin-top: 16px; }
    .section-advance-btn { padding: 12px 20px; }
    .section-summary { font-size: 12px; }
}

/* ═══════════════════════════════════════════════════════════
   CHECKOUT SUMMARY — the sidebar sticks, not the card
   ═══════════════════════════════════════════════════════════
   The sidebar is the grid item, so its containing block is
   .checkout-grid — which is as tall as the taller form column.
   That is the travel distance the sticky needs.

   align-self MUST stay at its natural height here (the grid
   already sets align-items:start). Stretching it would make it
   fill the whole row, leaving nowhere to travel, which is
   exactly why the previous attempt — sticky on the inner card
   inside a sidebar that hugged it — never moved at all.

   (html{overflow-x:clip} is deliberate: it replaced
   overflow-x:hidden, which had been silently disabling every
   position:sticky on the site. See the note near
   .product-breadcrumbs.) */
.checkout-summary-sidebar {
    position: -webkit-sticky;
    position: sticky;
    top: 130px;
    align-self: start;
}

.checkout-summary-card {
    background: var(--bg-surface);
    border: 1px solid var(--border-strong);
    padding: 25px;
    box-shadow: var(--shadow-sm);
}

@media (max-width: 991px) {
    /* Stacked below the form and collapsible on a phone — pinning it there
       would just eat screen while the shopper is typing. */
    .checkout-summary-sidebar { position: static; top: auto; align-self: auto; }
}

/* ═══════════════════════════════════════════════════════════
   HEADER LOGO LINK
   Caps how wide the logo may sit in the header. Note this
   targets the <a> — the <img> inside it is sized separately by
   `.logo img`, which is declared TWICE in this file (line ~125
   and line ~2535); the later one wins, so that is the one to
   edit if the logo height ever needs changing.
   ═══════════════════════════════════════════════════════════ */
a.logo {
    max-width: 200px;
    display: block;
}

/* ═══════════════════════════════════════════════════════════
   CART PAGE
   Extracted from 71 style="" attributes in pages/cart.php,
   including the ones inside the JavaScript item template.
   Values copied verbatim from the inline rules they replace.
   ═══════════════════════════════════════════════════════════ */

/* Continue Shopping now sits above the cart rather than below the
   last item — on a phone it was under the fold. */
.cart-top-actions { margin-bottom: 22px; }
.cart-continue-link { font-size: 12px; padding: 12px 24px; }

.cart-panel-header {
    display: flex; justify-content: space-between; align-items: center;
    border-bottom: 1px solid var(--border-strong);
    padding-bottom: 15px; margin-bottom: 25px; gap: 14px; flex-wrap: wrap;
}
.cart-panel-title {
    font-family: var(--font-heading); font-size: 24px; font-weight: var(--font-heading-weight);
    text-transform: uppercase; letter-spacing: 0.05em; margin: 0;
}
.cart-count-badge { font-size: 14px; color: var(--text-muted); font-family: var(--font-body); }
.cart-clear-btn {
    background: none; border: none; color: var(--color-danger);
    font-size: 12px; font-weight: 600; cursor: pointer;
    text-transform: uppercase; letter-spacing: 0.05em; white-space: nowrap;
}

/* ── one line item ── */
.cart-item-row {
    display: flex; gap: 20px; align-items: center; padding: 25px 0;
    border-bottom: 1px solid var(--border-light); position: relative; flex-wrap: wrap;
}
.cart-item-img {
    width: 90px; height: 110px; object-fit: cover;
    border: 1px solid var(--border-light); flex: 0 0 auto;
}
.cart-item-img-fallback {
    background: var(--bg-surface-soft);
    display: flex; align-items: center; justify-content: center; font-size: 30px;
}
.cart-item-main { flex: 1; min-width: 200px; }
.cart-item-name { font-family: var(--font-heading); font-size: 18px; font-weight: var(--font-heading-weight); margin-bottom: 6px; }
.cart-item-name a { color: var(--text-primary); text-decoration: none; }
.cart-item-options { margin-bottom: 8px; }
.cart-item-option { font-size: 12px; color: var(--text-muted); font-weight: 500; }
.cart-item-price { font-size: 14px; color: var(--color-primary); font-weight: 600; }
.cart-item-links { display: flex; gap: 15px; margin-top: 12px; font-size: 12px; align-items: center; }
.cart-item-link {
    background: none; border: none; color: var(--text-secondary); cursor: pointer;
    display: flex; align-items: center; gap: 5px; padding: 0; font-weight: 500; font-size: 12px;
}
.cart-item-link-danger { color: var(--color-danger); }
.cart-item-link-sep { color: var(--border-strong); }
.cart-item-subtotal {
    font-size: 16px; font-weight: 700; color: var(--text-primary);
    min-width: 80px; text-align: right;
}

/* .cart-qty / .cart-qty-btn / .cart-qty-value were the cart page's private copy
   of the stepper. The control has one look now and one class set — see the
   block at the end of this file. These selectors are kept, aliased to the same
   rules, purely so a browser still holding a cached cart page with the old
   markup renders a correct control instead of an unstyled one; nothing emits
   them any more and they can be deleted a release later. */



/* ── shipping estimate / gift card boxes ── */
.cart-tool-box { background: var(--bg-surface-soft); border: 1px solid var(--border-light); padding: 24px; }
.cart-tool-title {
    font-family: var(--font-heading); font-size: 16px; font-weight: var(--font-heading-weight);
    text-transform: uppercase; margin-bottom: 15px;
    display: flex; align-items: center; gap: 8px;
}
.cart-tool-title i { color: var(--color-primary); }
.cart-tool-fields { display: flex; flex-direction: column; gap: 12px; }
.cart-tool-row { display: flex; gap: 10px; }
.cart-tool-input { padding: 10px; font-size: 13px; }
.cart-tool-input-grow { flex: 1; min-width: 0; }
.cart-tool-input-pin { width: 90px; }
.cart-tool-btn { padding: 10px 16px; font-size: 11px; white-space: nowrap; }
.cart-tool-btn-grow { flex: 1; }
.cart-tool-msg { font-size: 12px; font-weight: 500; }
.cart-tool-msg.is-ok { color: var(--color-success); font-weight: 600; }

/* ── summary card ── */
.cart-summary-card {
    background: var(--bg-surface); border: 1px solid var(--border-strong);
    padding: 30px; box-shadow: var(--shadow-sm);
    position: -webkit-sticky; position: sticky; top: 96px;
}
.cart-summary-title {
    font-family: var(--font-heading); font-size: 22px; font-weight: var(--font-heading-weight);
    text-transform: uppercase; letter-spacing: 0.08em;
    margin-bottom: 25px; border-bottom: 1px solid var(--border-light); padding-bottom: 15px;
}
.cart-summary-row { display: flex; justify-content: space-between; margin-bottom: 15px; font-size: 14px; }
.cart-summary-row.is-hidden { display: none; }
.cart-summary-row-promo { color: var(--color-success); }
.cart-summary-row-divider { margin-bottom: 25px; border-bottom: 1px solid var(--border-light); padding-bottom: 15px; }
.cart-summary-label { color: var(--text-secondary); }
.cart-summary-value { font-weight: 600; color: var(--text-primary); }
.cart-summary-shipping { font-weight: 600; text-transform: uppercase; color: var(--color-accent); font-size: 12px; }
.cart-summary-total {
    margin-bottom: 30px; font-size: 18px; font-weight: 600;
    text-transform: uppercase; letter-spacing: 0.05em;
}
.cart-summary-total span:last-child { color: var(--color-primary); }

.cart-promo-box { margin-bottom: 30px; border-top: 1px solid var(--border-light); padding-top: 25px; }
.cart-promo-label {
    display: block; font-size: 11px; text-transform: uppercase; font-weight: 700;
    letter-spacing: 0.08em; color: var(--text-primary); margin-bottom: 10px;
}
.cart-promo-row { display: flex; gap: 8px; }
.cart-promo-input {
    flex: 1; min-width: 0; padding: 12px; border: 1px solid var(--border-strong);
    background: var(--bg-surface-soft); text-transform: uppercase; font-size: 13px; outline: none;
}
.cart-promo-btn { padding: 12px 20px; font-size: 11px; text-transform: uppercase; white-space: nowrap; }
.cart-promo-msg { font-size: 12px; margin-top: 8px; font-weight: 500; }
.cart-promo-remove { color: var(--text-muted); text-decoration: underline; }

.cart-checkout-btn {
    display: flex; justify-content: center; width: 100%; text-align: center;
    height: 52px; align-items: center; font-size: 13px; font-weight: 700;
    text-transform: uppercase; letter-spacing: 0.1em; gap: 10px;
}

/* ── empty state ── */
.cart-empty { text-align: center; padding: 100px 0; }
.cart-empty.is-hidden { display: none; }
.cart-empty-icon { font-size: 60px; margin-bottom: 20px; color: var(--color-primary); }
.cart-empty-title {
    font-family: var(--font-heading); font-size: 32px; font-weight: var(--font-heading-weight);
    text-transform: uppercase; margin-bottom: 15px;
}
.cart-empty-text {
    color: var(--text-secondary); font-size: 14px;
    max-width: 440px; margin: 0 auto 30px; line-height: 1.7;
}
.cart-empty-btn { padding: 16px 36px; font-size: 13px; }

@media (max-width: 640px) {
    .cart-item-row { gap: 14px; padding: 18px 0; }
    .cart-item-img { width: 72px; height: 88px; }
    .cart-item-name { font-size: 16px; }
    .cart-item-subtotal { min-width: 0; }
    .cart-summary-card { padding: 20px; position: static; }
    .cart-empty { padding: 60px 0; }
    .cart-empty-title { font-size: 24px; }
}



/* ═══════════════════════════════════════════════════════════
   WELCOME / JOIN POPUP
   ═══════════════════════════════════════════════════════════ */
.welcome-overlay {
    position: fixed; inset: 0; z-index: 99800;
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
    background: rgba(28, 10, 16, .55);
    -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
    opacity: 0; visibility: hidden;
    transition: opacity .25s ease, visibility .25s ease;
}
.welcome-overlay.is-open { opacity: 1; visibility: visible; }
body.welcome-open { overflow: hidden; }

.welcome-modal {
    position: relative;
    width: 100%; max-width: 440px;
    max-height: 92vh; max-height: 92dvh; overflow-y: auto;
    background: var(--bg-surface);
    border: 1px solid var(--border-strong);
    box-shadow: 0 24px 60px rgba(40, 12, 24, .3);
    padding: 40px 34px 28px;
    text-align: center;
    transform: translateY(14px) scale(.97);
    transition: transform .3s cubic-bezier(.22, 1, .36, 1);
}
.welcome-overlay.is-open .welcome-modal { transform: translateY(0) scale(1); }

.welcome-close {
    position: absolute; top: 10px; right: 12px;
    width: 36px; height: 36px;
    background: none; border: none; cursor: pointer;
    font-size: 26px; line-height: 1; color: var(--text-muted);
}
.welcome-close:hover { color: var(--color-primary); }

.welcome-eyebrow {
    display: block; font-size: 11px; letter-spacing: .18em;
    text-transform: uppercase; color: var(--color-accent);
    font-weight: 600; margin-bottom: 10px;
}
.welcome-title {
    font-family: var(--font-heading); font-size: 30px;
    font-weight: var(--font-heading-weight);
    text-transform: uppercase; letter-spacing: .04em;
    margin: 0 0 12px; color: var(--text-primary);
}
.welcome-text {
    font-size: 13.5px; line-height: 1.7; color: var(--text-secondary);
    margin: 0 auto 24px; max-width: 330px;
}

.welcome-actions { display: flex; gap: 10px; margin-bottom: 22px; }
.welcome-btn { flex: 1; justify-content: center; padding: 13px 10px; font-size: 12px; }

.welcome-divider {
    display: flex; align-items: center; gap: 12px;
    font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
    color: var(--text-muted); margin-bottom: 16px;
}
.welcome-divider::before, .welcome-divider::after {
    content: ''; flex: 1; height: 1px; background: var(--border-light);
}

.welcome-form { display: flex; gap: 8px; }
.welcome-input {
    flex: 1; min-width: 0; padding: 12px;
    border: 1px solid var(--border-strong);
    background: var(--bg-surface-soft);
    font-size: 13px; outline: none; font-family: var(--font-body);
}
.welcome-submit { padding: 12px 22px; font-size: 11px; white-space: nowrap; }

.welcome-msg { font-size: 12px; font-weight: 500; margin-top: 10px; min-height: 16px; }
.welcome-msg.is-ok    { color: var(--color-success); }
.welcome-msg.is-error { color: var(--color-danger); }

/* An explicit way out. A popup whose only exit is a small × reads as a trap. */
.welcome-skip {
    display: block; width: 100%; margin-top: 18px;
    background: none; border: none; cursor: pointer;
    font-size: 12px; color: var(--text-muted); text-decoration: underline;
    font-family: var(--font-body);
}
.welcome-skip:hover { color: var(--text-primary); }

@media (max-width: 520px) {
    .welcome-modal { padding: 34px 20px 22px; }
    .welcome-title { font-size: 24px; }
    .welcome-actions { flex-direction: column; }
    .welcome-form { flex-direction: column; }
    .welcome-submit { width: 100%; }
}

/* Someone who has asked their system not to animate should not get a
   modal that slides in either. */
@media (prefers-reduced-motion: reduce) {
    .welcome-overlay, .welcome-modal { transition: none; }
}

/* ============================================================
   Legal pages (privacy / terms / returns / shipping)
   Replaces the repeated inline styles those pages carried. The
   same classes suit terms.php, returns.php and shipping.php,
   which still use the inline version.
   ============================================================ */
.legal-heading {
    color: var(--text-primary);
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    margin-bottom: 16px;
    font-size: 24px;
}

.legal-text { margin-bottom: 24px; }
.legal-text-last { margin-bottom: 0; }

.legal-link {
    color: var(--color-primary);
    font-weight: 600;
}

/* The cookie table is the one wide element on these pages. It scrolls
   inside its own wrapper so the page body never scrolls sideways. */
.legal-table-wrap {
    overflow-x: auto;
    margin-bottom: 24px;
    border: 1px solid var(--border-light);
}

.legal-table {
    width: 100%;
    border-collapse: collapse;
    font-size: 14px;
    min-width: 520px;
}

.legal-table th,
.legal-table td {
    text-align: left;
    padding: 12px 16px;
    border-bottom: 1px solid var(--border-light);
    vertical-align: top;
}

.legal-table th {
    color: var(--text-primary);
    font-weight: 600;
    background: var(--bg-body);
    white-space: nowrap;
}

.legal-table tr:last-child td { border-bottom: none; }

.legal-inline-btn {
    display: inline-block;
    width: auto;
    padding: 10px 22px;
    font-size: 13px;
}


/* Measurement tolerance line under the garment table in the size guide.
   Deliberately quiet — it is a caveat, not a headline — but always present
   when a chart declares one, because an unstated tolerance becomes a return. */
.dievon-sg-tolerance {
    /* Was margin:14px 0 0 with no padding, so the sentence ran flush into the
       left and right edges of the popup and sat hard against its bottom — the
       one block in the size guide with nothing around it. Padding rather than
       margin, so the inset holds even when the parent is edge-to-edge. */
    margin: 16px 0 4px;
    padding: 12px 16px;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-muted);
    background: var(--bg-surface-soft);
}
.dievon-sg-tolerance strong { color: var(--text-secondary); }

/* ── Review integrity UI ─────────────────────────────────────
   The sign-in gate, the optional-title hint, and the report link.
   The report button is deliberately quiet: it must be findable when
   something is wrong, without inviting idle clicks. */
.review-gate-text {
    font-size: 13px;
    line-height: 1.7;
    color: var(--text-secondary);
    margin: 0 0 18px;
}
.review-gate-actions { display: flex; gap: 12px; flex-wrap: wrap; }
.review-gate-btn { width: auto; padding: 12px 26px; font-size: 12px; }
.review-optional { color: var(--text-muted); font-weight: 400; font-size: 11px; }

.review-report-btn {
    margin-top: 12px;
    background: none;
    border: none;
    padding: 0;
    font-size: 11px;
    color: var(--text-muted);
    cursor: pointer;
    letter-spacing: 0.02em;
}
.review-report-btn:hover { color: var(--color-primary); text-decoration: underline; }

/* ── Show/hide password ──────────────────────────────────────
   The wrapper is created by dievon-password.js, so these rules only
   ever apply to fields that opted in. */
.pw-field-wrap { position: relative; display: block; }
.pw-field-wrap .form-luxury-input { padding-right: 46px; width: 100%; }
.pw-toggle-btn {
    position: absolute;
    top: 50%;
    right: 10px;
    transform: translateY(-50%);
    background: none;
    border: none;
    padding: 6px;
    cursor: pointer;
    color: var(--text-muted);
    font-size: 14px;
    line-height: 1;
}
.pw-toggle-btn:hover { color: var(--color-primary); }
.pw-hint { font-size: 11px; color: var(--text-muted); margin-top: 6px; line-height: 1.5; }

/* ── Admin: staff accounts ───────────────────────────────── */
.au-msg { padding: 12px 16px; font-size: 13px; margin-bottom: 16px; }
.au-msg.is-ok  { background: #ecfdf5; border: 1px solid #10b981; color: #065f46; }
.au-msg.is-err { background: #fef2f2; border: 1px solid #f87171; color: #991b1b; }
.au-warn {
    padding: 14px 16px; font-size: 13px; line-height: 1.6;
    background: #fffbeb; border: 1px solid #f59e0b; color: #92400e; margin-bottom: 16px;
}
.au-warn code { background: rgba(0,0,0,0.06); padding: 1px 5px; }
.au-actions { margin: 18px 0 8px; }
.au-subhead { font-size: 15px; margin: 28px 0 12px; color: var(--text-primary); }
.au-newgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; align-items: center; }
.au-muted { color: var(--text-muted); }
.au-ok   { color: #10b981; font-weight: 600; }
.au-fail { color: #ef4444; font-weight: 600; }
.au-pill {
    display: inline-block; margin-left: 6px; padding: 1px 7px;
    font-size: 10px; font-weight: 700; background: rgba(81,17,38,0.1); color: var(--color-primary);
}
.au-pw-btn { padding: 6px 12px; font-size: 12px; }

/* ── Admin: countries we sell to ─────────────────────────── */
.cn-msg { padding: 12px 16px; font-size: 13px; margin-bottom: 16px; }
.cn-msg.is-ok  { background: #ecfdf5; border: 1px solid #10b981; color: #065f46; }
.cn-msg.is-err { background: #fffbeb; border: 1px solid #f59e0b; color: #92400e; }
.cn-note {
    padding: 14px 16px; font-size: 13px; line-height: 1.7;
    background: var(--bg-surface-soft); border: 1px solid var(--border-light);
    color: var(--text-secondary); margin-bottom: 18px;
}
/* A note that is reporting a fault, not explaining a rule. Amber ground and
   border like .cn-msg.is-err rather than the .cn-warn text colour, which is a
   utility for a word or two and turns a whole paragraph bold amber. */
.cn-note--warn {
    background: #fffbeb;
    border-color: #f59e0b;
    color: #92400e;
}
.cn-note--warn code {
    background: rgba(146, 64, 14, 0.10);
    padding: 1px 5px;
    font-size: 12px;
}
.cn-in-xs { width: 70px; }
.cn-in-sm { width: 80px; }
.cn-in-md { width: 110px; }
.cn-muted { color: var(--text-muted); font-size: 12px; }
/* When a rate was last fetched, under the rate itself. Small and grey unless it
   is stale, in which case .cn-warn (already defined for the unpriced-country
   count) makes it amber — the same colour the product form uses for the same
   condition, so the two screens agree. */
.cn-fx-when { font-size: 10.5px; line-height: 1.4; color: var(--text-muted); margin-top: 3px; }
.cn-warn  { color: #b45309; font-weight: 600; }
.cn-actions { margin-top: 18px; display: flex; align-items: center; gap: 14px; }
.cn-check { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; cursor: pointer; }
.cn-pill { display: inline-block; margin-left: 6px; padding: 1px 7px; font-size: 10px; font-weight: 700; }
.cn-pill-home { background: rgba(81,17,38,0.1); color: var(--color-primary); }
.cn-pill-on   { background: #ecfdf5; color: #065f46; }
.cn-switch { position: relative; display: inline-block; width: 42px; height: 22px; }
.cn-switch input { opacity: 0; width: 0; height: 0; }
.cn-slider {
    position: absolute; inset: 0; cursor: pointer; background: #cbd5e1;
    transition: 0.2s;
}
.cn-slider::before {
    content: ""; position: absolute; height: 16px; width: 16px; left: 3px; bottom: 3px;
    background: #fff; transition: 0.2s;
}
.cn-switch input:checked + .cn-slider { background: var(--color-primary); }
.cn-switch input:checked + .cn-slider::before { transform: translateX(20px); }

/* ── Admin: two-step sign-in ─────────────────────────────── */
.au-2fa { padding: 18px; border: 1px solid var(--border-light); background: var(--bg-surface-soft); }
.au-2fa-on  { font-size: 13px; color: #065f46; margin: 0 0 12px; }
.au-2fa-off { font-size: 13px; line-height: 1.7; color: var(--text-secondary); margin: 0 0 14px; }
.au-2fa-emailrow { padding: 12px 14px; margin-bottom: 16px; border: 1px solid var(--border-light); background: #ffffff; }
.au-2fa-emailrow label { display: block; font-size: 12.5px; font-weight: 700; color: var(--text-secondary); margin: 0 0 8px; text-transform: uppercase; letter-spacing: 0.05em; }
.au-2fa-emailinput { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.au-2fa-emailinput .form-control { flex: 1; min-width: 220px; }
.au-2fa-note { font-size: 12.5px; color: var(--text-muted); line-height: 1.6; margin: 8px 0 0; }
.au-2fa-setup { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--border-light); }
.au-2fa-step { font-size: 13px; line-height: 1.8; margin: 0 0 8px; color: var(--text-secondary); }
.au-secret {
    display: inline-block; font-size: 14px; letter-spacing: 2px; padding: 6px 10px;
    background: #fff; border: 1px solid var(--border-strong); user-select: all;
}
.au-2fa-confirm { display: flex; gap: 10px; align-items: center; margin-top: 10px; max-width: 320px; }
.au-codes {
    background: #fff; border: 1px solid var(--border-strong);
    padding: 14px 16px; font-size: 14px; letter-spacing: 1px; line-height: 2; user-select: all; margin: 10px 0 0;
}
.admin-2fa-hint { font-size: 13px; color: var(--text-secondary); line-height: 1.7; margin: 0 0 16px; }
/* Resend, for the emailed-code method. A quiet link-style button on purpose —
   it must be findable when a code has not arrived, without competing with the
   Verify button for the person whose code did. */
.admin-2fa-resend {
    display: inline-flex; align-items: center; gap: 7px;
    background: none; border: none; padding: 4px 0; margin: -4px 0 14px;
    font-size: 12.5px; font-weight: 600; color: var(--color-primary);
    text-decoration: underline; cursor: pointer;
}
.admin-2fa-resend:hover { opacity: .8; }
.admin-2fa-resend:focus-visible { outline: 2px solid var(--color-secondary) !important; outline-offset: 2px; }

/* Draft / published hint under the publish switch in the product form. */
.admin-publish-hint { font-size: 11px; line-height: 1.6; color: var(--text-muted); margin: 6px 0 0; max-width: 380px; }

/* GST / invoice identity notice in Store Settings. */
.gst-note {
    padding: 12px 14px; font-size: 12px; line-height: 1.7;
    background: var(--bg-surface-soft); border: 1px solid var(--border-light); color: var(--text-secondary);
}
.gst-note-warn { color: #b45309; font-weight: 600; }
.form-hint { color: var(--text-muted); font-weight: 400; font-size: 11px; }

/* ── Collection page: breadcrumb + intro copy ─────────────────────────────────
   Appended at the end of the file on purpose. style.css declares a number of
   selectors twice and the later rule wins, so a block added mid-file can be
   silently overridden by an older duplicate further down. */
.shop-breadcrumb { margin-bottom: 14px; }
.shop-breadcrumb-list {
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
    font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase;
    /* The hero is text-align:center, but this is a flex row and flex ignores
       text-align, so the trail sat hard against the left edge while the eyebrow,
       H1 and intro above and below it were all centred. */
    justify-content: center;
}
/* Light text over the photo hero.
   .luxury-hero.has-bg-image lays a dark overlay over the image and re-colours
   its H1 and P to white for it — the breadcrumb was missed, so it kept the
   page's default near-black maroon (#3A0C1B) and was effectively invisible
   against the photograph. Contrast here is legibility, not decoration. */
.luxury-hero.has-bg-image .shop-breadcrumb-list { color: rgba(255, 255, 255, 0.82); }
.luxury-hero.has-bg-image .shop-breadcrumb-list a { color: #ffffff; opacity: 0.8; }
.luxury-hero.has-bg-image .shop-breadcrumb-list a:hover { opacity: 1; }
.luxury-hero.has-bg-image .shop-breadcrumb-list li[aria-current="page"] span { color: #ffffff; }
.shop-breadcrumb-list li { display: flex; align-items: center; gap: 8px; }
.shop-breadcrumb-list li + li::before {
    content: "/"; opacity: 0.5; font-size: 11px;
}
.shop-breadcrumb-list a { color: inherit; opacity: 0.75; text-decoration: none; }
.shop-breadcrumb-list a:hover { opacity: 1; text-decoration: underline; }
.shop-breadcrumb-list li[aria-current="page"] span { font-weight: 600; }

.shop-hero-intro { max-width: 62ch; }

/* Mobile drawer: heading that separates a category's fabric/badge edits from
   its subcategories. Was an inline style block on the <li>. */
.mobile-subnav-divider {
    margin-top: 6px; padding-top: 6px;
    border-top: 1px dashed var(--border-light);
    font-size: 11px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.08em; color: var(--text-muted);
}

/* Product page: the returns sentence under the shipping paragraph. */
.product-returns-line { margin-top: 10px; }

/* ── Journal article body ─────────────────────────────────────────────────── */
.article-para    { margin-bottom: 25px; }
.article-heading {
    font-family: var(--font-heading);
    font-size: 24px; line-height: 1.35; font-weight: 600;
    color: var(--text-primary);
    margin: 40px 0 16px;
}
.article-heading:first-child { margin-top: 0; }

/* Journal article hero image (was an inline style block). */
.article-hero-img { width: 100%; height: auto; max-height: 100%; object-fit: cover; }

/* ── Policy pages ─────────────────────────────────────────────────────────── */
.legal-value   { font-weight: 600; color: var(--text-primary); }
.legal-missing {
    display: inline-block; padding: 1px 7px;
    background: rgba(217, 119, 6, 0.12); color: #b45309;
    font-weight: 600; font-size: 0.95em;
}
.legal-page          { max-width: 860px; margin: 0 auto; }
.legal-page h2       { font-family: var(--font-heading); font-size: 22px; line-height: 1.35;
                       color: var(--text-primary); margin: 44px 0 14px; }
.legal-page h2:first-child { margin-top: 0; }
.legal-page h3       { font-size: 16px; font-weight: 700; color: var(--text-primary); margin: 26px 0 10px; }
.legal-page p        { margin-bottom: 18px; line-height: 1.85; }
.legal-page ul       { margin: 0 0 18px 20px; padding: 0; }
.legal-page li       { margin-bottom: 9px; line-height: 1.8; }
.legal-page table    { width: 100%; border-collapse: collapse; margin-bottom: 22px; font-size: 14px; }
.legal-page th,
.legal-page td       { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border-light); vertical-align: top; }
.legal-page th       { font-weight: 700; color: var(--text-primary); background: var(--bg-surface-soft); }
.legal-updated       { font-size: 13px; color: var(--text-muted); margin-bottom: 32px; }
.legal-toc           { background: var(--bg-surface-soft); border: 1px solid var(--border-light);
    padding: 20px 24px; margin-bottom: 40px; }
.legal-toc ol        { margin: 0 0 0 18px; padding: 0; columns: 2; column-gap: 32px; }
.legal-toc li        { margin-bottom: 6px; break-inside: avoid; }
.legal-toc a         { color: inherit; text-decoration: none; }
.legal-toc a:hover   { text-decoration: underline; }
@media (max-width: 640px) { .legal-toc ol { columns: 1; } }

/* ── Cookie consent notice ────────────────────────────────────────────────────
   Hidden entirely while no tracker is configured — see includes/consent_notice.php. */
.consent-notice {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 9000;
    background: var(--bg-surface); border-top: 1px solid var(--border-strong);
    box-shadow: 0 -8px 32px rgba(0, 0, 0, 0.12);
    padding: 20px 24px;
}
.consent-notice-inner {
    max-width: 1180px; margin: 0 auto;
    display: flex; align-items: center; justify-content: space-between;
    gap: 24px; flex-wrap: wrap;
}
.consent-notice-title { font-size: 15px; font-weight: 700; color: var(--text-primary); margin: 0 0 6px; }
.consent-notice-body  { font-size: 13px; line-height: 1.7; color: var(--text-secondary); margin: 0; max-width: 70ch; }
.consent-notice-body a { color: var(--color-accent); }
.consent-notice-actions { display: flex; gap: 12px; flex-wrap: wrap; flex-shrink: 0; }
@media (max-width: 640px) {
    .consent-notice-inner   { flex-direction: column; align-items: stretch; }
    .consent-notice-actions { justify-content: stretch; }
    .consent-notice-actions > * { flex: 1; }
}

/* Policy pages: the notice shown when the business identity is not filled in. */
.legal-gap-notice {
    background: rgba(217, 119, 6, 0.10); border: 1px solid rgba(217, 119, 6, 0.35);
    padding: 16px 20px; margin-bottom: 28px;
    font-size: 14px; line-height: 1.75; color: #92400e;
}
.legal-gap-notice strong { display: block; margin-bottom: 4px; color: #7c2d12; }
.legal-gap-notice a      { color: inherit; text-decoration: underline; }

/* Account: the note shown when no order is inside the return window. */
.acc-return-empty { margin-top: 6px; }

/* ── Policy pages: the short summary box ─────────────────────────────────────
   The sections themselves print plainly. They were collapsible while the privacy
   notice ran to 5,800 words; cut to about 1,200 there is nothing worth hiding,
   and a policy behind ten clicks is a policy nobody reads. */
.legal-summary {
    background: var(--bg-surface-soft); border: 1px solid var(--border-light);
    border-left: 3px solid var(--color-accent);
    padding: 22px 26px; margin-bottom: 32px;
}
.legal-summary h2 { margin: 0 0 12px; font-size: 17px; }
.legal-summary ul { margin: 0 0 12px 18px; }
.legal-summary li { margin-bottom: 8px; line-height: 1.75; }
.legal-summary-note { margin: 0; font-size: 12.5px; color: var(--text-muted); }

/* Colour swatch beside a name in the shop filter. Shown only when a real hex is
   recorded in Colours & Sizes — see the note in pages/shop.php. Decorative, so
   it carries aria-hidden and the colour name is always present as text. */
.filter-color-chip {
    display: inline-block;
    width: 13px; height: 13px;
    border: 1px solid var(--border-strong);
    margin-right: 7px;
    vertical-align: -1px;
    flex-shrink: 0;
}


/* ── SALE stands out in the top navigation ────────────────────────────────
   It sat in the same grey as Shop and Journal, so the one link meant to pull
   the eye read as ordinary navigation. A tinted pill rather than just a colour
   change: at 12px, uppercase and letter-spaced, recolouring the text alone
   barely registers against the neighbouring links.

   var(--color-danger) rather than a new red — .badge-luxury.badge-sale already
   uses it for every SALE tag on the product cards, and the header should agree
   with the badges it is pointing at.

   !important on the colour because .nav-link-item.active carries it too. Without
   it this rule loses the moment SALE is the current page, which is exactly when
   it should look strongest. */
.nav-link-item.nav-link-sale {
    color: var(--color-danger) !important;
    background: rgba(169, 68, 66, 0.10);
    padding: 6px 13px;
    font-weight: 700 !important;
    transition: background 0.2s ease, color 0.2s ease;
}
.nav-link-item.nav-link-sale:hover,
.nav-link-item.nav-link-sale.active {
    color: #fff !important;
    background: var(--color-danger);
}
/* The sliding underline the other links grow would sit under the pill and read
   as a stray line, so it is suppressed for this one. */
.nav-link-item.nav-link-sale::after { display: none !important; }

/* ── Breathing room between the header icons ──────────────────────────────
   8px put the account, wishlist and bag icons close enough that the wishlist
   heart read as part of the profile control, and on a phone the tap targets
   nearly touched. 14px separates them without pushing the bag off a narrow
   header. */
.nav-actions { gap: 14px; }

/* ═══════════════════════════════════════════════════════════
   SOLD-OUT PRODUCT CARDS  (see includes/product_card.php)
   ═══════════════════════════════════════════════════════════ */
.badge-luxury.badge-sold-out { background: #1f2937; color: #ffffff; }
.product-card.sold-out .card-img,
.product-card.sold-out .card-img-fallback { filter: grayscale(0.85); opacity: 0.6; }
.product-card.sold-out .product-card-price { opacity: 0.6; }
/* Muted by COLOUR, not by group opacity. `opacity: 0.45` fades the chip and
   its white label together, so the label never gains on its own ground: measured
   from rendered pixels it came out 2.48:1 on the shop grid and 2.78:1 on a New
   Arrivals panel, against the 4.5:1 an 11px label needs. Worse, the figure moves
   with the photograph behind it — 0.45 over a pale garment is the 2.48, over a
   dark one it is 3.95, so no amount of tuning the alpha fixes it for every
   upload. An opaque ground does: #6b4a56 is the burgundy desaturated and
   lightened, still visibly muted next to the live #511126, and white on it is
   7.6:1 whatever the picture underneath. */
.product-card.sold-out .quick-view-btn { opacity: 1; background: #6b4a56; }
.product-card-cta.sold-out-cta {
    pointer-events: none;
    background: var(--bg-muted, #e5e7eb);
    color: var(--text-muted, #6b7280);
    border: 1px solid var(--border-light, #e5e7eb);
    cursor: not-allowed;
}

/* ═══════════════════════════════════════════════════════════
   SITE MOTION — parallax, slow drift, entrances & hover fx
   ───────────────────────────────────────────────────────────
   Added as one self-contained block so it can be deleted as a
   whole to return the site to its previous static look.
   ═══════════════════════════════════════════════════════════ */

/* Slow drift of cover backgrounds (Ken Burns) */
@keyframes dievonKenBurns {
    from { background-size: 108% auto; }
    to   { background-size: 124% auto; }
}

/* Occasion panels: parallax (background-attachment: fixed) + gentle drift.
   The image now arrives as a CSS variable (--occ-bg) set inline in home.php,
   which lets this sheet own size/position/attachment instead of fighting the
   old inline background-image !important rules. */
.occasion-banner {
    background-image: var(--occ-bg, none);
    background-size: cover;
    background-position: center center;
    background-repeat: no-repeat;
    background-attachment: fixed;
    animation: dievonKenBurns 34s ease-in-out infinite alternate;
}
.occasion-banner-overlay { transition: background 0.5s ease; }
.occasion-banner:hover .occasion-banner-overlay { background: rgba(0,0,0,0.46); }
.occasion-banner-content { transition: transform 0.6s ease; }
.occasion-banner:hover .occasion-banner-content { transform: translateY(-6px); }
.occasion-banner:hover .occasion-banner-eyebrow { letter-spacing: 0.32em; }
.occasion-banner-eyebrow { transition: letter-spacing 0.5s ease; }

/* Hero: content cascades in when a slide becomes active (JS toggles
   .is-active in updateSliderPosition). No base opacity rules — if the
   script ever fails the text simply sits visible, as before. */
.slide.is-active .slide-content > * {
    animation: dievonSlideUp 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.slide.is-active .slide-content > *:nth-child(1) { animation-delay: 0.05s; }
.slide.is-active .slide-content > *:nth-child(2) { animation-delay: 0.18s; }
.slide.is-active .slide-content > *:nth-child(3) { animation-delay: 0.32s; }
.slide.is-active .slide-content > *:nth-child(4) { animation-delay: 0.46s; }
@keyframes dievonSlideUp {
    from { opacity: 0; transform: translateY(26px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* Product cards: slow image zoom on hover + a diagonal light sweep */
.product-card:hover .card-img { transform: scale(1.08); }
.card-img-container::after {
    content: "";
    position: absolute; top: 0; left: 0;
    width: 42%; height: 100%;
    background: linear-gradient(105deg,
        rgba(255,255,255,0) 0%,
        rgba(255,255,255,0.22) 50%,
        rgba(255,255,255,0) 100%);
    transform: skewX(-18deg) translateX(-180%);
    pointer-events: none;
    z-index: 3;
}
.product-card:hover .card-img-container::after {
    animation: dievonShine 1s ease 0.05s;
}
@keyframes dievonShine {
    0%        { transform: skewX(-18deg) translateX(-180%); }
    55%, 100% { transform: skewX(-18deg) translateX(520%); }
}

/* Primary buttons: one soft sheen pass on hover */
.btn-luxury { position: relative; overflow: hidden; }
.btn-luxury::after {
    content: "";
    position: absolute; top: 0; left: -70%;
    width: 45%; height: 100%;
    background: linear-gradient(105deg,
        rgba(255,255,255,0) 0%,
        rgba(255,255,255,0.30) 50%,
        rgba(255,255,255,0) 100%);
    transform: skewX(-18deg);
    pointer-events: none;
}
.btn-luxury:hover::after { animation: dievonBtnShine 0.8s ease; }
@keyframes dievonBtnShine {
    0%        { left: -70%; }
    60%, 100% { left: 135%; }
}

/* Page heroes (shop, wishlist, cart, product, …): parallax + slow drift on
   the photo heroes, and a one-time cascade entrance for their content. */
.luxury-hero.has-bg-image {
    background-attachment: fixed;
    animation: dievonKenBurns 36s ease-in-out infinite alternate;
}
.luxury-hero .shop-breadcrumb,
.luxury-hero .luxury-hero-eyebrow,
.luxury-hero h1,
.luxury-hero p {
    animation: dievonSlideUp 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
}
/* Delays stay short — the h1 is the page's LCP element, so it must paint
   almost immediately; the cascade is still readable, just compressed. */
.luxury-hero .shop-breadcrumb     { animation-delay: 0.02s; }
.luxury-hero .luxury-hero-eyebrow { animation-delay: 0.06s; }
.luxury-hero h1                   { animation-delay: 0.12s; }
.luxury-hero p                    { animation-delay: 0.22s; }

/* Newsletter, benefits, blog & footer — the newsletter container, benefits
   grid, footer columns and bottom bar cascade in via the same stagger in
   footer.php. The hovers below are the "matching treatment". */

/* Blog cards: lift + image zoom + accent link */
.blog-card {
    transition: transform 0.5s ease, box-shadow 0.5s ease, border-color 0.5s ease;
}
.blog-card:hover {
    transform: translateY(-6px);
    box-shadow: var(--shadow-md);
    border-color: var(--border-color);
}
.blog-card:hover .blog-card-img { transform: scale(1.07); }
.blog-card:hover .blog-card-title a { color: var(--color-primary); }
.blog-card-link { transition: color 0.3s ease, letter-spacing 0.3s ease; }
.blog-card:hover .blog-card-link { color: var(--color-accent); letter-spacing: 0.2em; }

/* Benefits: gentle lift on hover — the homepage tiles only.
   Unscoped, this also lifted the product page's facts strip, which is not a
   link, has nothing to open, and sits in the middle of the buy box. */
.home-benefits-grid .benefit-item { transition: transform 0.4s ease; }
.home-benefits-grid .benefit-item:hover { transform: translateY(-5px); }
.home-benefits-grid .benefit-item:hover .benefit-icon { color: var(--color-primary); }

/* Footer: slide-in underline on links, social icon lift, brand hover */
.footer-col ul li a { position: relative; }
.footer-col ul li a::after {
    content: '';
    position: absolute; left: 0; bottom: -2px;
    width: 100%; height: 1px;
    background: currentColor;
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
.footer-col ul li a:hover::after { transform: scaleX(1); }
.footer-brand img { transition: opacity 0.3s ease; }
.footer-brand:hover img { opacity: 0.85; }
.footer-social a { transition: transform 0.3s ease, color 0.3s ease; }
.footer-social a:hover { transform: translateY(-3px); }

/* Respect reduced-motion preferences */
@media (prefers-reduced-motion: reduce) {
    .occasion-banner { animation: none; }
    .slide.is-active .slide-content > * { animation: none; }
    .product-card:hover .card-img { transform: none; }
    .product-card:hover .card-img-container::after,
    .btn-luxury:hover::after { animation: none; }
    .luxury-hero.has-bg-image { animation: none; }
    .luxury-hero .shop-breadcrumb,
    .luxury-hero .luxury-hero-eyebrow,
    .luxury-hero h1,
    .luxury-hero p { animation: none; }
    .blog-card:hover { transform: none; box-shadow: none; }
    .blog-card:hover .blog-card-img { transform: none; }
    .home-benefits-grid .benefit-item:hover { transform: none; }
    .footer-social a:hover { transform: none; }
}

/* ── Hero banner as a real <img> ──────────────────────────────────────────
   The slide used to carry the photograph as an inline background-image. That
   served the original PNG and never the .webp twin the uploader had already
   generated — 2,317KB instead of 273KB for one banner — and a background cannot
   start downloading until the stylesheet parses, which delays the largest and
   most-watched element on the page.

   object-fit: cover reproduces background-size: cover exactly, so the crop is
   unchanged. z-index 0 keeps it beneath .slide-overlay (1) and .slide-content
   (2). The background declaration is cleared on these slides so a fallback
   .slide-1/2/3 rule cannot download a second photograph behind the image. */
.slide-has-img { background-image: none !important; }
.slide-img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: top;
    z-index: 0;
    display: block;
}

/* ── Hero text legibility ─────────────────────────────────────────────────
   The overlay was a flat rgba(0,0,0,0.3) across the whole slide, which is the
   worst of both: not dark enough to carry white text over a pale photograph —
   "COORD SETS" was close to invisible on the linen banner — and dark enough to
   dull the garment everywhere else, which is the one thing the banner is for.

   A gradient puts the weight only where the words are. The text sits at
   bottom:15% (bottom:10% on a phone, responsive.css:524), so the darkening runs
   from the bottom up and has almost cleared by halfway. The top of the frame —
   usually the model's face and the fabric — keeps its real colour.

   Kept as one linear-gradient rather than a second element so nothing changes
   in the stacking: still one .slide-overlay at z-index 1, between the image and
   the text. */
.slide-overlay {
    background: linear-gradient(
        to top,
        rgba(0, 0, 0, 0.78) 0%,
        rgba(0, 0, 0, 0.62) 30%,
        rgba(0, 0, 0, 0.28) 55%,
        rgba(0, 0, 0, 0.06) 100%
    );
}

/* The headings need their own shadow strengthened, and it has to carry
   !important to reach them at all: style.css:2372 already sets
   `text-shadow: 0 2px 12px rgba(0,0,0,.5) !important` on .slide-content h1/h2.
   A 12px blur is a soft glow — it spreads the darkness so thinly that white
   serif capitals over a bright emerald photograph still washed out. A tight
   3px shadow with real opacity draws an edge around each letter instead. */
.slide-content h1,
.slide-content h2 {
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8), 0 0 18px rgba(0, 0, 0, 0.45) !important;
}

/* The supporting line and the eyebrow, for the case the gradient cannot solve:
   a banner that is bright white exactly where the text falls.

   h1/h2 are deliberately NOT listed. They already carry
   `text-shadow: 0 2px 12px rgba(0,0,0,.5) !important` at style.css:2372, so
   repeating them here would lose to that !important and leave a rule that reads
   as if it were doing something. This file already suffers from selectors
   declared twice; it does not need another. */
.slide-content p,
.slide-content .slide-eyebrow {
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* ── Contact page ─────────────────────────────────────────────────────────
   Lifted out of 21 inline style attributes. Inline styles cannot be adjusted
   per breakpoint, which is why the two cards kept their desktop 40px padding
   on a 375px phone — 80px of the screen width spent on empty margin. */
.contact-container { max-width: 1100px; margin: 0 auto; padding: 0 20px; }

.contact-card {
    border: 1px solid var(--border-light);
    padding: 40px;
}
.contact-card--form { background: var(--bg-surface); }
.contact-card--info {
    background: var(--bg-surface-soft);
    font-size: 14px;
    line-height: 1.8;
    color: var(--text-secondary);
}

.contact-card-title {
    font-family: var(--font-heading);
    font-size: 24px;
    font-weight: 400;
    text-transform: uppercase;
    margin-bottom: 25px;
    color: var(--text-primary);
}
.contact-sub-title {
    font-family: var(--font-heading);
    font-size: 18px;
    font-weight: 500;
    text-transform: uppercase;
    margin-bottom: 10px;
    color: var(--text-primary);
}
.contact-block { margin-bottom: 30px; }
.contact-block--last { margin-bottom: 0; }
.contact-muted { color: var(--text-muted); }
.contact-link { color: var(--color-accent); font-weight: 600; text-decoration: underline; }

.contact-alert {
    font-size: 14px;
    margin-bottom: 25px;
    border: 1px solid;
}
.contact-alert--ok {
    padding: 20px;
    background: #e6faf7;
    color: #2e7d32;
    border-color: #c3e6cb;
    text-align: center;
}
.contact-alert--error {
    padding: 12px;
    background: #fdf2f2;
    color: #a94442;
    border-color: #f5c6cb;
    font-size: 13px;
}
.contact-alert-icon { font-size: 24px; margin-bottom: 10px; display: block; }
.contact-alert-error-icon { margin-right: 5px; }

.contact-select {
    height: 46px;
    outline: none;
    cursor: pointer;
    text-transform: uppercase;
    font-size: 12px;
    letter-spacing: 0.05em;
}
.contact-textarea { resize: none; }
.contact-submit {
    width: 100%;
    justify-content: center;
    height: 50px;
    font-size: 13px;
    font-weight: 600;
}

/* ── "Keep me signed in" checkbox alignment ───────────────────────────────
   .auth-remember set display:flex, but .auth-form label (style.css:3949) is a
   more specific selector — 0,2,1 against 0,1,0 — and sets display:block. So the
   flexbox never applied, align-items:center did nothing, and the checkbox fell
   back to vertical-align:baseline, sitting 4px above the middle of its own
   label. Measured: checkbox centre 441px, text centre 445px.

   Matched on specificity rather than reached for !important, so the rule wins
   honestly and the next person can still override it. Also restores the 12px
   size and sentence case the label was meant to have — the generic rule was
   forcing 11px uppercase bold on it too. */
.auth-form label.auth-remember {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    font-weight: 400;
    text-transform: none;
    margin-bottom: 0;
    color: var(--text-secondary);
    cursor: pointer;
    user-select: none;
}
.auth-form label.auth-remember input {
    /* Belt and braces: centred by flex, but this keeps it honest if the label
       ever loses its flex context again. */
    margin: 0;
    vertical-align: middle;
}

/* ── Header search sits on the page centre ───────────────────────────────
   The search box is a flex item between the logo and the icon cluster, so
   space-between centred it BETWEEN THEM — at x=759 on a 1434px viewport. The
   navigation row directly beneath is centred on the PAGE, at x=717. Two rows,
   42px apart, stacked one above the other: that reads as crooked even though
   each is correctly centred by its own rule.

   Absolute centring lines the two up. The logo and the icons keep their places
   at either end, and the flex row still lays them out — the search is simply
   lifted out of the space-between arithmetic that was pulling it right.

   Below 992px the search is display:none (it collapses into an icon), so this
   only applies where the box is actually on screen. */
@media (min-width: 992px) {
    .main-header-container { position: relative; }
    .header-search-wrap {
        position: absolute;
        left: 50%;
        transform: translateX(-50%);
        /* flex-basis no longer applies once absolutely positioned, so the width
           it used to take from `flex: 1` is restated here.

           It also has to RESERVE the space either side of it. Being absolutely
           positioned takes it out of the flex row, so the logo and the icons no
           longer make room for it — they simply sit where they sit and the box
           is drawn over the top. At `width: 100%` (capped at 580px) that meant
           overlap on every screen below about 1160px: at a 992px viewport the
           container holds 880px, a centred 580px box leaves 150px each side,
           and the logo alone is 200px with roughly 250px of icons opposite.

           Subtracting 620px reserves ~310px on each side, which clears both
           neighbours: the 200px logo on the left, and on the right the country
           picker (~62px now that it shows a country code only) plus five icons
           and their gaps, about 242px.

           THIS NUMBER TRACKS THE COUNTRY PICKER. It was briefly 760 while that
           control carried full country names and ran to ~190px; at 620 the icon
           row grew past the reserve and the search box was drawn over the top
           of it. If the label ever gets longer again, this has to grow too. */
        width: min(460px, calc(100% - 620px));
    }
}

/* ── Journal article body, editor-written articles ─────────────────────────
   Posts written in the visual editor produce bare <p>/<h2>/<ul>, with none of
   the .article-para / .article-heading classes the old plain-text renderer
   added by hand. Styling by descendant here means both formats look identical
   on the page, and neither renderer has to know about the other.
   Appended at the end of the file deliberately: several selectors in this
   stylesheet are declared more than once, and the last declaration is the one
   that survives. */
.article-body p  { margin-bottom: 25px; }
.article-body h2 {
    font-family: var(--font-heading);
    font-size: 24px; line-height: 1.35; font-weight: 600;
    color: var(--text-primary);
    margin: 40px 0 16px;
}
.article-body h3 {
    font-family: var(--font-heading);
    font-size: 19px; line-height: 1.4; font-weight: 600;
    color: var(--text-primary);
    margin: 32px 0 12px;
}
.article-body h2:first-child,
.article-body h3:first-child { margin-top: 0; }
.article-body ul,
.article-body ol { margin: 0 0 25px; padding-left: 24px; }
.article-body li { margin-bottom: 10px; }
.article-body a  { color: var(--text-primary); text-decoration: underline; text-underline-offset: 3px; }
.article-body a:hover { opacity: 0.7; }
.article-body blockquote {
    margin: 32px 0;
    padding: 4px 0 4px 22px;
    border-left: 2px solid var(--border-light);
    font-family: var(--font-heading);
    font-size: 19px; line-height: 1.6; font-style: italic;
    color: var(--text-primary);
}
.article-body strong { font-weight: 600; color: var(--text-primary); }

@media (max-width: 600px) {
    .article-body h2 { font-size: 21px; margin: 32px 0 14px; }
    .article-body h3 { font-size: 17px; }
    .article-body blockquote { font-size: 17px; padding-left: 16px; }
}

/* ── Article share footer ──────────────────────────────────────────────────
   These rules were seven inline style="" attributes in pages/blog-single.php.
   Moved here so the row can be restyled in one place, and so the share buttons
   could be given a real tap target: they were bare <button>s at font-size 14px
   with no padding, i.e. roughly a 14px hit area on a phone — under the 24 CSS px
   floor, on the page type most likely to be read on one. The icon still draws at
   14px; only the invisible target around it grew. */
.article-share-footer {
    margin-top: 60px;
    padding-top: 30px;
    border-top: 1px solid var(--border-light);
    display: flex;
    justify-content: space-between;
    align-items: center;
    flex-wrap: wrap;
    gap: 15px;
}
.article-share {
    display: flex;
    align-items: center;
    gap: 12px;               /* clears the 8px minimum between adjacent targets */
}
.article-share-label {
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--text-muted);
}
.article-share-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    font-size: 14px;
    color: var(--text-primary);
    background: none;
    border: none;
    cursor: pointer;
    transition: color 0.2s ease, background-color 0.2s ease;
}
.article-share-btn:hover {
    color: var(--color-primary, #511126);
    background-color: var(--border-light);
}
.article-share-cta {
    font-size: 11px;
    padding: 10px 24px;
}

/* ── Header hide on scroll down, show on scroll up ─────────────────────────
   Appended at the very end on purpose. .navbar-luxury is declared three times
   above, and the second declaration narrows `transition` to background,
   box-shadow and border-color — so a transform added anywhere earlier would
   simply snap instead of sliding. This restates the whole transition with
   transform included, and being last it is the one that wins. */
.navbar-luxury {
    transition: background 0.3s ease,
                box-shadow 0.3s ease,
                border-color 0.3s ease,
                transform 0.35s cubic-bezier(0.4, 0, 0.2, 1) !important;
    will-change: transform;
}

/* The script adds .header-scroll-down while the reader is scrolling down and
   .header-scroll-up while scrolling back up. Near the top of the page, with a
   panel open, or while the header is hovered or focused, NEITHER class is
   present — so a rule written here is guaranteed not to fire at those moments.

   translateY rather than top, because the header is position:fixed and a
   transform moves it on the compositor without forcing a layout pass every
   frame.

   A fixed -80px, chosen deliberately: it slides the header up by the height of
   the main bar and leaves the strip below it peeking at the top of the screen,
   rather than taking the whole header away. Measured on the homepage, the
   header is 101px once compact, so roughly 21px stays on screen. If that strip
   ever looks wrong after the bars change height, this is the number to revisit
   — translateY(-101%) is the version that hides the header completely at any
   height without needing to be kept in sync. */

/* Keeping the header open while it is hovered or focused is handled in the
   scroll script, NOT here.

   The CSS version of that exemption was `.navbar-luxury:hover,
   .navbar-luxury:focus-within { transform: translateY(0) !important }`, which
   carries the same specificity as `.navbar-luxury.header-hidden` and sat later
   in the file — so it won every tie and the header simply never moved. Two
   rules fighting over one property is what made it silently do nothing;
   the JS owns the decision now and CSS only draws the result. */

@media (prefers-reduced-motion: reduce) {
    .navbar-luxury { transition: none !important; }
}


/* ── Country picker: full label on desktop, code only on a phone ──────────
   A <select> renders one string and CSS cannot rewrite an <option>, so the
   header prints both controls and hides one. Desktop shows "IN R INR";
   below 992px, where the row is tight, it shows just "UK".
   Only one is ever in the layout, so the hidden one costs no space. */


/* ── Shop by Occasion ─────────────────────────────────────────────────────
   This section is a navigational step, not merchandising: it sits between the
   product grids above and the two full-bleed Occasion Edits below, and its job
   is to be a calm band that lets the eye rest before the imagery starts.

   It now carries the standard .section-title-wrapper heading (see home.php),
   so the only work left here is the band itself and the chips.

   Hairlines on BOTH edges, not just the bottom. With a border on one side only
   the band had a floor but no ceiling, so it leaned into the section above
   instead of reading as its own thing.

   Declared at the end of the file deliberately — .occasion-bar-section and
   .occasion-bar-pills are also declared around line 2609, and in this
   stylesheet the last declaration is the one that applies. */
.occasion-bar-section {
    background: var(--bg-surface);
    border-top: 1px solid var(--border-light);
    border-bottom: 1px solid var(--border-light);
    text-align: center;
    font-family: var(--font-body);
}

/* 50px under the heading is right above a grid of tall cards; above a single
   row of chips it just looks like the two halves are unrelated. */
/* 22px, the same as every other section heading on the page. It was 32 — the
   one heading on the homepage sitting further from its content than the rest,
   which reads as a section that has drifted rather than as emphasis. */
.occasion-bar-section .section-title-wrapper { margin-bottom: 22px; }

/* ── The tiles ────────────────────────────────────────────────────────────
   Four photographs rather than four buttons. A fashion shopper picks an
   occasion by seeing it, not by reading the word for it, and the outlined
   chips this replaced were carrying no information at all.

   Grid, not flex: four equal columns stay equal whatever the labels are, so
   "Party" and "Wedding Guest" get the same tile instead of one shrinking to
   fit its shorter word. */
.occasion-slider {
    display: flex;
    gap: 14px;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    /* `proximity`, not `mandatory`. Mandatory fights a shopper who flicks hard —
       it hauls the strip back to the nearest tile and the throw feels broken.
       Proximity tidies up a slow stop and stays out of the way of a fast one. */
    scroll-snap-type: x proximity;
    scroll-padding-left: 16px;
    padding: 0 16px 6px;

    /* No arrows, no dots, and no scrollbar either — the strip is read by the tile
       clipped at the right edge, not by furniture around it. */
    scrollbar-width: none;
    -ms-overflow-style: none;

    /* `safe` matters. Plain `center` centres the tiles when they fit, but the
       moment they overflow it pushes the first one off the left edge into a place
       no scroll can reach — a well-known flexbox trap. `safe` centres only while
       there is room and falls back to the left edge once there is not, which is
       exactly the behaviour needed here: the men's side has three occasions and
       the women's has eight, and both have to look deliberate. */
    justify-content: safe center;
}
.occasion-slider::-webkit-scrollbar { display: none; }

.occasion-tile {
    position: relative;
    display: block;
    /* Percentage width against a full-bleed strip, deliberately NOT a whole
       fraction of the screen: 18% puts five and a half tiles in view, so there is
       always a half tile cut off at the edge saying there is more to see. A round
       20% would end the row flush and the strip would look like it had finished. */
    flex: 0 0 18%;
    scroll-snap-align: start;
    /* 3:4 — the ratio the photograph was actually shot at.
       ────────────────────────────────────────────────────────────────────────
       These tiles show product photographs straight out of uploads/products/,
       and every one of those is 3:4: the product form enforces it on upload,
       and the card, the gallery, the category tile and the related strip all
       frame at 3:4 too.

       This was 4:5, which is wider relative to its height, so object-fit: cover
       trimmed the top and bottom off every tile — and object-position had to be
       pulled to 28% to stop that crop landing on a midriff. Matching the frame
       to the source removes the crop instead of correcting for it, so the tile
       shows the shot as it was composed and the nudge is no longer needed.

       Each tile grows about 7% taller. Width is unchanged, so the row still
       breaks the same way at every screen size. */
    aspect-ratio: 3 / 4;
    overflow: hidden;
    text-decoration: none;
    background: var(--bg-surface-soft);
}
.occasion-tile-img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* No object-position nudge any more.
       It existed to drag the visible window upward because the tile was 4:5
       and the photograph 3:4, so cover() was cutting the top and bottom off.
       With the frame now matching the source there is nothing being cut, and
       leaving the 28% would push the shot off-centre for no reason. */
    transition: transform 0.6s ease;
}
.occasion-tile-img--blank { background: var(--grad-warm); }

/* Scrim, not a flat overlay. White label type has to stay readable over a
   photograph whose bottom edge could be any colour, but darkening the whole
   tile would waste the picture — so the shading is only where the words are. */
.occasion-tile-label {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    padding: 30px 12px 14px;
    color: #ffffff;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    text-align: center;
    background: linear-gradient(transparent, rgba(20, 4, 8, 0.72));
}
.occasion-tile:hover .occasion-tile-img { transform: scale(1.07); }

/* style.css:119 strips every focus ring globally with !important, so keyboard
   users would tab through these four links with no indication of where they
   are unless it is put back here. */
.occasion-tile:focus-visible {
    outline: 2px solid var(--color-accent) !important;
    outline-offset: 3px !important;
}
@media (prefers-reduced-motion: reduce) {
    .occasion-tile-img { transition: none; }
    .occasion-tile:hover .occasion-tile-img { transform: none; }
}

/* The chips inherit .btn-luxury-outline, which is a 1.5px maison-maroon CALL
   TO ACTION — correct for "Shop the Edit", too loud repeated four times in a
   row. These are a way in, not four competing buttons, so they read as quiet
   white chips and only commit to maroon on hover, where the inherited fill and
   lift already do the work. */
.occasion-bar-section .pill-btn {
    background: #ffffff !important;
    border: 1px solid var(--border-strong) !important;
    color: var(--text-primary) !important;
    letter-spacing: 0.12em !important;
}
.occasion-bar-section .pill-btn:hover,
.occasion-bar-section .pill-btn:focus-visible {
    background: var(--color-primary) !important;
    border-color: var(--color-primary) !important;
    color: #ffffff !important;
}


/* ── Account: order status filters ────────────────────────────────────────
   Functional filters, not brand navigation, so they are deliberately plainer
   than anything in the shop chrome — the customer is scanning for their own
   order here, and the controls should get out of the way. */
.acc-order-filters {
    display: flex;
    gap: 8px;
    margin-bottom: 22px;
    padding-bottom: 4px;
    /* One row, scrolled sideways rather than wrapped. With five buckets on a
       narrow screen a wrapping row silently becomes two and shoves the whole
       order list down the page; a single scrolling strip keeps the list where
       the customer expects it. Same treatment the admin tabs already use. */
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
}
.acc-order-filters::-webkit-scrollbar { display: none; }

.acc-order-filter {
    flex: 0 0 auto;
    background: transparent;
    border: 1px solid var(--border-strong);
    padding: 8px 18px;
    font-family: var(--font-body);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text-primary);
    cursor: pointer;
    white-space: nowrap;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
}
/* The count is supporting detail, not the label — it should not compete. */
.acc-order-filter span { opacity: 0.55; font-weight: 500; }

.acc-order-filter:hover { border-color: var(--color-primary); }

.acc-order-filter.is-active {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #ffffff;
}
.acc-order-filter.is-active span { opacity: 0.75; }

/* style.css:119 kills every focus ring globally with !important, so keyboard
   users get nothing back from these buttons unless it is restated here. */
.acc-order-filter:focus-visible {
    outline: 2px solid var(--color-accent) !important;
    outline-offset: 2px !important;
}


/* A buy button that cannot be used says so by looking unusable. Replaces the
   inline opacity/cursor that was on the "Not Available Here" button, and now
   covers "Sold Out" as well — both are the same state to a shopper: this one
   is not going in the bag today. */
.btn-add-bag.is-unavailable,
.btn-add-bag[disabled] {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* ── Cart: stock reality ───────────────────────────────────────────────
   Appended at the end deliberately. .cart-item-price and friends are each
   declared twice in this file (around line 947 and again around 5626), so a
   rule added next to the earlier block would be overridden by the later one.
   Last declaration wins — see the duplicate-blocks note. */
.cart-item-stock-note {
    margin-top: 6px;
    font-size: 12px;
    font-weight: 600;
    color: #9a6b00;
}

/* A quantity button that has nothing left to add says so by looking spent,
   rather than staying bright and failing when pressed. That rule now lives in
   the single stepper block at the end of this file; removing the bodies here
   left these two selectors dangling, which silently attached them to the
   .product-card-bag-note rule below. */

/* ── Product card: "already in your bag" ───────────────────────────────
   Filled in by syncBagNotes() in includes/footer.php for both card
   renderers. Hidden by the [hidden] attribute until there is something to
   say, so a card for a piece that is not in the bag reserves no space. */
.product-card-bag-note {
    margin: 0 0 6px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--color-primary);
}
.product-card-bag-note[hidden] { display: none; }

/* ── Return tracking on the customer's RMA card ────────────────────────
   Only rendered once the shop has booked a courier — see pages/account.php. */
.rma-return-tracking {
    margin-top: 8px;
    padding: 8px 12px;
    background: var(--color-primary-bg, #f7f1f3);
    font-size: 12px;
    color: var(--text-secondary);
}
.rma-return-tracking i { margin-right: 6px; color: var(--color-primary); }
.rma-return-tracking .rma-awb {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    letter-spacing: 0.04em;
    user-select: all;              /* one tap selects the whole number to copy */
}

/* ── Hero slider: the photograph must not be draggable ─────────────────
   Every slide holds a real <img>, and a press-and-drag on one starts the
   browser's native image drag — the ghost thumbnail — which swallows the
   mousemove events the swipe handler in pages/home.php needs. The handler
   now calls preventDefault() on mousedown, and this is the same guard
   expressed in CSS so the drag cannot begin in the first place.
   Appended at the end: .slide-img is declared earlier in this file, and
   the last declaration wins. */
.slide-img,
.slider-container img,
.slider-container picture {
    -webkit-user-drag: none;
    user-select: none;
    -webkit-user-select: none;
}

/* ── Hero slider under OwlCarousel ─────────────────────────────────────
   The design is unchanged; only the engine moved. These rules undo the layout
   the hand-rolled slider needed so Owl can position the track itself.

   Hooked on .owl-loaded, which is the class Owl actually adds to the element
   it initialises — NOT .owl-carousel, which it only expects you to have put in
   the markup yourself. Selecting on the wrong one silently matched nothing and
   collapsed every slide to zero height.

   Appended at the end: .slider-container and .slide are each declared earlier
   in this file, and the last declaration wins. */
.slider-container.owl-loaded { display: block; transition: none; height: 100%; }
.slider-container.owl-loaded .owl-stage-outer { height: 100%; }
.slider-container.owl-loaded .owl-stage { height: 100%; }
.slider-container.owl-loaded .owl-item { height: 100%; float: left; }
/* flex-basis:100% came from the old one-slide-per-viewport flex row. Owl sets
   the width on .owl-item itself, so the slide just fills what it is given. */
.slider-container.owl-loaded .slide { flex: 1 1 auto; width: 100%; height: 100%; }

/* ── Product rails under OwlCarousel ───────────────────────────────────
   Same look as the scroll-snap version: fixed-width cards in a row with the
   next one peeking. Owl replaces the native scrolling with its own stage, so
   the flex/overflow rules the old version needed have to stand down on the
   rails that opted in. Hooked on .owl-loaded — the class Owl actually adds.
   Appended at the end because these selectors are declared earlier and the
   last declaration wins. */
.js-owl-rail.owl-loaded { display: block; overflow: visible; }
.js-owl-rail.owl-loaded .owl-stage-outer { overflow: hidden; }
.js-owl-rail.owl-loaded .owl-stage { display: flex; }
/* The card keeps its own 280px width — autoWidth means Owl measures rather
   than stretches, so the row looks exactly as it did. */
.js-owl-rail.owl-loaded .owl-item { display: flex; }
/* Owl divides the row into whole cards, so the card fills the slot it is
   given rather than holding the old fixed 280px — that fixed width is what
   left 0.43 of a card hanging off the right edge on a wide screen. */
.js-owl-rail.owl-loaded .product-card-carousel { flex: 1 1 auto; width: 100%; }
/* Owl adds its own margin between items; the container gap would double it. */
.js-owl-rail.owl-loaded { gap: 0; }

/* ── Owl's own nav and dots, hidden ────────────────────────────────────
   Owl builds .owl-nav and .owl-dots into the DOM even when nav:false and
   dots:false — it only marks them .disabled. Its default THEME stylesheet is
   what normally hides them, and that sheet is deliberately not loaded here
   because it would restyle controls this site already draws itself.

   Without the theme, those elements rendered as two stray ‹› characters under
   the occasion strip and the product rail. Every slider on this site supplies
   its own arrows and dots, so Owl's are hidden everywhere, not case by case. */
.owl-carousel .owl-nav,
.owl-carousel .owl-dots,
.js-owl-rail .owl-nav,
.js-owl-rail .owl-dots,
#heroSlider .owl-nav,
#heroSlider .owl-dots {
    display: none !important;
}

/* ── Occasion strip: absolute tile width ───────────────────────────────
   The tile is `flex: 0 0 18%` by default — 18% of a full-bleed strip, chosen
   (see the rule itself) so five and a half tiles show and the half tile at the
   edge says there is more.

   Two problems with a percentage once OwlCarousel drives the strip:

   1. Inside Owl the percentage stops resolving against the strip and resolves
      against Owl's own stage instead, so the tiles scatter.
   2. Owl MEASURES each item during init — before it has added .owl-loaded to
      the element. So any width that depends on that class does not exist yet at
      the moment it matters: Owl sized the stage to 560px for 888px of tiles and
      the last ones could not be reached.

   So the width is stated absolutely and unconditionally, not under .owl-loaded.
   Because the strip is full-bleed — it sits outside .container on purpose —
   18% of it and 18vw are the same measurement, so this changes nothing about
   the design with or without Owl. It simply gives Owl a number it can read at
   the time it reads it.

   Breakpoints mirror responsive.css exactly (23 / 30 / 42 / 56). */
.occasion-slider .occasion-tile { flex: 0 0 18vw; width: 18vw; }
@media (max-width: 1199px) { .occasion-slider .occasion-tile { flex-basis: 23vw; width: 23vw; } }
@media (max-width: 991px)  { .occasion-slider .occasion-tile { flex-basis: 30vw; width: 30vw; } }
/* Below 768 this is the same rail as Dievon Collections and should read as it,
   so it is sized by the same solved formula rather than a percentage guess:
   two whole tiles and a third of the next, at every width. 56px is the two 16px
   stage insets plus the two 12px gaps that sit inside the visible strip.

   vw, not %. Owl wraps every card in an .owl-item and autoWidth measures the
   card — a percentage would resolve against that wrapper rather than the rail,
   which is how this strip once ended up an 888px row inside a 560px stage. A
   viewport unit cannot be fooled by the wrapper.

   It was 42vw, stepping to 56vw under 420px, which put 1.63 tiles on a 390px
   phone against the collections rail's 2.38 directly above it. Neither number
   was chosen; they were what the percentages happened to produce. */
@media (max-width: 768px) {
    .occasion-slider .occasion-tile {
        flex: 0 0 calc((100vw - 56px) / 2.3);
        width: calc((100vw - 56px) / 2.3);
    }
}

.occasion-slider.owl-loaded { display: block; overflow: visible; gap: 0; }
.occasion-slider.owl-loaded .owl-stage-outer { overflow: hidden; }

/* ── A rail with too little to scroll sits centred ─────────────────────
   .owl-content-fits is set by the shared initialiser in includes/footer.php
   whenever the row is narrower than the strip that shows it. Owl lays its
   stage out from the left, so four occasion tiles on a wide screen sat hard
   against the left edge with empty space beside them — which reads as
   something having failed to load rather than as a deliberate row.


/* Owl puts its item margin on EVERY item including the last, so a centred row
   carries one trailing gap that is not balanced on the left — the tiles sat
   14px off true centre. Dropped only while centred; when the rail scrolls, a
   trailing margin is harmless and Owl's spacing maths expects it. !important
   because Owl writes the margin inline. */
.js-owl-rail.owl-content-fits .owl-item:last-child { margin-right: 0 !important; }

/* ── dv-notice — the one notice box, used everywhere ───────────────────
   Rendered by dvNotice() in config/config.php. See that function for why this
   exists: seven hand-written notice styles across 85 places, and the same
   display:flex-on-prose mistake found and fixed four separate times because
   fixing one taught the others nothing.

   The layout rule that matters: the BODY is a normal block, so the sentence
   flows as a sentence. The icon is positioned, not a flex sibling, so it keeps
   its hanging indent without turning every <strong> into a column. */
.dv-notice {
    position: relative;
    display: block;
    padding: 12px 16px 12px 40px;
    margin-bottom: 16px;
    border-left: 3px solid var(--dvn-accent, #999);
    background: var(--dvn-bg, #f6f6f6);
    color: var(--dvn-fg, inherit);
    font-size: 13.5px;
    line-height: 1.6;
}
.dv-notice > i {
    position: absolute;
    left: 15px;
    top: 14px;
    color: var(--dvn-accent, #999);
}
/* The body is deliberately a plain block: prose flows, nothing becomes a column. */
.dv-notice-body { display: block; }
.dv-notice-body > :first-child { margin-top: 0; }
.dv-notice-body > :last-child  { margin-bottom: 0; }
.dv-notice strong { color: var(--dvn-strong, inherit); font-weight: 700; }
.dv-notice a { color: inherit; text-decoration: underline; font-weight: 600; }
.dv-notice code {
    background: rgba(0,0,0,0.06); padding: 1px 5px;
    font-size: 0.92em;
}

.dv-notice-info    { --dvn-accent:#c79a3a; --dvn-bg:#fdf9f0; --dvn-strong:#7a5a12; }
.dv-notice-success { --dvn-accent:#1e6b37; --dvn-bg:#f1f8f3; --dvn-strong:#155029; }
.dv-notice-warning { --dvn-accent:#b8860b; --dvn-bg:#fdf8ec; --dvn-strong:#8a5a00; }
.dv-notice-danger  { --dvn-accent:#b91c1c; --dvn-bg:#fff5f5; --dvn-strong:#8f1414; }
.dv-notice-muted   { --dvn-accent:#9a8f80; --dvn-bg:#f7f5f2; --dvn-strong:#5a5148; }

/* ═══════════════════════════════════════════════════════════════════════
   SMOOTH SCROLL — landing position, and people who ask for less motion
   ───────────────────────────────────────────────────────────────────────
   `scroll-behavior: smooth` was already set on html near the top of this
   file, so jumping to an anchor already glided. Two things were missing.

   .navbar-luxury is position: fixed, so an anchor scrolled its target to
   y=0 — underneath the header, with the heading you asked for hidden
   behind the logo. scroll-padding-top stops the scroll short by the
   header's height, which is what that property exists for.

   And smooth scrolling is a common migraine and vertigo trigger, so the
   reduced-motion request has to be honoured — otherwise the OS setting is
   simply ignored by the site.
   ═══════════════════════════════════════════════════════════════════════ */
html {
    scroll-padding-top: calc(var(--header-height, 145px) + 16px);
}

/* Declared on body as well as html.
   ────────────────────────────────────────────────────────────────────
   scroll-behavior only applies to whichever element is actually doing the
   scrolling. html here sets `overflow-x: clip`, and engines disagree about
   whether that leaves html the scrolling box or promotes body to it — on
   the browsers that pick body, a rule sitting only on html does nothing
   and anchor jumps snap instead of gliding. Setting both costs nothing and
   removes the disagreement.

   Worth being clear about the limit: this governs anchor jumps and
   scrollTo() only. It has never affected the mouse wheel. A Windows mouse
   wheel scrolls in fixed notches while a Mac trackpad glides, so the same
   page genuinely feels different on the two — and no CSS changes that. */
body {
    scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
    html,
    body { scroll-behavior: auto; }
}

/* ═══════════════════════════════════════════════════════════════════════
   ONE HOVER FOR EVERY TEXT LINK
   ───────────────────────────────────────────────────────────────────────
   Links behaved differently depending on which part of the site they were
   in: the main nav turned gold, footer links turned blush, and a dozen
   others — Continue Shopping, View Bag, the legal links, contact details,
   the blog and category "read more" links — did nothing at all. Several
   carried `transition: all 0.3s` while having no hover state to spend it
   on, so the transition was real and invisible.

   Rather than invent a new effect, this borrows the one the design already
   uses: .nav-link-item.active draws a 2px gold bar under itself. That bar
   now grows from the left on hover, on every text link on the site.

   currentColor, not a fixed gold — so the bar always matches the link it
   belongs to. Gold under the nav, blush under the footer's pale links, red
   under Remove. One movement everywhere, readable on every background.

   Only text links are listed. Buttons, product cards and image links keep
   their own treatments; an underline sliding under a filled gold button
   would be noise, not consistency.
   ═══════════════════════════════════════════════════════════════════════ */
.nav-link-item,
.audience-switch-link,
.mobile-audience-link,
.footer-links a,
.footer-col ul li a,
.legal-link,
.contact-link,
.in-bag-view-link,
.auth-admin-link a,
.cart-item-link,
.cart-continue-link,
.category-card-link,
.blog-card-link {
    position: relative;
}

/* The footer menu's jump.
   ────────────────────────────────────────────────────────────────────
   .footer-col ul li a carried `transition: color 0.2s` while its hover
   changed BOTH colour and padding-left. Padding was never in the
   transition list, so the colour faded gently while the text snapped 4px
   to the right — the one link on the page that visibly lurched. The shift
   is dropped; the underline below is the hover now, same as everywhere
   else. */
.footer-col ul li a:hover { padding-left: 0; }

.nav-link-item::after,
.audience-switch-link::after,
.mobile-audience-link::after,
.footer-links a::after,
.footer-col ul li a::after,
.legal-link::after,
.contact-link::after,
.in-bag-view-link::after,
.auth-admin-link a::after,
.cart-item-link::after,
.cart-continue-link::after,
.category-card-link::after,
.blog-card-link::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: -2px;
    width: 100%;
    height: 2px;
    background-color: currentColor;
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.28s cubic-bezier(0.25, 1, 0.5, 1);
    pointer-events: none;
}

.nav-link-item:hover::after,
.nav-link-item:focus-visible::after,
.audience-switch-link:hover::after,
.audience-switch-link:focus-visible::after,
.mobile-audience-link:hover::after,
.mobile-audience-link:focus-visible::after,
.footer-links a:hover::after,
.footer-links a:focus-visible::after,
.footer-col ul li a:hover::after,
.footer-col ul li a:focus-visible::after,
.legal-link:hover::after,
.legal-link:focus-visible::after,
.contact-link:hover::after,
.contact-link:focus-visible::after,
.in-bag-view-link:hover::after,
.in-bag-view-link:focus-visible::after,
.auth-admin-link a:hover::after,
.auth-admin-link a:focus-visible::after,
.cart-item-link:hover::after,
.cart-item-link:focus-visible::after,
.cart-continue-link:hover::after,
.cart-continue-link:focus-visible::after,
.category-card-link:hover::after,
.category-card-link:focus-visible::after,
.blog-card-link:hover::after,
.blog-card-link:focus-visible::after {
    transform: scaleX(1);
}

/* The nav's current page keeps its bar permanently — the rule above would
   otherwise collapse it to scaleX(0), since the original .active::after
   never set a transform of its own. */
.nav-link-item.active::after,
.audience-switch-link.is-active::after,
.mobile-audience-link.is-active::after {
    transform: scaleX(1);
}

/* .nav-link-sale hides its bar deliberately; keep it hidden. */
.nav-link-item.nav-link-sale::after { display: none !important; }

/* These already underline themselves in text; a second line beneath would
   be two underlines stacked. */
.in-bag-view-link,
.contact-link { text-decoration: none; }

@media (prefers-reduced-motion: reduce) {
    .nav-link-item::after,
    .audience-switch-link::after,
    .mobile-audience-link::after,
    .footer-links a::after,
.footer-col ul li a::after,
    .legal-link::after,
    .contact-link::after,
    .in-bag-view-link::after,
    .auth-admin-link a::after,
    .cart-item-link::after,
    .cart-continue-link::after,
    .category-card-link::after,
    .blog-card-link::after {
        transition: none;
    }
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — the promo card when there is no "Shop by Fabric" column
   ───────────────────────────────────────────────────────────────────────
   The container declared three fixed tracks — 220px 220px 1fr — but the
   middle column is written conditionally in includes/header.php:

       <?php if ($pFabrics || $pBadges): ?>   ← only when the collection
                                                 has fabrics or badges

   With no fabrics there are only two children, so the promo card landed in
   the 220px MIDDLE track and the 1fr track stayed empty: a shrunken card
   beside a wide blank space, on a menu that looks correct everywhere else.

   It never showed in development because seeded catalogues have fabrics on
   every collection. It shows on a fresh shop, where they have not been set
   yet — which is exactly when a new owner is first opening these menus.

   Flex instead of fixed tracks: the text columns keep their 220px and the
   promo takes whatever is left, with or without the middle column. Capped
   so the card keeps its proportions rather than stretching into a letterbox,
   and pushed right so the leftover space reads as spacing, not a hole.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-dropdown-container {
    display: flex;
    gap: 40px;
    align-items: flex-start;
}

.mega-dropdown-container > .mega-column {
    flex: 0 0 220px;
    min-width: 0;
}

.mega-dropdown-container > .mega-promo-column {
    flex: 1 1 0;
    min-width: 0;
    /* Sized to the pair of cards rather than to whatever space is spare: two
       upright cards stretched across 800px would be 500px tall and push the
       menu past the fold. Leftover width becomes spacing, and margin-left
       keeps the block on the right edge whether or not the fabric column is
       there — so both versions of the menu line up the same. */
    max-width: 520px;
    margin-left: auto;
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — two upright promo cards
   ───────────────────────────────────────────────────────────────────────
   The old single card was 802 x 210: a 3.8:1 letterbox holding portrait
   photography, so object-fit: cover kept a horizontal band of background
   and cut the model out of her own picture.

   Two cards at roughly 4:5 match the shape the photographs actually are.
   The title and Shop Now move out from on top of the image to a caption
   underneath, so nothing is printed over a model's face and the type is
   legible whatever the photograph behind it happens to be.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-promo-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
}

.mega-promo-card {
    position: relative;
    display: block;
    width: 100%;
    /* height:auto is doing real work: the original rule sets height: 210px,
       and with that still in force aspect-ratio solved for WIDTH instead —
       giving two 168px slivers rather than two upright cards. */
    height: auto;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    background: var(--bg-surface-soft);
}

.mega-promo-card img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* Fashion photography puts the subject above centre; a plain centre crop
       on a tall image takes the hem and leaves the face out of frame. */
    object-position: center 22%;
    transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1);
}

.mega-promo-card:hover img,
.mega-promo-card:focus-visible img { transform: scale(1.06); }

.mega-promo-caption {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
    margin-top: 14px;
}

/* Older browsers without aspect-ratio would give the cards no height at all,
   which is a blank menu rather than an imperfect one. */
@supports not (aspect-ratio: 4 / 5) {
    .mega-promo-card { height: 280px; }
}

@media (prefers-reduced-motion: reduce) {
    .mega-promo-card img { transition: none; }
    .mega-promo-card:hover img { transform: none; }
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — closing the gap in the middle, and an invisible Shop Now
   ───────────────────────────────────────────────────────────────────────
   Two problems that only appeared once the promo became two cards.

   1. The columns measured about 1040px inside a 1322px container and the
      promo was pinned right, so all ~280px of slack collected in a single
      hole between the fabric list and the pictures. space-between shares it
      out as breathing room between every column instead, and the cards grow
      to take most of it — which makes the photographs the feature they were
      meant to be rather than two stamps in a corner.

   2. .btn-mega-shop is `color: #ffffff` because it used to sit on the dark
      gradient over the image. Moving the caption out from on top of the
      photograph left white type on a white background: Shop Now was still
      there, still clickable, and completely invisible.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-dropdown-container {
    justify-content: space-between;
}

.mega-dropdown-container > .mega-promo-column {
    max-width: 640px;
    /* space-between distributes the slack; an auto margin would swallow all
       of it again and put the hole straight back. */
    margin-left: 0;
}

.mega-promo-caption .btn-mega-shop {
    color: var(--color-primary);
}

.mega-promo-caption .btn-mega-shop:hover,
.mega-promo-caption .btn-mega-shop:focus-visible {
    color: var(--color-accent);
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — centre a narrower block instead of filling a wide one
   ───────────────────────────────────────────────────────────────────────
   The gap between the fabric list and the pictures was never really a gap:
   the menu was stretched to the full 1322px container while holding about
   1040px of content, so ~280px had to go somewhere. Spreading it made two
   smaller holes. Growing the photographs to 313x391 filled it, but a menu
   is a signpost — pictures that size stop being a hint and start being the
   page.

   So the content is no longer stretched at all. The columns keep their own
   size, the block is centred, and the leftover width becomes even margins
   on both sides — which reads as a composed menu rather than a hole in the
   middle of one. The cards go back to roughly the size they were.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-dropdown-container {
    max-width: 1080px;
    margin-inline: auto;
    justify-content: flex-start;
    gap: 56px;
}

.mega-dropdown-container > .mega-promo-column {
    flex: 0 0 auto;
    width: 528px;
    max-width: 528px;
    margin-left: 0;
}

/* One product photograph is one card at its normal size, sitting where the
   first card always sits — not one stretched to twice the width. */
.mega-promo-grid {
    grid-template-columns: repeat(2, minmax(0, 257px));
}

@media (max-width: 1180px) {
    .mega-dropdown-container { max-width: 100%; gap: 40px; }
    .mega-dropdown-container > .mega-promo-column { width: auto; flex: 1 1 0; }
    .mega-promo-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — three cards, so the row reaches across the menu
   ───────────────────────────────────────────────────────────────────────
   Filling the empty band by stretching two cards made them 313x391, which
   was too big for a menu. Three cards at the ORIGINAL size close the same
   distance without any of them growing:

       220 + 56 + 220 + 56 + (247 x 3 + 14 x 2) = 1321   in a 1322px row

   The columns are a fixed 247px rather than fractions, so a collection with
   one or two photographs gets one or two normal cards instead of two huge
   ones. The container is then only as wide as it needs to be and centres
   itself, so a shorter row reads as composed rather than as a hole.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-dropdown-container {
    width: fit-content;
    max-width: 100%;
    margin-inline: auto;
}

.mega-dropdown-container > .mega-promo-column {
    /* Sized by its cards now, not by the space left over. */
    flex: 0 0 auto;
    width: auto;
    max-width: none;
}

.mega-promo-grid {
    /* One column per card, however many there are — 1, 2 or 3. */
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 247px);
    grid-template-columns: none;
}

@media (max-width: 1180px) {
    /* Two cards below this width: three would push the row past the screen. */
    .mega-promo-card:nth-child(3) { display: none; }
    .mega-promo-grid { grid-auto-columns: minmax(0, 220px); }
    .mega-dropdown-container { gap: 40px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — full width again, with the middle band given something to do
   ───────────────────────────────────────────────────────────────────────
   Centring a narrower block removed the empty band but shrank the menu to
   do it, which is not what was wanted. The menu spans the row again; the
   space is filled with content instead of margin — a third link column
   (Shop by Occasion, built from the same product data as Shop by Fabric)
   and up to three photographs.

   space-between, not an auto margin: whatever slack is left after the
   columns is shared evenly between them, so it reads as spacing rather
   than collecting into one hole beside the pictures.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-dropdown-container {
    width: auto;
    max-width: none;
    margin-inline: 0;
    justify-content: space-between;
    gap: 48px;
}

/* Three link columns plus three cards would overrun the row, so the third
   photograph steps aside once the third column is there. */
.mega-dropdown-container:has(.mega-column:nth-of-type(3)) .mega-promo-card:nth-child(3) {
    display: none;
}

@media (max-width: 1180px) {
    .mega-dropdown-container { gap: 32px; }
    .mega-promo-grid { grid-auto-columns: minmax(0, 200px); }
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — the third slot is a tile, not a third photograph
   ───────────────────────────────────────────────────────────────────────
   Three cards is what lets the row reach across the menu without any card
   growing. A third PHOTOGRAPH could not deliver that: most collections hold
   exactly two distinct product images and the galleries are empty, so the
   slot would be missing on nearly every menu and the empty band would come
   straight back.

   A designed tile is always there. It carries the piece count — something
   the photographs cannot say — so it earns its place rather than padding
   the row, and it can never duplicate a picture beside it.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-promo-tile {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    gap: 2px;
    padding: 22px 20px;
    background: var(--color-primary);
    color: #ffffff;
}

.mega-promo-tile-count {
    font-family: var(--font-heading);
    font-size: 44px;
    line-height: 1;
    font-weight: 500;
    color: var(--color-accent);
}

.mega-promo-tile-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: rgba(255, 255, 255, 0.7);
}

.mega-promo-tile-cta {
    margin-top: 14px;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    transition: gap 0.28s cubic-bezier(0.25, 1, 0.5, 1),
                color 0.28s ease;
}

.mega-promo-tile:hover .mega-promo-tile-cta,
.mega-promo-tile:focus-visible .mega-promo-tile-cta {
    gap: 14px;
    color: var(--color-accent);
}

/* The third slot is the tile now, so nothing needs suppressing. */
.mega-dropdown-container:has(.mega-column:nth-of-type(3)) .mega-promo-card:nth-child(3) {
    display: flex;
}

@media (prefers-reduced-motion: reduce) {
    .mega-promo-tile-cta { transition: color 0.28s ease; }
    .mega-promo-tile:hover .mega-promo-tile-cta { gap: 8px; }
}

/* ═══════════════════════════════════════════════════════════════════════
   MEGA MENU — the third slot, now a photograph wherever one exists
   ───────────────────────────────────────────────────────────────────────
   A flat panel beside two photographs reads as an image that failed to
   load, which is the last thing a fashion menu should suggest. So the tile
   now carries a third product photograph when the collection has one, with
   the count and the CTA laid over it; the solid version survives only for a
   collection with fewer than three photographs, and it stops being used the
   moment that changes.
   ═══════════════════════════════════════════════════════════════════════ */
.mega-promo-tile { position: relative; padding: 0; }

.mega-promo-tile img {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 22%;
}

.mega-promo-tile-body {
    position: relative;      /* above the photograph */
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-end;
    gap: 2px;
    width: 100%;
    height: 100%;
    padding: 22px 20px;
}

/* One wash for the tile, whichever photograph is behind it.
   ────────────────────────────────────────────────────────────────────
   There were two: a light bottom-up fade for a genuine third photograph
   and this heavier burgundy for a reused one. That made the third card
   look like a different component depending on how many products a
   collection happened to hold — the menus stopped matching each other.

   This one is used for both. It is opaque enough that the type reads over
   any photograph, pale hem or bright sky, and opaque enough that a reused
   picture stays texture rather than a recognisable repeat. */
.mega-promo-tile--photo .mega-promo-tile-body {
    background: linear-gradient(180deg,
        rgba(81, 17, 38, 0.82) 0%,
        rgba(48, 8, 22, 0.94) 100%);
}

.mega-promo-tile:hover img,
.mega-promo-tile:focus-visible img { transform: scale(1.06); }

/* ═══════════════════════════════════════════════════════════════════════
   FOCUS — the controls that strip their own outline
   ───────────────────────────────────────────────────────────────────────
   The blanket reset near the top of this file is gone, but several
   components still remove the outline in their own rules, and each one
   outranks a bare `input:focus-visible`:

       .form-control:focus      { outline: none; }              line 1085
       .form-luxury-input:focus { outline: none; }              line 2097
       .search-input            { outline: none; }              line 2140
       .animated-input          { outline: none !important; }   line 2187

   .form-luxury-input is the one that matters most: ten fields on checkout,
   plus cart, login and register. Every text box a shopper types their
   address and card details into had no visible focus at all.

   Restored here rather than by editing each rule, so the focus treatment
   for the whole shop lives in one place and a new component that strips its
   outline can be added to this list instead of inventing its own ring.
   :focus-visible throughout, so clicking still leaves no ring.
   ═══════════════════════════════════════════════════════════════════════ */
.form-control:focus-visible,
.form-luxury-input:focus-visible,
.search-input:focus-visible,
.animated-input:focus-visible,
.status-select:focus-visible {
    outline: 2px solid var(--color-primary, #511126) !important;
    outline-offset: 2px !important;
    box-shadow: 0 0 0 2px #fff !important;
}

/* ═══════════════════════════════════════════════════════════════════════
   HEADER ACTIONS — tappable area
   ───────────────────────────────────────────────────────────────────────
   Account, Wishlist and Cart sized themselves to their glyph: measured on
   a 375px screen they were 16×18px and 18×18px, set 7px apart. WCAG 2.2 AA
   asks for 24×24 CSS px on the web, and the mobile guidance is 44pt on iOS
   and 48dp on Android — these were under half of the web minimum, on the
   three controls a phone shopper reaches for most.

   The icons themselves are NOT enlarged. Only the hit area grows, by giving
   each control a minimum box and centring the glyph inside it, so the header
   looks exactly as it did and simply stops being hard to hit. At 44px the
   targets still fit the 59px header.
   ═══════════════════════════════════════════════════════════════════════ */
/* 24px, which is the WEB rule — not 44.
   ────────────────────────────────────────────────────────────────────────
   The skill states both and they are not the same thing: WCAG 2.2 AA asks
   for 24 CSS px on the web, while 44pt/48dp is native iOS and Android
   guidance. I tried 44 first and it pushed the header from 59px to 78px —
   a fixed header that every sticky offset on the site is measured against,
   so the whole page would have shifted to fix a control that only needed
   six more pixels. Conformance did not require it and the layout could not
   afford it. */
.nav-action-item {
    min-width: 24px;
    min-height: 24px;
    justify-content: center;
}

/* The hit area beyond that is added WITHOUT taking up space, so the header
   keeps its height while the tappable box reaches a comfortable 40px. */
.nav-action-item::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 40px;
    height: 40px;
    transform: translate(-50%, -50%);
    /* Behind the badge and the glyph; it exists only to catch the tap. */
    z-index: 0;
}

/* ═══════════════════════════════════════════════════════════════════════
   TOUCH TARGETS — the three controls still under 24px
   ───────────────────────────────────────────────────────────────────────
   Measured at 768px on a product page. None of these qualifies for the
   WCAG inline-text exception: each is a standalone control, so each needs
   24 CSS px.

     .nav-hamburger          22×22   the menu button — the whole navigation
                                     on a phone, and the smallest target of
                                     the three
     .size-guide-trigger-btn 93×16   padding: 0 on a text button, so its
                                     height was just the 12px line
     .offer-code             74×16   a promo code the shopper taps to copy

   Height is added with padding rather than a bare min-height, so the label
   stays centred instead of sitting against the top edge. The hamburger also
   gets the invisible 40px reach used on the header actions above.
   ═══════════════════════════════════════════════════════════════════════ */
.nav-hamburger {
    min-width: 24px;
    min-height: 24px;
    position: relative;
}

.nav-hamburger::before {
    content: '';
    position: absolute;
    left: 50%;
    top: 50%;
    width: 44px;
    height: 44px;
    transform: translate(-50%, -50%);
    z-index: 0;
}

.size-guide-trigger-btn,
.offer-code {
    min-height: 24px;
    padding-block: 4px;
    display: inline-flex;
    align-items: center;
}

/* Compare bar and welcome modal — controls that only appear in those states,
   so they were missed by a scan of the page at rest. A native 13px checkbox
   has a user-agent exception on paper; it is still a 13px target on a phone,
   and the row it sits in is a product card, so there is room. */
.compare-check {
    width: 20px;
    height: 20px;
    min-width: 20px;
    min-height: 20px;
    accent-color: var(--color-primary);
}

.cmp-clear,
.cmp-close,
.welcome-skip {
    min-height: 24px;
    padding-block: 5px;
}


/* ══ Feedback at the two moments a shopper acts ══════════════════════════
   Adding to the bag and saving to the wishlist were both silent: the bag
   badge changed number with no movement, and the heart swapped outline for
   solid in a single frame. Motion here is not decoration — it is the
   confirmation that the tap registered, which is the one thing a shopper
   needs to know before they carry on browsing.

   Written as @keyframes rather than a transition on purpose. The first item
   added takes the badge from display:none to display:flex in the same tick,
   and a transition has no starting frame to animate from in that case — so
   the very first add, the one that matters most, would not have moved.
   An animation plays on a newly shown element.

   These rules live at the end of the file because several badge selectors
   are declared more than once above; the last declaration is the one that
   wins. ───────────────────────────────────────────────────────────────── */

@keyframes dievonBadgePop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.35); }
    70%  { transform: scale(0.94); }
    100% { transform: scale(1); }
}

/* .action-badge is what the header bag actually wears. The bump class had
   only ever been styled for .cart-badge, so bumpBadge() has been adding and
   removing a class that matched nothing on the header for as long as it has
   existed. */
.action-badge.bump,
.cart-badge.bump,
.wishlist-badge.bump {
    animation: dievonBadgePop 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

@keyframes dievonHeartPop {
    0%   { transform: scale(1); }
    40%  { transform: scale(1.3); }
    75%  { transform: scale(0.94); }
    100% { transform: scale(1); }
}

/* inline-block because <i> is inline, and transforms do not apply to inline
   boxes — without it the pop silently does nothing. */
.wishlist-pop {
    display: inline-block;
    animation: dievonHeartPop 320ms cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

/* Both are confirmations, so the state they confirm must still be reached
   with motion turned off — the badge still shows its new number and the
   heart is still filled. Only the movement is dropped. */
@media (prefers-reduced-motion: reduce) {
    .action-badge.bump,
    .cart-badge.bump,
    .wishlist-badge.bump,
    .wishlist-pop {
        animation: none;
    }
}


/* ══ The second photograph, on hover ═════════════════════════════════════
   The one motion a clothing shop earns more from than any other: the back or
   the detail shot without leaving the grid. Built per product — the layer
   only exists where a genuinely different second picture does (see
   productHoverImage()), so single-photograph products are untouched.

   Wrapped in @media (hover: hover) on purpose. On a touch screen a :hover
   rule latches after the tap and leaves the tile showing its second photo
   with no way to undo it, so phones and tablets never load this at all —
   there the tap opens the product page, which has the full gallery. ────── */
/* Taken out of flow unconditionally, NOT only where hovering is possible.
   Scoping this to the hover media query left the layer as a second in-flow
   image on touch devices — invisible, because of the opacity rule below, but
   still occupying its own 100%-height box inside the card and pushing the
   tile's layout. It has to be positioned everywhere it exists. */
.card-img-hover {
    position: absolute;
    inset: 0;
    opacity: 0;
    pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
    .card-img-hover {
        /* Matches the cover image's own zoom so the two photographs are never
           momentarily at different scales mid-cross-fade. */
        transition: opacity 0.45s ease, transform 1.2s cubic-bezier(0.22, 1, 0.36, 1);
    }
    .product-card:hover .card-img-hover { opacity: 1; }
    .product-card:hover .card-img-hover.is-ready { transform: scale(1.08); }

    /* Sold-out tiles are greyed and dimmed; the hover shot has to match or it
       arrives at full colour over a faded card. */
    .product-card.sold-out .card-img-hover { filter: grayscale(0.85); opacity: 0; }
    .product-card.sold-out:hover .card-img-hover { opacity: 0.6; }
}

/* Until the file has actually decoded there is nothing to show, so the layer
   stays transparent rather than flashing the card's background through. */
.card-img-hover:not(.is-ready) { opacity: 0 !important; }

@media (prefers-reduced-motion: reduce) {
    .card-img-hover { transition: none; }
    .product-card:hover .card-img-hover.is-ready { transform: none; }
}


/* ══ Gallery parallax: the tiles drift, the photographs are untouched ═════
   The columns move against each other by a few pixels. Nothing is scaled and
   nothing is cropped.

   This started as a parallax INSIDE each frame — the picture scaled to 1.06
   to create room, then slid within it. That crops 3% off every edge before
   it moves at all, and the movement then takes up to 5% off the top of the
   travelling column. On a full-length fashion photograph the top of the frame
   is the model's head, so the effect was quietly cutting into exactly the
   part of the picture that must not be touched — and worst on the first
   column, which was given the larger travel.

   Moving the tile instead costs nothing: transform does not affect layout, so
   the grid does not reflow, and the photograph inside is shown whole at its
   natural crop. Travel is kept to a few pixels, comfortably inside the 12px
   grid gap, so two tiles can never appear to touch.

   Desktop only. The gallery is a horizontal slider below 992px, where there
   is no sticky progress to drive this and a transform would fight the scroll.
   The original code returned early on narrow screens for the same reason. ── */
@media (min-width: 992px) {
    .dv-parallax-on .gallery-grid-item {
        transform: translate3d(0, var(--dv-par, 0px), 0);
        /* No transition — this tracks scroll position directly, and easing it
           would lag behind the page and read as jank rather than depth. */
    }
}

@media (prefers-reduced-motion: reduce) {
    .dv-parallax-on .gallery-grid-item { transform: none; }
}


/* "From" on a card whose sizes are priced individually — see
   productPriceRange(). Quieter than the figure it qualifies, because the
   number is what the shopper is scanning for. */
/* "From ₹300.00" should read as one price, not a grey label stuck to a maroon
   number. It was changing four things at once against the price beside it —
   28% smaller, 200 lighter, wide letter-spacing, and a neutral grey against the
   brand maroon — so the eye separated them and "FROM" looked like it belonged
   to something else.

   Now it changes two: a little smaller and a little lighter. The colour comes
   from the price itself at reduced opacity, so the pair always harmonises even
   if the price colour changes, and opacity is used rather than color-mix()
   because this shop serves plenty of older Android browsers.

   Uppercase is kept — it matches SELECT SIZE, SIZE GUIDE and ADD TO BAG, and
   dropping it here would make this the one lowercase label on the page. */
.price-from-label {
    font-size: 0.8em;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: inherit;
    opacity: 0.62;
    margin-right: 1px;
}

/* ── Checkout ──────────────────────────────────────────────────────────────
   These were inline style="" attributes repeated across pages/checkout.php —
   the same field-label declaration was written out seven times, so changing the
   look of a checkout label meant finding all seven. Grouped here by what the
   element IS, not by what it measures, so the names survive a restyle.

   NOT moved, deliberately: the ten elements whose style is written by the page's
   own JavaScript (passwordGroup, multiAddrNote, giftMessageWrap, razorpayPanel,
   billingCustomWrap, chkDiscountRow, pmCodLabel, chkCodFeeRow, checkoutLoginBox,
   checkoutCouponMsg). Those inline values are state, not styling. checkoutLoginBox
   is the clearest case — its handler READS `box.style.display === 'none'` to decide
   which way to toggle, so moving that value to a class would make the login box
   open when it should close, on the checkout page. */
/* The doubled class name is deliberate, and is why these three rules look odd.
   An inline style beats every selector; a single class does not. Moving these
   declarations out of style="" put them into a fight they had not been in before:
   `.form-luxury-group label` (0,1,1) outranks `.chk-field-label` (0,1,0), so the
   labels silently rendered at 12px/600 instead of 11px/700, and `.form-luxury-input`
   re-imposed its own padding on the inputs. Repeating the class raises specificity
   to (0,2,0) without `!important`, which would have started the same arms race one
   level up and made these impossible to override from a component later. */
.chk-field-label.chk-field-label {
    display: block;
    font-size: 11px;
    font-weight: 700;
    text-transform: uppercase;
    margin-bottom: 6px;
}
.chk-accent      { color: var(--color-primary); }
.chk-note        { font-size: 12px; color: var(--text-muted); }
.chk-note-block  { font-size: 12px; color: var(--text-muted); display: block; margin-top: 2px; }
.chk-subnote     { margin-top: 3px; }
.chk-block       { margin-bottom: 25px; }
.chk-line        { font-size: 14px; display: block; }
/* The padding here only applies above 640px, and that is not a mistake.
   responsive.css declares `.form-luxury-input { padding: 10px 12px !important }`
   under max-width:640px, and an author !important outranks an inline style — so
   on a phone these fields were already rendering at 10px 12px while the inline
   `padding: 10px` sat there doing nothing. Keeping the value means the two widths
   behave exactly as they did before this refactor; it is not an attempt to win
   against that !important. */
.chk-input-sm.chk-input-sm { width: 100%; padding: 10px; font-size: 13px; }
.chk-textarea.chk-textarea { width: 100%; padding: 12px; font-size: 13px; resize: none; }
.chk-flex-fill   { flex: 1; min-width: 0; }
.chk-row         { display: flex; align-items: center; gap: 12px; }
.chk-row-title   { margin: 0; font-size: 14px; font-weight: 600; }

/* ── Account ───────────────────────────────────────────────────────────────
   Lifted from repeated inline style="" attributes in pages/account.php.

   The doubled class names are for the same reason as the checkout block above:
   these labels sit inside .form-luxury-group, whose `label` rule is (0,1,1) and
   beats a single class, so a plain .acc-field-label would render at the wrong
   size and nothing would look obviously broken.

   NOT lifted: the seven `display: none` attributes on the account tab panels.
   toggleAccountTab() writes t.style.display directly, and the order filter at
   line 421 resets with `card.style.display = ''` — which falls back to the
   stylesheet. A class carrying display:none would make that reset reveal
   nothing, so searching orders would hide rows and never bring them back. */
.acc-field-label.acc-field-label {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    margin-bottom: 6px;
}
.acc-block   { margin-bottom: 15px; }
.acc-note    { color: var(--text-muted); font-size: 13px; }
.acc-hint    { font-size: 12px; color: var(--text-muted); margin-top: 5px; }
.acc-intro   { color: var(--text-secondary); font-size: 13px; margin-bottom: 20px; }
.acc-stack   { display: flex; flex-direction: column; gap: 12px; }
.acc-btn-sm.acc-btn-sm { padding: 10px 24px; font-size: 12px; }

/* ── Skip link ────────────────────────────────────────────────────────────
   Off-screen until focused, then pinned top-left over everything. Not
   display:none — a hidden element is removed from the tab order entirely,
   which would defeat the point of the link. */
.skip-to-content {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 100000;
    padding: 12px 20px;
    background: #511126;
    color: #fff;
    font-size: 14px;
    font-weight: 600;
    text-decoration: none;
}
.skip-to-content:focus {
    left: 0;
    outline: 3px solid #d4af37;
    outline-offset: 2px;
}

/* Focusing <main> via the skip link must not draw a ring around the whole page. */
main#mainContent:focus { outline: none; }

/* ── Shop pagination ──────────────────────────────────────────────────────
   Real anchors so the catalogue is crawlable; the Load More button remains
   the control a person actually uses. */
.shop-page-link {
    display: inline-block;
    min-width: 38px;
    padding: 8px 12px;
    text-align: center;
    border: 1px solid var(--border-light, #e5e0d8);
    color: var(--text-secondary, #6b5c47);
    text-decoration: none;
    font-size: 13px;
}
.shop-page-link:hover { border-color: var(--color-secondary, #511126); }
.shop-page-current {
    background: var(--color-secondary, #511126);
    border-color: var(--color-secondary, #511126);
    color: #fff;
    font-weight: 700;
}

/* ── Product page: fit help ────────────────────────────────────────────────
   Was a third outlined button beside Add to Bag and Buy Now, at equal weight.
   This is a question asked instead of buying, so it reads as a sentence and
   sits below the two actions that complete a sale.

   Styled as a button element rather than an <a> because it opens a modal and
   goes nowhere — a link that does not navigate is a lie to anyone using a
   screen reader or middle-clicking it. */
.product-fit-help {
    margin: 14px 0 0;
    font-size: 13px;
    color: var(--text-secondary);
}
.product-fit-help-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    /* 44px of target without 44px of visible chrome: the padding does the work
       while the row still reads as text beside the question. */
    min-height: 44px;
    padding: 0 2px;
    background: none;
    border: 0;
    font: inherit;
    color: var(--color-primary);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}
.product-fit-help-link:hover { opacity: 0.75; }
.product-fit-help-link i { font-size: 12px; }

/* ── Product details: tabs on desktop, stacked on a phone ──────────────────
   The tab row asked a shopper to click through About / Shirt / Palazzo to read
   three short spec lists — on a screen they are already scrolling, where each
   list is a handful of lines. Tapping to reveal a paragraph is a cost with no
   saving: the content is short, so hiding it only hides it.

   Desktop keeps the tabs, where the panels sit beside a pinned gallery and
   horizontal space is worth rationing.

   `!important` is deliberate and narrow. switchProductDetailsTab() writes
   style="display:none" directly onto the inactive panels, and an inline style
   beats any normal rule — so a phone would otherwise show only whichever panel
   the JS last left visible. This is the case where overriding it is correct: the
   layout mode has changed, not the tab state. */
.product-details-panel-title { display: none; }

@media (max-width: 991px) {
    .product-details-tabs { display: none; }

    .product-details-tab-panel {
        display: block !important;
        padding-top: 4px;
    }
    /* A rule between sections, so three stacked spec lists do not read as one. */
    .product-details-tab-panel + .product-details-tab-panel {
        margin-top: 22px;
        padding-top: 22px;
        border-top: 1px solid var(--border-light);
    }
    .product-details-panel-title {
        display: block;
        margin: 0 0 12px;
        font-family: var(--font-heading);
        font-size: 15px;
        font-weight: 600;
        color: var(--text-primary);
    }
}

/* ── Product page: Design & Fit as a static section ────────────────────────
   Matches the closed accordion headers around it, so the stack still reads as
   one list — the only difference a shopper should notice is that this one is
   already open and has no +/- to press.

   No cursor:pointer and no hover colour: it is a heading, not a control, and
   suggesting otherwise invites a click that does nothing. */
.product-detail-static {
    border-bottom: 1px solid var(--border-light);
    padding-bottom: 4px;
}
/* Typography deliberately NOT restated here. It is declared once, on the
   .product-accordion-header selector this shares, so the heading beside five
   accordion headers cannot drift from them again — it already had, at 15px
   against 14px. This block sat LAST in the file and therefore won, which is
   how a rule that looked corrected upstream kept rendering the old size. */
.product-detail-static-content { padding-bottom: 18px; }

@media (max-width: 991px) {
    .product-detail-static-title { padding: 15px 0; font-size: 14px; }
}

/* ── Homepage: let the categories reach the fold ───────────────────────────
   Measured at 1440×900: the hero ends at 762px and the categories section
   begins at 821 — but its heading sat at 955, fifty-five pixels below the fold.
   So a shopper's first screen was hero and nothing else, and the only signal
   that a catalogue existed was to scroll on faith.

   80px is right BETWEEN two content sections, where it separates two things of
   equal weight. Under a full-bleed hero it is doing a job the hero's own edge
   already does, and it costs the one thing worth having above the fold: a
   visible way in.

   Scoped with the adjacent sibling selector so only the section directly after
   the hero is tightened — every other section keeps its 80px rhythm. */
/* The hero is full-bleed and carries no padding of its own, so the section
   below it would otherwise open on 40px where every other pair gets 80. It
   pays both halves instead — the hero stays flush to the header, and the gap
   underneath matches every other gap on the page. */
/* .hero-slider-section + .section-space { padding-top: 80px !important; } */

/* Same 80px on every screen — one spacing, no exceptions. */

/* The occasion banners are the one content section without .section-space, so
   they sat on 0 and their two neighbours met them at 40px while every other
   pair got 80. Same 40px as everything else, so the rhythm holds straight
   through them. */
/* .occasion-banners-section padding is set alongside .section-space in
   responsive.css, so the two cannot drift apart. */




/* ── Section rhythm: stop the 80px being paid twice ────────────────────────
   .section-space is `padding: 80px 0`, so where two of them sit next to each
   other the first's bottom and the second's top BOTH apply — 160px of gap for
   a rule that reads as 80. Measured across the homepage: every adjacent pair
   showed 80 + 80.

   The doubling went unnoticed while sections were tall; once the collections
   grid became a single row the gap was larger than the content above it.

   Zeroing the top of a FOLLOWING section leaves exactly the 80px the value was
   always meant to mean. A section that opens a page keeps its own top padding,
   because it has nothing above it to pay for the space. */
/* The neighbour-collapse rule was removed on request. Every .section-space now
   pays for its own 40px top AND bottom, so the gap between any two of them is a
   plain 80px, the same everywhere, with nothing to work out in your head. The
   two rules below exist only so the sections that DON'T carry .section-space
   cannot break that rhythm. */

/* ── Collection tile type, sized for the tile it sits in ───────────────────
   The heading was 26px with 30px of padding either side. That was set when the
   grid was 3 across and a tile was ~420px wide; at 5 across a tile is 245px, so
   the type had 185px to work in and "3 Piece Suits" was close to wrapping onto
   two lines over the model's face.

   Scaled to the container rather than guessed: 18px leaves the longest name in
   this catalogue on one line with room to spare, and the padding comes in with
   it so the text block stays proportionate to the photograph instead of
   crowding its lower edge. */
.category-card-overlay { padding: 20px !important; }
.category-card-overlay h3 {
    font-size: 18px;
    letter-spacing: 0.04em;
    line-height: 1.25;
    margin-bottom: 3px;
}
.category-card-link { font-size: 10px; letter-spacing: 0.12em; }

@media (max-width: 1100px) {
    .category-card-overlay h3 { font-size: 19px; }
}
@media (max-width: 820px) {
    .category-card-overlay { padding: 16px !important; }
    .category-card-overlay h3 { font-size: 16px; }
    .category-card-link { font-size: 9.5px; }
}

/* ── Collections: a peeking rail on a phone, the grid everywhere else ──────
   Two rows of two-and-a-bit instead of three full rows. The partial tile at the
   right edge is the whole point — it is what tells a thumb the row moves. A row
   that ends flush looks finished, so nobody swipes it.

   Deliberately NOT owlCarousel, though it is already loaded:
     - Owl clones slides to loop, and the clones keep their headings and links.
       That is exactly what put duplicate <h2>s and untabbable duplicate "Shop
       Now" buttons on this homepage's hero, fixed earlier tonight.
     - This needs no JS at all, so there is nothing to initialise, nothing to
       re-measure on rotate, and it cannot break if a script fails to load.
     - iOS gives momentum scrolling free; a JS carousel has to imitate it.

   The percentages mirror .occasion-slider, which already does this further down
   the same page — so the two rails behave identically rather than each being
   tuned by hand to a different feel. */
/* ── The mosaic on smaller screens ─────────────────────────────────────────
   This was a swipeable rail below 768px, with roughly two and a half tiles in
   view and the rest off-screen. It is a mosaic at every width now: two columns
   on a tablet or a phone, one below 380px, and every collection on screen with
   nothing to scroll sideways.

   The eighteen-column arrangement cannot survive the narrowing — a four-column
   tile is 60px at phone width — so the composition is replaced rather than
   squeezed.

   ABOUT THE SELECTORS. Every span above is set by either :nth-child() or a
   [data-count] rule, which are specificity (0,2,1) and (0,3,0). A plain
   `.home-categories-grid > .category-card` reset is (0,2,0) and loses to both:
   written that way, tiles five onward kept span 6 here and the browser invented
   implicit columns to hold them, collapsing the feature to 18px wide. Matching
   [data-count] AND :nth-child(n) puts this at (0,3,1), above everything it has
   to undo, without reaching for !important. data-count is always written, so
   the attribute is always there to match. */
@media (max-width: 1024px) {
    .home-categories-grid {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        grid-auto-rows: auto;
        gap: 18px;
    }
    .home-categories-grid[data-count] > .category-card:nth-child(n) {
        grid-column: span 1;
        grid-row: auto;
        aspect-ratio: 3 / 4;
        height: auto;
    }
    /* The feature keeps its prominence by taking the full width in a landscape
       crop, which is the shape a wide short tile wants. */
    .home-categories-grid[data-count] > .category-card.is-feature:nth-child(n) {
        grid-column: span 2;
        aspect-ratio: 16 / 10;
    }
    /* Two collections are a pair, so neither leads — a full-width tile above a
       half-width one would read as one collection mattering twice as much. */
    .home-categories-grid[data-count="2"] > .category-card.is-feature:nth-child(n) {
        grid-column: span 1;
        aspect-ratio: 3 / 4;
    }
}

/* ── The phone: a swipeable rail, as it was before the mosaic ──────────────
   Restored on request. Below 768px the tiles stop being a grid and become a
   horizontal rail with about two and a half in view, which is what this section
   did until the mosaic replaced it.

   Worth being honest about the trade: collections past the second sit
   off-screen until someone swipes, and some people never do. The arrows below
   exist because of that, and because the scrollbar is hidden — without them a
   rail is reachable by touch and by nothing else.

   Two lines here are load-bearing rather than cosmetic, both learned the hard
   way and both kept exactly as they were:

     overflow-y: hidden — the spec says that when one axis is auto and the other
     is visible, the visible one computes to auto too. overflow-x alone quietly
     made this a VERTICAL scroll container with nothing to scroll, and it then
     swallowed every up-and-down swipe that began on a tile: the page stopped
     moving because the finger was panning a box with no spare height.

     touch-action: pan-x pan-y — a horizontal scroller can claim a vertical drag
     that starts on it. Naming both axes hands the vertical gesture back to the
     document while the horizontal one still moves the rail. */
@media (max-width: 768px) {
    .home-categories-grid {
        display: flex;
        grid-template-columns: none;
        justify-content: flex-start;
        flex-wrap: nowrap;
        gap: 12px;
        overflow-x: auto;
        overflow-y: hidden;
        scroll-snap-type: x proximity;
        scroll-padding-left: 2px;
        padding-bottom: 8px;
        -webkit-overflow-scrolling: touch;
        touch-action: pan-x pan-y;
        overscroll-behavior-x: contain;
    }
    .home-categories-grid::-webkit-scrollbar { display: none; }

    /* The mosaic's spans and its 16/10 feature are set at (0,3,1) and (0,4,1),
       so a plain rule here loses to them. In a flex container grid-column is
       inert, but aspect-ratio is not — left alone the first tile stayed a wide
       landscape crop in a rail of portraits. Matched at the same specificity so
       every tile in the rail is one shape, which is what it was before. */
    /* Two whole tiles and a third of the next, at every phone width.
       ──────────────────────────────────────────────────────────────────────
       This was a flat 42%, dropping to 56% below 420px. A percentage cannot
       hold a composition across widths: 42% of a 430px screen is 2.3 tiles in
       view, and 56% of a 390px screen is 1.74 — so the rail showed two and a
       peek on a large phone and less than two on a small one, which is the
       arrangement changing under you as the screen shrinks.

       Solved arithmetically instead. For two whole tiles, the two gaps between
       and after them, and 0.3 of a third:

           2t + 2g + 0.3t = C   →   t = (C - 2g) / 2.3

       That is the calc below, so the peek is 30% of a tile whether the screen
       is 320 or 430 and the layout never reorganises itself.

       The gap has to be OURS for the arithmetic to hold. responsive.css sets
       it three separate times across these widths — 20px, 12px and 10px — and
       it loads after this file, so a formula assuming 12 would be wrong at two
       of those. Declaring the variable and the gap together on [data-count]
       puts both at (0,2,0), above responsive.css's (0,1,0), so the number in
       the calc is the number on screen. */
    /* Full bleed, the same as every other rail — see .dv-rail-bleed. This one
       cannot leave .container in the markup, because above 768px the same
       element is the mosaic and has to stay aligned with the heading. Cancelling
       the container's padding does it exactly, with no viewport units: the
       container is max-width 1440 and every screen this rail appears on is well
       under that, so its border box IS the screen. */
    .home-categories-grid[data-count] {
        --rail-gap: 12px;
        gap: var(--rail-gap);
        margin-inline: calc(-1 * var(--dv-gutter));
        padding-inline: 16px;
        scroll-padding-inline-start: 16px;
    }
    .home-categories-grid[data-count] > .category-card:nth-child(n),
    .home-categories-grid[data-count] > .category-card.is-feature:nth-child(n) {
        flex: 0 0 calc((100% - 2 * var(--rail-gap)) / 2.3);
        scroll-snap-align: start;
        aspect-ratio: 3 / 4;
        height: auto;
    }

    /* Fitting two whole tiles instead of one and three quarters made each tile
       about 55px narrower, and "View Collection →" stopped fitting on one line
       — it broke after "VIEW", leaving a two-line caption over the model. Less
       tracking and a size down puts it back on one line inside the narrowest
       tile this rail produces (120px at a 320px screen). nowrap so a longer
       label in future fails visibly here rather than silently wrapping. */
    .home-categories-grid > .category-card .category-card-link {
        font-size: 8.5px;
        letter-spacing: 0.06em;
        white-space: nowrap;
    }
}

/* A tighter gap on the smallest screens, matching what responsive.css already
   wanted there — set through the variable so the tile width follows it and the
   peek stays exactly 30%. */
@media (max-width: 380px) {
    .home-categories-grid[data-count] { --rail-gap: 10px; }
}

/* The touch-action and overscroll-behavior-x guards were here. They existed
   because the collections were a horizontal scroll container that could swallow
   a vertical drag beginning on a tile — a thumb on a tile wobbled the rail
   instead of scrolling the page. The mosaic scrolls in neither direction, so
   there is no gesture to hand back. */

/* The Owl rails: the page owns up-and-down, the rail owns side-to-side.
   ────────────────────────────────────────────────────────────────────────────
   Owl's own stylesheet puts touch-action: pan-y on .owl-item only. Everything
   around the items — the rail itself, the stage and the stage wrapper — was
   left on `auto`, which means "browser, take every gesture". So a thumb that
   landed in the gap between two tiles, or on the strip's padding, gave the
   browser BOTH axes: it scrolled the page vertically while Owl was also
   dragging the rail sideways. That is the screen sliding up and down while you
   are trying to swipe across.

   pan-y on the whole rail says it once: a vertical swipe is the page's, and a
   horizontal one belongs to the slider and must not move the page at all.

   Owl's own sheet sets touch-action: manipulation on .owl-stage, which permits
   both axes again. The selectors below carry .owl-carousel as well as
   .js-owl-rail — both classes are on the same element once Owl has started — so
   they are simply more specific and win. That matters here: header.php loads
   owl.carousel.min.css AFTER style.css, so on equal specificity Owl would take
   the tie every time.

   Written against .owl-carousel.owl-drag rather than .js-owl-rail so it covers
   EVERY draggable Owl slider — including the hero at the top of the homepage,
   which is an Owl carousel that never carried the rail class and was sitting on
   touch-action: auto, the worst of the lot. */
.owl-carousel.owl-drag,
.owl-carousel.owl-drag .owl-stage-outer,
.owl-carousel.owl-drag .owl-stage { touch-action: pan-y; }

/* Drag-to-slide, on a rail built out of links and photographs.
   ────────────────────────────────────────────────────────────────────────────
   Every occasion tile is an <a> wrapped around an <img>, and browsers treat
   both as draggable objects. Pressing one and pulling started a native
   drag-and-drop — the translucent ghost of the picture following the cursor —
   which swallows the mouse events Owl needs, so the rail never moved. Owl was
   loaded and mouseDrag was on the whole time; it simply never saw the drag.

   Turning off the browser's own dragging inside a rail hands those events back.
   Links still click; only the drag-the-object behaviour goes. */
.owl-carousel a,
.owl-carousel img {
    -webkit-user-drag: none;
    user-drag: none;
}

/* Say that it can be dragged.
   ────────────────────────────────────────────────────────────────────────────
   Every tile is a link, so the cursor was a pointing hand — which means CLICK,
   and nothing on screen suggested the strip could be pulled. Drag worked and
   still read as broken, because a person tries what the cursor invites.

   An open hand over a draggable rail, closing while it is held, is the
   convention people already know from maps and photo galleries. Scoped to
   .owl-drag so it only appears on rails Owl has actually made draggable — never
   on a strip that cannot move.

   Restored after the redesign: the three selectors were deleted and this comment
   left behind it, so the rails read as un-draggable again while still dragging
   perfectly well. The .photo-fan section has its own grab cursor and is not
   touched by these. */
.owl-carousel.owl-drag { cursor: grab; }
.owl-carousel.owl-drag a,
.owl-carousel.owl-drag img { cursor: grab; }
.owl-carousel.owl-drag:active,
.owl-carousel.owl-drag:active a,
.owl-carousel.owl-drag:active img { cursor: grabbing; }

/* The collections rail's navigation is the shared .dv-pager now. Its old pair
   of outlined squares lived only inside @media (max-width: 768px), which is
   half of why this page had five navigations: the control existed on a phone
   and nowhere else, while New Arrivals' existed on a desktop and nowhere else.
   The shared pager decides by measuring the rail instead, so the same control
   appears at whatever width the rail can actually move — and disappears above
   768 where this element is a mosaic grid, without a breakpoint saying so. */

/* ── One arrow, everywhere ─────────────────────────────────────────────────
   These six controls all do the same job — move a rail, close an overlay — and
   had grown three shapes and five sizes between them: square at 44/50px, round
   at 34/36/46px. Nothing chose that; each was styled when it was written.

   Square wins because it is the majority and it matches the buttons, inputs and
   size pills already on these pages — a circle among them reads as borrowed from
   another design. The 2px radius is the same one .category-card-media uses.

   44px is a floor, not a preference: .gallery-nav at 36 and .btn-close-cart at
   34 were under the size a finger reliably hits, and the gallery arrow is how a
   shopper looks at the garment they are deciding to buy.

   Colour is deliberately NOT unified. .product-lightbox-nav sits over a
   photograph and needs its scrim to stay legible; these sit on the page ground
   where that scrim would be a dark box on cream. Same shape, same size,
   ground-appropriate contrast. */
/* .gallery-nav and .product-lightbox-nav are NOT in this list.
   They are absolutely positioned inside the gallery, and resizing them from
   36px round to 44px square moved them off the marks their own rules place
   them on — the arrows ended up floating between the photograph and the
   video rather than sitting on the image edge. Consistency is not worth
   breaking placement for; those two keep their own shape. */
/* Two members have left. .cat-rail-arrow went when Collections moved to the
   shared .dv-pager, and .hero-arrow went when the hero did. Their rules are
   deleted, so they are dropped from this list rather than left as selectors
   matching nothing. */
.btn-close-cart,
.btn-carousel-arrow {
    min-width: 44px;
    min-height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── Product details: one size through the whole stack ─────────────────────
   The heading rendered at 13px on a phone while the spec text under it was 15px
   — the body larger than the title introducing it, which inverts the hierarchy
   and is why the block read as unsettled rather than merely mixed.

   Everything in the stack now sits at 13px: the heading, the label ("Colour:")
   and its value. The label keeps weight 600 to stay scannable, so the pair still
   separates without a size change doing the work. */
.product-accordion-content .specifications-grid,
.product-detail-static-content .specifications-grid,
.product-accordion-content .specifications-grid div,
.product-detail-static-content .specifications-grid div {
    font-size: 13px;
    line-height: 1.65;
}
.product-accordion-content .specifications-grid strong,
.product-detail-static-content .specifications-grid strong {
    font-size: 13px;
    font-weight: 600;
}

/* ── The sticky buy bar, trimmed ───────────────────────────────────────────
   69px tall with a 247px button — the button was flexing to eat the row, so the
   bar read as a block of colour across the bottom of the phone rather than a
   control sitting under the page.

   The height floor stays 44px: this is the button that takes the money, and it
   is the one place on the shop where a mis-tap costs a sale. Narrower and
   shorter, not smaller than a thumb. */
/* space-between pushes the price to one edge and the button to the other, so
   the bar reads as two things rather than a pair huddled in the middle. */
.pdp-buy-bar { padding: 8px 14px; display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.pdp-buy-bar-btn {
    flex: 0 1 auto;
    min-height: 44px;
    padding: 11px 22px !important;
    font-size: 12px;
}
.pdp-buy-bar-price { font-size: 14px; }

/* ── Product page on a phone: close the gaps between decisions ─────────────
   Measured at 375px: the page ran 6.5 screens, and the blocks a shopper moves
   through — title, colour, size, quantity — each carried 20-24px above AND
   below. Adjacent blocks therefore paid the gap twice, exactly like the 80px
   section padding on the homepage: a 20px rhythm rendering as 44.

   These are steps in one decision, not separate sections. Tightening them puts
   the size picker and Add to Bag nearer the photograph that sold them, which is
   the whole job of this page on a small screen.

   The title keeps more room than the rest — it is the one thing that should
   breathe, and it is what a shopper reads first. */
@media (max-width: 991px) {
    .product-title-heading   { margin-bottom: 10px; }
    .product-price-heading   { margin-bottom: 14px; }
    .product-description-text{ margin-bottom: 14px; }

    .product-colors-wrap   { margin-top: 14px; margin-bottom: 14px; }
    .product-variants-wrap { margin-top: 14px; margin-bottom: 14px; }
    .product-quantity-row  { margin-top: 14px; }
    .product-action-buttons-group { margin: 18px 0; }
    .product-fit-help { margin-top: 8px; }

    /* The accordion rows were 53px each for a 13px line of text — mostly padding
       on a stack of six. */
    .product-accordion-header,
    .product-detail-static-title { padding: 14px 0; }
    .product-detail-static-content { padding-bottom: 14px; }
}


/* A size that costs something different says so on its own pill. Small and
   under the size code, so the ladder still scans as sizes first — the price is
   the exception, not the label. */
.size-pill-has-price { flex-direction: column; gap: 2px; line-height: 1.15; }
.size-pill-price {
    display: block;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--color-primary);
    opacity: 0.85;
}
.size-pill-btn.size-pill-selected .size-pill-price { color: inherit; opacity: 0.9; }

/* ── Image viewer: same controls as the rest of the shop ───────────────────
   Three things were out of step with every other control on this site.

   1. The close button's hover was color: var(--color-primary) — #511126, a
      near-black burgundy, on a black overlay. Hovering it made it HARDER to
      see, on the one control a shopper needs to leave a full-screen view. It
      now brightens and gains a ground, the way the nav arrows do.

   2. The motion was scale(1.08) and scale(1.15). Nothing else on this shop
      scales on hover — the arrows, the carousel controls and the buttons all
      change ground and keep their geometry. Two motion languages in one
      product page reads as two designs.

   3. Timing was 0.2s against the site's --transition of 0.3s.

   Shape stays round here, unlike the square arrows elsewhere: these sit on a
   photograph in a dark overlay rather than on the page, and the circle is what
   separates them from the image behind. Consistency of BEHAVIOUR matters more
   than consistency of outline when the ground is different. */
.product-lightbox-nav {
    transition: var(--transition);
}
.product-lightbox-nav:hover,
.product-lightbox-nav:focus-visible {
    background: rgba(255, 255, 255, 0.28);
    border-color: rgba(255, 255, 255, 0.45);
    transform: translateY(-50%);   /* no scale — matches every other arrow */
}

.product-lightbox-close {
    width: 44px;
    height: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
    line-height: 1;
    background: rgba(255, 255, 255, 0.1);
    border: 1px solid rgba(255, 255, 255, 0.15);
    transition: var(--transition);
}
.product-lightbox-close:hover,
.product-lightbox-close:focus-visible {
    background: rgba(255, 255, 255, 0.28);
    border-color: rgba(255, 255, 255, 0.45);
    color: #fff;                   /* stays legible; was burgundy on black */
    transform: none;
}

@media (prefers-reduced-motion: reduce) {
    .product-lightbox-nav,
    .product-lightbox-close { transition: none; }
}

/* ── Product title on a phone ──────────────────────────────────────────────
   40px with a 48px line-height, unchanged from desktop. Measured at 375px:
   "QA PRODUCT D - colour variants" ran to three lines and 144px — a fifth of
   the visible screen spent on the title, pushing the price, the colours and
   the size picker down behind it.

   26px keeps it clearly the largest thing on the page (the price is 20px) while
   fitting most garment names in two lines. text-wrap: balance splits those two
   lines evenly instead of leaving one word stranded on the second — supported
   where it is understood, ignored harmlessly elsewhere. */
@media (max-width: 991px) {
    .product-title-heading {
        font-size: 26px;
        line-height: 1.25;
        text-wrap: balance;
    }
}
@media (max-width: 480px) {
    .product-title-heading { font-size: 23px; }
}

/* The Read more toggle belongs to the description above it, not to the colour
   block below. It carried 20px beneath a 44px tap target, so on a product long
   enough to clamp — the ones that actually show it — description and colour ended
   up 86px apart while every other pair on this column sits at 14.
   The 44px target is untouched; only the space after it closes. */
@media (max-width: 991px) {
    .desc-read-more { margin-bottom: 4px; }
    .product-description-text.is-clamped { margin-bottom: 4px; }
}

/* The [hidden] guard for .cat-rail-nav was here: a media query set display:flex
   on it, and an author display beats the browser's [hidden] rule. Both the rail
   and its arrows are gone, so there is nothing left to guard. */

/* ══ Colour swatch — sold out ═══════════════════════════════════════════════
   A colour whose every size is gone looked identical to one in stock, so the
   only way to find out was to click it and read the struck-through size pills
   underneath. The swatch now says it before the click.

   Three signals, because one is not enough on a photograph: the picture is
   drained and washed back, a diagonal rule crosses it corner to corner, and
   the colour name is struck through the same way the size pills are. The
   button stays clickable — a shopper may still want to look at the colour.

   `to top right` is the corner-to-corner keyword: CSS angles the gradient so
   the 50% band passes exactly through the opposite corners, so the rule meets
   the corners on the 66×88 ring rather than drifting off at 45°.
   ─────────────────────────────────────────────────────────────────────────── */
.color-swatch-btn.color-swatch-soldout .color-swatch-ring { position: relative; }
.color-swatch-btn.color-swatch-soldout .color-swatch-ring img,
.color-swatch-btn.color-swatch-soldout .color-swatch-fallback-emoji { filter: grayscale(1); opacity: 0.5; }
.color-swatch-btn.color-swatch-soldout .color-swatch-ring::after {
    content: '';
    position: absolute; inset: 0; pointer-events: none;
    background:
        linear-gradient(to top right,
            transparent calc(50% - 1px),
            var(--text-primary) calc(50% - 1px),
            var(--text-primary) calc(50% + 1px),
            transparent calc(50% + 1px)),
        rgba(255, 255, 255, 0.45);
}
.color-swatch-btn.color-swatch-soldout .color-swatch-name { text-decoration: line-through; color: var(--text-muted); }

/* ══ Quick View — chips grouped under their colour ══════════════════════════
   Quick View lists one chip per size PER COLOUR, so a six-colour garment came
   out as 24 chips in a single wrapped row captioned "30/XS · Navy Blue". When a
   whole colour had gone the only sign was that four chips somewhere in that run
   were struck through, and the shopper had to find and read them all.

   The chips now sit under the colour they belong to, and a colour with nothing
   left says so once, at the heading — the same call the swatch makes on the
   full product page. Only the name is struck: the words "Out of stock" stay
   upright, since that is the last label you want hard to read.
   ─────────────────────────────────────────────────────────────────────────── */
.qv-colour-head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px;
                  font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
                  color: var(--text-secondary); }
.qv-colour-head.is-out .qv-colour-name { text-decoration: line-through; color: var(--text-muted); }
.qv-colour-gone { font-weight: 600; letter-spacing: 0.04em; color: var(--color-primary); opacity: 0.75; }
.qv-colour-group .qv-size-row { margin-bottom: 14px; }
.qv-colour-group:last-child .qv-size-row { margin-bottom: 16px; }

/* ── Sticky bar in price-only mode ──────────────────────────────────────────
   Shown from the moment the price heading leaves the top of the screen, so a
   figure is on screen the whole time the shopper is picking a colour or size —
   the point at which that figure is changing. Measured before this existed: on
   a 375×812 screen the price was gone by scrollY 780 while the size pills were
   still in view, and the bar did not appear until Add to Bag had also gone, so
   checking a new price meant scrolling up and back down for every change.

   The button is dropped while the real Add to Bag is still on screen. Two
   identical buy buttons visible at once is why this bar was tied to the buttons
   in the first place; hiding just the button keeps that fixed while letting the
   price come early. Declared after the second .pdp-buy-bar block near line 8823
   — that one wins, and these have to beat it. */
.pdp-buy-bar.is-price-only { justify-content: flex-start; }
.pdp-buy-bar.is-price-only .pdp-buy-bar-btn { display: none; }


/* ── "Details" on the photograph, and the sheet it opens ────────────────────
   Every fact about the garment was already on this page, but only past the
   colour picker, the size ladder, the quantity stepper and both buy buttons —
   so a shopper still deciding whether they wanted it had to scroll through
   everything meant for someone who already had.

   The button sits bottom-right of the gallery: the counter holds the bottom
   edge and the nav arrows hold the middle, so this is the corner that is free.
   Labelled rather than icon-only — a glyph alone cannot say whether it opens
   sizing, fabric or care. 44px minimum target. */
.gallery-details-btn {
    position: absolute;
    right: 12px;
    bottom: 12px;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* 44x44 and no smaller. The label is gone, but the finger is the same size —
       shrinking the tile to match the glyph is how an icon button becomes one
       you have to aim at. All of it is the target; the icon just sits in it. */
    width: 44px;
    height: 44px;
    padding: 0;
    border: 1px solid var(--border-light);
    background: rgba(255, 255, 255, 0.94);
    backdrop-filter: blur(6px);
    color: var(--text-primary);
    cursor: pointer;
    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.10);
    transition: background 0.2s, border-color 0.2s, transform 0.2s;
}
.gallery-details-btn:hover { background: #fff; border-color: var(--color-primary); transform: translateY(-1px); }
.gallery-details-btn i { font-size: 17px; color: var(--color-primary); }

/* Bottom sheet on a phone, centred panel from 768px up — a sheet pinned to the
   bottom edge of a wide screen is a long way from where the eye is. */
/* The `hidden` attribute is only a default of display:none, and ANY author
   display rule beats it — the rule below is exactly such a rule. Without this
   line the sheet stayed in the layout permanently: a fixed, full-viewport,
   z-index 10000 box at opacity 0 with pointer-events auto, sitting over every
   product page and swallowing every click and scroll. It looked like the page
   had frozen at random.
   Third time this trap has been sprung in this stylesheet — .desc-read-more and
   .cat-rail-nav both needed the same guard. Any new element that carries
   `hidden` AND a display rule needs one too. */
.pd-sheet[hidden] { display: none; }
.pd-sheet {
    position: fixed;
    inset: 0;
    z-index: 10000;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    background: rgba(0, 0, 0, 0.45);
    opacity: 0;
    transition: opacity 0.3s ease;
}
.pd-sheet.is-open { opacity: 1; }
.pd-sheet-panel {
    width: 100%;
    max-height: 82vh;
    overflow-y: auto;
    /* The sheet scrolls, so it must not hand the scroll back to the page
       underneath when it reaches its end. */
    overscroll-behavior: contain;
    padding: 20px 20px calc(20px + env(safe-area-inset-bottom));
    background: var(--bg-surface, #fff);
    transform: translateY(100%);
    transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}
.pd-sheet.is-open .pd-sheet-panel { transform: translateY(0); }
.pd-sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 16px;
}
.pd-sheet-title {
    margin: 0;
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--text-primary);
}
.pd-sheet-close {
    width: 44px;
    height: 44px;
    border: 0;
    background: var(--bg-surface-soft);
    color: var(--text-primary);
    font-size: 26px;
    line-height: 1;
    cursor: pointer;
    transition: background 0.2s, color 0.2s;
}
.pd-sheet-close:hover { background: var(--color-primary); color: #fff; }
.pd-sheet-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
    margin: 0 0 18px;
    padding: 0;
    list-style: none;
}
/* One column, not Aza's two: their grid crushes "All-over printed motif,
   Embroidered bodice, Includes matching dupatta" into half a phone width and
   runs it four lines deep beside a one-line neighbour. */
.pd-sheet-rows { margin: 0; }
.pd-sheet-row {
    display: grid;
    grid-template-columns: 116px 1fr;
    gap: 12px;
    padding: 11px 0;
    border-top: 1px solid var(--border-light);
}
.pd-sheet-row dt {
    margin: 0;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.pd-sheet-row dd { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--text-primary); }
.pd-sheet-empty { margin: 0; font-size: 13.5px; color: var(--text-secondary); }

@media (min-width: 768px) {
    .pd-sheet { align-items: center; }
    .pd-sheet-panel {
        max-width: 520px;
        max-height: 76vh;
        padding: 26px;
        transform: translateY(18px) scale(0.98);
    }
    .pd-sheet.is-open .pd-sheet-panel { transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
    .pd-sheet, .pd-sheet-panel { transition: none; }
}

/* The gallery wrap is only position:relative inside a max-width:768px block, so
   above that the Details button — absolutely positioned — would escape to
   whatever ancestor happened to be positioned and land somewhere unrelated.
   Stated unconditionally here: it establishes a containing block and changes no
   layout on its own.

   The button itself is for narrow screens. On a desktop the details column sits
   beside the photograph rather than a scroll below it, so the button would be
   solving a problem that width already solved. 991px matches the breakpoint the
   sticky buy bar uses, so the two agree on what "a phone" means. */
.product-gallery-slider-wrap { position: relative; }
@media (min-width: 992px) {
    .gallery-details-btn { display: none; }
}

/* ── Details sheet: the pieces, and each piece's own measurements ───────────
   Chips and groups both come from product_components / the specifications
   attached to them — the same rows the Product Details accordion on the page
   reads, so the sheet and the page can never name different pieces for one
   garment.

   Grouped under the piece's name rather than merged into one list: a suit's top
   and its trousers both have a Length, and flattening them gives two rows
   called "Length" with nothing to say which garment either belongs to. */
.pd-sheet-chip {
    padding: 6px 13px;
    border: 1px solid var(--border-light);
    background: var(--bg-surface-soft);
    font-size: 12.5px;
    font-weight: 600;
    color: var(--text-primary);
    white-space: nowrap;
}
.pd-sheet-group + .pd-sheet-group { margin-top: 20px; }
.pd-sheet-group-title {
    margin: 0 0 2px;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--color-primary);
}
/* The title already separates the group, so its first row needs no rule too. */
.pd-sheet-group-title + .pd-sheet-rows .pd-sheet-row:first-child { border-top: 0; }

/* Quick View: the main photograph fades between shots instead of snapping.
   .is-swapping is added once the NEW file has downloaded and decoded, so the
   fade covers a change that is already ready — there is never a blank frame or
   a half-drawn picture, which is what setting .src directly produced.
   The 160ms in dievon-quickview.js is this duration minus a frame, so the src
   changes at the bottom of the fade rather than on the way back up. */
.qv-img { transition: opacity 0.18s ease; }
.qv-img.is-swapping { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
    .qv-img { transition: none; }
}


/* ══ Checkout — a field-level error ═══════════════════════════════════════════
   The hidden attribute is the state; the [hidden] rule beside it is what makes
   the attribute WORK. Any author display rule outrules `hidden` on its own, and
   this project has been caught by exactly that four times — .desc-read-more,
   .cat-rail-nav, .pd-sheet. Declaring both together is the only arrangement
   that cannot come apart. */
.chk-field-error {
    display: block;
    margin: 6px 0 0;
    font-size: 12.5px;
    line-height: 1.5;
    color: #991b1b;                 /* 6.8:1 on the form's background */
    font-weight: 600;
}
.chk-field-error[hidden] { display: none; }

/* The box itself, so the eye lands on the field and not only the sentence.
   Colour is not the only signal — the message says what is wrong in words. */
/* Written as .form-luxury-input.chk-input-error, not .chk-input-error alone.
   .form-luxury-input sets `border: 1px solid var(--border-strong)` — a shorthand
   containing a var(), which the browser resolves late and which kept winning
   over a plain !important border-color. Matching both classes settles it on
   specificity, where the outcome is readable rather than a matter of when each
   value happens to be substituted. */
.form-luxury-input.chk-input-error,
input.chk-input-error {
    border: 1px solid #991b1b !important;
    background: #fef2f2;
}
.form-luxury-input.chk-input-error:focus,
input.chk-input-error:focus {
    border: 1px solid #991b1b !important;
    box-shadow: 0 0 0 3px rgba(153, 27, 27, .12);
}

/* ═══════════════════════════════════════════════════════════════════════════
   HEADER — 2026 REDESIGN
   ───────────────────────────────────────────────────────────────────────────
   A restyle of the header that was already here, not a replacement for it.
   Every element, id, handler and link is the one includes/header.php always
   printed; what changed is spacing, type, colour and how the row behaves when
   there are more collections than fit.

   Appended at the end on purpose. The header's original rules are spread over
   three places in this file — a block near the top that sets the bar's height
   with !important, a second .navbar-luxury further down that repaints the
   background, and the main run around line 3070 — and several of them are
   !important, so a rule placed anywhere earlier would lose. Last wins at equal
   specificity, which is why this sits here rather than beside what it edits.

   The four things the brief ruled out, and where each is enforced:
     · no gradients      — flat --dv-header-bg throughout
     · no heavy shadows  — the header carries one hairline divider and casts
                           nothing; the mega menu is not touched by this block
                           at all and keeps the shadow it has on live
     · no rounded shell  — no radius on any header container
     · no wrapped nav    — the row is one line, and overflow goes to More
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
    /* Warm ivory rather than pure white: against #fdfbf9 page stock a #ffffff
       header reads as a cold panel laid on top of the site. Two points warmer
       than the body and the join stops announcing itself. */
    --dv-header-bg:      #fffdfa;
    --dv-header-line:    rgba(81, 17, 38, 0.10);
    --dv-header-line-lo: rgba(81, 17, 38, 0.06);
    --dv-nav-ink:        #3a0c1b;
    --dv-nav-ink-soft:   #7a674f;
    /* The accessible gold. --color-accent (#D4AF37) measures 2.09:1 on this
       ground and is a WCAG AA failure as text; --color-accent-ink is 5.06:1 on
       white. Hover and active states are text, so they use the ink. */
    --dv-nav-hover:      #8a6a12;
    --dv-ease:           cubic-bezier(0.22, 1, 0.36, 1);
}

/* ── The shell ──────────────────────────────────────────────────────────────
   Flat, opaque, one hairline underneath. The old header was rgba white at 80%
   over a 5px backdrop blur with a 15px drop shadow: the blur dragged whatever
   was scrolling underneath into the header as a smear of colour, and the
   shadow put a grey band across the top of every page. Neither survives a
   "minimal, not marketplace-like" brief. */
.navbar-luxury,
.navbar-luxury.header-compact {
    background: var(--dv-header-bg) !important;
    -webkit-backdrop-filter: none !important;
    backdrop-filter: none !important;
    box-shadow: none !important;
    border-bottom: 1px solid var(--dv-header-line) !important;
    transition: box-shadow 0.3s var(--dv-ease), border-color 0.3s var(--dv-ease) !important;
}

/* Full bleed. Every other .container on the site is capped at 1440px and that
   is right for reading; a navigation row is not reading. With twelve
   collections the cap was throwing away 300px of usable bar on a wide screen
   and forcing items into More that had somewhere to go. The gutter scales with
   the viewport so the logo still has air at 1024px. */
.navbar-luxury .container {
    max-width: none;
    padding-inline: clamp(20px, 3.2vw, 60px);
}

/* ── Tier 1: logo · actions ─────────────────────────────────────────────────
   The centre of this row is deliberately empty. It held a readonly search pill
   that took no typing, and clearing it is most of what makes the header read as
   a fashion house rather than a storefront. */
.navbar-luxury .main-header-bar {
    padding: 18px 0 !important;
    border-bottom: 1px solid var(--dv-header-line-lo) !important;
    transition: padding 0.3s var(--dv-ease) !important;
}
.navbar-luxury .main-header-container { gap: 24px; }

.navbar-luxury a.logo { max-width: none; }
.navbar-luxury .logo img {
    height: 44px;
    width: auto;
    max-width: 200px;
    object-fit: contain;
    transition: height 0.3s var(--dv-ease) !important;
}

/* ── The action cluster ─────────────────────────────────────────────────────
   Search, account, wishlist, bag — one row, one rhythm. The old items were
   flex COLUMNS carrying an optional caption under each glyph, so the icons sat
   at different heights depending on whether a label was present. */
/* Tightened from clamp(14px, 1.5vw, 24px) when the country picker rejoined the
   row: six controls at 24px apart on a wide screen spread the cluster across
   more of the header than the wordmark occupies, and the group stopped reading
   as one object. */
.navbar-luxury .nav-actions { gap: clamp(12px, 1.1vw, 18px); }

/* The Google Translate slot is a 40px hole.
   ───────────────────────────────────────────────────────────────────────────
   #google_translate_element is a placeholder the widget fills in — and nothing
   in this codebase loads the widget, so it is always empty and always 40px
   wide. Between the country picker and the search icon that read as a deliberate
   gap separating the two, which is the opposite of putting them in one row.

   :empty rather than deleting the div: if the widget is ever added back it
   still has its place, and it collapses only while there is nothing in it. */
.navbar-luxury .nav-actions #google_translate_element:empty { display: none; }
.navbar-luxury .nav-action-item {
    flex-direction: row;
    width: 40px;
    height: 40px;
    justify-content: center;
    color: var(--dv-nav-ink);
    transition: color 0.2s ease;
}
.navbar-luxury .nav-action-item:hover,
.navbar-luxury .nav-action-item:focus-visible { color: var(--dv-nav-hover); }

/* One drawn set at one weight — see the note in includes/header.php for why
   these are inline SVG and not Font Awesome. vector-effect keeps the hairline
   at 1.4px however the box is scaled. */
.dv-icon {
    width: 22px;
    height: 22px;
    display: block;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.4;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* The count. Was an 18px filled disc that sat level with the icon's own weight
   and read as part of the glyph; this is smaller, tighter and ringed in the
   header's own ivory so it lifts off the bag cleanly. */
.navbar-luxury .action-badge {
    top: 2px;
    right: 2px;
    min-width: 16px;
    width: auto;
    height: 16px;
    padding: 0 4px;
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.02em;
    background: var(--color-primary);
    box-shadow: 0 0 0 2px var(--dv-header-bg);
}

/* ── Tier 2: Women / Men ────────────────────────────────────────────────────
   Kept exactly as it works — two real hrefs — and quietened to a pair of
   letter-spaced words with a hairline under the active one. */
.navbar-luxury .audience-switch-bar { border-bottom: 1px solid var(--dv-header-line-lo); }
.navbar-luxury .audience-switch { gap: 30px; }
.navbar-luxury .audience-switch-link {
    padding: 9px 2px;
    font-family: var(--font-body), sans-serif;
    font-size: 10.5px;
    font-weight: 500;
    letter-spacing: 0.20em;
    text-transform: uppercase;
    color: var(--dv-nav-ink-soft);
    border-bottom: 1px solid transparent;
    transition: color 0.2s ease, border-color 0.2s ease;
}
.navbar-luxury .audience-switch-link.is-active {
    color: var(--dv-nav-ink);
    border-bottom-color: var(--dv-nav-ink);
    background: none;
}

/* ── Tier 3: the collections bar ────────────────────────────────────────────
   One line, centred, edge to edge. The gap is fluid rather than a fixed 32px:
   at 1024px it closes to 16px so more names survive on the bar, and on a wide
   screen it opens to 30px so twelve items still look composed instead of
   huddled in the middle. */
.navbar-luxury .bottom-header-bar { padding: 11px 0 13px !important; }
.navbar-luxury .nav-links {
    column-gap: clamp(16px, 1.9vw, 30px);
    row-gap: 8px;
}
/* Applied by the measuring script, never by the server: with scripting off the
   row keeps the wrap that stops a long name running off the page. */
.navbar-luxury .nav-links.nav-links-managed { flex-wrap: nowrap; }
.navbar-luxury .nav-links > li[hidden] { display: none !important; }

.navbar-luxury .nav-link-item {
    font-family: var(--font-body), sans-serif;
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.13em;
    color: var(--dv-nav-ink);
    padding: 5px 0;
    gap: 5px;
    white-space: nowrap;
}
/* The old hover swapped weight 600 for 700, which reflows the row on every
   pass of the mouse and nudges its neighbours sideways. Colour only. */
.navbar-luxury .nav-link-item:hover,
.navbar-luxury .nav-link-item.active {
    color: var(--dv-nav-hover) !important;
    font-weight: 500 !important;
}
.navbar-luxury .nav-link-item.active::after {
    height: 1px;
    bottom: -3px;
    background-color: var(--dv-nav-hover);
}
.navbar-luxury .nav-highlight-new { color: var(--dv-nav-hover); font-weight: 500; }
/* SALE stays the one coloured word on the bar — it is the only item whose
   meaning is time-limited. */
.navbar-luxury .nav-link-sale { color: var(--color-primary); }
.navbar-luxury .nav-arrow {
    width: 9px;
    height: 9px;
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
    opacity: 0.5;
    transition: transform 0.25s var(--dv-ease), opacity 0.2s ease;
}
.navbar-luxury .has-mega-menu:hover .nav-arrow { opacity: 1; }

/* ── The mega menu ────────────────────────────────────────────────────────
   Left exactly as it is on dievon.com.

   This branch had restyled it — panel background and shadow, heading type,
   link size and colour, square card corners, a slower hover, and an underlined
   Shop Now. All of that is reverted. The markup was already identical to live
   (compared element by element and class by class against the page dievon.com
   serves), and the appearance now is too, so the menu renders from the rules
   that were already in this file and nothing about it changes.

   The one addition below is .mega-shop-all, which does not appear in a category
   mega menu at all — it belongs to the More panel introduced by this branch,
   and there is nothing on live for it to differ from. */
.navbar-luxury .mega-shop-all {
    display: inline-block;
    margin-top: 18px;
    padding-top: 14px;
    border-top: 1px solid var(--dv-header-line-lo);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--dv-nav-ink);
    text-decoration: none;
}
.navbar-luxury .mega-shop-all:hover { color: var(--dv-nav-hover); }

/* Keep the card row inside the container.
   ─────────────────────────────────────────────────────────────────────────────
   The third card steps aside at 1180px. That threshold is too low for the row
   it is protecting: two 220px link columns, a 48px gap and a 769px three-card
   block need 1257px of content width, and the container only offers that from
   about 1350px up. Between 1180 and 1350 the row therefore ran past the
   container — measured at 1280, the cards ended 107px beyond the container's
   right edge and 66px beyond the VIEWPORT, so the third card was simply cut off
   by the page's overflow-x: clip. It is cropped on live at that width too.

   1366 rather than a tighter number because that is a real laptop width and the
   row should not be deciding this within a pixel of it.

   Scoped to .navbar-luxury and additive: it changes WHEN the third card steps
   aside, which the stylesheet already does on its own at 1180 — not how any of
   the cards look. */
@media (max-width: 1366px) {
    .navbar-luxury .mega-promo-card:nth-child(3) { display: none; }
}

/* ── More ───────────────────────────────────────────────────────────────────
   A narrow panel under the word, not a band across the page: it holds one
   column of names, and full-bleed for four links looks like a fault.
   .has-mega-menu is position:static !important so the mega panels can span the
   viewport — this one has to opt back out of that to anchor itself. */
.navbar-luxury .nav-more { position: relative !important; }
.navbar-luxury .nav-more.nav-more-empty { display: none !important; }
.navbar-luxury .mega-dropdown-compact {
    left: 50% !important;
    right: auto !important;
    width: max-content !important;
    min-width: 220px !important;
    max-width: 300px !important;
    padding: 26px 30px !important;
    transform: translateX(-50%) translateY(10px) !important;
}
.navbar-luxury .nav-more:hover .mega-dropdown-compact,
.navbar-luxury .nav-more:focus-within .mega-dropdown-compact,
.navbar-luxury .mega-dropdown-compact:hover {
    transform: translateX(-50%) translateY(0) !important;
}
.navbar-luxury .mega-dropdown-compact .mega-dropdown-container { display: block; }

/* ── Sticky, compact ────────────────────────────────────────────────────────
   The .header-compact class is added past 80px of scroll by the script in
   includes/footer.php and removed under 30px — untouched, only restyled. The
   bar loses 16px of padding and the logo 8px of height, all on the same easing
   so it reads as one movement rather than three. */
.navbar-luxury.header-compact { border-bottom-color: var(--dv-header-line) !important; }
.navbar-luxury.header-compact .main-header-bar { padding: 10px 0 !important; }
.navbar-luxury.header-compact .logo img { height: 36px; }
.navbar-luxury.header-compact .audience-switch-link { padding-top: 6px; padding-bottom: 6px; }
.navbar-luxury.header-compact .bottom-header-bar { padding: 7px 0 9px !important; }

/* Focus has to stay visible on a header this quiet. */
.navbar-luxury .nav-link-item:focus-visible,
.navbar-luxury .nav-action-item:focus-visible,
.navbar-luxury .mega-shop-all:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
    .navbar-luxury,
    .navbar-luxury .main-header-bar,
    .navbar-luxury .logo img,
    .navbar-luxury .nav-arrow {
        transition: none !important;
    }
    .navbar-luxury .mega-dropdown-compact { transform: translateX(-50%) !important; }
}

/* SALE, without the pill.
   ─────────────────────────────────────────────────────────────────────────────
   It rendered as a filled rounded capsule in --color-danger at 10% — the only
   shape on a bar of plain words, in a red that belongs to error messages, and a
   rounded container of exactly the kind this redesign removes everywhere else.
   It also ran at weight 700 against everything else's 500.

   Kept as the one coloured word on the bar, because it is the only item whose
   meaning expires — but in the brand burgundy, at the same weight and tracking
   as its neighbours, and with the active underline the pill used to suppress. */
.navbar-luxury .nav-link-item.nav-link-sale {
    color: var(--color-primary) !important;
    background: none !important;
    padding: 5px 0 !important;
    font-weight: 500 !important;
}
.navbar-luxury .nav-link-item.nav-link-sale:hover,
.navbar-luxury .nav-link-item.nav-link-sale.active {
    color: var(--color-primary-dark) !important;
    background: none !important;
}
.navbar-luxury .nav-link-item.nav-link-sale::after {
    display: block !important;
    background-color: var(--color-primary);
}
.navbar-luxury .nav-link-item.nav-link-sale:not(.active)::after { display: none !important; }

/* ── The top strip ──────────────────────────────────────────────────────────
   Women / Men now has a bar of its own above the wordmark, rather than a tier
   between the wordmark and the collections.

   It is the only control on the site that changes what every other link means,
   so it belongs before the shop rather than inside it — and lifting it out puts
   the logo row and the collections row next to each other, which is what makes
   the header read as one object instead of three stacked bands.

   A shade warmer than the header so the strip is legible as its own surface
   without a border doing the work, and slim enough (~34px) that it costs
   almost nothing. Still one flat colour — no gradient, no shadow.

   These rules override the ones further up this file, which styled the same
   element while it sat in the middle of the header. Same class, same markup,
   same two real hrefs. */
.navbar-luxury .header-top-bar {
    background: #f7f1e8;
    border-bottom: 1px solid rgba(81, 17, 38, 0.07);
    /* The strip's own height. The links carry no vertical padding of their own
       so that the underline below sits against the TEXT rather than four pixels
       further down against the strip's bottom edge. */
    padding: 9px 0;
    transition: padding 0.3s var(--dv-ease);
}
/* Left, aligned with the wordmark under it, not centred. Two small words in the
   middle of a full-bleed strip read as a stray caption; against the left edge
   they read as what they are — the switch that governs everything below. */
.navbar-luxury .header-top-bar .audience-switch {
    justify-content: flex-start;
    gap: 22px;
}
.navbar-luxury .header-top-bar .audience-switch-link {
    padding: 0;
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.18em;
    color: #8a7a63;
    background: transparent;
    border-bottom: 0;
    transition: color 0.2s ease;
}
.navbar-luxury .header-top-bar .audience-switch-link:hover { color: var(--dv-nav-ink); }
.navbar-luxury .header-top-bar .audience-switch-link.is-active {
    color: var(--dv-nav-ink);
    background: transparent;
    border-bottom: 0;
}
/* The site's shared sliding underline, kept here rather than suppressed.
   ─────────────────────────────────────────────────────────────────────────────
   It was suppressed while the active side was a filled ivory tab: the line was
   then a second indicator saying the same thing, and with the tab's padding
   under it the line landed on the strip's bottom edge, closing off the join.

   With the tab gone and the links carrying no vertical padding, the underline
   is the only thing marking the active side and it sits directly beneath the
   word. Thinner than the 2px default — at this size 2px is a rule under a
   caption. */
.navbar-luxury .header-top-bar .audience-switch-link::after {
    display: block !important;
    height: 1px;
    bottom: -4px;
}
/* One child now — the audience switch — but still a flex row: the country
   picker lived here for a release and this is what kept the two apart. Left
   aligned either way. */
.navbar-luxury .header-top-bar .container {
    display: flex;
    align-items: center;
    gap: 16px;
}
.navbar-luxury.header-compact .header-top-bar { padding: 6px 0; }
.navbar-luxury .header-top-bar .audience-switch-link:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: 3px;
}


/* ═══════════════════════════════════════════════════════════════════════════
   THE QUANTITY STEPPER — one control, one look, everywhere
   ───────────────────────────────────────────────────────────────────────────
   It appeared in three places in two designs: the cart page drew flat buttons
   inside a bordered pill, while the cart drawer and the product page's picker
   drew a pair of separate circles. Same control, same job, two appearances —
   and two sets of CSS to keep in step.

   The pill is the one kept. Two loose circles read as two buttons that happen
   to sit near a number; a single bordered container reads as one control with a
   value inside it, which is what it is.

   Sizing is the other half of the change. The circles were 26px and the pill's
   buttons 30x32 — both under the 44px minimum for a touch target, on the one
   screen where a mistap costs the shopper money. 36px on a pointer device, 44px
   the moment the viewport is small enough to be a phone.

   .cart-qty* is aliased here so a cached page with the old markup still renders
   correctly through a deploy.
   ═══════════════════════════════════════════════════════════════════════════ */
.qty-controls,
.cart-qty {
    display: inline-flex;
    align-items: center;
    gap: 0;
    flex-shrink: 0;
    border: 1px solid var(--border-strong);
    background: var(--bg-surface-soft);
    padding: 2px 4px;
}
.qty-btn,
.cart-qty-btn {
    width: 36px;
    height: 36px;
    min-width: 36px;
    border: 0;
    background: none;
    color: var(--text-primary);
    font-size: 16px;
    font-weight: 500;
    line-height: 1;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background-color 0.18s ease, color 0.18s ease;
}
.qty-btn:hover,
.cart-qty-btn:hover {
    background: var(--color-primary-bg);
    border-color: transparent;
    color: var(--color-primary);
}
.qty-btn:disabled,
.qty-btn:disabled:hover,
.cart-qty-btn:disabled,
.cart-qty-btn:disabled:hover {
    opacity: 0.4;
    cursor: not-allowed;
    background: none;
    color: var(--text-primary);
}
.qty-value,
.cart-qty-value {
    font-size: 14px;
    font-weight: 700;
    min-width: 30px;
    width: auto;
    text-align: center;
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}
/* The product page's picker is the same control and no longer overrides it. */
.product-qty-controls .qty-btn { width: 36px; height: 36px; font-size: 16px; }
.product-qty-controls .qty-value { font-size: 14px; min-width: 30px; }

/* Touch. 44px is the floor Apple and Google both give, and the cart is the
   worst place on the site to miss a button. */
@media (max-width: 991px) {
    .qty-btn,
    .cart-qty-btn,
    .product-qty-controls .qty-btn { width: 44px; height: 44px; min-width: 44px; font-size: 18px; }
    .qty-value,
    .cart-qty-value,
    .product-qty-controls .qty-value { min-width: 34px; font-size: 15px; }
}
@media (prefers-reduced-motion: reduce) {
    .qty-btn, .cart-qty-btn { transition: none; }
}

/* The country / currency picker, as a sixth glyph
   ───────────────────────────────────────────────────────────────────────────
   Three shapes were tried in this row: a filled bordered pill, then the two
   letter code with a chevron, then this. The pill was the only solid shape
   among five 1.4px strokes. The code was better but still a WORD among five
   drawn glyphs, and the chevron had to sit at the far edge of the control's
   box, which left a hole between the letters and the arrow — 13px of padding
   reading as a gap that belonged to nothing.

   A globe drawn at the same 1.4px stroke, in the same 40x40 box, has no type
   to align and no chevron to place. There is nothing left to get wrong, and
   the row is six of one thing instead of five of one and one of another.

   The <select> is laid over the glyph at full size and zero opacity. That is
   the standard pattern for restyling a native picker without replacing it: the
   keyboard behaviour, the screen-reader semantics, the iOS wheel and the
   onchange handler are all the browser's own. opacity, not visibility or
   display — a hidden select is not focusable.

   What the glyph cannot do is say which country is active. That is why the
   full label went into the footer; see includes/footer.php. */
.navbar-luxury .nav-actions .currency-selector-wrapper {
    position: relative;
    /* Width follows the glyph plus its label rather than being fixed at the
       icon box: the code is two characters today and could be three. */
    height: 40px;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
}
.navbar-luxury .nav-actions .dv-country-glyph {
    display: flex;
    align-items: center;
    justify-content: center;
    /* 7px, not the row's 12-18px. The glyph and its code are ONE control, so
       they have to sit closer to each other than the control sits to the search
       icon beside it — otherwise the code reads as a stray word between two
       glyphs. It went 5 -> 7 with the type: a bigger label needs a little more
       air or the two touch. */
    gap: 7px;
    height: 100%;
    padding: 0 2px;
    color: var(--dv-nav-ink);
    transition: color 0.2s ease;
    pointer-events: none;   /* the select underneath takes the click */
}
/* The label.
   ───────────────────────────────────────────────────────────────────────────
   This was 10.5px at weight 600 — caption-sized type at a caption's weight,
   sitting beside a 22px drawn glyph. The mismatch in scale was the problem,
   and 600 made it worse: heavy small type reads as a badge, not as a label.

   12.5px at 500 with 0.16em is the opposite move and the one luxury setting
   actually uses — a size you can read, a weight that stays quiet, and the air
   between letters doing the work that weight was doing badly. Tracking adds
   space after the last letter as well as between them, so the negative margin
   takes that back; without it the gap to the search icon looks two pixels
   wider than the gap on the glyph's other side. */
.navbar-luxury .nav-actions .dv-country-code {
    font-family: var(--font-body), sans-serif;
    font-size: 12.5px;
    font-weight: 500;
    letter-spacing: 0.16em;
    margin-right: -0.16em;
    line-height: 1;
    color: inherit;
}
.navbar-luxury .nav-actions .currency-selector-wrapper:hover .dv-country-glyph {
    color: var(--dv-nav-hover);
}

/* Every one of these fights a rule from the days of the pill — the base block
   near the top of this file still sets a background, a border, a radius, a
   height, a max-width and a chevron background-image on #countrySelector. */
.navbar-luxury .nav-actions #countrySelector {
    position: absolute;
    inset: 0;
    width: 100% !important;
    height: 100% !important;
    max-width: none !important;
    min-width: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    box-shadow: none !important;
    background: none !important;
    background-image: none !important;
    -webkit-appearance: none;
    appearance: none;
    opacity: 0;
    cursor: pointer;
    font-size: 16px !important;   /* iOS zooms the page on a smaller focused control */
    color: transparent !important;
}
/* The select is invisible, so its focus ring would be too. */
.navbar-luxury .nav-actions .currency-selector-wrapper:focus-within {
    outline: 2px solid var(--color-secondary);
    outline-offset: 2px;
}
.navbar-luxury .nav-actions #countrySelector:focus-visible { outline: none; }
/* The option list is drawn by the operating system and cannot take the header's
   type, but it can at least not be white-on-white in a dark theme. */
.navbar-luxury .nav-actions #countrySelector option { color: #3a0c1b; background: #fff; }

/* The drawer's Shopping Bag count.
   ─────────────────────────────────────────────────────────────────────────────
   The brackets are here rather than in the markup so they belong to the number.
   Written into the sentence they were literal text, and hiding the span at zero
   — which is right, the button should read "Shopping Bag" when it is empty —
   left them behind as "Shopping Bag ()".

   No display declaration here on purpose. The script used to set display:flex on
   this span — right for the round badge on the bag icon, wrong for a number
   inside a line of text — and that is fixed in footer.php. Restating "inline"
   as a guard would be theatre: the button is a flex container, so this span is a
   flex item and any inline value is blockified to block regardless. Checked
   rather than assumed — computed display is block whatever is written here, and
   the row lays out correctly because that is how flex items behave. */
.drawer-bag-count::before { content: "("; }
.drawer-bag-count::after  { content: ")"; }
.drawer-bag-count[hidden] { display: none; }

/* ═══════════════════════════════════════════════════════════════════════════
   HOMEPAGE HERO — editorial split
   ───────────────────────────────────────────────────────────────────────────
   The banner used to be words printed on the photograph: white type, a dark
   gradient laid across the picture to keep it readable, and a text-shadow on
   top of that to rescue the cases the gradient could not.

   That was never going to hold, and it is measurable rather than a matter of
   taste. Of the two banners on the shop today, the zone the text falls in
   averages luminance 78 on one and 196 on the other. White type vanishes on
   the second, dark type on the first, and the scrim that saves one dulls the
   garment on the other — which is the single thing a banner exists to show.
   The owner uploads the picture; the design cannot depend on what is in it.

   So the words get their own ground. A solid ivory panel beside the
   photograph: legibility is constant whatever is uploaded, the scrim and both
   text shadows are gone, and the picture is finally shown undimmed. It is also
   the more editorial arrangement — a fashion book gives the image a page and
   the words the facing one.

   Nothing about the slider changes. Same .slide elements, same OwlCarousel,
   same .is-active entrance animation on .slide-content's four children, same
   arrows and dots, same admin banners feeding it.
   ═══════════════════════════════════════════════════════════════════════════ */

/* 100vh was too tall to be elegant — on a laptop the banner WAS the page and
   nothing below it was ever suggested. A clamp keeps it generous without
   swallowing the fold. */
.hero-slider-section {
    background: var(--bg-hero);
}

/* The words sit ON the photograph, in its own negative space.
   ────────────────────────────────────────────────────────────────────────────
   The wash was ivory with dark type for one release. It worked, but it depended
   on the photograph: ivory only lifts type off a banner that is already darker
   than the ink, and a bright upload flattens it. Black is the opposite bet and
   the more robust one — a banner can be arbitrarily bright and white type over a
   dark scrim still reads, which is why the live site has always darkened rather
   than lightened.

   So these are dievon.com's own overlay stops (style.css:6712 on main), turned
   from bottom-up to left-to-right. Live's text sits at the bottom of the frame,
   so live darkens from the bottom; this hero's text sits at the left, vertically
   centred, so the same weight has to fall on the left instead. Same colour, same
   opacities, same falloff — rotated to where the words actually are.

   0.06 at the far edge rather than 0 is live's value too: a whisper of scrim
   across the whole frame, so the right side of a very bright banner does not
   glare against the darkened left. It is imperceptible as tint and it keeps the
   image reading as one photograph rather than two halves. */
.slide {
    position: relative;
    display: block;
    background-image: none;
}
.slide-media { position: absolute; inset: 0; overflow: hidden; background: var(--bg-surface-soft); background-size: cover; background-position: center; }
/* The three fallback hero slides used to name seed photographs here. Those
   files only ever existed on a development machine, so on the live shop each
   page load fired eight requests that 404ed and printed three errors into
   every visitor's console -- for slides that sit behind the real banners and
   are never seen while any banner exists. The panel colour below is what the
   slides fall back to now, which is also what they showed in practice. Point
   these at real uploads only if the fallback hero is ever meant to carry a
   photograph of its own. */

.slide-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center 2%; z-index: 0; }

.slide-panel {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    background: linear-gradient(
        to right,
        rgba(0, 0, 0, 0.78) 0%,
        rgba(0, 0, 0, 0.62) 30%,
        rgba(0, 0, 0, 0.28) 55%,
        rgba(0, 0, 0, 0.06) 100%
    );
    /* Left padding tracks the container's real content edge — see --dv-gutter.
       Below 1440 that is the gutter; above it the container centres, so the edge
       moves inward by half the overflow and the headline follows it. */
    padding-block: clamp(32px, 5vw, 80px);
    padding-right: clamp(24px, 4vw, 64px);
    padding-left: max(var(--dv-gutter), calc((100vw - 1440px) / 2 + var(--dv-gutter)));
    pointer-events: none;   /* the slide stays draggable; the links opt back in */
}
.slide-content { pointer-events: auto; }

.slide-content {
    position: static;
    left: auto;
    bottom: auto;
    max-width: 30rem;
    margin-left: 0;
    color: #ffffff;
    text-align: left;
}
.slide-eyebrow {
    display: block;
    font-family: var(--font-body), sans-serif;
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.28em;
    text-transform: uppercase;
    /* 0.88, not 0.82: at 10px this is small text, so it needs 4.5:1, and the
       thinnest part of the mobile scrim over a white studio banner leaves it
       about 4.7:1 at 0.82 — passing, but with nothing to spare. */
    color: rgba(255, 255, 255, 0.88);
    margin-bottom: clamp(14px, 1.6vw, 22px);
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}
/* The headline. Marcellus at a size it can actually be seen at — the whole
   point of a display serif is the modelling in the strokes, and 56px capitals
   under a scrim showed none of it. Sentence case for the same reason: uppercase
   flattens a serif's ascenders and descenders, which is most of its character.
   No shadow: there is nothing behind it now to fight. */
.hero-slider-section .slide-content h1,
.hero-slider-section .slide-content h2 {
    font-family: var(--font-heading);
    font-size: clamp(40px, 5.2vw, 82px) !important;
    font-weight: var(--font-heading-weight) !important;
    line-height: 1.02 !important;
    letter-spacing: -0.005em !important;
    text-transform: none !important;
    /* Near-black, as the reference has it, rather than the brand burgundy: over
       a photograph the burgundy reads as a colour choice competing with the
       garment, where ink reads as type. --text-primary is #3a0c1b, which is a
       very dark burgundy — on brand, but it behaves like black at this size. */
    color: #ffffff !important;
    margin: 0 0 clamp(16px, 1.8vw, 26px) !important;
    text-shadow: 0 2px 4px rgba(0, 0, 0, 0.8), 0 0 18px rgba(0, 0, 0, 0.45) !important;
}
.hero-slider-section .slide-content p {
    font-family: var(--font-body), sans-serif;
    font-size: clamp(13px, 1.05vw, 15px);
    font-weight: 400;
    line-height: 1.75;
    letter-spacing: 0.01em;
    /* Not --text-muted. That token is a warm grey tuned for body copy on a flat
       cream page, and over a photograph it measured 1.94:1 against a 4.5:1
       requirement — the line was legible in the reference only because that
       shot's backdrop is a plain wall. This is the same ink as the headline at a
       lower weight, so it still reads as secondary without depending on the
       picture behind it. */
    color: rgba(255, 255, 255, 0.9);
    max-width: 27rem;
    margin: 0 0 clamp(26px, 2.6vw, 38px);
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* Two buttons, one weight apart. Square, because every rounded container has
   come out of this header and shop grid already. */
.slide-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.hero-slider-section .btn-hero-primary,
.hero-slider-section .btn-hero-ghost {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-height: 48px;
    padding: 0 30px;
    font-family: var(--font-body), sans-serif;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    text-decoration: none;
    transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
    box-shadow: none;
    transform: none;
}
/* Solid white with near-black type, and burgundy on hover — live's own hero
   button, squared off to match the rest of this build. Against a dark scrim the
   filled button has to be the light one; ink on ink would disappear. */
.hero-slider-section .btn-hero-primary {
    background: #ffffff;
    border: 1px solid #ffffff;
    color: #111111 !important;
}
.hero-slider-section .btn-hero-primary:hover {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: #ffffff !important;
    box-shadow: none;
    transform: none;
}
/* Outlined, on the photograph. The border is white held back, so it stays
   related to the type beside it instead of reading as a stray rule. */
.hero-slider-section .btn-hero-ghost {
    background: transparent;
    border: 1px solid rgba(255, 255, 255, 0.55);
    color: #ffffff !important;
}
.hero-slider-section .btn-hero-ghost:hover {
    background: rgba(255, 255, 255, 0.16);
    border-color: #ffffff;
    color: #ffffff !important;
    transform: none;
}

/* Controls, quietened to match. They were dark translucent squares with a
   blurred pill behind the dots — three more surfaces on a banner that is now
   two. Over the picture, so they stay light. */
@media (prefers-reduced-motion: reduce) {
    .hero-slider-section .btn-hero-primary,
    .hero-slider-section .btn-hero-ghost { transition: none; }
}


/* ══════════════════════════════════════════════════════════════════════════
   FULL-BLEED RAILS — one behaviour for every horizontal strip on the site
   ──────────────────────────────────────────────────────────────────────────
   A rail is read by the tile clipped at the edge of the screen: that is what
   says "there is more" without adding arrows or dots to look at. A rail that
   stops at the page gutter clips its last tile 72px early on a desktop, and
   the cut reads as the end of the list rather than the edge of the window.

   .occasion-slider has done this since it was written, by sitting outside
   .container in the markup. .new-arrivals-carousel now does the same, and this
   class carries the shared part so the two cannot drift: the same 16px inset,
   the same scroll padding, the same hidden scrollbar.

   16px, not --dv-gutter. A rail indented to the full 72px gutter is not edge to
   edge in any meaningful sense; 16px is enough to keep the first tile off the
   glass without pretending to be a margin. The heading above keeps the real
   gutter, because it is still page text. */
/* Nothing carries this class at the moment, and that is not an oversight. The
   one rail that wants it is Shop by Occasion, and Owl ignores stagePadding
   whenever autoWidth is on — see the note in responsive.css. Kept because the
   initialiser reads it (includes/footer.php) and any future rail that is not
   autoWidth gets the bleed by adding the class alone. */
.dv-rail-bleed {
    padding-inline: 16px;
    scroll-padding-inline-start: 16px;
    scrollbar-width: none;
    -ms-overflow-style: none;
}
.dv-rail-bleed::-webkit-scrollbar { display: none; }

/* Owl replaces the element's children with its own stage, so CSS padding on the
   rail would inset the CLIP as well as the content — the strip would be cut 16px
   early instead of at the edge of the screen. Owl has stagePadding for exactly
   this, and it is set in the initialiser in includes/footer.php.

   Do NOT reach for `overflow: visible` on .owl-stage-outer to achieve it. That
   was tried: the stage is every card laid out in one row, so unclipping it spills
   the whole track into the document and the PAGE scrolls sideways — measured at
   1648px of horizontal overflow on a 1600px screen. */
.dv-rail-bleed.owl-loaded { padding-inline: 0; }

/* ══════════════════════════════════════════════════════════════════════════
   NEW ARRIVALS — an editorial gallery that expands on hover
   ──────────────────────────────────────────────────────────────────────────
   Four garments across the full width: one open at 50%, three narrow beside
   it. Hover or focus a narrow panel and it takes the 50% while the open one
   collapses. Nothing moves on its own, there is no slider under it, and there
   is no JavaScript — the whole behaviour is flex-basis and a transition, so it
   cannot break if a script fails to load and it needs nothing initialised.

   HOW THE DEFAULT COLLAPSES. The trick is the :not(). The first panel is open
   at rest; when the gallery is hovered anywhere it collapses — unless it is
   itself the thing being hovered, which :not(:hover) says exactly. Written the
   obvious way instead, as two competing rules, the collapse rule is the more
   specific of the two and wins over the panel's own hover, so the first panel
   could never be re-opened once the pointer had entered the gallery.
   ══════════════════════════════════════════════════════════════════════════ */
/* The arrows sit in here beside the gallery rather than inside it.
   .na-gallery:hover is what collapses the open panel, so a control INSIDE the
   gallery would trigger that rule while being the thing under the pointer —
   nothing would be expanded and the row would come up a third short. Out here,
   hovering an arrow is not hovering the gallery. No positioning: the arrows are
   in the flow under the gallery, and nothing is laid over the photographs any
   more. */

/* Arrivals beyond the four on screen. Only honoured on the desktop composition:
   below 1024px the same panels become one swipe rail and it keeps all of them,
   which is why this is a class and not the hidden attribute. display:none also
   means the browser never fetches those photographs until the set is paged to. */
@media (min-width: 1025px) {
    /* .na-gallery-scoped, and it has to be. Every panel rule was raised to
       (0,3,0) so it would beat the shared card's own styling — which left this
       one at (0,2,0), tying with `.na-gallery .na-panel { display: block }`,
       losing on source order, and quietly un-hiding the off-page panels: eight
       live panels summing to twice the gallery's width, with the counter and
       the arrows reading from a set that was no longer what was on screen. */
    .na-gallery .na-panel.na-off { display: none; }
}

/* ── Specificity note ────────────────────────────────────────────────────────
   Every panel rule below is written .na-gallery .na-panel, not .na-panel.

   The panel IS the shared product card now, so a rule like
   `.na-panel .product-card-title` is (0,2,0) — exactly the same weight as the
   card's own `.product-card .product-card-title`. Ties break on source order,
   the card block is appended at the end of this file, and the card therefore
   won every one of them: the caption came out in the card's dark burgundy on
   top of a dark photograph. The extra class buys the gallery (0,3,0) and the
   argument is over.
   ─────────────────────────────────────────────────────────────────────────── */
.na-gallery {
    position: relative;
    display: flex;
    /* No gutters between panels: the reference is one continuous image, and a
       gap would cut it into four cards, which is the thing being replaced. */
    gap: 0;
    /* Not a taste number. The open panel is half the gallery's width, so making
       the gallery 0.625 of that width makes the open SLOT 0.5/0.625 = 0.80 wide
       to tall — which is the shape the product photography is actually shot in
       (1122x1402 and 1086x1448 both land on 0.78-0.80). A slot the same shape as
       the picture is a slot the picture fills exactly: no burgundy either side,
       and nothing cropped to achieve it.

       It was 54vw, which made the slot 0.93 — wider than any of the photographs
       — so every one of them sat in the middle with 41 to 72px of ground showing
       down both edges.

       The 1000px ceiling stops the band growing without limit on a wide monitor.
       Past 1600px it is the width that has to give instead — see the media query
       below, which holds the open panel at 800px so the slot keeps its shape. */
    height: clamp(520px, 62.5vw, 1000px);
    overflow: hidden;
    background: var(--color-primary-dark);

    /* The two numbers the whole composition is derived from: how wide the open
       panel is, and how many narrow ones share what is left. Kept as a pair so
       the row always adds to exactly 100% and never wraps or leaves a strip of
       bare burgundy at the end.

       --na-open is not always half. One panel on its own has nothing to sit
       beside and takes the whole width — a shop with a single new arrival, or a
       final page of one. At 50% both of those left half the gallery empty. */
    --na-open: 50%;
    --na-rest: 3;
}
.na-gallery[data-count="1"] { --na-open: 100%; --na-rest: 1; }
.na-gallery[data-count="2"] { --na-rest: 1; }
.na-gallery[data-count="3"] { --na-rest: 2; }

/* Past 1600px the height has hit its 1000px ceiling, so half the width is no
   longer 0.80 of it — at 1920 the slot reopens to 0.96 and cover starts taking
   a fifth of the garment off the bottom, which is the whole failure this was
   built to avoid. 1920x1080 is the commonest desktop there is, so it is not an
   edge case.

   A length rather than a percentage fixes it: 800px is exactly 0.80 of the
   1000px the gallery has settled at, so the slot keeps its shape however wide
   the screen gets, and the extra width goes to the narrow panels instead. The
   open one is still 2.1x the width of a narrow one at 1920. */
@media (min-width: 1601px) {
    .na-gallery { --na-open: 800px; }
}

.na-gallery .na-panel {
    position: relative;
    /* The panel IS the shared product card now, so the card's own chrome comes
       off: this is a photograph in a row of photographs, not a tile. */
    display: block;
    overflow: hidden;
    background: transparent;
    border: 0;
    padding: 0;
    text-decoration: none;
    min-width: 0;
    /* The narrow panels share whatever the open one does not take. Both numbers
       are declared on the GALLERY and inherited, not set on each panel: the
       paging script rewrites them when a page holds fewer than four, and an
       inline value on the gallery reaches every panel — whereas
       .na-gallery[data-count] .na-panel would have out-specified it. */
    flex: 0 0 calc((100% - var(--na-open)) / var(--na-rest));
    transition: flex-basis 500ms cubic-bezier(0.4, 0, 0.2, 1);
}

.na-gallery .na-panel.is-default { flex-basis: var(--na-open); }
.na-gallery:hover        .na-panel.is-default:not(:hover),
.na-gallery:focus-within .na-panel.is-default:not(:focus-within) {
    flex-basis: calc((100% - var(--na-open)) / var(--na-rest));
}
.na-gallery .na-panel:hover,
.na-gallery .na-panel:focus-visible,
.na-gallery .na-panel:focus-within { flex-basis: var(--na-open); }

/* ── The photograph ────────────────────────────────────────────────────────
   One fit, everywhere: cover. It is the only one that cannot letterbox, so the
   gallery reads as four photographs meeting edge to edge rather than four
   pictures mounted on burgundy.

   The open panel used to be contain, to show the whole outfit rather than crop
   it. That was the right instinct and the wrong lever — contain leaves a bar
   wherever the slot and the picture disagree, and they always disagreed. The
   slot is now shaped like the photographs instead (see the gallery height), so
   cover has almost nothing left to cut: nothing at all for a 0.80 photograph,
   and under 9% for the narrowest one in the catalogue.

   What little it does cut comes off the bottom, because the crop sits at 30%
   rather than centred. The models stand with their feet at the very bottom of
   frame, so a centred crop takes the shoes AND the top of the head; biased
   upward it keeps the face, the neckline and the embroidery, which is what the
   panel is being looked at for. */
.na-gallery .na-panel .card-img-container {
    position: absolute;
    inset: 0;
    display: block;
    overflow: hidden;
    /* No rule under the picture here — that belongs to the boxed shop card, and
       this panel has no box for it to divide. */
    border-bottom: 0;
    aspect-ratio: auto;
    height: auto;
}
.na-gallery .na-panel .card-img-link { display: block; height: 100%; }
.na-gallery .na-panel picture { display: contents; }
.na-gallery .na-panel .card-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center 18%;
    transition: transform 550ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.na-gallery .na-panel:hover .card-img,
.na-gallery .na-panel:focus-visible .card-img,
.na-gallery .na-panel:focus-within .card-img,
.na-gallery .na-panel.is-default .card-img {
    object-position: center 30%;
}
/* 1.03, the ceiling the brief sets. */
.na-gallery .na-panel:hover .card-img,
.na-gallery .na-panel:focus-visible .card-img,
.na-gallery .na-panel:focus-within .card-img { transform: scale(1.03); }

.na-gallery .na-panel .card-img-fallback {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 54px;
    opacity: 0.5;
}

/* ── The caption ───────────────────────────────────────────────────────────
   Hidden until the panel is the open one: on a 200px-wide strip there is no
   room for a name and a price, and four captions competing is the card grid
   this replaced. It rises 12px as it fades, so it arrives with the panel
   rather than appearing on top of it. */
.na-gallery .na-panel .product-card-details {
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    z-index: 2;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;
    padding: clamp(20px, 2.4vw, 34px);
    /* Tuned for a white photograph, not for the ones uploaded today.
       ──────────────────────────────────────────────────────────────────────
       At 0.72 / 0.34 the caption measured 3.61:1 on the price and 1.49:1 on
       the gold call to action against a 4.5:1 requirement — the garment behind
       it happened to be pale, and a scrim set by eye on a dark photograph
       fails the moment a light one is uploaded.

       White text needs its ground at or below about 118 to clear 4.5:1, and
       0.54 alpha over pure white leaves 117. Every stop across the caption's
       own band — the bottom fifth of the panel — is above that, so no upload
       can break it. It clears above the type quickly enough that the garment
       is not dulled: nothing at all by 76% of the way up. */
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 500ms ease, transform 500ms cubic-bezier(0.22, 0.61, 0.36, 1);
    pointer-events: none;
    isolation: isolate;
}
/* The scrim reaches 58px ABOVE the caption rather than being a background on it.
   Gradient stops are percentages of their own box, and this box is short — so
   the name, the topmost and therefore weakest line, sat in the thin end of the
   fade and measured 2.05:1 against the 4.5:1 it needs. Extending the box upward
   puts every line in the strong part and leaves the fade where there is no text
   to fail. Same stops the photo fan arrived at, for the same reason. */
.na-gallery .na-panel .product-card-details::before {
    content: '';
    position: absolute;
    inset: -58px 0 0 0;
    z-index: -1;
    background: linear-gradient(to top,
        rgba(0, 0, 0, 0.88) 0%,
        rgba(0, 0, 0, 0.80) 34%,
        rgba(0, 0, 0, 0.68) 58%,
        rgba(0, 0, 0, 0.24) 84%,
        rgba(0, 0, 0, 0) 100%);
}
.na-gallery .na-panel:hover .product-card-details,
.na-gallery .na-panel:focus-visible .product-card-details,
.na-gallery .na-panel:focus-within .product-card-details,
.na-gallery .na-panel.is-default .product-card-details { opacity: 1; transform: translateY(0); }
.na-gallery:hover        .na-panel.is-default:not(:hover) .product-card-details,
.na-gallery:focus-within .na-panel.is-default:not(:focus-within) .product-card-details {
    opacity: 0;
    transform: translateY(12px);
}

.na-gallery .na-panel .product-card-title {
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: clamp(19px, 1.7vw, 26px);
    line-height: 1.15;
    color: #ffffff;
    text-wrap: balance;
}
.na-gallery .na-panel .product-card-price {
    font-family: var(--font-body), sans-serif;
    font-size: 14px;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: rgba(255, 255, 255, 0.92);
}
.na-gallery .na-panel .price-from-label { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; opacity: 0.75; }

/* The colour has to go on the elements that actually carry the text, not on
   their containers. The title is an <a> inside the h3 and the price is a set of
   spans, and the card styles every one of them by name — so colouring only
   .product-card-title and .product-card-price left the words themselves in the
   card's dark burgundy, on top of a dark photograph. Same lesson the photo fan
   learned; written out here so the next person does not learn it a third time. */
.na-gallery .na-panel .product-card-title a { color: #ffffff; }
.na-gallery .na-panel .price-now   { color: rgba(255, 255, 255, 0.92); font-size: 14px; font-weight: 500; }
.na-gallery .na-panel .price-mrp-strike { color: rgba(255, 255, 255, 0.66); font-size: 12.5px; }
.na-gallery .na-panel .price-off   { color: var(--color-accent); font-size: 11.5px; }
.na-gallery .na-panel .product-card-bag-note { color: var(--color-accent); }
.na-gallery .na-panel .product-card-cta { color: var(--color-accent) !important; border-bottom-color: var(--color-accent); }
.na-gallery .na-panel .product-card-cta.sold-out-cta { color: rgba(255, 255, 255, 0.78) !important; border-bottom-color: currentColor; }
/* Sold out over a dark ground, not the shop's white tile: 0.6 opacity reads as
   a failed image here, exactly as it did in the fan. */
.na-gallery .na-panel.sold-out .card-img { opacity: 0.82; }
.na-gallery .na-panel.sold-out .price-now { color: rgba(255, 255, 255, 0.72); }

/* Wishlist and compare pull in from the panel edge, and compare waits for the
   panel to be the one being looked at — a narrow panel is 200px wide and two
   round buttons on it is most of the garment. */
.na-gallery .na-panel .product-card-tools { top: 12px; right: 12px; gap: 7px; z-index: 6; }
.na-gallery .na-panel .product-card-wishlist-btn,
.na-gallery .na-panel .compare-toggle { width: 38px; height: 38px; }
.na-gallery .na-panel .compare-toggle { opacity: 0; transition: opacity 260ms ease; }
.na-gallery .na-panel:hover .compare-toggle,
.na-gallery .na-panel:focus-within .compare-toggle,
.na-gallery .na-panel.is-default .compare-toggle { opacity: 1; }
.na-gallery .na-panel .badge-luxury { z-index: 6; }

/* Quick View, centred on the picture rather than pinned to its foot. The card
   puts it 16px off the bottom, which on a panel is exactly where the caption
   lives — it landed on top of the Details link and hid it. */
.na-gallery .na-panel .quick-view-btn {
    top: 50%;
    bottom: auto;
    transform: translateY(-50%);
    z-index: 6;
}
/* And not at all on touch. The rail's panels are ~250px wide with the caption
   already on them; the card's (hover: none) rule forces the button visible, and
   on a narrow panel it both covered the caption and had its own label clipped
   to "Q". The caption already offers the product, and a tap on the panel opens
   it. */
@media (hover: none) {
    .na-gallery .na-panel .quick-view-btn { display: none !important; }
}
/* Not an anchor — the panel is already the link, and an anchor inside an anchor
   is invalid and gets unnested by the browser. This is the same gold the
   collection tiles use on a dark ground. */
.na-gallery .na-panel .product-card-cta {
    margin-top: 4px;
    font-family: var(--font-body), sans-serif;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--color-accent);
    border-bottom: 1px solid currentColor;
    padding-bottom: 3px;
}

/* ── The caption scales with its PANEL, not with the viewport ───────────────
   The panel is 240px when it is a sliver and 720px when it is open, at the same
   viewport. So a viewport unit cannot describe this caption: clamp(19px,1.7vw,
   26px) gave a 720x900 plate and a 240px sliver identical 24.5px type, and it
   sized the whole lockup for the sliver. The result was card type on a poster —
   the title was 3.4% of the panel it sat on where the mobile rail card, which
   reads correctly, is 10.6%, and the price and the DETAILS link did not scale at
   ALL: 14px and 10.5px, the same values a 179px phone card uses, and a SMALLER
   price than the 299px shop tile's 16px.

   Container queries are the unit that actually names the thing that varies. One
   declaration then covers the sliver, the open panel and the phone card, and
   because the size resolves from the panel's width it interpolates across the
   500ms flex-basis transition instead of snapping — measured mid-open at a
   625px panel: 31.3px, on its way from 23 to 30.

   Sizes are fitted to the measured panel widths, not chosen by eye:

     viewport   panel     title  price strike %off DETAILS  pad
      1025      513x641    23      16    14    12.5   11     28
      1280      640x800    26      16    14    12.5   11     36
      1440      720x900    28      16    14    12.5   11     40
      1600+     800x1000   30      16    14    12.5   11     44

   calc(10.5px + 2.44cqw) passes through 23 at 513 and 30 at 800, so the two
   clamp ends are hit exactly rather than approached. Only the title and the
   inset ride the panel; the four information lines are canvas-invariant, which
   is what the house already does with its on-photo micro-type — the same 11px
   label serves a 259px occasion tile and a 1440px banner. Every one of those
   four is an existing house value: 16 the shop card's price, 14 and 12.5 this
   component's own, 11 the occasion tile label at the same 0.16em.

   The title stops at 30. The panel stops growing at 800px and the section
   heading above never grows at all, so the name has to plateau too.

   Written as two declarations per property, px first and cq second, and NOT
   through a custom property. A var() holding a cq value stays valid at parse
   time and fails at computed-value time, which resolves to unset — padding 0,
   not the fallback. Written directly, a browser without container units drops
   the second declaration while parsing and keeps the first, so it renders
   exactly what it renders today. That is also why the scrim inset repeats the
   padding clamp instead of referring to it. */
@media (min-width: 1025px) {
    /* Safe because the panel's width comes from `flex: 0 0 <length>`. Inline-size
       containment removes an element's intrinsic width contribution, so anything
       that later sizes this panel from its CONTENT — flex-basis: auto, or
       flex: 1 1 auto — would collapse it to zero. It does not contain block
       size, so the image's height: 100% still resolves. */
    .na-gallery .na-panel { container-type: inline-size; }

    .na-gallery .na-panel .product-card-title {
        font-size: clamp(19px, 1.7vw, 26px);
        font-size: clamp(23px, calc(10.5px + 2.44cqw), 30px);
    }
    .na-gallery .na-panel .product-card-price,
    .na-gallery .na-panel .price-now        { font-size: 16px; }
    .na-gallery .na-panel .price-mrp-strike { font-size: 14px; }
    .na-gallery .na-panel .price-off        { font-size: 12.5px; }
    .na-gallery .na-panel .product-card-cta { font-size: 11px; }

    .na-gallery .na-panel .product-card-details {
        padding: 34px;
        padding: clamp(28px, 5.6cqw, 44px);
        gap: 8px;
    }
    /* The scrim has to grow with the caption or the fix undoes itself.
       The gradient's stops are percentages of THIS box, so a taller caption
       inside a fixed-height box slides the title up into the thin end of the
       fade — which is exactly how the title came to measure 2.05:1 before the
       -58px lead-in was added. 58 was padding + 24 when the padding was 34;
       keeping that relationship is what holds the 0.68 stop above the name at
       every size. */
    .na-gallery .na-panel .product-card-details::before {
        inset: -58px 0 0 0;
        inset: calc(-1 * (clamp(28px, 5.6cqw, 44px) + 24px)) 0 0 0;
    }
    /* A long name must not recompose the plate. Three lines of a 30px serif
       pushes the price and the link off the bottom of a 1000px panel and drags
       the whole lockup into the pale part of the photograph. */
    .na-gallery .na-panel .product-card-title {
        display: -webkit-box;
        -webkit-box-orient: vertical;
        -webkit-line-clamp: 2;
        line-clamp: 2;
        overflow: hidden;
    }
}

/* The heading is not in here. It is the ordinary .section-title-wrapper above
   the gallery, inside .container, like every other section on the page.

   What used to be here: the heading absolutely positioned in the open panel's
   top left, a radial wash under it, and a gradient down the gallery's left
   third to keep white type off the photograph. All three are gone with it. The
   wash was never decoration — white "NEW ARRIVALS" measured 2.17:1 over garment
   photography shot on pale studio walls — and the gallery gradient existed only
   to serve it, at the cost of dimming the corner of the one garment the section
   exists to show. */


/* ── The account page's wishlist tab ───────────────────────────────────────
   The grid is .products-grid, the same one /shop and /wishlist use, because the
   cards in it are the same shared card. Only the empty state is local. */
.acc-wishlist-grid { margin-top: 4px; }
.acc-wishlist-empty {
    padding: 44px 0;
    text-align: center;
}
.acc-wishlist-empty-text {
    margin: 0 0 18px;
    font-family: var(--font-body), sans-serif;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-secondary);
}

/* ══════════════════════════════════════════════════════════════════════════
   ONE PAGER FOR EVERY RAIL  (.dv-pager)
   ──────────────────────────────────────────────────────────────────────────
   Five carousels on this page had grown five unrelated navigations: the hero's
   44px dark squares on the image, Collections' pair of outlined squares that
   appear ONLY under 768px, New Arrivals' burgundy pill, Trending's 48px circles
   hung out in the page gutter with a counter and a progress bar under them, and
   Shop by Occasion with no control at all. Four of the five also deleted their
   navigation somewhere — .cat-rail-nav above 768, .na-pager below 1024,
   .pf-arrow below 767, the hero below 480 — so on a phone most of the homepage
   was swipe-only, which is invisible, unreachable by keyboard, and the one
   affordance a switch user cannot perform.

   This is the single control they all use instead. It is the New Arrivals pill,
   generalised: the only one of the five that was a block in NORMAL FLOW carrying
   its own ground, which is what lets it sit under any rail — including the
   Occasion strip, where Owl clips its stage at the element's box and ignores
   stagePadding under autoWidth, so nothing can hang outside the edges and
   nothing can live inside without scrolling away with the tiles.

   Geometry is flush: a 44px disc in a 44px capsule with no pill padding, so the
   disc's circle and the capsule's terminal are the same curve. The old pill
   padded 34px buttons by 5px, which made a 54px pill around a target under the
   44px floor.

   The counter is opt-in, via data-pager-mode="count". A page number under a
   curated row of occasion tiles reframes browsing as a chore; it belongs where
   paging is a real idea (New Arrivals' sets of four, Trending's 12 products)
   and not where the rail simply slides. */
.dv-pager {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    padding: 16px var(--dv-gutter) 0;
}
/* An author display rule beats the [hidden] attribute, and this element has
   one — so hidden has to be restated or the pager shows through it. The same
   trap is documented at .cat-rail-nav. */
.dv-pager[hidden] { display: none; }

/* The gutter is applied ONCE, not twice.
   ────────────────────────────────────────────────────────────────────────────
   .dv-pager carries `padding-inline: var(--dv-gutter)` so a pager under a
   full-bleed rail lines up with the page. Three of them are not under
   full-bleed rails though — Collections, The Dievon Edit and Shop by Occasion
   sit inside .container, which already supplies that same gutter. Those got it
   twice and their arrows sat at 24px from the edge while the hero's and New
   Arrivals' sat at 12, on the same screen.

   Inside a container the container is the gutter, so the pager adds none. */
.container .dv-pager { padding-inline: 0; }

.dv-pager-pill {
    display: flex;
    align-items: center;
    height: 44px;
    background: var(--color-primary-dark);
}
.dv-pager-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    /* min-* as well as width/height: this stylesheet has had button sizes
       flattened by a shorthand before, and 44px is the touch floor. */
    width: 44px;
    height: 44px;
    min-width: 44px;
    min-height: 44px;
    padding: 0;
    border: 0;
    color: var(--color-accent);
    background: transparent;
    cursor: pointer;
    /* Kills the 300ms tap delay and the grey iOS flash over a round control. */
    touch-action: manipulation;
    -webkit-tap-highlight-color: transparent;
    transition: background-color 200ms ease, color 200ms ease;
}
.dv-pager-btn svg { width: 19px; height: 19px; display: block; }
.dv-pager-btn:hover { background: var(--color-accent); color: var(--color-primary-dark); }
/* With the tap highlight suppressed and no :active, a tap on a round button
   reads as a dead control. */
.dv-pager-btn:active { background: var(--color-accent); color: var(--color-primary-dark); }

/* Focus, and why it is spelled out rather than inherited.
   ──────────────────────────────────────────────────────────────────────────
   The site-wide ring is burgundy with a white halo, which is right on the pale
   pages and invisible here: a burgundy ring on a burgundy pill has nothing to
   read against. So this control draws its own, and two things about it are
   deliberate.

   White, and INSET. The button fills its capsule edge to edge — 44px in a 44px
   pill with no padding — so a ring drawn outside would spill past the pill onto
   the page rather than reading as part of the control. A negative offset keeps
   it on the burgundy, where white is 16.82:1.

   And it refuses the hover fill. Sharing one declaration between :hover and
   :focus-visible would put the white ring against the gold ground at 2.10:1,
   under the 3:1 an indicator needs — so focus keeps the burgundy and only
   hover goes gold.

   (This block used to restate border-radius as well, because the site-wide
   ring forced 2px onto every focused element and squared off what was then a
   round button. Both the button and that declaration are square now.) */
.dv-pager-btn:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: -4px;
    box-shadow: none;
    background: transparent;
    color: var(--color-accent);
}

.dv-pager-count {
    font-family: var(--font-body), sans-serif;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.1em;
    /* The digits change under the reader's eye; without tabular figures the
       counter jitters horizontally every time a 1 becomes a 4. */
    font-variant-numeric: tabular-nums;
    color: var(--color-accent);
    min-width: 3.6em;
    text-align: center;
}
.dv-pager:not([data-pager-mode="count"]) .dv-pager-count { display: none; }

/* Windows High Contrast throws background and color away, which would leave an
   edgeless shape with no boundary between prev and next. */
@media (forced-colors: active) {
    .dv-pager-pill { border: 1px solid CanvasText; }
    .dv-pager-btn + .dv-pager-btn { border-left: 1px solid CanvasText; }
}
@media (prefers-reduced-motion: reduce) {
    .dv-pager-btn { transition: none; }
}

/* ── Back to top ───────────────────────────────────────────────────────────
   Same geometry it always had — 40px, above the WhatsApp button, which sits at
   bottom 25px and is 50px tall — but declared here instead of in a style
   attribute, and hidden until the reader is a screenful down.

   visibility, not the hidden attribute, because the two states have to be able
   to cross-fade; and unlike opacity alone, visibility: hidden also takes the
   button out of the tab order, so a keyboard reader at the top of the page does
   not tab onto a control they cannot see. */
#backToTopBtn {
    position: fixed;
    bottom: 85px;
    right: 25px;
    width: 40px;
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 0;
    background: var(--color-primary);
    color: #ffffff;
    font-size: 16px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
    z-index: 998;
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transition: opacity 220ms ease, visibility 220ms ease, background-color 200ms ease;
}
#backToTopBtn.is-visible { opacity: 0.8; visibility: visible; }
#backToTopBtn.is-visible:hover,
#backToTopBtn.is-visible:focus-visible { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
    #backToTopBtn { transition: none; }
}

/* ── Over a photograph ─────────────────────────────────────────────────────
   The hero is the one band where the pager cannot sit underneath: it is
   full-bleed with the next section hard against its bottom edge, so "under the
   rail" would put the control on top of Dievon Collections. It goes in the same
   bottom-right corner, over the image.

   The shadow is not decoration. The pill's ground is a fixed burgundy and its
   glyphs are fixed gold, so the TEXT is safe on any banner — but the pill's own
   EDGE has to be perceivable too, and a burgundy capsule on a dark lehenga shot
   has no boundary at all. A shadow works where a hairline does not, because it
   darkens whatever is behind it rather than assuming the banner is light. */
.dv-pager--onmedia {
    position: absolute;
    right: var(--dv-gutter);
    bottom: clamp(20px, 3.4vw, 40px);
    z-index: 10;
    padding: 0;
}
.dv-pager--onmedia .dv-pager-pill {
    box-shadow: 0 2px 16px rgba(0, 0, 0, 0.45);
}
/* This used to carry a 96px inset to clear #backToTopBtn, which was pinned to
   the viewport with no scroll threshold and so sat in this corner at the top of
   every page — the two came within 7px at 1280, 1440 and 1600. That button now
   appears only after a screenful of scrolling, by which point the hero and its
   pager are gone from the viewport; measured across scroll positions at five
   sizes, the two are never on screen together. So the inset is dropped and the
   pager keeps the page gutter, which is the alignment every other rail uses. */

/* Announced, never shown. The visible counter is aria-hidden because it is
   repainted on resize as well as on a press, and a live region wired to that
   would fire on every address-bar collapse on a phone. This region is written
   ONLY by a button press, and it carries the section name — "New Arrivals,
   page 2 of 2" is worth the same bytes as "2 / 2" and is far more use.
   The clip/clip-path pair is the pattern already used by the card's compare
   label; it is here so the next component does not inline a fourth copy. */
.dv-sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* The New Arrivals pager is the shared .dv-pager, defined above. Its placement
   was decided here and the reasoning is worth keeping: under the photographs,
   right aligned to the page gutter, never on them. Every corner inside the
   gallery is taken or unsafe — bottom right is where the open panel's caption
   appears, and top right is where a portrait photograph puts the model's head,
   so a pill there sat on her face and would on any garment shot the same way.
   That is the rule the shared component now applies to all four rails. */


/* The whole panel is the link, so the ring has to be on the panel. inset, or a
   ring on a flush panel is clipped by its neighbour. */
.na-panel:focus-visible {
    outline: 2px solid var(--color-secondary);
    outline-offset: -4px;
}

@media (prefers-reduced-motion: reduce) {
    .na-panel,
    .na-panel .card-img,
    .na-panel .product-card-details { transition: none; }
    /* !important, because .product-card carries `transition: all 0.5s
       !important` and would otherwise keep animating for someone who has asked
       the operating system for less movement. */
    .na-panel { transition: none !important; }
    .na-panel:hover .card-img,
    .na-panel:focus-visible .card-img,
    .na-panel:focus-within .card-img { transform: none; }
}

/* ── Touch: no hover, so no expanding ──────────────────────────────────────
   A hover-expand gallery on a phone is a gallery with one garment visible and
   no way to reach the others: the first tap would only be read as a hover and
   the second as the link. So below 1024px the composition changes rather than
   shrinking — the panels become one full-bleed swipe rail running to both
   edges of the glass, one garment and a third of the next in view, every
   caption already visible because there is no hover to reveal it, and a tap
   goes straight to the product.

   A rail rather than a grid, for two reasons. Four products in "one big, then
   the rest two across" leaves the fourth alone in a half row with a white hole
   beside it — the arrangement is only whole at three, five, seven. And the
   rail takes every arrival, not four: the desktop pages through them with the
   arrows, and swiping is the same gesture the collections rail directly above
   this one already uses.

   Every desktop hover rule is undone here rather than left to fall through: a
   stylus or a hybrid laptop can produce :hover on a touch layout, and a panel
   that expanded to 50% of the rail would shove its neighbours off screen. */
@media (max-width: 1024px) {
    /* Every panel selector in here is .na-gallery-scoped, and it has to be.
       The desktop rules above were all raised to (0,3,0) so they would beat the
       shared card's own styling — which left this block's bare `.na-panel` at
       (0,1,0) losing to `.na-gallery .na-panel` whatever the media query said.
       The rail rendered one 179px card followed by three 60px slivers, on
       repeat: the desktop gallery's flex-basis leaking through a breakpoint
       that was supposed to have replaced it. A media query changes which rules
       are LIVE, never which one wins. */
    .na-gallery {
        /* Authoritative, because the tile width is computed from it below and
           responsive.css loads after this file. */
        --na-rail-gap: 12px;

        display: flex;
        gap: var(--na-rail-gap);
        height: auto;
        overflow-x: auto;
        overflow-y: hidden;
        background: transparent;
        scroll-snap-type: x mandatory;
        -webkit-overflow-scrolling: touch;

        /* Full bleed, with nothing to cancel. Every other rail on the site sits
           inside .container and has to pull back out of its gutter with a
           negative margin; this section is outside .container already, so the
           gallery's border box IS the screen. Adding that negative margin here
           pushed the rail 16px past both edges and the whole PAGE scrolled
           sideways — measured, not guessed. Only the 16px inset is wanted, and
           it is the same 16px as .dv-rail-bleed, which this element cannot
           simply use because above 1024px it is the gallery. */
        padding-inline: 16px;
        scroll-padding-inline-start: 16px;
        scrollbar-width: none;
        -ms-overflow-style: none;
    }
    .na-gallery::-webkit-scrollbar { display: none; }

    /* One tile and a third of the next: t + gap + t/3 = the visible width, so
       t = (C - gap) / 1.35. Solved rather than guessed at, because a flat
       percentage gives a different number of tiles at 320 than at 430 — the
       collections rail was rebuilt for exactly this. The third of a tile is
       what tells you the rail scrolls; without it a phone user sees one photo
       and no reason to swipe. */
    .na-gallery .na-panel,
    .na-gallery .na-panel.is-default,
    .na-gallery .na-panel:hover,
    .na-gallery .na-panel:focus-visible,
    .na-gallery .na-panel:focus-within,
    .na-gallery:hover .na-panel.is-default:not(:hover),
    .na-gallery:focus-within .na-panel.is-default:not(:focus-within) {
        flex: 0 0 calc((100% - var(--na-rail-gap)) / 1.35);
        height: auto;
        aspect-ratio: 3 / 4.2;
        scroll-snap-align: start;
    }

    /* cover everywhere on touch. contain in a tile this size leaves bars on a
       screen that has no width to spare. */
    .na-gallery .na-panel .card-img,
    .na-gallery .na-panel.is-default .card-img,
    .na-gallery .na-panel:hover .card-img,
    .na-gallery .na-panel:focus-visible .card-img,
    .na-gallery .na-panel:focus-within .card-img,
    .na-gallery:hover .na-panel.is-default:not(:hover) .card-img,
    .na-gallery:focus-within .na-panel.is-default:not(:focus-within) .card-img {
        object-fit: cover;
        object-position: center 18%;
        transform: none;
    }

    /* Always visible: there is no hover to reveal it. */
    .na-gallery .na-panel { background: transparent; border: 0; padding: 0; }
    .na-gallery .na-panel .card-img-container { border-bottom: 0; }
    .na-gallery .na-panel .product-card-details,
    .na-gallery .na-panel.is-default .product-card-details,
    .na-gallery:hover .na-panel.is-default:not(:hover) .product-card-details,
    .na-gallery:focus-within .na-panel.is-default:not(:focus-within) .product-card-details {
        opacity: 1;
        transform: none;
        padding: 16px;
        gap: 4px;
    }
    /* The title is already right here — 19px on a 179px card is the one caption
       on the site whose proportion reads correctly, and it is the anchor the
       desktop scale above was fitted to. Only the two lines that had fallen
       below their own floor move. 13px was never a price value anywhere on the
       site (it is the occasion banner's CTA); 14 is. And 9.5px uppercase gold
       tracked over a photograph is under any sensible floor for a link — 10.5
       is what the shop card and the photo fan both use, so the tracking comes
       from the base rule rather than being overridden thinner here. */
    .na-gallery .na-panel .product-card-title { font-size: clamp(15px, 4vw, 20px); }
    .na-gallery .na-panel .product-card-price { font-size: 14px; }
    .na-gallery .na-panel .product-card-cta   { font-size: 10.5px; }
}

/* The rail needs no one-column fallback. At the narrowest screen the site
   supports a tile is (320 - 32 - 12) / 1.35 = 205px, which is wider than the
   two-column grid this replaced ever managed, and the arrangement does not
   change shape — it just scrolls further. */


/* ══════════════════════════════════════════════════════════════════════════
   BEST SELLERS / TRENDING NOW — the photo fan
   ──────────────────────────────────────────────────────────────────────────
   Four garments arranged like physical prints dropped on a table: alternating
   rotations, overlapping slightly, straightening and lifting under the pointer.
   A fifth sits half in frame so the strip reads as continuing.

   THESE ARE NOT PRODUCT CARDS. The boxed card — white ground, hairline border,
   name and price stacked underneath — is a separate component used elsewhere on
   the site, and it is not what this section is. The tiles here are photographs
   with the caption written over them, built from their own markup. Only the
   LOGIC is shared with the card: the same price resolver, badge rule, URL and
   alt-text builders and wishlist handler, so the two can never disagree about a
   price or a link while looking completely different.

   No carousel library either. Owl drives the hero and Shop by Occasion, but it
   wraps every item and clips its stage — a print that rotates, lifts and scales
   would be cut off at the moment it is being looked at. This is a flex track
   moved by transform.
   ══════════════════════════════════════════════════════════════════════════ */
.photo-fan-section {
    /* How many prints are in frame. The slider reads this back off the rendered
       layout rather than keeping its own copy, so these four breakpoints are the
       only place the number is written down. The fraction is the point: a
       partial print is what says the strip continues. */
    --pf-visible: 4.2;
    /* How far the prints sit into each other — what makes it a fan rather than a
       row of tiles with gaps. */
    --pf-overlap: 26px;
    /* Room for the lift and the scale, so overflow:hidden on the viewport does
       not shave the top off whichever print is being hovered. */
    --pf-lift-room: 42px;

    /* Warm ivory, not the white every other band uses. The prints are shot on
       pale studio walls and a white page makes them float with no edge; the
       ivory gives them a table to sit on. */
    background: var(--bg-surface-soft);
}

/* ── Head: tabs left, View all right ───────────────────────────────────── */
.photo-fan-section .pf-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
    margin-bottom: 34px;
}
.photo-fan-section .pf-tabs { display: inline-flex; gap: 34px; }
.photo-fan-section .pf-tab {
    position: relative;
    padding: 0 0 10px;
    background: none;
    border: 0;
    cursor: pointer;
    font-family: var(--font-body), sans-serif;
    font-size: 13px;
    font-weight: 600;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--color-accent-ink);
    transition: color 220ms ease;
}
/* The underline scales rather than appearing: a border toggling on and off
   shifts the label by its own width. */
.photo-fan-section .pf-tab::after {
    content: '';
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    background: var(--color-primary-dark);
    transform: scaleX(0);
    transition: transform 260ms cubic-bezier(0.4, 0, 0.2, 1);
}
.photo-fan-section .pf-tab.is-active { color: var(--color-primary-dark); }
.photo-fan-section .pf-tab.is-active::after { transform: scaleX(1); }
.photo-fan-section .pf-tab:focus-visible { outline: 2px solid var(--color-primary-dark); outline-offset: 4px; }

.photo-fan-section .pf-viewall {
    font-family: var(--font-body), sans-serif;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--color-primary-dark);
    text-decoration: none;
    border-bottom: 1px solid var(--color-accent-ink);
    padding-bottom: 3px;
    transition: color 200ms ease, border-color 200ms ease;
}
.photo-fan-section .pf-viewall:hover { color: var(--color-accent-ink); }
.photo-fan-section .pf-viewall:focus-visible { outline: 2px solid var(--color-primary-dark); outline-offset: 3px; }
.photo-fan-section .pf-single-title { margin: 0; }

/* ── Panel ─────────────────────────────────────────────────────────────── */
.photo-fan-section .pf-panel { position: relative; }
/* The inactive panel is [hidden], so it costs no layout, cannot be tabbed into
   and is invisible to a screen reader. The fade is on the way IN only — fading
   out would need the outgoing panel to stay in flow and the two would overlap. */
.photo-fan-section .pf-panel.is-active { animation: pf-fade-in 340ms ease both; }
@keyframes pf-fade-in { from { opacity: 0; } to { opacity: 1; } }

.photo-fan-section .pf-viewport {
    overflow: hidden;
    padding-block: var(--pf-lift-room);
    margin-block: calc(-1 * var(--pf-lift-room));
    padding-inline: 4px;
    margin-inline: -4px;
    /* Horizontal gestures belong to the slider, vertical ones to the page.
       Without this a swipe down the phone that starts on a print is swallowed. */
    touch-action: pan-y;
    cursor: grab;
    /* Focused only by script, when a print is paged out from under the pointer;
       never a tab stop of its own. */
    outline: none;
}
.photo-fan-section .pf-track {
    display: flex;
    align-items: flex-start;
    list-style: none;
    margin: 0;
    padding: 0;
    /* transform only. Nothing here triggers layout. */
    transition: transform 620ms cubic-bezier(0.22, 0.61, 0.36, 1);
    will-change: transform;
}
.photo-fan-section .pf-track.is-dragging { transition: none; }
.photo-fan-section .pf-viewport:has(.pf-track.is-dragging) { cursor: grabbing; }

/* A print is a link wrapped round a photograph, and both are draggable objects
   to the browser by default. Starting a drag on one fires native drag-and-drop,
   which cancels the pointer stream mid-gesture — measured: pointerdown, one
   pointermove, then pointercancel, and the strip never moved. */
.photo-fan-section .pf-item {
    flex: 0 0 calc(100% / var(--pf-visible));
    max-width: calc(100% / var(--pf-visible));
    /* The overlap, halved onto both sides so the row stays centred in the
       viewport instead of drifting left as prints are added. */
    margin-inline: calc(var(--pf-overlap) / -2);
    position: relative;
    /* Earlier prints on top. A stack of photographs spread across a table falls
       left-over-right, and source order paints the opposite way — which put each
       print's right-hand corner UNDER its neighbour and swallowed the wishlist
       heart on every one of them. 40 is simply higher than any plausible number
       of prints in one tab; the hovered print overrides it below. */
    z-index: calc(40 - var(--pf-i, 0));
}

/* ══ THE PRINT — DESKTOP AND TABLET ONLY ═══════════════════════════════════
   Everything in this block dresses the SHARED product card as a photographic
   print: the box stripped off, the caption lifted onto the picture, the tilt,
   the lift on hover.

   It is gated at 768px and up, and that gate is the design decision, not a
   convenience. Below it the base .product-card styling applies untouched, so a
   phone gets the ordinary shop card — one card design on a phone, two only on a
   desktop. Written as a gate rather than as a list of undo rules further down,
   because "revert fourteen properties" is a list that silently rots the next
   time one is added up here.
   ══════════════════════════════════════════════════════════════════════════ */
@media (min-width: 768px) {
    /* ── The print ───────────────────────────────────────────────────────────────
       Everything below dresses the SHARED product card in its 'home' variant. The
       markup is includes/product_card.php — the same file that draws the shop grid —
       so there is no second component to keep in step. .pf-card exists only inside
       this section, and .product-card--home only on the home page, so the shop is
       untouched by any of it. */
    .photo-fan-section .pf-card {
        position: relative;
        /* The card's own chrome, undone. This is a photograph, not the boxed white
           tile the shop uses. */
        background: transparent;
        border: 0;
        padding: 0;
        overflow: visible;
        transform-origin: center bottom;
        /* !important, reluctantly. .product-card carries
           `transition: all 0.5s ease 0s !important`, which outranks anything written
           here — measured: the fan's 400ms never applied and the lift ran at 500ms,
           outside the 350-450ms the design calls for. `all` is also wrong for a card
           that moves: it animates layout properties too. Naming transform keeps it
           on the compositor. */
        transition: transform 400ms cubic-bezier(0.22, 0.61, 0.36, 1) !important;
    }
    .photo-fan-section .pf-card:hover { transform: none; }   /* the item drives it, not the card */

    /* The tilt used to live here as .pf-item:nth-child(4n+k). It does not any
       more: Owl wraps every print in its own .owl-item, so each .pf-item became
       an only child and ALL of them matched 4n+1 — the whole fan straightened
       out. The pattern moved to the wrapper, where the siblings actually are
       (search for `.owl-item:nth-child(5n` further down this file).

       These four rules were left behind. They still matched every print, and
       only lost because the replacement out-ranks them on specificity — so the
       fan was one selector away from silently flattening again, while the
       comment above them claimed they were what produced it. Removed rather
       than left as a trap for whoever reads this next. */

    .photo-fan-section .pf-card .card-img-container {
        position: relative;
        /* 3:4, held by the box rather than by the file, so nothing shifts while the
           photograph is still arriving. */
        aspect-ratio: 3 / 4;
        height: auto;
        overflow: hidden;
        background: var(--color-primary-dark);
        box-shadow: 0 14px 34px rgba(58, 12, 27, 0.14);
    }
    .photo-fan-section .pf-card .card-img-link { display: block; height: 100%; }
    .photo-fan-section .pf-card .card-img {
        width: 100%;
        height: 100%;
        object-fit: cover;
        /* The garment and the face are in the upper two thirds of a fashion plate;
           centring the crop takes the head off. */
        object-position: center 18%;
    }
    .photo-fan-section .pf-card .card-img-fallback {
        display: flex;
        align-items: center;
        justify-content: center;
        height: 100%;
        font-size: 52px;
        opacity: 0.5;
    }

    /* ── The caption ─────────────────────────────────────────────────────────────
       The card's own .product-card-details, moved rather than rebuilt — so the name,
       the price, the strike-through, the "From" spread, the bag note and the link
       are all still the markup the shop grid draws.

       A SIBLING of the image container, not a child, which is what lets it flip
       between lying over the photograph here and sitting below it on a phone. A
       caption inside the container would be clipped away by the overflow:hidden
       that keeps the picture in its ratio. */
    .photo-fan-section .pf-card .product-card-details {
        position: absolute;
        inset: auto 0 0 0;
        z-index: 2;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 6px;
        /* Scaled to this box: a print is ~283px across where the New Arrivals panel
           these values come from is 720. */
        padding: clamp(16px, 1.3vw, 22px);
        /* Clear of the print lying over this one's left edge. The captions used
           to be revealed one at a time on hover, where the hovered print is on
           top and nothing covers it; now that they are all on at once, the
           resting ones were being sliced — "IEW PRODUCT", "lid Co-Ords 2". Same
           inset the badge and the compare toggle already use. */
        padding-left: calc(var(--pf-overlap) + 14px);
        margin: 0;
        text-align: left;
        /* Always on. The name and the price are what a shopper is scanning for,
           and hiding them behind a hover means four photographs that say nothing
           until you touch them. What the hover adds is Quick View, below. */
        opacity: 1;
        /* Not `none`: a transform is what makes this a stacking context, which is
           what keeps the scrim behind the type. isolate says the same thing out
           loud so removing the transform later cannot quietly break it. */
        transform: translateY(0);
        isolation: isolate;
    }
    /* The scrim reaches 58px ABOVE the caption rather than being a background on it.
       Gradient stops are percentages of their own box, and this box is small — a
       print is ~377px tall where the gallery panel the stops came from is 778 — so
       pasted verbatim the name, the topmost and weakest line, measured 2.72:1
       against the 4.5:1 it needs. Extending the box upward puts every line in the
       strong part and leaves the fade where there is no text to fail.

       Tuned, not maximised. White type needs its ground at or below L 0.183 to clear
       4.5:1, which over a pure-white photograph means at least 0.55 alpha, so the
       stop where the name sits is held at 0.68. 0.58 was tried and is not enough:
       the name's position inside the box MOVES with the length of the name — a
       one-line title sits higher in the fade than a two-line one — and over a bright
       mustard photograph a short title measured 3.49:1. The stop has to clear the
       worst case, not the one that happened to be tested first.

       Still not maximised. An early attempt held 0.62 up to 70% of a taller box and
       measured 16:1 — and painted out the bottom half of the garment, so the price
       was perfectly legible and the hem being sold was not. */
    .photo-fan-section .pf-card .product-card-details::before {
        content: '';
        position: absolute;
        inset: -58px 0 0 0;
        z-index: -1;
        background: linear-gradient(to top,
            rgba(0, 0, 0, 0.88) 0%,
            rgba(0, 0, 0, 0.80) 34%,
            rgba(0, 0, 0, 0.68) 58%,
            rgba(0, 0, 0, 0.24) 84%,
            rgba(0, 0, 0, 0) 100%);
    }

    /* The same caption New Arrivals writes. A shopper meets this pairing twice on
       the page and it has to be the same thing both times. */
    .photo-fan-section .pf-card .product-card-title {
        margin: 0;
        font-family: var(--font-heading);
        font-weight: var(--font-heading-weight);
        /* Same clamp shape the New Arrivals gallery uses, stepped down for the
           narrower print. */
        font-size: clamp(16px, 1.3vw, 20px);
        line-height: 1.15;
        text-wrap: balance;
    }
    .photo-fan-section .pf-card .product-card-title a { color: #ffffff; text-decoration: none; }
    .photo-fan-section .pf-card .product-card-price {
        margin: 0;
        font-family: var(--font-body), sans-serif;
        font-size: 14px;
        font-weight: 500;
        letter-spacing: 0.02em;
        color: rgba(255, 255, 255, 0.92);
    }
    /* The price is spans now, not one text node: .price-now and .price-off were
       added when the shop card was redesigned, and neither inherits the caption
       colour — the default gives them dark burgundy, which on this scrim is
       invisible. Colour follows the element that actually carries the text. */
    .photo-fan-section .pf-card .price-now { color: rgba(255, 255, 255, 0.92); font-size: 14px; font-weight: 500; }
    .photo-fan-section .pf-card .price-off { color: var(--color-accent); font-size: 11.5px; }
    .photo-fan-section .pf-card .price-mrp-strike { color: rgba(255, 255, 255, 0.66); font-size: 12.5px; }
    .photo-fan-section .pf-card .price-from-label { font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; opacity: 0.75; }
    .photo-fan-section .pf-card .product-card-bag-note {
        margin: 0;
        font-size: 10.5px;
        letter-spacing: 0.1em;
        text-transform: uppercase;
        color: var(--color-accent);
    }
    /* !important on the colour only, to beat .btn-luxury's own
       `color: #ffffff !important`. Without it the call to action came out white
       rather than gold — legible, but not the gold rule the rest of the page uses
       for "go and look at this". */
    .photo-fan-section .pf-card .product-card-cta {
        color: var(--color-accent) !important;
        width: auto;
        margin: 4px 0 0;
        padding: 0 0 3px;
        background: none;
        border: 0;
        border-bottom: 1px solid var(--color-accent);
        color: var(--color-accent);
        font-size: 10.5px;
        font-weight: 600;
        letter-spacing: 0.16em;
        text-transform: uppercase;
    }
    .photo-fan-section .pf-card .product-card-cta.sold-out-cta { color: rgba(255, 255, 255, 0.78) !important; border-bottom-color: currentColor; }

    /* Hover and keyboard focus do the same thing, or the section is usable with a
       mouse only. */
    .photo-fan-section .pf-item:hover,
    .photo-fan-section .pf-item:focus-within { z-index: 60; }
    .photo-fan-section .pf-item:hover .pf-card,
    .photo-fan-section .pf-item:focus-within .pf-card {
        /* Straight, lifted, barely enlarged. 1.03 is the ceiling; past that it reads
           as a zoom rather than a print being picked up. */
        transform: rotate(0deg) translateY(-14px) scale(1.03);
    }
    .photo-fan-section .pf-item:hover .card-img-container,
    .photo-fan-section .pf-item:focus-within .card-img-container { box-shadow: 0 26px 56px rgba(58, 12, 27, 0.26); }
    /* ── Quick View, the thing the hover is for ────────────────────────────
       The base hides it with `display: none` and reveals it on
       .card-img-container:hover. That is half the story here: the hover target
       is the ITEM — the card inside it is scaled and rotated, and the pointer is
       often over the lifted card rather than the container beneath — and a
       keyboard shopper never fires :hover at all. Both are covered.

       display, not opacity, deliberately. A button faded to nothing is still in
       the tab order and still announced; display:none is not, so tabbing into a
       print reveals Quick View and the next Tab reaches it, which is the order a
       sighted mouse user gets too. */
    .photo-fan-section .pf-item:hover .quick-view-btn,
    .photo-fan-section .pf-item:focus-within .quick-view-btn { display: block !important; }
    .photo-fan-section .pf-card .quick-view-btn {
        /* Centred on the picture. The base pins it 15px off the bottom, which is
           where the caption now lives permanently. */
        top: 50%;
        bottom: auto;
        left: 50%;
        transform: translate(-50%, -50%);
        z-index: 5;
        padding: 11px 22px;
        border: 1px solid var(--color-accent);
        background: rgba(255, 255, 255, 0.95);
        color: var(--color-primary-dark);
        font-size: 10.5px;
        font-weight: 600;
        letter-spacing: 0.16em;
    }
    .photo-fan-section .pf-card .quick-view-btn:focus-visible {
        outline: 2px solid var(--color-accent);
        outline-offset: 3px;
    }
    .photo-fan-section .pf-card .card-img-link:focus-visible,
    .photo-fan-section .pf-card .product-card-title a:focus-visible,
    .photo-fan-section .pf-card .product-card-cta:focus-visible {
        outline: 2px solid var(--color-accent);
        outline-offset: 2px;
    }

    /* ── Badge, wishlist, compare ──────────────────────────────────────────────
       A print is a link wrapped round a photograph, and both are draggable objects
       to the browser by default. Starting a drag on one fires native drag-and-drop,
       which cancels the pointer stream mid-gesture — measured: pointerdown, one
       pointermove, then pointercancel, and the strip never moved. */
    .photo-fan-section .pf-card .badge-luxury {
        top: 12px;
        /* Clear of the neighbour lying over this print's left edge. At a flat 12px
           the discount badge was half-covered by the print in front of it. */
        left: calc(var(--pf-overlap) + 14px);
        right: auto;
        z-index: 3;
        padding: 5px 10px;
        font-size: 9.5px;
        letter-spacing: 0.14em;
        color: var(--color-accent);
        background: var(--color-primary-dark);
    }
    .photo-fan-section .pf-card .badge-sold-out { background: rgba(58, 12, 27, 0.86); }

    /* On the photograph at all times — it is the one control a shopper looks for
       without hovering first — but quiet until the print is the one in hand. */
    .photo-fan-section .pf-card .product-card-wishlist-btn {
        top: 10px;
        right: 10px;
        z-index: 4;
        display: flex;
        align-items: center;
        justify-content: center;
        /* 44px, the minimum touch target, met by the box itself. */
        width: 44px;
        height: 44px;
        background: rgba(255, 255, 255, 0.9);
        color: var(--color-primary-dark);
        opacity: 0.85;
    }
    .photo-fan-section .pf-item:hover .product-card-wishlist-btn,
    .photo-fan-section .pf-item:focus-within .product-card-wishlist-btn { opacity: 1; }

    /* Compare is the one control that would clutter a photograph, so it waits for
       the print to be picked up. It is still reachable by keyboard at any time —
       focus-within opens it — and it is never display:none, which would take it out
       of the tab order entirely. */
    /* Wishlist and compare travel together in .product-card-tools now, so they
       are positioned as a pair rather than pinned separately. Pulled in from the
       card's 14px, because these prints overlap and a control on the right edge
       of one lands on top of the next. */
    .photo-fan-section .pf-card .product-card-tools {
        top: 12px;
        right: calc(var(--pf-overlap) + 10px);
        gap: 7px;
    }
    .photo-fan-section .pf-card .product-card-wishlist-btn,
    .photo-fan-section .pf-card .compare-toggle { width: 38px; height: 38px; }
    /* Compare waits for the print to be picked up — it is the one control that
       clutters a photograph. Opacity, not display, so it keeps its place in the
       tab order; focus-within is what makes it reachable by keyboard. */
    .photo-fan-section .pf-card .compare-toggle { opacity: 0; transition: opacity 260ms ease; }
    .photo-fan-section .pf-item:hover .compare-toggle,
    .photo-fan-section .pf-item:focus-within .compare-toggle { opacity: 1; }

    /* Sold out, softened for this ground only. The shop greys the photograph to 0.6
       against a white tile, where it reads as unavailable; over this section's dark
       burgundy the same 0.6 reads as an image that failed to load. The grayscale —
       which is the part actually carrying the meaning — is untouched, and the badge
       and the button still say Sold Out in words. */
    .photo-fan-section .pf-card.sold-out .card-img,
    .photo-fan-section .pf-card.sold-out .card-img-fallback { opacity: 0.82; }
    .photo-fan-section .pf-card.sold-out .product-card-price { opacity: 0.85; }
}

/* A print is a link wrapped round a photograph, and both are draggable objects
   to the browser by default. Starting a drag on one fires native drag-and-drop,
   which cancels the pointer stream mid-gesture — measured: pointerdown, one
   pointermove, then pointercancel, and the strip never moved. Every width: a
   finger swipe is cancelled by it just as a mouse drag is. */
.photo-fan-section .pf-card a,
.photo-fan-section .pf-card img { -webkit-user-drag: none; user-select: none; }

/* ── Controls ──────────────────────────────────────────────────────────────
   The shared .dv-pager, in normal flow under the strip. What was here: a range
   counter, a progress bar, and a pair of 48px circles absolutely positioned
   OUTSIDE the viewport edges, hanging into the page gutter — which is why this
   section needed --pf-arrow-out to stop the circles pushing the whole page into
   a horizontal scrollbar at 768px, and why the arrows were deleted outright
   below 767px, leaving the strip swipe-only on every phone.

   The counter and the progress bar also said the same thing eighteen pixels
   apart. The pill says it once. */

/* ── Reduced motion ────────────────────────────────────────────────────── */
/* The rotations go too. To someone who asked for less movement, a wall of
   tilted photographs that snap straight IS the motion, not the transition. */
@media (prefers-reduced-motion: reduce) {
    /* !important on the card, because .product-card's own
       `transition: all 0.5s !important` would otherwise keep animating for
       someone who has asked the operating system for less movement. */
    .photo-fan-section .pf-card { transition: none !important; }
    .photo-fan-section .pf-track,
    .photo-fan-section .pf-card .product-card-details,
    .photo-fan-section .pf-card .compare-toggle { transition: none; }
    .photo-fan-section .pf-panel.is-active { animation: none; }
    .photo-fan-section .pf-item:nth-child(4n + 1) .pf-card,
    .photo-fan-section .pf-item:nth-child(4n + 2) .pf-card,
    .photo-fan-section .pf-item:nth-child(4n + 3) .pf-card,
    .photo-fan-section .pf-item:nth-child(4n + 4) .pf-card { transform: none; }
    .photo-fan-section .pf-item:hover .pf-card,
    .photo-fan-section .pf-item:focus-within .pf-card { transform: none; }
}

/* ── Standard desktop ──────────────────────────────────────────────────── */
@media (max-width: 1279px) {
    .photo-fan-section { --pf-visible: 3.2; --pf-overlap: 22px; }
}

/* ── Tablet ────────────────────────────────────────────────────────────── */
@media (max-width: 991px) {
    .photo-fan-section { --pf-visible: 2.2; --pf-overlap: 16px; --pf-lift-room: 30px; }
}

/* ── Touch: no hover, so nothing may hide behind one ───────────────────── */
/* A caption revealed on hover is a caption a phone cannot reach, and making the
   first tap reveal it and the second open the product is the pattern every
   shopper gets wrong. So below 768 the fan flattens and the caption comes out
   from under the gradient to sit below the photograph, permanently visible, and
   one tap opens the product.
   767, not 768: at exactly 768 this and the tablet block above both matched and
   the later won, so an iPad in portrait got the one-print phone layout. */
@media (max-width: 767px) {
    .photo-fan-section {
        /* 1.5, not 1.15. Every rail on this page shows about two thirds of the
           screen with the next item peeking; at 1.15 this one filled 83% and
           read as a different slider on the same page. */
        --pf-visible: 1.5;
        --pf-overlap: 0px;
        --pf-lift-room: 10px;
    }
    .photo-fan-section .pf-head { align-items: center; margin-bottom: 24px; }
    .photo-fan-section .pf-tabs { gap: 22px; }
    .photo-fan-section .pf-tab { font-size: 11.5px; letter-spacing: 0.16em; }

    /* No tilt. The print block above does not apply below 768, so what is in the
       rail here is the ordinary shop card — white ground, hairline border, name
       and price underneath — and a boxed card rotated a degree reads as a
       rendering fault rather than as a photograph laid down at an angle. The
       tilt belongs to the print, and the print is a desktop idea. */
    .photo-fan-section .pf-item { padding-inline: 6px; }
    /* Compare needs no rule here either way. The site hides both the toggle and
       the compare bar below 900px on purpose — the bar is fixed at z-index 9500
       and on a phone it covers the page it exists to help you shop — and this
       section inherits that rather than carving out an exception. */

    /* Arrows off: the strip is swiped, and two 48px circles over a full-width
       photograph cover the garment they are meant to help you reach. The counter
       and the progress bar stay — they are the only thing saying how much
       further the strip goes. */
    .photo-fan-section .pf-viewall { padding: 12px 0; }   /* 44px tap target */
}


/* ══════════════════════════════════════════════════════════════════════════
   PRODUCT CARD — the design in the brief
   ──────────────────────────────────────────────────────────────────────────
   Photograph, a badge top left, wishlist and compare as round buttons top
   right, Quick View on the picture when you point at it, a burgundy rule, then
   the category, the name and the price ranged left.

   Appended rather than edited into the original .product-card block above, so
   the whole redesign is one contiguous thing to read, revert or replace when
   the next iteration arrives. Everything here is the DEFAULT variant; the home
   page's .product-card--home overrides sit in the photo-fan block and are
   unaffected.
   ══════════════════════════════════════════════════════════════════════════ */
.product-card {
    /* Ranged left. The card was centred, which is fine for one product and
       restless across a grid of twenty-four: every name starts in a different
       place and the eye has nothing to run down. */
    text-align: left;
    padding-bottom: 0;
    border-color: rgba(81, 17, 38, 0.10);
}
/* The rule under the photograph. It is the only thing separating a pale garment
   shot from the white card below it, which otherwise run together. */
.product-card .card-img-container {
    border-bottom: 2px solid var(--color-primary);
}

/* ── Badge ─────────────────────────────────────────────────────────────── */
.product-card .badge-luxury {
    top: 14px;
    left: 14px;
    right: auto;
    z-index: 3;
    padding: 7px 12px;
    background: var(--color-accent-ink);
    color: #ffffff;
    font-family: var(--font-body), sans-serif;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}
/* Sold out and not-sold-here are states, not offers, so they do not wear the
   gold an offer wears. */
.product-card .badge-sold-out { background: var(--color-primary-dark); }

/* ── Wishlist and compare ──────────────────────────────────────────────── */
/* -tools, not -actions. .product-card-actions is already taken further up this
   file by a stacked, full-width button group — dead CSS from an older card, but
   live enough to win: it set flex-direction: column and stood the two round
   buttons on top of each other. */
.product-card-tools {
    position: absolute;
    top: 14px;
    right: 14px;
    z-index: 4;
    display: flex;
    align-items: center;
    gap: 8px;
}
.product-card .product-card-wishlist-btn,
.product-card .compare-toggle {
    position: static;
    display: flex;
    align-items: center;
    justify-content: center;
    /* 40px. Not the 44 a bare touch target wants — these two sit side by side on
       a 165px-wide card in a two-up mobile grid, and 44 each plus the gap starts
       crowding the garment. The 8px gap between them is what keeps a thumb from
       hitting the wrong one. */
    width: 40px;
    height: 40px;
    padding: 0;
    border: 0;
    background: #ffffff;
    color: var(--color-primary-dark);
    box-shadow: 0 2px 10px rgba(58, 12, 27, 0.16);
    cursor: pointer;
    opacity: 1;
    transition: background-color 200ms ease, color 200ms ease, transform 200ms ease;
}
.product-card .product-card-wishlist-btn:hover,
.product-card .compare-toggle:hover { transform: scale(1.07); }
.product-card .product-card-wishlist-btn:focus-visible,
.product-card .compare-toggle:focus-within {
    outline: 2px solid var(--color-primary);
    outline-offset: 2px;
}
.product-card .product-card-wishlist-btn i { font-size: 15px; }

/* The checkbox is still the control — see the note in product_card.php — so it
   is taken out of the layout rather than out of the DOM. Not display:none:
   that would drop it from the tab order and make compare keyboard-unreachable. */
.product-card .compare-toggle input.compare-check {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: 0;
    opacity: 0;
    pointer-events: none;
}
.product-card .compare-toggle-icon { display: flex; }
.product-card .compare-toggle-icon svg { width: 19px; height: 19px; }
/* The label text stays for a screen reader and goes for everyone else. */
.product-card .compare-toggle-word {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}
/* Selected reads as filled, so it is obvious which of three is already chosen. */
.product-card .compare-toggle:has(input:checked) {
    background: var(--color-primary);
    color: #ffffff;
    border-color: var(--color-primary);
}

/* ── Quick View ────────────────────────────────────────────────────────── */
.product-card .quick-view-btn {
    bottom: 16px;
    /* Centred with auto margins between left:0 and right:0, NOT with
       left:50% + translateX(-50%).
       ────────────────────────────────────────────────────────────────────────
       The transform trick centres correctly but sizes wrongly: an absolutely
       positioned box with `left: 50%` and `right: auto` shrink-to-fits into the
       space from that point to the container's right edge — 86px on a 173px
       card in a two-up phone grid — and `white-space: nowrap` content then
       spilled straight out of its own button, so the words ran past the edge of
       the photograph. Anchoring both sides gives it the full width to size
       against, and `margin-inline: auto` does the centring. */
    left: 0;
    right: 0;
    width: fit-content;
    max-width: calc(100% - 20px);
    margin-inline: auto;
    transform: none;
    display: none;
    align-items: center;
    gap: 9px;
    padding: 12px 22px;
    border: 0;
    background: var(--color-primary);
    color: #ffffff;
    font-family: var(--font-body), sans-serif;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    white-space: nowrap;
    z-index: 5;
}
.product-card .quick-view-eye { width: 17px; height: 17px; flex: none; }
/* On hover OR focus-within. The base opened it on hover alone, which means a
   keyboard shopper could never reach it: display:none is not focusable, so
   without this the button does not exist for them at all. */
.product-card:hover .quick-view-btn,
.product-card:focus-within .quick-view-btn { display: flex !important; }
.product-card .quick-view-btn:hover {
    background: var(--color-primary-dark) !important;
    color: #ffffff !important;
}
.product-card .quick-view-btn:focus-visible { outline: 2px solid #ffffff; outline-offset: -4px; }

/* No hover, no way to reveal it — so show it outright. Keyed on the POINTER,
   not on a width. A width was tried and was wrong: at 600px it left every
   tablet between 601 and 1024 with a Quick View button that could never appear,
   measured at 768 as a control zero pixels wide. Screen size has never been the
   question; whether the device has a hover has. */
@media (hover: none) {
    .product-card .quick-view-btn { display: flex !important; }
}

/* ── The details ───────────────────────────────────────────────────────── */
.product-card .product-card-details {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    padding: 18px 18px 20px;
    text-align: left;
}
.product-card .product-card-category {
    margin: 0;
    font-family: var(--font-body), sans-serif;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.product-card .product-card-title {
    margin: 0;
    font-family: var(--font-heading);
    font-weight: var(--font-heading-weight);
    font-size: clamp(16px, 1.25vw, 20px);
    line-height: 1.25;
    /* No reserved height. Two lines were held open so a row of cards would line
       its prices up, and the cost was a visible hole under every one-line name —
       the commonest case. Alignment is bought at the bottom instead: the details
       column grows and the Details link is pushed down by margin-top: auto, so
       the links line up across a row however long the names are, and nothing is
       padded out in the middle to achieve it. */
    min-height: 0;
    /* Ranged left, and never hyphenated mid-word — a garment name broken across
       lines is unreadable at a glance. */
    overflow-wrap: break-word;
}
.product-card .product-card-title a { color: var(--text-primary); text-decoration: none; }
.product-card .product-card-title a:hover { color: var(--color-primary); }


/* Price, saving and struck MRP on one line, wrapping as a unit rather than
   breaking a figure in half. */
.product-card .product-card-price {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 10px;
    margin: 0;
    font-family: var(--font-body), sans-serif;
}
.product-card .price-now {
    font-size: 18px;
    font-weight: 700;
    color: var(--text-primary);
}
.product-card .price-mrp-strike {
    font-size: 14px;
    font-weight: 400;
    text-decoration: line-through;
    /* --text-muted, not a lighter grey: this is still a real number and has to
       clear 4.5:1 on white like any other. */
    color: var(--text-muted);
}
.product-card .price-off {
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.04em;
    color: var(--color-primary);
}
.product-card .price-from-label {
    font-size: 11px;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-muted);
}
.product-card .product-card-bag-note {
    margin: 0;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-accent-ink);
}
/* A link, not a slab. A filled full-width button at the foot of every tile is
   the heaviest thing on a page of twenty-four of them, and it competes with the
   photograph it is selling. This is the same gold underlined treatment the home
   page already uses, so the two are now one standard rather than two. */
.product-card .product-card-cta {
    width: auto;
    /* auto, so the link is pushed to the bottom of the details column and every
       card in a row lines its link up regardless of how long the name ran. */
    margin: auto 0 0;
    padding: 0 0 3px;
    background: none !important;
    border: 0;
    border-bottom: 1px solid var(--color-accent-ink);
    /* !important to beat .btn-luxury's own `color: #ffffff !important`. */
    color: var(--color-accent-ink) !important;
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    justify-content: flex-start;
    align-self: flex-start;
    transition: color 200ms ease, border-color 200ms ease;
}
.product-card .product-card-cta:hover {
    color: var(--color-primary) !important;
    border-bottom-color: var(--color-primary);
}
.product-card .product-card-cta:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 3px; }
/* Breathing room above the link, since it no longer has a button's own padding
   holding it away from the price. */
.product-card .product-card-details { padding-bottom: 20px; }

/* ── Sold out ──────────────────────────────────────────────────────────── */
/* Badge and a greyed photograph carry it; the price stays, because a shopper
   still wants to know what the thing costs before deciding to wait for it. */
.product-card.sold-out .price-now { color: var(--text-muted); }

/* The Sold Out button was white on #e5e7eb — 1.24:1, which is not low contrast,
   it is invisible. .btn-luxury sets `color: #ffffff !important` and outranked
   the muted grey .sold-out-cta asks for, so every sold-out product on the site
   has been showing a blank grey slab.

   Fixed here rather than left for later: this is the card being redesigned, and
   shipping a new card with an unreadable state in it is shipping the bug twice.
   #5f5346 is chosen against the requirement, not by eye — white type needs its
   ground below L 0.138 to clear 4.5:1 and this ground is 0.795, so the TEXT has
   to come down instead; 0.090 puts it at 6.0:1. Muted enough to read as
   unavailable, dark enough to actually read. */
.product-card .product-card-cta.sold-out-cta {
    /* No ground to sit on any more, so the old white-on-grey problem cannot
       recur — but the colour still has to earn its 4.5:1 on white, and
       --text-muted does at 5.41:1. */
    color: var(--text-muted) !important;
    border-bottom-color: var(--text-muted);
}

/* ── Narrow screens ────────────────────────────────────────────────────── */
@media (max-width: 600px) {
    .product-card .product-card-details { padding: 14px 13px 16px; gap: 6px; }
    .product-card .product-card-title { font-size: 15px; }
    .product-card .price-now { font-size: 16px; }
    .product-card .price-mrp-strike { font-size: 12.5px; }
    .product-card .price-off { font-size: 11.5px; }
    .product-card-tools { top: 10px; right: 10px; gap: 6px; }
    .product-card .product-card-wishlist-btn,
    .product-card .compare-toggle { width: 36px; height: 36px; }
    .product-card .badge-luxury { top: 10px; left: 10px; padding: 5px 9px; font-size: 9.5px; }
    /* Tighter, because the card is 173px wide in a two-up grid and the label
       must fit inside it whole. */
    .product-card .quick-view-btn { bottom: 10px; padding: 9px 12px; font-size: 9px; letter-spacing: 0.08em; gap: 6px; }
    .product-card .quick-view-eye { width: 14px; height: 14px; }
}

/* ── New Arrivals, once Owl has it ─────────────────────────────────────────────
   Appended at the end of the file deliberately. Several of the panel rules
   above are (0,3,0) — `.na-gallery .na-panel:hover`, `.na-gallery
   .na-panel.is-default` — and so is everything here. A tie is settled by source
   order, so these have to come last or the hover rule would still be resizing
   panels underneath a carousel that has already fixed their widths.

   Everything the section looked like is above this block and untouched: the
   photograph filling the slot, the caption sliding up, the zoom, the badge,
   the price. Only the geometry moves — from a flex row that redistributed
   itself to four equal slots Owl can position. */
.na-gallery.owl-loaded {
    /* The flex row is gone; Owl's stage is the row now. */
    display: block;
    /* Height was clamp(520px, 62.5vw, 1000px) because the OPEN panel was half
       the width and the slot had to come out at 0.80 — the shape the garments
       are photographed in. With four equal panels that arithmetic changes: the
       slot is a quarter of the row, so the shape is set on the panel itself and
       the row takes whatever height that gives. Same 0.80 slot, same reason —
       nothing letterboxed, nothing cropped to make it fit. */
    height: auto;
    overflow: hidden;
}
.na-gallery.owl-loaded .owl-stage-outer { overflow: hidden; }
.na-gallery.owl-loaded .owl-stage { display: flex; }
.na-gallery.owl-loaded .owl-item  { display: flex; }

.na-gallery.owl-loaded .na-panel,
.na-gallery.owl-loaded .na-panel.is-default,
.na-gallery.owl-loaded .na-panel:hover,
.na-gallery.owl-loaded .na-panel:focus-visible,
.na-gallery.owl-loaded .na-panel:focus-within {
    /* Owl writes the width onto .owl-item; the panel fills it. flex-basis is
       explicitly released — the calc() above resolves against the ITEM once the
       panel is a flex child of one, which sized every panel at a sixth. */
    flex: 1 1 auto;
    width: 100%;
    /* Nothing animates its own width any more — Owl animates the stage. Leaving
       the 500ms flex-basis transition on would have every panel easing to a
       width it was never going to change to. */
    transition: none;
}

/* The expanding row, desktop only.
   ────────────────────────────────────────────────────────────────────────────
   The widths themselves are written in pixels by the script — see the long note
   there for why, and for the invariant that keeps Owl's offsets correct. This
   block supplies only what CSS is actually better at: the animation, and the
   height.

   Height first, because it is what broke the first attempt. The original rule
   is unchanged and is here for its original reason: the open panel is half the
   row, so a gallery 0.625 as tall as it is wide makes the open SLOT
   0.5/0.625 = 0.80 — the shape the garments are photographed at, which is a
   slot the picture fills exactly with nothing cropped to achieve it.

   It has to be handed down explicitly through EVERY wrapper. Owl inserts a
   stage-outer and a stage between the gallery and the panels, and a panel
   asking for height:100% against a parent of height:auto resolves to nothing —
   which is what left a tall band of bare burgundy with the photographs
   flattened inside it. */
@media (min-width: 1025px) {
    .na-gallery.owl-loaded             { height: 100vh; }
    .na-gallery.owl-loaded .owl-stage-outer,
    .na-gallery.owl-loaded .owl-stage,
    .na-gallery.owl-loaded .owl-item,
    .na-gallery.owl-loaded .na-panel   { height: 100%; }

    /* The panel fills whatever width the script gave its item. aspect-ratio is
       released: the slot's shape comes from the gallery's height and the open
       panel's half-width, exactly as it did in the flex version — an
       aspect-ratio here would fight both. */
    .na-gallery.owl-loaded .na-panel   { width: 100%; aspect-ratio: auto; }

    /* The 500ms the flex version used, moved from flex-basis to width, which is
       the property the script now animates. */
    .na-gallery.owl-loaded .owl-item {
        /* The stage is a flex row, so an item's width is a SUGGESTION until
           shrinking is switched off — the default flex-shrink:1 quietly pulled
           every panel back to an equal quarter no matter what the script wrote
           on it. The four visible and the four off screen come to exactly the
           stage width, so nothing needs to shrink for the row to fit. */
        flex: 0 0 auto;
        transition: width 500ms cubic-bezier(0.4, 0, 0.2, 1);
    }

    /* The open panel carries the caption, as it always did: a 239px strip has
       no room for a name and a price, and four captions competing is the card
       grid this section replaced. is-open sits on the .owl-item, because the
       item is what the width lives on. */
    .na-gallery.owl-loaded .owl-item.is-open .product-card-details,
    .na-gallery.owl-loaded .owl-item.is-open .compare-toggle {
        opacity: 1;
        transform: translateY(0);
    }
    .na-gallery.owl-loaded .owl-item.is-open .card-img { object-position: center 30%; }
}

/* Below the breakpoint every panel keeps its caption. One garment is on screen,
   so nothing competes for the space — and a touchscreen has no hover, so a
   caption waiting for one never arrives. Without this the phone showed
   photographs with no name, no price and no sold-out state. */
@media (max-width: 1024px) {
    .na-gallery.owl-loaded .na-panel .product-card-details {
        opacity: 1;
        transform: translateY(0);
    }
    .na-gallery.owl-loaded .na-panel .compare-toggle { opacity: 1; }
}

/* The caption is no longer a hover secret.
   ────────────────────────────────────────────────────────────────────────────
   In the flex row one panel was always open and always captioned; the three
   narrow ones showed their name and price on hover, and that was a fair trade
   because a 200px strip has no room for either.

   Four equal panels changes the arithmetic twice over. There is room now — a
   358px panel carries a name and a price comfortably. And with no open panel
   there is no captioned one, so at rest the row became four unlabelled
   photographs. On a touchscreen that is permanent: there is no hover, so the
   name, the price, the sale percentage and the sold-out state would never
   appear at all, on the one section of the homepage whose whole job is to show
   what is new. */


/* New Arrivals keeps the ordinary pointer.
   ────────────────────────────────────────────────────────────────────────────
   The grab cursor is right for the other rails: they are strips of cards whose
   only affordance is being pulled. This row is not that — every panel responds
   to the pointer by opening, and each one is a link to a garment. A hand that
   says "drag me" sitting over something that says "click me" is the wrong
   instruction on the wrong element.

   The global rule at :9087 is (0,2,0) and stays exactly as it is for every
   other rail; this is (0,3,0) so it wins here and nowhere else. */
.na-gallery.owl-carousel.owl-drag,
.na-gallery.owl-carousel.owl-drag a,
.na-gallery.owl-carousel.owl-drag img,
.na-gallery.owl-carousel.owl-drag:active,
.na-gallery.owl-carousel.owl-drag:active a,
.na-gallery.owl-carousel.owl-drag:active img { cursor: default; }
/* The links inside get the arrow too — see the slider-cursor block near the
   end of this file. This rule used to hand them a pointer back. */

/* ══ THE FAN, ONCE OWL HAS IT ═══════════════════════════════════════════════
   Owl inserts .owl-stage-outer > .owl-stage > .owl-item between the track and
   the prints, which moves three things the design depended on. Nothing here
   changes how the fan LOOKS — it re-attaches the same rules to the elements
   that now carry them. Appended last so it beats the originals on a tie. */

/* 1. The lift needs room INSIDE the clip. A print rises 14px and scales 1.03,
      and Owl's stage-outer clips at its own box — so the top of whichever print
      is being hovered was shaved off. Same trick the viewport already used:
      pad the box out, pull it back with a negative margin, so the clip is
      taller than the content while the horizontal clip is untouched. */
.photo-fan-section .pf-track.owl-loaded .owl-stage-outer {
    padding-block: var(--pf-lift-room);
    margin-block: calc(-1 * var(--pf-lift-room));
    overflow: hidden;
}
.photo-fan-section .pf-track.owl-loaded .owl-stage { display: flex; align-items: flex-start; }
.photo-fan-section .pf-track.owl-loaded .owl-item  { flex: 0 0 auto; }

/* 2. The tilt. It was .pf-item:nth-child(4n+k), and every .pf-item is now an
      only child of its own .owl-item — so every print was print number one and
      the whole fan straightened out. The pattern moves to the wrapper, where
      the siblings actually are. Same four angles, same cycle. */
@media (min-width: 768px) {
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(4n + 1) .pf-card { transform: rotate(-3deg); }
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(4n + 2) .pf-card { transform: rotate(-1deg); }
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(4n + 3) .pf-card { transform: rotate(1deg);  }
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(4n + 4) .pf-card { transform: rotate(3deg);  }
}

/* 3. The width now comes from the script in pixels, because the fan shows 4.2
      prints and Owl counts whole ones. The flex basis and max-width that used
      to size the print would fight it. */
.photo-fan-section .pf-track.owl-loaded .pf-item {
    flex: 0 0 auto;
    max-width: none;
}

/* ══ SHOP BY OCCASION — THE FAN, FIVE ACROSS ═══════════════════════════════
   The same treatment Best Sellers and Trending get: photographic prints laid
   out like a stack dropped on a table, alternating tilt, the middle one
   straight and slightly larger, caption under the print rather than on it.

   Written here rather than by borrowing .pf-card, because these tiles are not
   product cards — they are an occasion, one photograph and a label — and
   pointing the fan's selectors at both would tie two sections together that
   have to be free to move apart. The NUMBERS are shared deliberately: same
   angles, same overlap, same lift, same 3:4 print. */
.occasion-slider { --occ-overlap: 26px; --occ-lift-room: 42px; }

/* Five across, exactly. 20vw against a strip that runs edge to edge is one
   fifth of the screen. It was 18vw for five and a half, so a half tile was
   always cut at the edge saying there was more; the pager says that now, and
   asking for five means five. */
.occasion-slider .occasion-tile { flex: 0 0 20vw; width: 20vw; }

/* Room for the lift inside the clip — a print rises and scales, and Owl's
   stage clips at its own box, so the top of the hovered one was shaved off.
   Pad the box out, pull it back with a negative margin. */
.occasion-slider.owl-loaded .owl-stage-outer {
    padding-block: var(--occ-lift-room);
    margin-block: calc(-1 * var(--occ-lift-room));
    overflow: hidden;
}
.occasion-slider.owl-loaded .owl-stage { display: flex; align-items: flex-start; }
.occasion-slider.owl-loaded .owl-item  { flex: 0 0 auto; }

/* The tile is picture-plus-caption now, so the ratio lives on the picture and
   the box comes off the tile: this is a photograph, not a filled panel. */
.occasion-slider .occasion-tile {
    aspect-ratio: auto;
    overflow: visible;
    background: transparent;
    transform-origin: center bottom;
    transition: transform 400ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.occasion-slider .occasion-tile-frame {
    display: block;
    position: relative;
    aspect-ratio: 3 / 4;
    overflow: hidden;
    background: var(--bg-surface-soft);
    box-shadow: 0 18px 40px -24px rgba(20, 4, 8, 0.55);
}

/* Caption under the print. The scrim and the white type are gone with it —
   there is no photograph behind these words any more. */
.occasion-slider .occasion-tile-caption {
    display: block;
    padding-top: 18px;
}
.occasion-slider .occasion-tile-label {
    position: static;
    display: block;
    padding: 0;
    background: none;
    color: var(--color-primary-dark);
    font-family: var(--font-heading, Georgia, serif);
    font-size: clamp(15px, 1.15vw, 19px);
    font-weight: 400;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.occasion-slider .occasion-tile-cta {
    display: inline-block;
    margin-top: 7px;
    padding-bottom: 3px;
    color: var(--color-accent-ink, #8c2f39);
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    border-bottom: 1px solid currentColor;
}

@media (min-width: 768px) {
    /* The overlap, halved onto both sides so the row stays centred in the strip
       instead of drifting left as tiles are added. */
    .occasion-slider .occasion-tile { margin-inline: calc(var(--occ-overlap) / -2); }

    /* -3, -1.5, straight, 1.5, 3 and repeat — a five-wide cycle, because five is
       what is on screen. The middle print stands upright and a little larger,
       which is what makes the row read as a fan with a centre rather than a
       row leaning one way. scale() is a transform, so it changes nothing about
       the layout and Owl's measurements are untouched. */
    .occasion-slider.owl-loaded .owl-item:nth-child(5n + 1) .occasion-tile { transform: rotate(-3deg); }
    .occasion-slider.owl-loaded .owl-item:nth-child(5n + 2) .occasion-tile { transform: rotate(-1.5deg); }
    .occasion-slider.owl-loaded .owl-item:nth-child(5n + 3) .occasion-tile { transform: scale(1.06); }
    .occasion-slider.owl-loaded .owl-item:nth-child(5n + 4) .occasion-tile { transform: rotate(1.5deg); }
    .occasion-slider.owl-loaded .owl-item:nth-child(5n + 5) .occasion-tile { transform: rotate(3deg); }

    /* Hovering straightens the print and lifts it, the same gesture the fan
       makes: the one being looked at comes up out of the stack. */
    .occasion-slider.owl-loaded .owl-item .occasion-tile:hover,
    .occasion-slider.owl-loaded .owl-item .occasion-tile:focus-visible {
        transform: rotate(0deg) translateY(-14px) scale(1.03);
        z-index: 60;
    }
}

/* The photograph's own zoom stays, just scoped to the frame it now lives in. */
.occasion-slider .occasion-tile:hover .occasion-tile-img { transform: scale(1.07); }

@media (prefers-reduced-motion: reduce) {
    .occasion-slider .occasion-tile,
    .occasion-slider .occasion-tile-img { transition: none; }
    .occasion-slider.owl-loaded .owl-item .occasion-tile:hover { transform: none; }
}

/* Shop by Occasion on a phone: one print, not five.
   ────────────────────────────────────────────────────────────────────────────
   20vw is a fifth of the screen, which is the point of it on a desktop and
   absurd on a phone — five prints across a 375px screen came out 75px wide,
   smaller than the caption underneath them. Every other rail here shows one
   item with the next one peeking, so this one does too. */
@media (max-width: 767px) {
    .occasion-slider .occasion-tile { flex: 0 0 67vw; width: 67vw; }
}

/* ── Quick View, centred properly on a print ──────────────────────────────────
   The base card centres this button between left:0 and right:0 with
   margin-inline:auto, and there is a long comment at :11984 explaining why:
   `left:50%` with `right:auto` makes an absolutely positioned box shrink to the
   space from the middle to the right edge, and nowrap text then runs straight
   out of its own button.

   The fan's rule set left:50% and a translate WITHOUT clearing the right:0 it
   inherited — so the button was anchored at the centre AND at the right edge,
   sized to that half, then dragged back by half of that wrong width. Measured:
   left:50%, right:0, on every print.

   Same technique as the base, plus the vertical centring the fan wants. */
@media (min-width: 768px) {
    .photo-fan-section .pf-card .quick-view-btn {
        top: 50%;
        bottom: auto;
        left: 0;
        right: 0;
        width: fit-content;
        max-width: calc(100% - 24px);
        margin-inline: auto;
        transform: translateY(-50%);
    }
}

/* ── The prints fan out properly now ─────────────────────────────────────────
   -3/-1/1/3 was too even to read as a stack: four prints leaning almost the
   same amount looked like a row that had been nudged, not photographs dropped
   on a table. Shop by Occasion got a wider spread and an upright print at its
   centre, and that is the one that reads — so the same idea comes back here.

   Wider angles, a vertical stagger so the tops do not line up, and the third
   print upright and slightly larger to give the group a centre. translateY and
   scale are transforms: they change nothing about the layout, so Owl's widths
   and offsets are untouched by any of it. */
@media (min-width: 768px) {
    /* The same rotation and position pattern Shop by Occasion uses.
       ────────────────────────────────────────────────────────────────────────
       Identical values, identical five-step cycle: -3, -1.5, upright and a
       little larger, 1.5, 3. Read from the same numbers as .occasion-tile
       rather than approximated, because two rows meant to look alike drift the
       first time either is touched otherwise.

       Two things go, and both are why this row did not read like that one. The
       translateY stagger — 8px down, 6px up — pushed the prints off a shared
       baseline, which Occasion never does. And the four-step cycle meant the
       upright print landed every fourth rather than every fifth, so the row had
       a different rhythm even where the angles happened to agree. */
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(5n + 1) .pf-card {
        transform: rotate(-3deg);
    }
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(5n + 2) .pf-card {
        transform: rotate(-1.5deg);
    }
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(5n + 3) .pf-card {
        transform: scale(1.06);
    }
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(5n + 4) .pf-card {
        transform: rotate(1.5deg);
    }
    .photo-fan-section .pf-track.owl-loaded .owl-item:nth-child(5n + 5) .pf-card {
        transform: rotate(3deg);
    }

    /* Hover still wins over all four — it is what lifts the print being looked
       at out of the stack, and it has to out-rank the resting pose whichever
       one that print happens to be. */
    .photo-fan-section .pf-track.owl-loaded .owl-item .pf-item:hover .pf-card,
    .photo-fan-section .pf-track.owl-loaded .owl-item .pf-item:focus-within .pf-card {
        transform: rotate(0deg) translateY(-14px) scale(1.03);
    }
}

@media (prefers-reduced-motion: reduce) {
    .photo-fan-section .pf-track.owl-loaded .owl-item .pf-card { transform: none !important; }
}

/* The link replacing the footer's old Newsletter form. Styled like the column
   list links beside it rather than as a button — it is one more way to reach
   the boutique, not a call to action competing with the Invitations band
   directly above it. */
.footer-care-link {
    display: inline-block;
    margin-top: 4px;
    padding-bottom: 3px;
    color: inherit;
    font-size: 12px;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    text-decoration: none;
    border-bottom: 1px solid currentColor;
    opacity: 0.85;
    transition: opacity 220ms ease;
}
.footer-care-link:hover,
.footer-care-link:focus-visible { opacity: 1; }

/* The Invitations field's floating label wrapped onto two lines and sat across
   the input's own underline: the label was 200px wide inside a 400px field, so
   "Enter your email address" broke after "email". It was always like this — it
   is just visible on every page now instead of only at the foot of the home
   page. The label has the field's width to use, and no reason to wrap. */
.newsletter-section .newsletter-input-wrap .animated-label {
    width: 100%;
    white-space: nowrap;
}


/* New Arrivals on a phone: the panel carries its own width, which is what Owl
   measures under autoWidth. Two thirds of the screen — the same as the Occasion
   rail — so the whole remainder goes to the peek on the right instead of being
   split into a gutter either side. */
@media (max-width: 1024px) {
    /* .owl-loaded in the selector on purpose: the desktop block above sets
       `.na-gallery.owl-loaded .na-panel { width: 100% }` outside any media
       query, at (0,3,0), and a bare `.na-gallery .na-panel` here loses to it.
       100% of an item whose width Owl is trying to MEASURE from this panel is
       circular, and both resolved to zero — the row rendered as an empty band
       with the pager still counting underneath it. */
    .na-gallery.owl-loaded .na-panel { width: 67vw !important; }
}

/* ── New Arrivals: no hover behaviour below the breakpoint ────────────────────
   The panel opening under the pointer is the DESKTOP idea — one of four panels
   grows and the others give way. On a phone there is one garment on screen and
   nothing to give way, so the same rules only produce a card that flinches when
   you touch it. Worse on a touchscreen: :hover sticks after a tap, so a garment
   stayed zoomed and lifted until something else was tapped.

   Written as a max-width gate rather than (hover: none) on purpose — a laptop
   with a touchscreen matches both, and this is about the SIZE of the row, not
   the input device. */
@media (max-width: 1024px) {
    .na-gallery.owl-loaded .na-panel:hover,
    .na-gallery.owl-loaded .na-panel:focus-within,
    .na-gallery.owl-loaded .owl-item:hover .na-panel,
    .na-gallery.owl-loaded .owl-item .na-panel:hover {
        transform: none;
    }
    .na-gallery .na-panel:hover .card-img,
    .na-gallery .na-panel:focus-visible .card-img,
    .na-gallery .na-panel:focus-within .card-img,
    .na-gallery .na-panel.is-default .card-img {
        transform: none;
    }
    /* The caption is already permanently visible down here, so the hover rules
       that faded it in and out have nothing to do but flicker it. */
    .na-gallery .na-panel:hover .product-card-details,
    .na-gallery:hover .na-panel:not(:hover) .product-card-details {
        opacity: 1;
        transform: none;
    }
    .na-gallery .na-panel .quick-view-btn { display: none !important; }
}

/* ── Best Sellers and Trending: the same card as the rest, on a phone ─────────
   The print styling is gated at 768px and up, so below it these two fell back
   to the ordinary boxed shop card: a white panel under the photograph with the
   name, the price and DETAILS inside it, plus a Quick View button. Every other
   rail on the homepage shows a photograph with its caption ON it — the row read
   as a different component sitting in the middle of the page.

   This is not the desktop print brought down: no tilt, no overlap, no lift.
   Just the chrome off and the caption where the other rails put it. */
@media (max-width: 767px) {
    .photo-fan-section .pf-card {
        background: transparent;
        border: 0;
        padding: 0;
        overflow: hidden;
        position: relative;
    }
    .photo-fan-section .pf-card .card-img-container {
        position: relative;
        aspect-ratio: 3 / 4;
        overflow: hidden;
        border-bottom: 0;
    }
    .photo-fan-section .pf-card .card-img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
    /* Caption over the photograph, on the same scrim New Arrivals uses, so the
       two rails read as one family rather than two. */
    .photo-fan-section .pf-card .product-card-details {
        position: absolute;
        inset-inline: 0;
        bottom: 0;
        z-index: 2;
        display: flex;
        flex-direction: column;
        align-items: flex-start;
        gap: 4px;
        padding: 16px;
        margin: 0;
        opacity: 1;
        transform: none;
        background: linear-gradient(transparent, rgba(20, 4, 8, 0.78));
    }
    .photo-fan-section .pf-card .product-card-title { font-size: clamp(15px, 4vw, 20px); }
    .photo-fan-section .pf-card .product-card-title,
    .photo-fan-section .pf-card .product-card-title a { color: #ffffff; }
    .photo-fan-section .pf-card .product-card-price,
    .photo-fan-section .pf-card .price-now   { color: rgba(255, 255, 255, 0.92); font-size: 14px; }
    .photo-fan-section .pf-card .price-mrp-strike { color: rgba(255, 255, 255, 0.66); }
    .photo-fan-section .pf-card .product-card-cta {
        color: var(--color-accent) !important;
        font-size: 10.5px;
        margin-top: 2px;
        width: auto;
        padding: 0;
        justify-content: flex-start;
    }
    /* Quick View goes for the same reason it goes on New Arrivals: on a card
       this size it covers the caption, and a tap on the photograph already
       opens the garment. */
    .photo-fan-section .pf-card .quick-view-btn { display: none !important; }
    /* And no hover pose down here either — there is no stack to lift out of. */
    .photo-fan-section .pf-item:hover .pf-card,
    .photo-fan-section .pf-item:focus-within .pf-card { transform: none; }
}

/* Quick View: flex, not block.
   ────────────────────────────────────────────────────────────────────────────
   The button is an eye icon and a label, laid out by the base card as a flex
   row — `align-items: center` to sit them on the same line, `gap: 9px` between
   them, `justify-content: center` to hold the pair in the middle of the button.
   The fan revealed it with `display: block !important`, which throws all three
   away: the icon and the words fall back to inline flow, the gap does nothing,
   and the pair sits left inside a button that is itself perfectly centred. The
   button looked misaligned because its CONTENTS were.

   Same reveal as the base card, which has used flex for this all along. */
@media (min-width: 768px) {
    .photo-fan-section .pf-item:hover .quick-view-btn,
    .photo-fan-section .pf-item:focus-within .quick-view-btn {
        display: flex !important;
    }
    .photo-fan-section .pf-card .quick-view-btn {
        align-items: center;
        justify-content: center;
        gap: 9px;
        line-height: 1;
        text-align: center;
    }
}

/* ── Two section headings read left ───────────────────────────────────────────
   The Dievon Edit and Shop by Occasion sit above rails whose captions are
   themselves left-aligned, so a centred heading over a left-aligned row put the
   title and the first garment's name on two different axes. These two line up
   with the content they introduce.

   The rule the rest of the page follows is at :2992 and is untouched — every
   other section still centres. The 50px gold divider is centred by
   `margin: 14px auto 0`, so it has to be told to start at the left edge too,
   or the heading moves and the line it sits under stays behind. */
.photo-fan-section .section-title-wrapper,
.occasion-bar-section .section-title-wrapper { text-align: left; }

.photo-fan-section .section-title-wrapper::after,
.occasion-bar-section .section-title-wrapper::after { margin-inline: 0 auto; }

/* ══ BOTH ROWS START AT THE PAGE GUTTER, AND THE PRINTS STAND APART ═════════
   Two changes to The Dievon Edit and Shop by Occasion.

   1. No more overlap. Both rows pulled their cards together with a negative
      margin — the stack-of-prints idea — which is why neither had any space
      between slides. The negative margin comes off and the gap is a real
      gutter that Owl owns (see PF_GAP in pages/home.php, and the Occasion
      rail's column-gap, which includes/footer.php reads at init).

   2. Both start on the page gutter. The heading above each now reads left, and
      a row that began 30-90px inside it put the title and the first garment on
      two different axes — the exact thing left-aligning the heading was meant
      to fix.

      The inset is derived, not typed: --dv-gutter is the page gutter and steps
      down per breakpoint in responsive.css, and the container is capped at
      1440 and centred, so this is the same pixel .container's content starts
      on at every width. Typing 72px would be right at 1440 and wrong at every
      other size. */

/* ── The Dievon Edit ── */
.photo-fan-section { --pf-overlap: 0px; }
.photo-fan-section .pf-item { margin-inline: 0; }
.photo-fan-section .pf-viewport {
    /* The 4px bleed existed to let a hovered print's shadow escape the clip.
       With the prints no longer overlapping the first one has room of its own,
       and the 4px was pulling the row off the gutter it should sit on. */
    padding-inline: 0;
    margin-inline: 0;
}

/* ── Shop by Occasion ── */
.occasion-slider {
    --occ-overlap: 0px;
    /* Read by includes/footer.php at init to set Owl's margin. 22px, the same
       number every other rail uses, at every width. */
    gap: 22px;
}
/* Both rails start where their heading starts.
   ────────────────────────────────────────────────────────────────────────────
   The inset is derived, not typed: --dv-gutter is the page gutter and steps
   down per breakpoint in responsive.css, and .container is capped at 1440 and
   centred — so this lands on the same pixel .container's content starts on at
   every width. Typing 72px would be right at 1440 and wrong everywhere else.

   .owl-loaded is in the selector because these rails only need the inset once
   Owl has taken them over. It used to need !important as well, to answer a
   `.owl-carousel.owl-loaded { padding-left: 0 !important }` that has since been
   removed — worth knowing if that ever comes back from a vendor update, because
   the symptom is this padding silently doing nothing at any specificity. */
/* New Arrivals takes NO left inset.
   ────────────────────────────────────────────────────────────────────────────
   It had one, to line the first panel up with its heading. That is right for a
   row of separate cards on a pale ground and wrong for this one: the gallery
   has a dark burgundy background, so the inset was not empty space, it was a
   340px block of solid colour sitting to the left of the photographs with
   nothing in it.

   This band is meant to read as one continuous run of photography from edge to
   edge. The heading above keeps the page gutter, as every heading does.

   Above the breakpoint only — see the phone rule further down, where the row is
   separate cards and the inset is real spacing. */
.new-arrivals-section .na-gallery.owl-loaded {
    padding-left: 0;
    padding-right: 0;
}


/* Shop by Occasion runs the full width of the screen.
   ────────────────────────────────────────────────────────────────────────────
   Edge to edge, both sides, at every width — no page gutter on the left and
   nothing holding the row back on the right, so the strip reads as continuing
   past the screen rather than as a row that ends. The heading above it keeps
   the page gutter; only the photographs bleed. */
/* Shop by Occasion keeps the page's edge spacing on the left, and runs off the
   right.
   ────────────────────────────────────────────────────────────────────────────
   It was forced to 0 on BOTH sides, which wiped the inset the site sets at the
   edge of every section — the sliders became the only place on the page where
   content ran into the glass. "Edge to edge" means the row CONTINUES past the
   right edge rather than stopping short of it; it does not mean deleting the
   page's own margin on the left.

   Same left inset as every other rail, nothing on the right. */
/* No inset rule here on purpose.
   Shop by Occasion's desktop inset is set in responsive.css (min-width: 769px,
   padding 0 both sides — full bleed, as asked for). A second declaration here
   only ever fought it, and which one won depended on file order rather than on
   anyone's intent. One rule, one place. */
.occasion-slider .occasion-tile { margin-inline: 0; }

/* On a phone the rail keeps its own small inset — the page gutter down there is
   already 16-24px, and the rows all agreed on 12-16px before any of this. */
@media (max-width: 768px) {
    /* 15px between slides on a phone; the desktop keeps 22. includes/footer.php
       reads this value at init to set Owl's margin, so this IS the gutter, not
       just a flex gap. */
    .occasion-slider { gap: 15px; }

    /* But it DOES take the inset on a phone. Down here the row is not the
       continuous band — it is one garment at a time with a 22px gutter between
       separate cards, on the same pale ground as every other rail — so the
       inset is real spacing rather than a slab of burgundy. 16px, the same as
       the rails beside it. */
       .trending-section .owl-carousel.owl-loaded,
    .new-arrivals-section .na-gallery.owl-loaded { padding-left: 16px; }
}



/* ══ DIEVON COLLECTIONS ON OWL (PHONE ONLY) ════════════════════════════════
   Above 768px this element is a mosaic grid and none of this applies — there
   is no carousel up there to style.

   Owl inserts .owl-stage-outer > .owl-stage > .owl-item between the rail and
   the tiles, which breaks the mosaic's own rules in a useful way and a harmful
   one at the same time:

   Useful — every rule that sized these tiles was written
   `.home-categories-grid[data-count] > .category-card`, a DIRECT child
   selector. Once each tile sits inside an .owl-item it is no longer a direct
   child, so those rules stop applying on their own and there is nothing to
   undo.

   Harmful — they took the tile's width with them, and it cannot simply be
   restated: it was `calc((100% - 2 * gap) / 2.3)`, and a percentage inside an
   .owl-item resolves against the STAGE, which is the whole track. Every tile
   would come out several screens wide. So the width is rewritten against the
   VIEWPORT, which is the one box that means the same thing inside a carousel
   as outside it: the screen, less the rail's own 16px inset either side, less
   the two 22px gutters between the 2.3 tiles on show. */
@media (max-width: 768px) {
    .home-categories-grid.owl-loaded {
        display: block;
        overflow: visible;
        gap: 0;
        /* Owl's own .owl-carousel.owl-drag rule hands vertical panning back to
           the page; the native rail's pan-x pan-y would fight it. */
        touch-action: auto;
        scroll-snap-type: none;
    }
    .home-categories-grid.owl-loaded .owl-stage-outer { overflow: hidden; }
    .home-categories-grid.owl-loaded .owl-stage { display: flex; align-items: stretch; }
    .home-categories-grid.owl-loaded .owl-item  { flex: 0 0 auto; }

    .home-categories-grid.owl-loaded .category-card {
        width: calc((100vw - 32px - 30px) / 2.3);   /* 2 gutters of 15 */
        flex: none;
        /* Restated because the direct-child rule that carried them no longer
           matches. Uniform 3:4 across the row — the mosaic's wide feature tile
           is a grid idea and has nothing to say in a carousel. */
        aspect-ratio: 3 / 4;
        height: auto;
        scroll-snap-align: none;
    }
}

/* ── Icon-only buttons get no label gap ───────────────────────────────────────
   `button svg, a.btn svg, .btn-luxury svg … { margin-right: 8px !important }`
   exists so an icon sitting BEFORE a word has room after it. On a button whose
   whole content is the icon there is no word, so the 8px is dead space on one
   side only — which pushes the glyph off centre in its own box. On the slider
   arrows that is a 19px arrow sitting 4px left of the middle of a 44px button,
   on every pager on the page.

   !important to answer !important, and scoped to the two buttons that are
   genuinely icon-only rather than to `button svg` at large, because the rule is
   right everywhere else. */
.dv-pager .dv-pager-btn svg,
.dv-pager .dv-pager-btn i,
.nav-action-item > svg,
.nav-action-item > i { margin: 0 !important; }

/* ── Shop by Occasion: how many prints fit, by width ──────────────────────────
   Five across is a DESKTOP number. Below 1025px it produced five prints of
   154px on a 768px tablet — narrower than the word "Wedding Guest" underneath
   them — because the old steps that handled this range were written in
   percentages of the flex container and went inert the moment Owl wrapped each
   tile in an .owl-item (a percentage in there resolves against the stage, not
   the screen).

   Restated in viewport units, which mean the same thing inside a carousel and
   out. The steps match the ones the rail always had: five, then four, then
   three, then one with the next peeking. */
@media (max-width: 1199px) {
    .occasion-slider .occasion-tile { flex: 0 0 25vw; width: 25vw; }   /* four */
}
@media (max-width: 991px) {
    .occasion-slider .occasion-tile { flex: 0 0 33vw; width: 33vw; }   /* three */
}
@media (max-width: 767px) {
    .occasion-slider .occasion-tile { flex: 0 0 67vw; width: 67vw; }   /* one + peek */
}

/* ── New Arrivals on a tablet ─────────────────────────────────────────────────
   67vw is a PHONE number: one garment with the next peeking, on a screen about
   400px wide. It applied all the way up to 1024, where it made the panel 686px
   wide and — at the 3/4.2 shape the phone rail uses — 961px TALL, on a screen
   768px high. One garment taller than the entire viewport, in a section 1275px
   deep.

   The expanding four-panel row starts at 1025, so this band between the phone
   and the desktop needed its own number. Two and a half across, which is the
   same reading the other rails give at this width. */
@media (min-width: 768px) and (max-width: 1024px) {
    /* !important to answer the !important on the phone rule above. Both are
       (0,3,0), so with the flag on each it is source order that decides, and
       this comes later — which is what confines 67vw to actual phones.

       Without it the phone's width applied all the way to 1024 and the panel
       came out 686 wide and 961 TALL on a 768px-high screen: one garment taller
       than the whole viewport, in a section 1275px deep. */
    .na-gallery.owl-loaded .na-panel { width: 38vw !important; }
}

/* The print fills its slide, so 22px means 22px.
   ────────────────────────────────────────────────────────────────────────────
   .pf-item carried 6px of horizontal padding, left over from when the prints
   OVERLAPPED and needed a little room for the lift and its shadow to escape.
   With a real gutter it is not room, it is extra gap: the card sat 12px
   narrower than its own slide, so Owl's 22px margin read as 34px between
   photographs while every other rail on the page read as 22. The one slider
   with more space between slides than the rest. */
.photo-fan-section .pf-track.owl-loaded .pf-item {
    padding-left: 0;
    padding-right: 0;
}

/* ── Collections with fewer than three tiles ──────────────────────────────────
   The rail is sized to show 2.3 tiles, which is right when the shop has a
   handful of collections and wrong when it has one or two: the tiles stayed
   130px and left 53px of empty rail beside two of them, or 205px beside one —
   a row that looks like it failed to load rather than one that is simply short.

   data-count is already on the element (the section counts what it rendered),
   so the widths can answer it. One tile takes the row; two split it; three or
   more go back to 2.3 across with the next one peeking, which is what says the
   strip continues. */
/* ── A short Collections row sits centred, it does not stretch ────────────────
   One or two collections used to be widened to fill the rail. That keeps the
   row full but makes a tile a different size depending on how many the shop
   happens to have, so the same collection looks one way with two siblings and
   another with five.

   The tiles keep their size now and the ROW is centred instead — the same
   treatment the Occasion strip already gets from includes/footer.php when its
   content fits. owl-content-fits is set by catInit in pages/home.php. */
.home-categories-grid.owl-loaded.owl-content-fits .owl-stage {
    margin-inline: auto;
}
/* Owl puts its margin on every item including the last, so a centred row
   carries one trailing gap with nothing balancing it on the left. */
.home-categories-grid.owl-loaded.owl-content-fits .owl-item:last-child {
    margin-right: 0 !important;
}

/* The collection label keeps its one line under Owl too.
   ────────────────────────────────────────────────────────────────────────────
   The rule that sized it down and set nowrap is written
   `.home-categories-grid > .category-card .category-card-link` — a DIRECT child
   selector, and once Owl wraps each tile in an .owl-item the tile stops being a
   direct child, so the rule silently stopped applying. "View Collection →"
   went back to its full size and broke after the words, dropping the arrow onto
   a second line.

   Restated for the wrapped case, same values. */
@media (max-width: 768px) {
    .home-categories-grid.owl-loaded .category-card .category-card-link {
        font-size: 8.5px;
        letter-spacing: 0.06em;
        white-space: nowrap;
    }
}

/* ── Every rail runs to the right edge on a phone ─────────────────────────────
   Left keeps the page's inset; right runs off the screen, so the strip reads as
   continuing rather than stopping. Two of them were stopping:

   Collections sat inside the container (351px on a 375 screen) AND carried 16px
   of right padding, so its strip ended 40px short of the glass — the sliver of
   the next tile followed by a band of empty page.

   The Dievon Edit's viewport is inside .container too, so it ended 12px short.

   Both get a negative right margin the width of the page gutter, which is the
   same trick the rails already use on the left to break out of the container.
   Owl clips at the element's own box, so widening the box widens the clip and
   nothing overflows the document. */
@media (max-width: 768px) {
    /* [data-count] in the selector because the rule that sets this rail's
       margins is `.home-categories-grid[data-count]` — same specificity, so a
       plain `.owl-loaded` here lost the margin half of the fight and the strip
       stayed 24px short of the glass. */
    .featured-categories .home-categories-grid[data-count].owl-loaded {
        padding-right: 0;
        margin-right: calc(-1 * var(--dv-gutter));
        /* width:auto, or the negative margin does nothing on the right.
           Something pins this rail to its container's content width (351px on a
           375 screen), and a pinned width means pulling the right margin out
           cannot widen the box — so the strip stopped 24px short of the glass
           and .owl-stage-outer's overflow:hidden cut the peeking tile 103px
           early. It reads as a row that ends rather than one that continues.
           With the width released the box reaches the screen edge and the clip
           happens at the glass, which is where a peek is supposed to be cut. */
        width: auto;
    }
    .photo-fan-section .pf-viewport {
        margin-right: calc(-1 * var(--dv-gutter));
    }
    /* Occasion kept 16px of right padding, which held its strip that far off
       the edge while its left inset was doing the job it was meant to. */
    .occasion-bar-section .occasion-slider.owl-loaded {
        padding-right: 0;
    }
}

/* ── The actual cause of the width change on hover on a phone ─────────────────
   .owl-item is display:flex, so .na-panel is a FLEX ITEM. And the original
   flex-row design left this rule ungated:

       .na-gallery .na-panel:hover,
       .na-gallery .na-panel:focus-visible,
       .na-gallery .na-panel:focus-within { flex-basis: var(--na-open); }

   --na-open is 50%. So hovering — or tapping, which leaves :hover on a
   touchscreen, or focusing a link inside the card — set the panel to half its
   item's width at EVERY size, and the slide visibly resized. This is what was
   being reported over and over while I kept checking `transform`, which was
   never involved: a transform cannot change layout width.

   Released below the breakpoint. Above it the expanding row is the design and
   the script drives the widths in pixels, so nothing here touches it. */
/* At EVERY width, not just below the breakpoint.
   ────────────────────────────────────────────────────────────────────────────
   Under Owl the panel is not what varies — the .owl-item is. The script sizes
   the ITEM in pixels (that is the whole expanding-row mechanism) and the panel
   simply fills whatever item it is in. So a hover rule that resizes the PANEL
   is left over from the flex-row design and is wrong at any size: on a phone it
   halved the slide, and on a desktop it fights the pixel widths the script just
   wrote.

   The panel fills its item, always. What opens and closes is the item. */
.na-gallery.owl-loaded .na-panel:hover,
.na-gallery.owl-loaded .na-panel:focus-visible,
.na-gallery.owl-loaded .na-panel:focus-within,
.na-gallery.owl-loaded:hover .na-panel:not(:hover),
.na-gallery.owl-loaded:focus-within .na-panel:not(:focus-within) {
    /* flex-basis ONLY. Not width.
       ────────────────────────────────────────────────────────────────────────
       Setting width:100% here emptied the row on a phone. Below the breakpoint
       this carousel runs on autoWidth, which means Owl leaves .owl-item at
       width:auto and MEASURES the panel to decide how wide the item should be.
       A panel asking for 100% of an item whose width is being derived from that
       same panel is circular, and both collapse to zero — the slides vanished
       and left the heading and the pager over empty space.

       The panel already has its own width down here (67vw). All this rule has
       to do is stop the legacy hover declaration replacing the flex-basis. */
    flex-grow: 1;
    flex-shrink: 1;
    flex-basis: auto;
}

/* Above the breakpoint the item HAS a pixel width — the script writes it, and
   that is the expanding row — so the panel filling it is both safe and what is
   wanted. */
@media (min-width: 1025px) {
    .na-gallery.owl-loaded .na-panel:hover,
    .na-gallery.owl-loaded .na-panel:focus-visible,
    .na-gallery.owl-loaded .na-panel:focus-within,
    .na-gallery.owl-loaded:hover .na-panel:not(:hover),
    .na-gallery.owl-loaded:focus-within .na-panel:not(:focus-within) {
        width: 100%;
    }
}

/* ── Collections: no zoom on hover, ON A PHONE ONLY ───────────────────────────
   The card's photograph scaled to 1.06 under the pointer, which reads as the
   tile growing. Not wanted on the slider — and a touchscreen makes it worse,
   because :hover sticks after a tap, so a tile stayed enlarged until something
   else was touched.

   Above 768px this section is the desktop mosaic, not a slider, and the zoom is
   the design there. It keeps it. This is gated to the width where the row IS a
   slider, which is exactly the width where Owl drives it.

   Both selector shapes, because Owl wraps each card in an .owl-item down here
   and the original rules are written as direct children — the trap this section
   has sprung three times already. */
@media (max-width: 768px) {
    .home-categories-grid > .category-card:hover .zoom-img,
    .home-categories-grid > .category-card:focus-visible .zoom-img,
    .home-categories-grid > .category-card:hover img,
    .home-categories-grid > .category-card:focus-visible img,
    .home-categories-grid .category-card:hover .zoom-img,
    .home-categories-grid .category-card:focus-visible .zoom-img,
    .home-categories-grid .category-card:hover img,
    .home-categories-grid .category-card:focus-visible img {
        transform: none;
    }
    .home-categories-grid > .category-card:hover,
    .home-categories-grid > .category-card:focus-visible,
    .home-categories-grid .category-card:hover,
    .home-categories-grid .category-card:focus-visible {
        box-shadow: none;
        transform: none;
    }
}

/* ── One cursor across every rail ─────────────────────────────────────────────
   Every slide in every rail is a link, and they all show the pointer. This
   block once did the opposite — it was written for an earlier ask for the
   plain arrow on slides — and the heading still said "arrow, not the hand"
   long after the declaration had been changed back to pointer. Corrected, so
   the comment stops contradicting the rule directly beneath it.

   Each selector carries .owl-carousel.owl-drag so it out-ranks Owl's own
   `.owl-carousel.owl-drag a { cursor: grab }` at (0,3,0). Without that the
   Occasion strip's links kept the grab hand while the others did not — three
   different cursors across five rails on one page.

   The controls are covered separately below: the pager arrows, the wishlist
   and compare buttons and Quick View are things you press, and they are small
   targets that rely on the cursor to say so. */
.na-gallery.owl-carousel.owl-drag a, .na-gallery.owl-carousel.owl-drag img,
.occasion-slider.owl-carousel.owl-drag a, .occasion-slider.owl-carousel.owl-drag img,
.home-categories-grid.owl-carousel.owl-drag a, .home-categories-grid.owl-carousel.owl-drag img,
.photo-fan-section .pf-track.owl-carousel.owl-drag a,
.photo-fan-section .pf-track.owl-carousel.owl-drag img,
#heroSlider.owl-carousel.owl-drag a, #heroSlider.owl-carousel.owl-drag img,
.na-gallery a, .na-gallery img,
.occasion-slider a, .occasion-slider img,
.home-categories-grid a, .home-categories-grid img,
.photo-fan-section .pf-card a, .photo-fan-section .pf-card img,
#heroSlider a, #heroSlider img {
    cursor: pointer;
}
.na-gallery .dv-pager-btn, .occasion-slider .dv-pager-btn,
.dv-pager-btn,
.product-card-wishlist-btn, .compare-toggle, .quick-view-btn,
#heroSlider .btn-luxury, #heroSlider .btn-luxury-outline {
    cursor: pointer;
}

/* ── Smaller wishlist and compare buttons ─────────────────────────────────────
   38x38 each, side by side, sat over roughly a fifth of a card's width on the
   shop grid — enough to cover the model's face on a two-up phone layout. 30px
   keeps a comfortable tap target (above the 24px minimum, and the row they sit
   in has padding around it) while giving the photograph back its corner. */
.product-card-tools { gap: 6px; }
.product-card .product-card-wishlist-btn,
.product-card .compare-toggle,
.na-gallery .na-panel .compare-toggle,
.na-gallery .na-panel .product-card-wishlist-btn {
    width: 30px;
    height: 30px;
}
.product-card .product-card-wishlist-btn i { font-size: 13px; }
.product-card .compare-toggle-icon svg { width: 15px; height: 15px; }

@media (max-width: 768px) {
    .product-card-tools { top: 8px; right: 8px; gap: 5px; }
    .product-card .product-card-wishlist-btn,
    .product-card .compare-toggle { width: 28px; height: 28px; }
    .product-card .product-card-wishlist-btn i { font-size: 12px; }
    .product-card .compare-toggle-icon svg { width: 14px; height: 14px; }
}

/* ── Collection captions stay readable over any photograph ────────────────────
   The tuned scrim and the caption colours are written
   `.home-categories-grid > .category-card ...` — direct children. Owl wraps
   each tile in an .owl-item, so on the phone slider none of them applied and
   the tile fell back to a weaker, shorter gradient: 0.6 at the very bottom
   fading out by half height. Over a bright photograph — a green suit against
   pale stone — the white title and the gold link sat on almost nothing.

   Restated for the wrapped case, and stronger than the original rather than
   equal to it: the scrim reaches higher up the frame and holds more density
   behind the words, because these tiles are a third of a phone screen wide and
   the caption occupies most of their lower half.

   The link also gets the brighter accent. It was resolving to a dark gold
   (138,106,18) which is legible on a dark scrim and invisible on a light one. */
.home-categories-grid .category-card .category-card-overlay {
    background: linear-gradient(to top,
        rgba(0, 0, 0, 0.80) 0%,
        rgba(0, 0, 0, 0.55) 30%,
        rgba(0, 0, 0, 0.18) 60%,
        rgba(0, 0, 0, 0) 85%);
}
.home-categories-grid .category-card .category-card-overlay h3,
.home-categories-grid .category-card h3 {
    color: #ffffff;
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.55);
}
.home-categories-grid .category-card .category-card-link {
    color: var(--color-accent);
    text-shadow: 0 1px 3px rgba(0, 0, 0, 0.65);
}

/* ── Captions that sit ON a photograph are white, heading included ────────────
   The rules that made these captions white targeted the ANCHOR —
   `.pf-card .product-card-title a` — and left the h3 itself at --text-primary,
   a dark maroon. Wherever the heading's own colour showed through, a product
   name rendered dark maroon over a photograph: on a mustard kurta it was
   effectively invisible.

   Both elements, at every width, for both rows that put their caption on the
   picture. The price and the prompt were already handled; this is the title
   catching up with them. */
.na-gallery .na-panel .product-card-title,
.na-gallery .na-panel .product-card-title a,
.photo-fan-section .pf-card .product-card-title,
.photo-fan-section .pf-card .product-card-title a {
    color: #ffffff;
}

/* ── One cursor rule for every slider ─────────────────────────────────────────
   The slides read as the arrow — they are photographs to look at, and the row
   is scrolled rather than clicked through. Everything you actually press keeps
   the pointer. Written once, for all five rails, so the answer cannot differ
   between them again. */
.na-gallery a, .na-gallery img, .na-gallery .na-panel,
.occasion-slider a, .occasion-slider img, .occasion-slider .occasion-tile,
.home-categories-grid a, .home-categories-grid img, .home-categories-grid .category-card,
.photo-fan-section .pf-card, .photo-fan-section .pf-card a, .photo-fan-section .pf-card img,
#heroSlider a, #heroSlider img, #heroSlider .slide,
.na-gallery.owl-carousel.owl-drag a, .na-gallery.owl-carousel.owl-drag img,
.occasion-slider.owl-carousel.owl-drag a, .occasion-slider.owl-carousel.owl-drag img,
.home-categories-grid.owl-carousel.owl-drag a, .home-categories-grid.owl-carousel.owl-drag img,
.photo-fan-section .pf-track.owl-carousel.owl-drag a,
.photo-fan-section .pf-track.owl-carousel.owl-drag img,
#heroSlider.owl-carousel.owl-drag a, #heroSlider.owl-carousel.owl-drag img {
    cursor: pointer;
}
.dv-pager-btn,
.product-card-wishlist-btn,
.compare-toggle,
.quick-view-btn,
.pf-tab,
.pf-viewall,
#heroSlider .btn-luxury,
#heroSlider .btn-luxury-outline {
    cursor: pointer;
}

/* ── The text links inside a slide keep the pointer ───────────────────────────
   The slide itself reads as the arrow — it is a photograph, and the row is
   scrolled rather than clicked through. But the small worded links sitting on
   it are a different thing: "Details", "View Collection", "Explore" are the
   written invitation to click, they are underlined and coloured to say so, and
   an arrow over them contradicts what they look like.

   So: photograph arrow, words pointer. */
/* Each carries .owl-carousel.owl-drag where the slide rule does, so these
   out-rank it — the slide rule is (0,4,0) and a plain `.na-gallery
   .product-card-cta` at (0,2,0) lost to it, which is why Details kept the
   arrow while Explore and View Collection had already taken the pointer. */
.na-gallery.owl-carousel.owl-drag .product-card-cta,
.na-gallery .product-card-cta,
.occasion-slider.owl-carousel.owl-drag .occasion-tile-cta,
.occasion-slider .occasion-tile-cta,
.home-categories-grid.owl-carousel.owl-drag .category-card-link,
.home-categories-grid .category-card-link,
.photo-fan-section .pf-track.owl-carousel.owl-drag .product-card-cta,
.photo-fan-section .pf-card .product-card-cta,
#heroSlider.owl-carousel.owl-drag .slide-content a,
#heroSlider .slide-content a {
    cursor: pointer;
}

/* ── Two collections should not look lost on a phone ──────────────────────────
   Centring a short row stopped it hugging the left edge, but two tiles at their
   normal 2.3-across width are 136px each on a 375px screen — a third of the
   screen between two margins, which reads as small rather than deliberate.

   So a short row grows as well as centring: one collection takes two thirds of
   the screen, two take a bit under half each. They still do not stretch to fill
   the rail edge to edge — that is what made a tile's size depend on how many
   the shop happened to have — but they are sized for a phone rather than for a
   row of five that never arrived. */
@media (max-width: 768px) {
    .home-categories-grid.owl-loaded[data-count="1"] .category-card { width: 67vw; }
    .home-categories-grid.owl-loaded[data-count="2"] .category-card { width: 44vw; }
}

/* ── Collection tiles frame from the top ──────────────────────────────────────
   The tile is a fixed frame and the photograph is cropped to fill it, so the
   crop has to choose which part to keep. Centred, it kept the middle of the
   garment and cut the top — on a full-length shot that takes the face and the
   neckline, which is the part that identifies the collection. Anchoring the
   crop to the top keeps the head of the frame and lets the hem go instead. */
.home-categories-grid a img {
    object-position: top;
}

/* ── Shop by Occasion: no hover zoom on a phone ───────────────────────────────
   Every other rail on this page already switches its hover gesture off below
   the breakpoint — New Arrivals at 1024, Collections at 768, the photo fan at
   767 — and this one was the only rail that never got the same treatment, so
   it was the only rail whose picture animated while you were dragging it.

   On a touchscreen :hover fires the moment a finger lands and STAYS until
   something else is touched. So a drag here started a 0.6s scale(1.07) on the
   photograph under the finger, ran it while the rail was moving, and left the
   tile zoomed after the finger lifted. That is what made this strip feel
   heavier than the others: they move the row and nothing else, this one
   animated a photograph at the same time.

   The tilt and the lift are already gated at 768 and up, so the picture zoom
   was all that was left to switch off. The desktop gesture is untouched. */
@media (max-width: 767px) {
    .occasion-slider .occasion-tile:hover .occasion-tile-img,
    .occasion-slider .occasion-tile:focus-visible .occasion-tile-img,
    .occasion-slider .occasion-tile:focus-within .occasion-tile-img,
    .occasion-tile:hover .occasion-tile-img {
        transform: none;
    }
}

/* ── The Dievon Edit runs edge to edge ────────────────────────────────────────
   The strip is a section child now rather than a container child, so it starts
   at the glass on its own. What has to come off is the right-edge bleed that
   was compensating for the container it no longer sits in:

       @media (max-width: 768px) { .photo-fan-section .pf-viewport {
           margin-right: calc(-1 * var(--dv-gutter)); } }

   That pulled the viewport one gutter PAST its parent's right edge to reach the
   screen — correct while the parent was inset, and one gutter of horizontal
   overflow now that the parent is the full width of the page. The left inset it
   never cancelled is what left this rail starting at 12px on a phone while
   New Arrivals, Collections and Shop by Occasion all started at 0.

   Written here rather than by deleting the rule above, because that rule sits
   in the second of two `@media (max-width: 768px)` blocks that open 22 lines
   apart — the duplicate-block trap this stylesheet is full of. Overriding from
   the end is the reliable half of it. */
@media (max-width: 768px) {
    .photo-fan-section .pf-viewport {
        margin-right: 0;
    }
}

/* ── The Dievon Edit sizes its prints like Shop by Occasion ───────────────────
   Going full width made the shelf wider, and --pf-visible is a COUNT rather
   than a width — so the prints grew instead of more of them arriving. On a
   1984px screen a print reached 450px against an Occasion tile's 398px, which
   is what made the two neighbouring rows stop reading as a pair.

   Laid side by side the two rails were running the same rhythm one step apart,
   and changing bands at different widths:

       Occasion    20vw (5 across)   25vw (4)   33vw (3)   67vw (1+peek)
                   >=1200            >=992      >=768      phone
       The Edit    4.2 across        3.2        2.2        1.5
                   >=1280            >=992      >=768      phone

   So the counts go up by one and the top boundary moves from 1280 to 1200 to
   line up with Occasion's. The numbers are not the round 5.2 / 4.2 / 3.2 they
   look like they should be, because the two rails measure differently: an
   Occasion tile is 20vw and Owl adds the 15px gutter BETWEEN tiles, while a
   print is sized to its share of the shelf MINUS the 22px gutter. Solving
   view/P - 22 = the Occasion tile width at each band gives 4.75, 3.7 and 2.8,
   which track Occasion within a few pixels right across the range rather than
   only at one width.

   Appended rather than edited in place: --pf-visible is declared in four
   separate blocks scattered over 500 lines, and this stylesheet's oldest trap
   is editing the wrong one of a duplicated pair.

   The phone is deliberately untouched at 1.5. A print is 228px there against
   an Occasion tile's 251px, which is close already, and that band was tuned
   and signed off recently — not worth reopening for 23px. */
@media (min-width: 1200px) {
    /* 4.62, not 4.75. The old number matched Occasion's print WIDTH; this one
       matches its PEEK — how much of the cut print shows at the right edge,
       which is the part the eye actually reads as "this row continues".
       Measured at 1434 before: the Edit showed 0.81 of the next print against
       Occasion's 0.67, so its cut print looked almost whole rather than
       deliberately clipped. */
    .photo-fan-section { --pf-visible: 4.62; }
}
@media (min-width: 992px) and (max-width: 1199px) {
    .photo-fan-section { --pf-visible: 3.7; }
}
@media (min-width: 768px) and (max-width: 991px) {
    .photo-fan-section { --pf-visible: 2.8; }
}

/* ── Shop by Occasion fades up like the other rails ───────────────────────────
   Its tiles were the only slides on the homepage without the fade. New Arrivals
   and The Dievon Edit get it free — their slides ARE .product-card, which carries
   reveal-on-scroll in the shared partial — and Collections is named in the
   stagger list in includes/footer.php. An Occasion tile is hand-built markup on
   neither route, so it was never opted in.

   The class alone is not enough here, which is the part worth writing down. The
   tile already declares `transition: transform 400ms` for the hover lift, and
   that rule out-ranks .reveal-on-scroll (0,2,0 against 0,1,0) — so the opacity
   had no transition to run on and the tile would have SNAPPED from invisible to
   visible instead of fading. Naming opacity here is what makes the fade exist.

   The hover lift keeps its own 400ms; only opacity is added alongside it.

   One deliberate difference by breakpoint, and it is the right way round: above
   768px each tile carries its resting tilt (rotate(-3deg) and so on) at (0,5,0),
   which out-ranks the reveal's translateY(24px) — so a desktop tile fades in
   place without moving, and the fan is never fought. Below 768px there is no
   tilt, so the tile does the full fade AND rise, which is the phone behaviour
   that was asked for.

   No stagger, matching The Dievon Edit and New Arrivals. Collections is the only
   rail that staggers, and footer.php explains why New Arrivals was taken out of
   that list: revealing a composed row one item at a time animates the layout
   apart before it settles. A fan of overlapping prints has the same problem. */
.occasion-slider .occasion-tile {
    transition: transform 400ms cubic-bezier(0.22, 0.61, 0.36, 1),
                opacity 0.6s ease;
}

/* ── The Dievon Edit peeks like the rails beside it, on a phone ───────────────
   Its cut print showed 0.51 of a slide at 375 — a true half — while Shop by
   Occasion and New Arrivals both showed 0.37. Half of a print reads as one that
   did not fit; a third reads as a row deliberately continuing past the edge.

   --pf-visible is a COUNT, so this is the lever: 1.5 means "one and a half
   across", and a half is exactly what was showing. 1.375 leaves the same third
   the other two rails leave — checked by measuring, not by arithmetic: 1.35 came
   out at 0.34 and 1.375 lands on 0.37.

   Worth knowing, and it is why the two cannot be locked together at every size:
   Occasion sizes its tiles as a share of the SCREEN (67vw here, 20vw on a
   desktop) while this rail divides its SHELF by a count. Those are different
   functions, so they agree at the width they are tuned for and drift a little
   either side of it. Tuned here at 375, the size actually being looked at. */
@media (max-width: 767px) {
    .photo-fan-section { --pf-visible: 1.375; }
}
