/* ========== TYPE SYSTEM (self-hosted WOFF2) ========== */
/* Display: Manrope — variable geometric humanist sans, weight 200..800.
   Slightly distinctive (open apertures, characterful curves on a / e /
   r / g) without being trendy. Used for h1/h2/h3, hero headline, and
   the section heading on grid pages.
   Body: Inter — variable sans, weight 300..700, opsz 14..32. The
   gold standard for UI legibility on the web; designed specifically
   for screens. Used for body copy, navigation, buttons, forms,
   product titles, and prices. The opsz axis lets micro-UI text
   sharpen automatically and large display text relax — invisible to
   the reader but the reason Inter feels right at every size.
   Self-hosted under wwwroot/lib/fonts/ so the strict CSP (font-src
   'self' data:) is preserved without needing fonts.gstatic.com on
   the allowlist — same pattern as bootstrap-icons. Latin + latin-ext
   subsets cover EN, BS (č ć đ š ž), DE umlauts, and TR (ı ş). Arabic
   falls through to system sans via the family stack below. */
@font-face {
    font-family: 'Manrope';
    font-style: normal;
    font-weight: 200 800;
    font-display: swap;
    src: url('lib/fonts/manrope-roman-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: 'Manrope';
    font-style: normal;
    font-weight: 200 800;
    font-display: swap;
    src: url('lib/fonts/manrope-roman-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: 'Inter';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('lib/fonts/inter-roman-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: 'Inter';
    font-style: normal;
    font-weight: 300 700;
    font-display: swap;
    src: url('lib/fonts/inter-roman-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: 'Inter';
    font-style: italic;
    font-weight: 300 700;
    font-display: swap;
    src: url('lib/fonts/inter-italic-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: 'Inter';
    font-style: italic;
    font-weight: 300 700;
    font-display: swap;
    src: url('lib/fonts/inter-italic-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;
}

/* ========== ROOT VARIABLES & GLOBAL STYLES ========== */
/* ── Dark theme (default) ─────────────────────────────────── */
/* Palette inspired by gsms.ba: pure-black grayscale backgrounds, translucent
   white-on-black card feel, and GSMS-gold (#ffd700) highlight for prices /
   secondary accent. Primary-red brand identity is preserved for CTAs and
   alerts — only the surrounding canvas shifts to pure black. */
:root, [data-theme="dark"] {
    --bg-page:           #000000;
    --bg-surface:        #0d0d0d;
    --bg-card:           #141414;
    --bg-input:          #1f1f1f;
    --bg-input-focus:    #2a2a2a;
    --bg-sidebar:        #0a0a0a;
    --bg-navbar:         #0a0a0a;

    --text-primary:      #ffffff;
    --text-secondary:    #e0e0e0;
    --text-muted:        #999999;

    --border-color:      #333333;
    --border-subtle:     #1f1f1f;

    --accent-red:        #c80000;
    /* Hover/active red for primary buttons. Kept here so the hex literal isn't
       repeated at every call site and so the light theme can shift it to a
       value that still darkens visibly on a near-white surface. */
    --accent-red-hover:  #a80000;
    /* Companion RGB triplet for rgba(..., α) alpha-blended accent use —
       e.g. tinted backgrounds, focus rings. Kept in sync with --accent-red.
       Must be a plain `R, G, B` list so callers can write
       `rgba(var(--accent-red-rgb), 0.1)`. */
    --accent-red-rgb:    200, 0, 0;
    --accent-yellow:     #ffd700;
    /* Companion RGB triplet for alpha-blended yellow accents (tinted
       backgrounds, focus rings, outlined buttons). Keeps the accent shift
       consistent in light theme without hardcoding hex values. */
    --accent-yellow-rgb: 255, 215, 0;
    --accent-yellow-hover: #e6c200;

    /* Semantic success/info tokens — used by OrderConfirmed and any other
       page that needs a "good to go" or "informational" colour. The light
       theme overrides below shift the same names darker for contrast. */
    --accent-success:    #2ec27e;
    --accent-success-fg: #082015; /* foreground on a success-tinted button */
    --accent-info:       #4dabf7;

    /* Badge semantic tokens — referenced from .badge-* so they adapt per
       theme. Previously hardcoded hex values did not respond to theme flips. */
    --badge-success-bg:  #2ec27e;
    --badge-success-fg:  #ffffff;
    --badge-info-bg:     #4dabf7;
    --badge-info-fg:     #07121d;
    --badge-secondary-bg:#4b4b55;
    --badge-secondary-fg:#ffffff;

    /* Validation semantic tokens (error / success borders and messages). */
    --validation-error:  #ff4d4d;
    --validation-error-rgb: 255, 77, 77;
    --validation-success: #2ec27e;

    /* Variant-picker swatch tokens — the colour-axis chip on the item
       detail page renders the chosen colour as a circle. The circle
       needs a hairline border to separate it from the card surface (it
       reads as "floating" otherwise) and an inner sheen so the border
       doesn't disappear when the swatch colour matches the surface
       (e.g. white-on-white in light theme, black-on-black in dark).
       Dark theme: dim border on dark surface + light inner sheen.
       Light theme overrides flip the relationship below. */
    --swatch-border:     rgba(255, 255, 255, 0.30);
    --swatch-inset:      rgba(255, 255, 255, 0.15);

    /* Password-strength tokens (4 stages: weak → fair → good → strong).
       Used by the .pwd-strength-* classes in Register / ResetPassword.
       Light theme overrides below shift these darker so the meter stays
       visible against a white surface — previously the bars were hardcoded
       and "good" (yellow) became unreadable on the light surface. */
    --strength-weak:     #ff4d4d;
    --strength-fair:     #f0ad4e;
    --strength-good:     #ffd700;
    --strength-strong:   #2ec27e;

    /* Alert / toast text tokens — separated from --accent-success/info so
       the dark-theme variant can be a softer tint that still hits AA against
       the alpha-blended surface, while light theme uses the darker accent
       value directly. */
    --alert-success-fg:  #5dd6a2;
    --alert-info-fg:     #5dd6d3;
    --alert-danger-fg:   #ff7070;
    --alert-warning-fg:  var(--accent-yellow);
    /* RGB triplet for alert-warning so the alpha background tint can route
       through the same token as the foreground. The success/info siblings
       below mirror the same pattern; before this they were hardcoded
       rgba(46,194,126,…) / rgba(77,171,247,…) literals on every alert+toast
       rule, so a palette flip required edits in five places. */
    --alert-warning-bg-rgb: 230, 168, 23;
    --accent-success-rgb: 46, 194, 126;
    --accent-info-rgb:    77, 171, 247;
    /* Backdrop alpha for app-level modals and overlays (reconnect, confirm,
       drawer scrims). Same value in both themes — the underlying surface
       is what shifts, not the dimming. */
    --modal-backdrop:     rgba(0, 0, 0, 0.5);

    /* Admin dashboard stat-card semantic tokens. Each tile keeps a
       recognisable hue (blue → products, orange → categories, green →
       orders) while routing through theme variables so light theme can
       darken them for AA contrast on a white surface. The companion
       *-rgb triplets back the alpha-blended icon backgrounds. */
    --stat-products:      #3a86ff;
    --stat-products-rgb:  58, 134, 255;
    --stat-categories:    #ff9f40;
    --stat-categories-rgb:255, 159, 64;
    --stat-orders:        #4caf50;
    --stat-orders-rgb:    76, 175, 80;

    /* Foreground tokens for content sitting on top of the brand accent red
       (admin chat bubbles, primary buttons with white text). Same value in
       both themes because --accent-red is dark-saturated in both, so white
       text passes AA against either palette. Lifted to a token so a future
       high-contrast / printer-friendly theme can override centrally. */
    --on-accent-fg:       #ffffff;
    --on-accent-fg-muted: rgba(255, 255, 255, 0.85);

    /* Spacing scale — use these instead of one-off rem values to keep a
       consistent rhythm across components. 4px base increments follow the
       common 4-8-12-16-24-32-48 scale. */
    --space-2xs: 0.25rem;
    --space-xs:  0.5rem;
    --space-sm:  0.75rem;
    --space-md:  1rem;
    --space-lg:  1.5rem;
    --space-xl:  2rem;
    --space-2xl: 3rem;

    --navbar-border:     rgba(255, 255, 255, 0.08);
    --navbar-shadow:     0 1px 0 rgba(255, 255, 255, 0.04), 0 2px 12px rgba(0, 0, 0, 0.60);

    --shadow-sm:         0 1px 4px  rgba(0, 0, 0, 0.40);
    --shadow-md:         0 4px 16px rgba(0, 0, 0, 0.50);
    --shadow-lg:         0 8px 32px rgba(0, 0, 0, 0.65);

    --scrollbar-thumb:   #444444;
    --scrollbar-track:   #141414;

    --gradient-main:    linear-gradient(135deg, #0a0a0a 0%, #1a1a1a 100%);
    --gradient-accent:  linear-gradient(135deg, #c80000 0%, #8b0000 100%);

    --primary-dark:   #000000;
    --primary-light:  #ffffff;
    --accent-gold:    #ffd700;
    --accent-gray:    #1f1f1f;
    --accent-border:  #333333;
    --card-bg:        #141414;

    /* Surface tokens for "always-visible" controls (quantity stepper,
       form inputs needing a guaranteed contrast baseline) and outline-
       button foregrounds. Centralises what used to be a dozen hardcoded
       hex literals scattered across the [data-theme="light"] overrides
       further down. Dark theme reuses --bg-input / --text-secondary; the
       light theme below pivots to pure-white surfaces for AAA contrast. */
    --surface-control-bg:            var(--bg-input);
    --surface-control-bg-focus:      var(--bg-input-focus);
    --surface-control-fg:            var(--text-primary);
    --surface-control-border:        var(--border-color);
    --surface-control-border-strong: var(--text-muted);
    --btn-outline-fg:                var(--text-secondary);
    --btn-outline-bg:                transparent;
    --btn-outline-bg-hover:          var(--bg-input);

    /* ── Type tokens ──────────────────────────────────────────────────────
       Display: Manrope — distinctive geometric humanist sans, used for
       headings and the hero. Carries personality without being loud.
       Body: Inter — designed for screen legibility, used for body copy,
       UI labels, navigation, buttons, forms, product titles, prices.
       The two fonts share enough geometric DNA that they pair cleanly
       (similar x-height, similar stroke contrast) without one fighting
       the other. */
    --font-display: 'Manrope', 'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --font-body:    'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
    --font-numeric: 'Inter', system-ui, sans-serif;

    /* Override Bootstrap's body font-family token so framework components
       (.btn, .form-control, .modal, .nav-link, etc.) pick up the new
       family without per-rule overrides. Bootstrap reads this at the
       top of its stylesheet; we land later in the cascade via app.css. */
    --bs-body-font-family: var(--font-body);
}

/* ── Light theme ──────────────────────────────────────────── */
[data-theme="light"] {
    color-scheme: light;
    --bg-page:           #f5f5f7;
    --bg-surface:        #ffffff;
    --bg-card:           #ffffff;
    --bg-input:          #eeeef2;
    --bg-input-focus:    #e4e4ea;
    --bg-sidebar:        #fafafa;
    --bg-navbar:         #111114;

    --text-primary:      #0a0a0d;
    /* Was #3c3c44 — bumped darker to clear WCAG AA (≥4.5:1) against white surfaces. */
    --text-secondary:    #2a2a30;
    /* Was #6b6b78 — bumped darker to meet AA for body copy. */
    --text-muted:        #52525c;

    --border-color:      #dddde4;
    --border-subtle:     #ebebf0;

    --accent-red:        #e02424;
    /* Hover red for the light theme — slightly darker than --accent-red so
       the press state is visible on white, while staying within the brand
       red ramp. */
    --accent-red-hover:  #c41c1c;
    /* Companion RGB triplet — see dark theme above for rationale. */
    --accent-red-rgb:    224, 36, 36;
    --accent-yellow:     #d97706;
    /* Light-theme companion RGB for alpha-blended yellow accents. */
    --accent-yellow-rgb: 217, 119, 6;
    --accent-yellow-hover: #b45309;

    /* Semantic success/info tokens — light-theme overrides for AA contrast
       against white surfaces. */
    --accent-success:    #198754;
    --accent-success-fg: #ffffff;
    --accent-info:       #0d6efd;

    /* Light-theme badge tokens — darker backgrounds, white text for AA. */
    --badge-success-bg:  #198754;
    --badge-success-fg:  #ffffff;
    --badge-info-bg:     #0d6efd;
    --badge-info-fg:     #ffffff;
    --badge-secondary-bg:#6c757d;
    --badge-secondary-fg:#ffffff;

    --validation-error:  #dc2626;
    --validation-error-rgb: 220, 38, 38;
    --validation-success: #198754;

    /* Light-theme swatch tokens — flips the dark-theme relationship so
       the hairline border is now a soft dark grey on near-white card
       surfaces, and the inner sheen drops the alpha enough that a dark
       swatch colour (e.g. black) still gets a perceptible inset edge. */
    --swatch-border:     rgba(0, 0, 0, 0.20);
    --swatch-inset:      rgba(0, 0, 0, 0.10);

    /* Light-theme password-strength tokens — pulled toward AA-compliant
       contrast on a white card. "good" becomes amber (not pure yellow) so
       the bar stays visible. */
    --strength-weak:     #dc2626;
    --strength-fair:     #c2410c;
    --strength-good:     #b45309;
    --strength-strong:   #15803d;

    /* Alert / toast text tokens — light theme uses the darker accent value
       so the message stays AA-compliant on a near-white surface. */
    --alert-success-fg:  #1a7a40;
    --alert-info-fg:     #0b5cad;
    --alert-danger-fg:   #b91c1c;
    --alert-warning-fg:  #92400e;
    /* Light theme keeps the same warm-amber alpha for the warning surface
       so the badge reads as warning regardless of theme; only the
       foreground deepens for AA contrast on white. */
    --alert-warning-bg-rgb: 217, 119, 6;
    /* Light-theme rgb companions for the success/info accent. Tracks
       --accent-success and --accent-info (#198754 / #0d6efd). */
    --accent-success-rgb: 25, 135, 84;
    --accent-info-rgb:    13, 110, 253;

    /* Light-theme dashboard stat-card tokens — saturation pushed down and
       lightness reduced so each hue keeps its identity but clears WCAG AA
       (≥4.5:1) against a white card. Previously the dark-theme bright
       values (#3a86ff / #ff9f40 / #4caf50) faded against #ffffff. */
    --stat-products:      #1d4ed8;
    --stat-products-rgb:  29, 78, 216;
    --stat-categories:    #b45309;
    --stat-categories-rgb:180, 83, 9;
    --stat-orders:        #15803d;
    --stat-orders-rgb:    21, 128, 61;

    --navbar-border:     rgba(200, 0, 0, 0.22);
    --navbar-shadow:     0 1px 0 rgba(200, 0, 0, 0.14), 0 2px 8px rgba(0, 0, 0, 0.12);

    --shadow-sm:         0 1px 4px  rgba(0, 0, 0, 0.07);
    --shadow-md:         0 4px 16px rgba(0, 0, 0, 0.10);
    --shadow-lg:         0 8px 32px rgba(0, 0, 0, 0.14);

    --scrollbar-thumb:   #c0c0c8;
    --scrollbar-track:   #ebebf0;

    --gradient-main:    linear-gradient(135deg, #111114 0%, #1c0b0b 100%);
    --gradient-accent:  linear-gradient(135deg, #c80000 0%, #8b0000 100%);

    --primary-dark:   #f5f5f7;
    --primary-light:  #111114;
    --accent-gold:    #d97706;
    --accent-gray:    #eeeef2;
    --accent-border:  #dddde4;
    --card-bg:        #ffffff;

    /* Light-theme overrides for the surface-control / outline-button tokens
       declared on :root. Pure-white control backgrounds, dark text, mid-grey
       borders so quantity steppers and dark-input fields read at AAA
       contrast on a near-white card. */
    --surface-control-bg:            #ffffff;
    --surface-control-bg-focus:      #f8f8fc;
    --surface-control-fg:            #111114;
    --surface-control-border:        #c0c0cc;
    --surface-control-border-strong: #8888a0;
    --btn-outline-fg:                #5c5c6e;
    --btn-outline-bg:                transparent;
    --btn-outline-bg-hover:          #f0f0f5;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* ── Reduced motion ─────────────────────────────────────────────────────────
   Users who opt into prefers-reduced-motion get vestibular-safe UI: the
   hover-lift translateY animations on cards and buttons, the scroll behaviour
   smoothers, and the image zoom-on-hover are all neutralised. We keep the
   colour transition on body (theme toggle) but shorten it dramatically so
   the switch still reads as "something happened" without being a ramp.
   Follows WCAG 2.3.3 (Animation from Interactions). */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    .btn:hover,
    .card:hover,
    .product-card:hover,
    .product-card:hover .product-image img,
    .pcard:hover {
        transform: none !important;
    }
    /* Wishlist heart pop and page-transition fade — keyframe-driven, so the
       hover-transform block above doesn't reach them. Disable explicitly. */
    .pcard-wishlist-btn .bi-heart-fill,
    .page-fade {
        animation: none !important;
    }
}

html, body {
    /* No `height: 100%` — combined with `body { padding-top: 56px;
       box-sizing: border-box }` and overflow-x: hidden (which forces
       overflow-y to compute as auto), it caused body.scrollHeight to
       overshoot html.scrollHeight by exactly the navbar padding,
       producing ~56–110 px of empty scrollable space below the footer.
       The page-shell uses `min-height: calc(100vh - 56px)` on `.page`
       to fill the viewport when content is short, which is what the
       `height: 100%` was trying (and failing) to do here. */
    background-color: var(--bg-page);
    color: var(--text-primary);
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 15px;
    line-height: 1.6;
    overflow-x: hidden;
    overflow-anchor: none;
    transition: background-color 0.25s ease, color 0.25s ease;
}
/* Smooth-scroll for in-page anchors (Home hero "Shop Now" → #featured, etc.).
   The reduced-motion media query at the top of this file already overrides
   this with `scroll-behavior: auto` so vestibular-sensitive users still get
   instant jumps. */
html { scroll-behavior: smooth; }
body {
    /* Top padding accommodates the fixed 56-px navbar AND the optional
       announcement strip above it. --announcement-h is 0 by default and
       gets bumped to the bar's height (36 px / 32 px on phones) by
       setAnnouncementHeight in jshelper.js when AnnouncementBar.razor
       mounts a visible bar. */
    padding-top: calc(56px + var(--announcement-h, 0px));
    font-family: var(--font-body);
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    text-rendering: optimizeLegibility;
}
/* Slide the fixed-top navbar down by the announcement bar's height so the
   two stack cleanly. Both offsets share the same variable, so dismiss /
   show transitions keep them aligned. */
.navbar.fixed-top {
    top: var(--announcement-h, 0px);
    transition: top 0.18s ease;
}
/* Reserve space at the bottom of the page when a mobile sticky purchase
   bar is rendered (only on ItemDetail today). :has() is supported in every
   evergreen browser we ship to; on legacy fallbacks the page just keeps its
   default bottom edge — the bar still works, the last section's natural
   bottom margin absorbs the small visual overlap. */
@media (max-width: 767.98px) {
    body:has(.mobile-buy-bar) { padding-bottom: 80px; }
}

::-webkit-scrollbar             { width: 8px; height: 8px; }
::-webkit-scrollbar-track       { background: var(--scrollbar-track); }
::-webkit-scrollbar-thumb       { background: var(--scrollbar-thumb); border-radius: 4px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
*                               { scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track); scrollbar-width: thin; }

/* ── Display headings ──────────────────────────────────────────────────────
   Manrope at weight 700 with progressively tighter tracking at larger
   sizes (display sans always reads tighter than its default metric).
   Manrope's open apertures and characterful curves give the headings
   personality without competing with the body Inter for attention. */
h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-display);
    font-weight: 700;
    letter-spacing: -0.015em;
    margin-bottom: 1rem;
    color: var(--text-primary);
    line-height: 1.18;
}
h1 { font-size: clamp(2rem,    5vw,   3.25rem); line-height: 1.08; letter-spacing: -0.03em; }
h2 { font-size: clamp(1.6rem,  3.6vw, 2.4rem);  line-height: 1.12; letter-spacing: -0.022em; }
h3 { font-size: clamp(1.3rem,  2.6vw, 1.8rem);  line-height: 1.2;  letter-spacing: -0.015em; }
h4 { font-size: clamp(1.1rem,  2vw,   1.4rem);  font-weight: 600; }
h5, h6 { font-weight: 600; }
p  { color: var(--text-secondary); margin-bottom: 1rem; line-height: 1.6; }
a, .btn-link { color: var(--accent-yellow); text-decoration: none; transition: color 0.2s ease; }
a:hover, .btn-link:hover { color: var(--accent-yellow-hover); text-decoration: underline; }

/* ── Buttons ── */
.btn { font-weight: 600; border-radius: 6px; padding: 0.6rem 1.5rem; border: none; cursor: pointer; transition: transform 0.2s ease, box-shadow 0.2s ease, background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease; box-shadow: var(--shadow-sm); }
/* Hover lift softened from -2px to -1px to avoid vertical jumping on small
   buttons (btn-sm moves by ~1% of its own height — felt jittery). Heavier
   shadow stays to give the same "raised" cue. */
.btn:hover { transform: translateY(-1px); box-shadow: var(--shadow-md); }
.btn:active { transform: translateY(0); box-shadow: var(--shadow-sm); }
/* Buttons in a "disabled" visual state (disabled attr or aria-disabled) must
   not reinforce a click affordance when hovered. Neutralises the lift. */
.btn:disabled, .btn[aria-disabled="true"] { cursor: not-allowed; opacity: 0.6; transform: none; box-shadow: var(--shadow-sm); }
.btn:focus-visible, .btn:active:focus-visible, .btn-link.nav-link:focus-visible, .form-control:focus-visible, .form-check-input:focus-visible { box-shadow: 0 0 0 3px rgba(var(--accent-red-rgb), 0.45); outline: none; }
/* Keep the pre-existing :focus rule as a fallback for older browsers that
   don't support :focus-visible. */
.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus { box-shadow: 0 0 0 3px rgba(var(--accent-red-rgb), 0.45); }

.btn-primary { background-color: var(--accent-red); color: #fff; border: 1px solid var(--accent-red); }
.btn-primary:hover, .btn-primary:active, .btn-primary:focus { background-color: var(--accent-red-hover); border-color: var(--accent-red-hover); color: #fff; box-shadow: 0 0 0 0.25rem rgba(var(--accent-red-rgb), 0.4); }

.btn-secondary { background-color: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border-color); }
.btn-secondary:hover { background-color: var(--bg-input-focus); border-color: var(--text-muted); color: var(--text-primary); }

.btn-outline-light { background-color: transparent; color: rgba(255,255,255,0.85); border: 1px solid rgba(255,255,255,0.35); }
.btn-outline-light:hover { background-color: rgba(255,255,255,0.1); color: #fff; border-color: rgba(255,255,255,0.6); }

.btn-warning { background-color: var(--accent-yellow); color: #000000; border: 1px solid var(--accent-yellow); font-weight: 600; }
.btn-warning:hover { background-color: var(--accent-yellow-hover); border-color: var(--accent-yellow-hover); color: #000000; }

.btn-outline-warning { color: var(--accent-yellow); border-color: var(--accent-yellow); background: transparent; }
.btn-outline-warning:hover { background-color: var(--accent-yellow); border-color: var(--accent-yellow); color: #000000; }

.btn-dark { background-color: var(--bg-input); color: var(--text-primary); border: 1px solid var(--border-color); }
.btn-dark:hover { background-color: var(--bg-input-focus); border-color: var(--text-muted); color: var(--text-primary); }

.btn-outline-secondary { background: transparent; color: var(--text-muted); border: 1px solid var(--border-color); }
.btn-outline-secondary:hover { background: var(--bg-input); color: var(--text-primary); border-color: var(--text-muted); }

.btn-sm { padding: 0.4rem 0.8rem;  font-size: 0.875rem; }
.btn-lg { padding: 0.8rem 2rem;    font-size: 1.125rem; }

/* ── Forms ── */
.form-control, .form-select, textarea {
    background-color: var(--bg-input); color: var(--text-primary);
    border: 1px solid var(--border-color); border-radius: 6px;
    padding: 0.6rem 0.875rem; font-size: 1rem; transition: all 0.25s ease;
}
.form-control:focus, .form-select:focus, textarea:focus {
    background-color: var(--bg-input-focus); color: var(--text-primary);
    border-color: var(--accent-red); box-shadow: 0 0 0 0.2rem rgba(var(--accent-red-rgb), 0.2); outline: none;
}
.form-control::placeholder, .dark-input::placeholder { color: var(--text-muted); opacity: 1; }
.form-label { color: var(--text-primary); font-weight: 500; margin-bottom: 0.5rem; }
.form-check-input { background-color: var(--bg-input); border: 1px solid var(--border-color); cursor: pointer; }
.form-check-input:checked { background-color: var(--accent-red); border-color: var(--accent-red); }
.form-check-input:focus { border-color: var(--accent-red); box-shadow: 0 0 0 0.2rem rgba(var(--accent-red-rgb), 0.2); }
.form-check-label { color: var(--text-primary); margin-left: 0.5rem; cursor: pointer; }
.darker-border-checkbox.form-check-input { border-color: var(--text-muted); }
.form-control-plaintext { color: var(--text-primary); }
.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder { color: var(--bs-secondary-color); text-align: end; }
.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder { text-align: start; }

.dark-input { background-color: var(--bg-input) !important; color: var(--text-primary) !important; border-color: var(--border-color) !important; }
.dark-input:focus { background-color: var(--bg-input-focus) !important; color: var(--text-primary) !important; border-color: var(--accent-red) !important; box-shadow: 0 0 0 0.2rem rgba(var(--accent-red-rgb), 0.2) !important; }

/* ── Cards ──
   Hover: lift + heavier shadow only. Dropped the simultaneous border-color
   flip to avoid three competing animations on one cue — the shadow increase
   alone reads as "raised". Cards with special accent-colour hover (product
   cards, promo tiles) keep their own override. */
.card { background-color: var(--bg-card); color: var(--text-primary); border: 1px solid var(--border-color); border-radius: 10px; box-shadow: var(--shadow-sm); transition: transform 0.2s ease, box-shadow 0.2s ease; overflow: hidden; }
.card:hover { transform: translateY(-3px); box-shadow: var(--shadow-lg); }
.card-body  { padding: 1.5rem; }
.card-title { color: var(--text-primary); margin-bottom: 0.75rem; font-weight: 600; }
.card-text  { color: var(--text-secondary); margin-bottom: 1rem; }
.card-img-top { width: 100%; height: 250px; object-fit: cover; background-color: var(--bg-surface); }
.card-header { background-color: var(--bg-surface); border-bottom: 1px solid var(--border-color); color: var(--text-primary); padding: 0.875rem 1.25rem; }
.card-footer { background-color: var(--bg-surface); border-top:    1px solid var(--border-color); color: var(--text-primary); }

/* ── Badges ── */
.badge         { padding: 0.35rem 0.75rem; border-radius: 4px; font-size: 0.875rem; font-weight: 600; display: inline-block; }
.badge-danger  { background-color: var(--accent-red);        color: #fff; }
.badge-warning { background-color: var(--accent-yellow);     color: #000000; }
.badge-success { background-color: var(--badge-success-bg);  color: var(--badge-success-fg); }
.badge-info    { background-color: var(--badge-info-bg);     color: var(--badge-info-fg); }
.badge-secondary{ background-color: var(--badge-secondary-bg); color: var(--badge-secondary-fg); }

/* ── Alerts ── */
.alert { padding: 1rem; margin-bottom: 1rem; border-radius: 6px; border: 1px solid; }
.alert-danger  { background-color: rgba(var(--accent-red-rgb), 0.10);            border-color: var(--accent-red);    color: var(--alert-danger-fg); }
.alert-warning { background-color: rgba(var(--alert-warning-bg-rgb), 0.10);      border-color: var(--accent-yellow); color: var(--alert-warning-fg); }
.alert-success { background-color: rgba(var(--accent-success-rgb), 0.10);       border-color: var(--accent-success); color: var(--alert-success-fg); }
.alert-info    { background-color: rgba(var(--accent-info-rgb), 0.10);           border-color: var(--accent-info);    color: var(--alert-info-fg); }
[data-theme="light"] .alert-danger  { background-color: rgba(var(--accent-red-rgb), 0.07); }
[data-theme="light"] .alert-warning { background-color: rgba(var(--alert-warning-bg-rgb), 0.10); }
[data-theme="light"] .alert-success { background-color: rgba(var(--accent-success-rgb), 0.08); }
[data-theme="light"] .alert-info    { background-color: rgba(var(--accent-info-rgb), 0.07); }

/* Light theme button improvements — all colours route through theme tokens
   so a future palette change updates one place, not every selector. */
[data-theme="light"] .btn-primary { background-color: var(--accent-red); border-color: var(--accent-red); }
[data-theme="light"] .btn-primary:hover, [data-theme="light"] .btn-primary:active { background-color: var(--accent-red-hover); border-color: var(--accent-red-hover); }
[data-theme="light"] .btn-secondary, [data-theme="light"] .btn-dark {
    background-color: var(--surface-control-bg); border-color: var(--surface-control-border); color: var(--surface-control-fg);
}
[data-theme="light"] .btn-secondary:hover, [data-theme="light"] .btn-dark:hover {
    background-color: var(--btn-outline-bg-hover); border-color: var(--surface-control-border-strong); color: var(--surface-control-fg);
}
[data-theme="light"] .btn-outline-secondary {
    color: var(--btn-outline-fg); border-color: var(--surface-control-border); background: var(--btn-outline-bg);
}
[data-theme="light"] .btn-outline-secondary:hover {
    background: var(--btn-outline-bg-hover); color: var(--surface-control-fg); border-color: var(--surface-control-border-strong);
}
[data-theme="light"] .pcard-btn-details {
    background-color: var(--surface-control-bg); border-color: var(--surface-control-border); color: var(--text-secondary);
}
[data-theme="light"] .pcard-btn-details:hover {
    background-color: var(--btn-outline-bg-hover); border-color: var(--surface-control-border-strong); color: var(--surface-control-fg);
}
[data-theme="light"] .btn-warning { background-color: var(--accent-red); border-color: var(--accent-red); color: var(--on-accent-fg); }
[data-theme="light"] .btn-warning:hover, [data-theme="light"] .btn-warning:active { background-color: var(--accent-red-hover); border-color: var(--accent-red-hover); color: var(--on-accent-fg); }
[data-theme="light"] .btn-outline-warning { color: var(--accent-red); border-color: var(--accent-red); }
[data-theme="light"] .btn-outline-warning:hover { background-color: var(--accent-red); border-color: var(--accent-red); color: var(--on-accent-fg); }

/* Light theme quantity fields — !important is retained because Bootstrap's
   form-control rules and -webkit-text-fill-color overrides on iOS Safari
   would otherwise win. The values still come from theme tokens, so a
   future palette adjustment updates these in lockstep with the rest of
   the light theme. */
[data-theme="light"] .qty-group { background-color: var(--surface-control-bg); border-color: var(--surface-control-border); }
[data-theme="light"] .qty-input { background-color: var(--surface-control-bg) !important; color: var(--surface-control-fg) !important; -webkit-text-fill-color: var(--surface-control-fg) !important; border-left-color: var(--surface-control-border); border-right-color: var(--surface-control-border); }
[data-theme="light"] .qty-btn { color: var(--accent-red); }
[data-theme="light"] .dark-input { background-color: var(--surface-control-bg) !important; color: var(--surface-control-fg) !important; -webkit-text-fill-color: var(--surface-control-fg) !important; }
[data-theme="light"] .dark-input:focus { background-color: var(--surface-control-bg-focus) !important; }
/* Navbar inputs stay dark in all themes */
[data-theme="light"] .navbar .dark-input { background-color: rgba(255,255,255,0.08) !important; color: #fff !important; -webkit-text-fill-color: #fff !important; }
[data-theme="light"] .navbar .dark-input:focus { background-color: rgba(255,255,255,0.12) !important; }

/* ── Light theme: Bootstrap dark utility class overrides (admin pages) ── */
[data-theme="light"] .bg-dark { background-color: var(--bg-card) !important; }
[data-theme="light"] .bg-secondary:not(.badge) { background-color: var(--bg-input) !important; color: var(--text-primary) !important; }
[data-theme="light"] .text-white { color: var(--text-primary) !important; }
[data-theme="light"] .text-white-50 { color: var(--text-muted) !important; }
[data-theme="light"] .border-secondary { border-color: var(--border-color) !important; }
[data-theme="light"] .table-secondary th { background-color: var(--bg-surface) !important; color: var(--text-primary) !important; }
[data-theme="light"] .list-group-item { background-color: var(--bg-card) !important; border-color: var(--border-color) !important; color: var(--text-primary) !important; }

/* Fix Bootstrap's dark table for light theme.
   Bootstrap 5 paints the dark overlay via box-shadow (not background-color),
   so we must clear box-shadow on every cell, then re-apply explicit backgrounds. */
[data-theme="light"] .table-dark {
    --bs-table-color:         var(--text-primary);
    --bs-table-bg:            transparent;
    --bs-table-accent-bg:     transparent;
    --bs-table-striped-color: var(--text-primary);
    --bs-table-striped-bg:    var(--bg-input);
    --bs-table-hover-color:   var(--text-primary);
    --bs-table-hover-bg:      rgba(var(--accent-red-rgb), 0.07);
    --bs-table-border-color:  var(--border-color);
    background-color: transparent !important;
    color: var(--text-primary) !important;
}
[data-theme="light"] .table-dark > :not(caption) > * > * {
    background-color: transparent !important;
    box-shadow: none !important;
    color: var(--text-primary) !important;
    border-bottom-color: var(--border-color) !important;
}
[data-theme="light"] .table-dark.table-striped > tbody > tr:nth-of-type(odd) > * {
    background-color: var(--bg-input) !important;
    box-shadow: none !important;
}
[data-theme="light"] .table-dark.table-hover > tbody > tr:hover > * {
    background-color: rgba(var(--accent-red-rgb), 0.07) !important;
    box-shadow: none !important;
    color: var(--text-primary) !important;
}

/* ── Validation visuals ──
   Replaces the old 1px outline with a subtle border color + focus ring.
   The 1px outline was too thin on dark backgrounds; the new pattern matches
   Bootstrap 5's `is-invalid` styling while still using our theme tokens so
   the red stays consistent with the brand accent. */
.valid.modified:not([type=checkbox]) { border-color: var(--validation-success); box-shadow: 0 0 0 3px rgba(46, 194, 126, 0.18); }
.invalid {
    border-color: var(--validation-error) !important;
    box-shadow: 0 0 0 3px rgba(var(--validation-error-rgb), 0.18);
}
.invalid:focus, .invalid:focus-visible {
    border-color: var(--validation-error) !important;
    box-shadow: 0 0 0 3px rgba(var(--validation-error-rgb), 0.35) !important;
}
.validation-message {
    color: var(--validation-error);
    font-size: 0.825rem;
    margin-top: var(--space-2xs);
    display: block;
}
.validation-message::before {
    content: "\26A0 \FE0F"; /* ⚠ */
    margin-right: 0.35rem;
    font-size: 0.9em;
}

/* ── Price ── */
.price          { font-size: 1.75rem; font-weight: 700; color: var(--accent-yellow); margin: 0.5rem 0; }
.price-original { font-size: 1.25rem; color: var(--text-muted); text-decoration: line-through; margin-right: 0.5rem; }
.price-sale     { color: var(--accent-red); }
.text-price     { color: var(--accent-yellow); }

/* ── Product card (generic) ── */
.product-card { background-color: var(--bg-card); border: 1px solid var(--border-color); border-radius: 10px; overflow: hidden; transition: transform 0.25s ease, box-shadow 0.25s ease, border-color 0.25s ease; display: flex; flex-direction: column; height: 100%; box-shadow: var(--shadow-sm); }
.product-card:hover { transform: translateY(-6px); border-color: var(--accent-red); box-shadow: var(--shadow-lg); }
.product-image { width: 100%; height: 240px; background-color: var(--bg-surface); overflow: hidden; position: relative; }
.product-image img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.3s ease; }
.product-card:hover .product-image img { transform: scale(1.05); }
.product-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background-color: var(--bg-surface); color: var(--text-muted); font-size: 3rem; }
.product-info { padding: 1.25rem; flex-grow: 1; display: flex; flex-direction: column; }
.product-title { color: var(--text-primary); font-weight: 600; font-size: 1rem; margin-bottom: 0.5rem; line-height: 1.3; min-height: 2.6em; }
.product-description { color: var(--text-muted); font-size: 0.875rem; margin-bottom: 0.75rem; flex-grow: 1; line-height: 1.4; max-height: 3em; overflow: hidden; text-overflow: ellipsis; }
.product-footer { display: flex; flex-direction: column; gap: 0.75rem; margin-top: auto; }
.product-price  { display: flex; align-items: center; gap: 0.5rem; }
.product-sale-badge { background-color: var(--accent-red); color: #fff; padding: 0.25rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
.product-actions { display: flex; gap: 0.5rem; }
.product-actions .btn { flex: 1; padding: 0.5rem 0.75rem; font-size: 0.875rem; }

/* ── PCARD — Editorial product tile ──────────────────────────────────────────
   Magazine-grade product card. Image-first (4:5 portrait, the proportions
   of a fashion-spread image), with serif typography for the name and
   price and a refined italic eyebrow for category. The hover state is
   restraint over animation: a slow image zoom, a hairline accent that
   sweeps under the card, a serif "View" caption sliding up from inside
   the gradient mask at the bottom of the image. No box-shadow lift, no
   abrupt scale — the card behaves like a printed page that just happens
   to react to a cursor.

   The same component is reused on Home (Featured + New Arrivals), Items
   (catalog grid), Wishlist, RecentlyViewed, and ItemDetail's Related
   section, so the editorial treatment cascades to every grid in the
   storefront in one place. */
.pcard {
    background: var(--bg-card);
    border: 1px solid var(--border-subtle);
    border-radius: 0;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    height: 100%;
    position: relative;
    transition: border-color 0.45s ease, background-color 0.45s ease;
    box-shadow: none;
}
/* Hairline accent that sweeps in under the card on hover. Pure typography
   detail — no movement, no bounce. The cubic-bezier mimics the easing
   feel of a printed press impression. */
.pcard::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 1px;
    background: var(--accent-yellow);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.55s cubic-bezier(0.2, 0.8, 0.2, 1);
    z-index: 4;
}
.pcard:hover { border-color: var(--border-color); }
.pcard:hover::after { transform: scaleX(1); }

.pcard-img-link { display: block; text-decoration: none; position: relative; }
.pcard-img-wrap {
    position: relative;
    overflow: hidden;
    aspect-ratio: 4 / 3;
    background: var(--bg-surface);
}
/* Bottom gradient mask — eased in on hover so the "View" caption and any
   bottom-left badge stay legible regardless of imagery (white ceramic on
   a white shirt was unreadable before). Sits above the image, below the
   caption. */
.pcard-img-wrap::before {
    content: '';
    position: absolute;
    left: 0; right: 0; bottom: 0;
    height: 45%;
    background: linear-gradient(180deg, transparent 0%, rgba(0, 0, 0, 0.55) 100%);
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.45s ease;
    z-index: 1;
}
.pcard:hover .pcard-img-wrap::before { opacity: 1; }
.pcard-img-wrap img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
    transition: transform 0.9s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.pcard:hover .pcard-img-wrap img { transform: scale(1.045); }
.pcard-no-img {
    width: 100%; height: 100%;
    display: flex; align-items: center; justify-content: center;
    font-size: 2.5rem;
    color: var(--text-muted);
}

/* Hover caption — sentence-case "View" with a subtly travelling arrow.
   Pure typography, no button ornament. The whole tile is already a link
   to the detail page; this is just a visual cue that hover = open. */
.pcard-img-caption {
    position: absolute;
    left: 1rem;
    right: 1rem;
    bottom: 0.95rem;
    z-index: 2;
    color: #ffffff;
    font-family: var(--font-body);
    font-weight: 600;
    font-size: 0.95rem;
    letter-spacing: -0.005em;
    line-height: 1;
    transform: translateY(8px);
    opacity: 0;
    transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1), opacity 0.4s ease;
    pointer-events: none;
    display: flex;
    align-items: center;
    gap: 0.45rem;
}
.pcard-img-caption::after {
    content: '\2192';                          /* → */
    font-weight: 500;
    font-size: 1rem;
    letter-spacing: 0;
    transition: transform 0.5s cubic-bezier(0.2, 0.8, 0.2, 1);
}
.pcard:hover .pcard-img-caption {
    transform: translateY(0);
    opacity: 1;
}
.pcard:hover .pcard-img-caption::after { transform: translateX(5px); }

/* SALE / OUT-OF-STOCK — typographic chips, not pills. Thin border, blurred
   surface, all-caps Plus Jakarta Sans with wide tracking. They read as
   editorial captions overlaid on the image rather than ecommerce stickers. */
.pcard-sale-badge {
    position: absolute;
    top: 0.85rem;
    left: 0.85rem;
    z-index: 2;
    background: var(--accent-red);
    color: #ffffff;
    font-family: var(--font-body);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.32rem 0.65rem 0.3rem;
    border-radius: 999px;
    line-height: 1;
    box-shadow: 0 2px 8px rgba(var(--accent-red-rgb), 0.30);
}
.pcard-oos-badge {
    position: absolute;
    top: 0.85rem;
    left: 0.85rem;
    z-index: 2;
    background: rgba(20, 20, 20, 0.85);
    color: #ffffff;
    font-family: var(--font-body);
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    padding: 0.32rem 0.65rem 0.3rem;
    border: 1px solid rgba(255, 255, 255, 0.25);
    border-radius: 999px;
    line-height: 1;
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
}

.pcard-body {
    padding: 1.1rem 1.05rem 1.1rem;
    display: flex;
    flex-direction: column;
    flex: 1;
    gap: 0.4rem;
}

/* Eyebrow — uppercase Inter with moderate tracking, gold accent. Sits
   clearly above the product title without dominating. The tracking
   was tightened from the previous design (0.16em → 0.10em) so the
   label reads more "refined product line" and less "shouty all-caps". */
.pcard-category {
    display: inline-block;
    font-family: var(--font-body);
    font-weight: 600;
    font-size: 0.7rem;
    letter-spacing: 0.10em;
    text-transform: uppercase;
    color: var(--accent-yellow);
    margin: 0 0 0.15rem;
    line-height: 1.2;
}

/* Product name — Inter at 600 with mild negative tracking so the title
   reads as the dominant text element on the card. 2-line clamp lets
   longer titles breathe across two lines instead of ellipsing
   aggressively. */
.pcard-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: ellipsis;
    font-family: var(--font-body);
    font-weight: 600;
    font-size: clamp(0.98rem, 1.3vw, 1.1rem);
    line-height: 1.35;
    color: var(--text-primary);
    text-decoration: none;
    margin: 0;
    letter-spacing: -0.012em;
    transition: color 0.3s ease;
    white-space: normal;
}
.pcard-title:hover { color: var(--accent-red); text-decoration: none; }

.pcard-desc {
    font-family: var(--font-body);
    font-size: 0.78rem;
    font-weight: 400;
    color: var(--text-muted);
    margin: 0;
    line-height: 1.45;
    display: -webkit-box;
    -webkit-line-clamp: 1;
    -webkit-box-orient: vertical;
    overflow: hidden;
    flex: 0 0 auto;
}

/* Price — large serif, tabular numerals so a 12,99 and a 1.299,00 line up
   along the same baseline across the grid. The `lnum` feature locks
   lining figures (some serif fonts default to old-style figures, which
   look elegant in body but not in a price column). */
.pcard-price-row {
    margin: 0.4rem 0 0.55rem;
    min-height: 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
}
.pcard-price-row > div {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.55rem;
}
.pcard-price,
.pcard-sale-price {
    font-family: var(--font-body);
    font-weight: 700;
    font-size: 1.3rem;
    line-height: 1;
    white-space: nowrap;
    font-feature-settings: 'tnum' 1, 'lnum' 1;
    letter-spacing: -0.022em;
}
.pcard-price      { color: var(--accent-yellow); }
.pcard-sale-price { color: var(--accent-red); }
.pcard-old-price {
    font-family: var(--font-body);
    font-size: 0.85rem;
    font-weight: 400;
    color: var(--text-muted);
    text-decoration: line-through;
    text-decoration-thickness: 1px;
    line-height: 1;
    white-space: nowrap;
    font-feature-settings: 'tnum' 1, 'lnum' 1;
}

/* Actions — sentence-case pill buttons. The previous all-caps wide-
   tracking treatment felt aggressive; refined to sentence case, normal
   tracking, slightly larger padding for breathing room. Primary (cart)
   is the brand red with a soft red-tinted shadow that lifts on hover.
   Secondary (details) is a ghost-outline that fills subtly on hover.
   Both rest at min-height 44 px for touch tap targets. */
.pcard-actions {
    display: flex;
    gap: 0.55rem;
    margin-top: auto;
    padding-top: 0.65rem;
}
.pcard-btn-details, .pcard-btn-cart {
    flex: 1;
    min-height: 44px;
    padding: 0.7rem 1rem;
    font-family: var(--font-body);
    font-size: 0.875rem;
    font-weight: 600;
    letter-spacing: -0.005em;
    border-radius: 999px;
    text-align: center;
    text-decoration: none;
    cursor: pointer;
    border: 1px solid transparent;
    transition: background 0.22s ease, color 0.22s ease, border-color 0.22s ease, transform 0.18s ease, box-shadow 0.22s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    white-space: nowrap;
    line-height: 1;
}
.pcard-btn-details {
    background: transparent;
    color: var(--text-primary);
    border-color: var(--border-color);
    box-shadow: none;
}
.pcard-btn-details:hover {
    background: var(--bg-input);
    color: var(--text-primary);
    border-color: var(--text-muted);
    text-decoration: none;
    transform: translateY(-1px);
}
.pcard-btn-cart {
    background: var(--accent-red);
    color: #ffffff;
    border-color: var(--accent-red);
    /* Red-tinted shadow gives the primary CTA a subtle "raised" feel
       that intensifies on hover — the kind of detail premium ecommerce
       buttons get right and generic ones miss. */
    box-shadow: 0 1px 2px rgba(var(--accent-red-rgb), 0.35),
                0 4px 12px rgba(var(--accent-red-rgb), 0.18);
}
.pcard-btn-cart:hover {
    background: var(--accent-red-hover);
    border-color: var(--accent-red-hover);
    color: #ffffff;
    text-decoration: none;
    transform: translateY(-1px);
    box-shadow: 0 2px 4px rgba(var(--accent-red-rgb), 0.40),
                0 8px 20px rgba(var(--accent-red-rgb), 0.28);
}
.pcard-btn-cart:active {
    transform: translateY(0);
    box-shadow: 0 1px 2px rgba(var(--accent-red-rgb), 0.35),
                0 4px 12px rgba(var(--accent-red-rgb), 0.18);
}
.pcard-btn-cart.added {
    background: var(--accent-success);
    color: var(--on-accent-fg);
    border-color: var(--accent-success);
    box-shadow: 0 1px 2px rgba(var(--accent-success-rgb), 0.35),
                0 4px 12px rgba(var(--accent-success-rgb), 0.18);
}

/* ── Wishlist heart on product cards ───────────────────────────────────────
   Top-right overlay on the image. Translucent surface with a small backdrop
   blur so it stays legible over both bright and dark imagery. The "in
   wishlist" state fills the heart and pulls in the brand red so a glance
   tells the user which products are saved. Tap target is 36×36 by default
   (still hits the WCAG 2.5.5 minimum once you account for the visible
   image padding around it on touch). */
.pcard-wishlist-btn {
    position: absolute;
    top: 0.85rem;
    right: 0.85rem;
    width: 38px;
    height: 38px;
    z-index: 3;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid rgba(255, 255, 255, 0.55);
    background: rgba(255, 255, 255, 0.10);
    color: #ffffff;
    border-radius: 999px;
    cursor: pointer;
    font-size: 0.95rem;
    line-height: 1;
    padding: 0;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    transition: background 0.2s ease, color 0.2s ease, transform 0.15s ease, border-color 0.2s ease;
}
.pcard-wishlist-btn:hover {
    background: rgba(255, 255, 255, 0.22);
    transform: scale(1.06);
    border-color: rgba(255, 255, 255, 0.95);
}
.pcard-wishlist-btn:active { transform: scale(0.94); }
.pcard-wishlist-btn:focus-visible {
    outline: 2px solid var(--accent-yellow);
    outline-offset: 2px;
}
.pcard-wishlist-btn.in-wishlist {
    background: var(--accent-red);
    border-color: var(--accent-red);
    color: #ffffff;
}
.pcard-wishlist-btn.in-wishlist:hover {
    background: var(--accent-red-hover);
    border-color: var(--accent-red-hover);
}
/* Brief scale-pop celebration on the heart icon when the wishlist toggle
   flips ON. The animation runs on the .bi-heart-fill child — toggling
   between bi-heart and bi-heart-fill changes the icon's class, and the
   newly-applied animation property fires once. Cubic-bezier overshoots
   gently so the heart "boings" rather than just snaps. The reduced-motion
   block above already neutralises hover transforms; we add an explicit
   override below so the keyframe also stops for those users. */
.pcard-wishlist-btn .bi-heart-fill {
    animation: heart-pop 0.45s cubic-bezier(0.34, 1.56, 0.64, 1) 1;
    transform-origin: center center;
}
@keyframes heart-pop {
    0%   { transform: scale(1); }
    35%  { transform: scale(1.35); }
    65%  { transform: scale(0.94); }
    100% { transform: scale(1); }
}

/* ── Page transition fade-in ─────────────────────────────────────────────────
   240ms opacity + slight upward translate on every keyed re-entry of the
   <article>'s page wrapper. MainLayout bumps `_routeKey` on intercepted
   navigation, which forces Blazor to recreate the wrapping <div> and
   re-fire this animation. Reduced-motion users get no motion (handled in
   the global prefers-reduced-motion block above). */
.page-fade {
    animation: page-fade-in 240ms cubic-bezier(0.2, 0.8, 0.2, 1) 1;
    will-change: opacity, transform;
}
@keyframes page-fade-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}
/* Light theme: the dark scrim chip-on-image still works, but on a white
   card the heart should pick up theme tokens so it doesn't look like a
   foreign black blob detached from the rest of the card. The translucent
   variant inverts to a translucent-white pill with theme-coloured ink. */
[data-theme="light"] .pcard-wishlist-btn {
    background: rgba(255, 255, 255, 0.85);
    border-color: rgba(0, 0, 0, 0.22);
    color: var(--text-secondary);
}
[data-theme="light"] .pcard-wishlist-btn:hover {
    background: rgba(255, 255, 255, 0.97);
    border-color: rgba(0, 0, 0, 0.40);
    color: var(--accent-red);
}
[data-theme="light"] .pcard-wishlist-btn.in-wishlist {
    background: var(--accent-red);
    border-color: var(--accent-red);
    color: var(--on-accent-fg);
}

/* ── Wishlist-mode action row (Wishlist page) ─────────────────────────────
   The dedicated /wishlist page uses ProductCard with WishlistMode=true.
   The cart button takes the bulk of the row width and the remove (trash)
   button collapses to a fixed-width icon affordance to the side. Sized
   to the same 32-px-tall rhythm as .pcard-btn-cart so the row keeps the
   same visual weight as the storefront pair. */
.pcard-btn-cart-wide { flex: 1 1 auto; }
.pcard-btn-remove {
    flex: 0 0 auto;
    width: 44px;
    min-height: 44px;
    padding: 0.55rem 0.4rem;
    border-radius: 999px;
    border: 1px solid var(--border-color);
    background: transparent;
    color: var(--text-secondary);
    cursor: pointer;
    transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}
.pcard-btn-remove:hover {
    background: rgba(var(--accent-red-rgb), 0.08);
    color: var(--accent-red);
    border-color: var(--accent-red);
}
.pcard-btn-remove:focus-visible {
    outline: 2px solid var(--accent-red);
    outline-offset: 2px;
}

/* Mobile: keep the editorial 4/5 portrait, reduce body padding so the
   typographic price still has room to read. The smallest tap target
   stays ≥ 44 px because pill buttons have generous vertical padding. */
@media (max-width: 576px) {
    .pcard-img-wrap   { aspect-ratio: 4 / 3; }
    .pcard-body       { padding: 0.95rem 0.9rem 1rem; gap: 0.4rem; }
    .pcard-title      { font-size: 0.98rem; }
    .pcard-price,
    .pcard-sale-price { font-size: 1.15rem; }
    .pcard-img-caption { display: none; }
}

/* ── Skeleton shimmer ── */
.skeleton { background: linear-gradient(90deg, var(--bg-input) 25%, var(--bg-input-focus) 50%, var(--bg-input) 75%); background-size: 200% 100%; animation: shimmer 2s infinite; }
@keyframes shimmer { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
.skeleton-card  { background-color: var(--bg-card); border-radius: 10px; overflow: hidden; }
.skeleton-image { width: 100%; height: 240px; border-radius: 10px 10px 0 0; }
.skeleton-text  { margin: 0.75rem; height: 1rem; border-radius: 4px; margin-bottom: 0.5rem; }

/* ── Toast ── */
.toast-container { position: fixed; top: 70px; right: 20px; z-index: 1050; display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
.toast { background-color: var(--bg-card); border: 1px solid var(--border-color); border-radius: 8px; padding: 1rem; box-shadow: var(--shadow-lg); animation: slideIn 0.3s ease; pointer-events: auto; max-width: 400px; color: var(--text-primary); }
.toast.success { background-color: rgba(var(--accent-success-rgb), 0.15);       border-color: var(--accent-success); color: var(--alert-success-fg); }
.toast.error   { background-color: rgba(var(--accent-red-rgb), 0.15);            border-color: var(--accent-red);     color: var(--alert-danger-fg); }
.toast.warning { background-color: rgba(var(--alert-warning-bg-rgb), 0.15);      border-color: var(--accent-yellow);  color: var(--alert-warning-fg); }
.toast.info    { background-color: rgba(var(--accent-info-rgb), 0.15);           border-color: var(--accent-info);    color: var(--alert-info-fg); }
[data-theme="light"] .toast.success { background-color: rgba(var(--accent-success-rgb), 0.10); }
[data-theme="light"] .toast.info    { background-color: rgba(var(--accent-info-rgb), 0.08); }
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
.toast-close { color: inherit; opacity: 0.7; cursor: pointer; float: right; font-size: 1.5rem; line-height: 1; transition: opacity 0.2s ease; }
.toast-close:hover { opacity: 1; }

/* ── Navbar ── */
.navbar { padding: 0 1rem; height: 56px; border-bottom: 1px solid var(--navbar-border); box-shadow: var(--navbar-shadow); }
.navbar .navbar-brand { font-size: 1.35rem; font-weight: 800; letter-spacing: 0.04em; color: var(--accent-yellow) !important; }
.navbar .nav-link { color: rgba(255,255,255,0.80) !important; font-size: 0.9rem; padding: 0.4rem 0.75rem !important; border-radius: 4px; transition: color 0.15s ease, background 0.15s ease; }
.navbar .nav-link:hover { color: #fff !important; background: rgba(255,255,255,0.07); }
.navbar .form-control { background: rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.12); color: #fff; font-size: 0.875rem; border-radius: 6px; }
.navbar .form-control:focus { background: rgba(255,255,255,0.14); border-color: rgba(var(--accent-red-rgb), 0.85); color: #fff; box-shadow: 0 0 0 3px rgba(var(--accent-red-rgb), 0.4); }
.navbar .form-control::placeholder { color: rgba(255,255,255,0.45); }
.navbar-toggler { border-color: rgba(255,255,255,0.25); padding: 0.3rem 0.5rem; }
.navbar-toggler:hover { background-color: rgba(255,255,255,0.07); }
.navbar-toggler:focus { box-shadow: 0 0 0 2px rgba(var(--accent-red-rgb), 0.35); }

/* ── Mobile collapsed navbar (<992px, since navbar-expand-lg) ── */
@media (max-width: 991.98px) {
    .navbar .navbar-collapse {
        background: var(--bg-navbar);
        border-top: 1px solid var(--navbar-border);
        box-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
        margin: 0 -1rem;
        padding: 0.5rem 1rem 0.75rem;
        /* `vh` fallback for older browsers; `dvh` so the dropdown's max
           height respects the actually-visible viewport on mobile FF /
           iOS Safari (URL bar collapses, so `vh` over-sizes by ~56-80px
           and the dropdown extends past the visible area). */
        max-height: calc(100vh - 56px);
        max-height: calc(100dvh - 56px);
        overflow-y: auto;
    }
    .navbar .navbar-collapse.collapsing,
    .navbar .navbar-collapse.show {
        /* Align to the full viewport width regardless of container-fluid padding */
        position: relative;
        z-index: 1050;
    }
    .navbar .navbar-nav {
        flex-direction: column;
        width: 100%;
        gap: 0.15rem;
        margin-bottom: 0.5rem;
    }
    .navbar .navbar-nav .nav-link {
        padding: 0.65rem 0.75rem !important;
        border-radius: 6px;
    }
    /* Search form becomes full-width block */
    .navbar .navbar-collapse > .d-flex.me-3 {
        margin: 0 0 0.5rem 0 !important;
        width: 100%;
    }
    .navbar .navbar-collapse > .d-flex.me-3 > .form-control {
        max-width: none !important;
        flex: 1 1 auto;
    }
    /* Buttons (login/register/user dropdown) stack full-width */
    .navbar .navbar-collapse > .btn,
    .navbar .navbar-collapse > a.btn,
    .navbar .navbar-collapse > .dropdown {
        width: 100%;
        margin: 0.2rem 0 !important;
    }
    .navbar .navbar-collapse > .dropdown > .btn {
        width: 100%;
        text-align: left;
    }
    /* User dropdown: full-width, anchored below its button */
    .navbar .navbar-collapse .dropdown-menu {
        position: static !important;
        float: none !important;
        width: 100%;
        margin-top: 0.25rem;
        transform: none !important;
        inset: auto !important;
    }
    /* Controls row (theme toggle + language switcher) — on desktop this is
       an inline flex item in the navbar, on mobile collapse it renders as a
       centered, full-width row with a top divider so it reads as its own
       "preferences" section distinct from the nav/search above and the
       account actions below. */
    .navbar .navbar-collapse > .nav-controls-row {
        width: 100%;
        justify-content: center;
        margin: 0.5rem 0 !important;
        padding: 0.65rem 0 0;
        border-top: 1px solid var(--navbar-border);
    }
    /* Language switcher inside the controls row on mobile collapse — let the
       dropdown toggle fill the remaining horizontal space next to the theme
       button so it reads as a proper "Language" chooser and not a tiny chip. */
    .navbar .navbar-collapse > .nav-controls-row > .dropdown {
        flex: 1 1 auto;
    }
    .navbar .navbar-collapse > .nav-controls-row > .dropdown > .dropdown-toggle {
        width: 100%;
        text-align: left;
    }
    /* And the dropdown menu still needs the static-block treatment so it
       opens in-flow instead of floating over the navbar collapse. */
    .navbar .navbar-collapse > .nav-controls-row > .dropdown > .dropdown-menu {
        position: static !important;
        float: none !important;
        width: 100%;
        margin-top: 0.25rem;
        transform: none !important;
        inset: auto !important;
    }
}

.theme-icon-sun  { display: inline; }
.theme-icon-moon { display: none;   }
[data-theme="light"] .theme-icon-sun  { display: none;   }
[data-theme="light"] .theme-icon-moon { display: inline; }

.btn-theme-toggle { background: transparent; border: 1px solid rgba(255,255,255,0.22); color: rgba(255,255,255,0.75); width: 36px; height: 36px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 1rem; transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease; padding: 0; cursor: pointer; }
.btn-theme-toggle:hover { background: rgba(255,255,255,0.1); border-color: rgba(255,255,255,0.5); color: #fff; }
/* Touch-input devices (phones, tablets): bump to the 44 px WCAG tap-target
   spec. Mouse-input desktops keep the tighter 36 px to preserve the navbar's
   visual rhythm where pointing precision is high. */
@media (pointer: coarse) {
    .btn-theme-toggle { width: 44px; height: 44px; }
}
/* Admin topbar: theme toggle needs theme-aware colors since the topbar is not always dark */
.admin-topbar .btn-theme-toggle { border-color: var(--border-color); color: var(--text-muted); }
.admin-topbar .btn-theme-toggle:hover { background: var(--bg-input); border-color: var(--text-muted); color: var(--text-primary); }

/* ── Mobile "Categories" button ────────────────────────────────────────────
   The primary mobile entry point into the catalog. Yellow-outlined over the
   dark navbar so it visually pops above the surrounding neutral icons —
   earlier white-border variant blended in with the brand/cart/hamburger row
   and users reported it was hard to find. Sizing hits the 44 px tap-target
   spec; the hamburger-lines icon (bi-list) is the most universally
   recognised "menu/categories" affordance across mobile e-commerce.

   On ≥420 px wide phones the label ("Categories" / "Kategorije" /
   "Kategorien") shows alongside the icon; below that it's icon-only so the
   brand + cart + hamburger have breathing room on the tightest screens. */
.btn-sidebar-toggle {
    display: none;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    background: rgba(var(--accent-yellow-rgb), 0.10);
    border: 1.5px solid rgba(var(--accent-yellow-rgb), 0.50);
    color: var(--accent-yellow);
    border-radius: 10px;
    padding: 0 0.75rem;
    min-width: 44px;
    height: 42px;
    font-size: 1.35rem;
    line-height: 1;
    font-weight: 600;
    flex-shrink: 0;
    cursor: pointer;
    transition: background 0.2s ease, border-color 0.2s ease, color 0.2s ease, transform 0.12s ease;
}
.btn-sidebar-toggle:hover,
.btn-sidebar-toggle:focus {
    background: rgba(var(--accent-yellow-rgb), 0.22);
    border-color: var(--accent-yellow);
    color: var(--accent-yellow);
}
.btn-sidebar-toggle:active { transform: scale(0.96); }
.btn-sidebar-toggle:focus-visible {
    outline: 2px solid var(--accent-yellow);
    outline-offset: 2px;
}
.btn-sidebar-toggle-label {
    display: none;
    font-size: 0.9rem;
    font-weight: 600;
    letter-spacing: 0.01em;
    line-height: 1;
}
@media (max-width: 767.98px) {
    .btn-sidebar-toggle { display: inline-flex; }
}
@media (min-width: 420px) and (max-width: 767.98px) {
    .btn-sidebar-toggle-label { display: inline; }
}

/* ── Layout ── */
.container, .container-fluid { color: var(--text-primary); }
.col, [class*="col-"] { min-width: 0; }

/* ── Sidebar ── */
.sidebar-header { flex-shrink: 0; padding: 0.85rem 1rem 0.65rem; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted); border-bottom: 1px solid var(--border-subtle); display: flex; align-items: center; justify-content: space-between; }
.sidebar-close-btn { display: none; background: transparent; border: none; color: var(--text-muted); cursor: pointer; font-size: 1.2rem; padding: 0; line-height: 1; transition: color 0.15s ease; }
.sidebar-close-btn:hover { color: var(--text-primary); }
@media (max-width: 767px) { .sidebar-close-btn { display: flex; align-items: center; } }
.sidebar-search { flex-shrink: 0; padding: 0.5rem 0.65rem; border-bottom: 1px solid var(--border-color); }
.sidebar-search-input { background-color: var(--bg-surface) !important; border: 1px solid var(--border-color) !important; color: var(--text-primary) !important; font-size: 0.8rem !important; border-radius: 5px; height: 30px; width: 100%; }
.sidebar-search-input::placeholder { color: var(--text-muted); }
.sidebar-search-input:focus { background-color: var(--bg-input) !important; border-color: var(--accent-red) !important; box-shadow: 0 0 0 2px rgba(var(--accent-red-rgb), 0.12) !important; color: var(--text-primary) !important; outline: none; }
.sidebar-nav { flex: 1; overflow-y: auto; overflow-x: hidden; padding: 0.3rem 0 1rem; }
.sidebar-nav::-webkit-scrollbar { width: 3px; }
.sidebar-nav::-webkit-scrollbar-track { background: transparent; }
.sidebar-nav::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 2px; }
.sidebar-link { display: flex !important; align-items: center; gap: 0.5rem; padding: 0.5rem 1rem; color: var(--text-secondary) !important; text-decoration: none !important; font-size: 0.875rem; border-left: 3px solid transparent; transition: background 0.15s, color 0.15s, border-color 0.15s; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.4; }
.sidebar-link:hover { background-color: rgba(var(--accent-red-rgb), 0.07) !important; color: var(--text-primary) !important; border-left-color: rgba(var(--accent-red-rgb), 0.35); }
.sidebar-link.active { background-color: rgba(var(--accent-red-rgb), 0.12) !important; color: var(--text-primary) !important; border-left-color: var(--accent-red); font-weight: 600; }
.sidebar-icon { font-size: 0.85rem; width: 1rem; text-align: center; flex-shrink: 0; opacity: 0.8; }
.sidebar-link.active .sidebar-icon { opacity: 1; }
.sidebar-empty { padding: 1rem; color: var(--text-muted); font-size: 0.78rem; text-align: center; }
.content { padding-top: 1.1rem; }

/* ── Hero (shared) ──────────────────────────────────────────────────────────
   The base hero block is shared across Home, About, and Contact. The Home
   page adds the slideshow layer (.hero-slides, .hero-scrim, effect
   variants) — those rules live in Components/Pages/Home.razor.css since
   they only apply where the slideshow exists. */
.hero { background: var(--gradient-main); padding: clamp(2.5rem,7vw,6rem) clamp(1rem,4vw,3rem); border-radius: 12px; margin-bottom: 3rem; text-align: center; position: relative; overflow: hidden; box-shadow: var(--shadow-md); border: 1px solid rgba(var(--accent-red-rgb), 0.18); }
.hero::before { content: ''; position: absolute; inset: 0; background: radial-gradient(ellipse at 60% 50%, rgba(var(--accent-red-rgb), 0.12) 0%, transparent 70%); pointer-events: none; }
/* Hero headline — Manrope at weight 800 (its top weight) for the most
   prominent voice on the site. Tight tracking keeps a multi-word
   headline from sprawling at large sizes. */
.hero h1 {
    font-family: var(--font-display);
    font-size: clamp(2.25rem, 6.5vw, 4.25rem);
    font-weight: 800;
    line-height: 1.05;
    letter-spacing: -0.035em;
    margin-bottom: 1.15rem;
    color: var(--accent-yellow);
    position: relative;
}
.hero p {
    font-family: var(--font-body);
    font-size: clamp(1.05rem, 2.4vw, 1.3rem);
    font-weight: 400;
    line-height: 1.45;
    color: rgba(255,255,255,0.88);
    margin-bottom: 2.25rem;
    position: relative;
    max-width: 42rem;
    margin-left: auto;
    margin-right: auto;
}
.hero .btn { position: relative; }

/* Section heading — kept the brand's red rule because it's been a
   recognisable marker on every page; refined to a 1px hairline so the
   sans h2 sitting above doesn't have to fight for weight.
   The serif sits flush-left over the rule and the rule extends across
   the section, giving each grid section the cadence of a magazine
   chapter break. */
.section-heading { margin: 3rem 0 1.85rem; padding-bottom: 0.85rem; border-bottom: 1px solid var(--accent-red); }

/* ── Legal pages (terms / refund / cookies / privacy) ──
   Constrained measure (the typographic ideal is ~65 characters per line)
   so long-form text doesn't run edge-to-edge on wide displays. h2 sections
   get a comfortable inter-paragraph rhythm; the cookie table is bordered
   so each row reads as a discrete record.

   Stays in app.css (not scoped) because Privacy.razor pre-existed without
   the .legal-page wrapper and we want a consistent presentation across
   all four legal pages with one ruleset. */
.legal-page { max-width: 860px; }
.legal-page h2 { margin-top: 1.5rem; }
.legal-page p  { line-height: 1.65; }
.legal-cookie-table th { background-color: var(--bg-surface); font-size: 0.85rem; }
.legal-cookie-table td { vertical-align: top; }
.legal-cookie-table code { color: var(--accent-yellow); font-size: 0.85rem; }

/* Sandboxed iframe in /admin/email's detail panel — renders an outbox
   row's HTML body without exposing the admin DOM to scripted content
   inside that body. Solid border + light surface so the email preview
   reads as a distinct artefact regardless of theme. */
.email-preview-frame {
    width: 100%;
    height: 420px;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    background: #ffffff;
}

/* Inline horizontal bar for /admin/analytics. The width comes from the
   --bar-pct CSS variable set on the element — a feature we can use safely
   per CSP because the variable is set via the style attribute (not a
   computed expression). Visually a slim accent-red strip; hidden under
   1% so empty rows aren't a sliver. */
.analytics-bar {
    height: 8px;
    border-radius: 4px;
    background: var(--accent-red);
    width: var(--bar-pct, 0%);
    min-width: 0;
    opacity: 0.85;
}
.section-heading h2 { margin: 0; display: inline-block; }

.breadcrumb { background-color: transparent; padding: 0; margin-bottom: 1.5rem; font-size: 0.9rem; }
.breadcrumb-item { color: var(--text-muted); }
.breadcrumb-item.active { color: var(--text-primary); font-weight: 500; }
.breadcrumb-item a { color: var(--accent-yellow); transition: color 0.15s ease; }
.breadcrumb-item a:hover { color: var(--accent-yellow-hover); }
.breadcrumb-item a:focus-visible {
    outline: 2px solid var(--accent-yellow);
    outline-offset: 2px;
    border-radius: 2px;
}
/* Chevron separator reads more clearly as "navigate forward" than a slash —
   especially on mobile where the slash blends into adjacent text. Bootstrap
   Icons aren't loaded in a pseudo-element, so we use the equivalent Unicode
   "›" (single right-pointing angle quote) which is supported in every web
   font we ship. RTL flip is handled in the directional-icons block at the
   bottom of this file. */
.breadcrumb-item + .breadcrumb-item::before {
    content: "›";
    color: var(--text-muted);
    padding-inline: 0.1rem;
    opacity: 0.7;
}
[dir="rtl"] .breadcrumb-item + .breadcrumb-item::before { content: "‹"; }

/* Editorial product-card hover caption — arrow points "forward" in the
   reading direction. LTR uses → (U+2192); RTL flips to ← (U+2190) and
   mirrors the slide animation by translating left instead of right. */
[dir="rtl"] .pcard-img-caption::after { content: "\2190"; }
[dir="rtl"] .pcard:hover .pcard-img-caption::after { transform: translateX(-5px); }

/* ── Pagination ── */
.pagination { justify-content: center; gap: 0.5rem; margin: 2rem 0; }
.page-link { background-color: var(--bg-card); border: 1px solid var(--border-color); color: var(--text-primary); }
.page-link:hover { background-color: var(--bg-input); color: var(--accent-yellow); border-color: var(--accent-red); }
.page-item.active .page-link { background-color: var(--accent-red); border-color: var(--accent-red); color: #fff; }

/* ── Modal ── */
.modal-content { background-color: var(--bg-card); border: 1px solid var(--border-color); border-radius: 10px; }
.modal-header  { border-bottom: 1px solid var(--border-color); color: var(--text-primary); }
.modal-title   { font-weight: 600; color: var(--text-primary); }
.modal-body    { color: var(--text-secondary); }
.modal-footer  { border-top: 1px solid var(--border-color); }
.btn-close { filter: invert(1); opacity: 0.7; }
[data-theme="light"] .btn-close { filter: none; }
.btn-close:hover { opacity: 1; }
/* Bootstrap's .btn-close-white forces filter: invert(1) grayscale(100%) brightness(200%),
   which renders near-white on white backgrounds in the light theme. Reset the filter so
   the × icon stays visible against our themed alert backgrounds. */
[data-theme="light"] .btn-close-white { filter: none !important; }

/* ── Table ── */
.table { color: var(--text-primary); border-color: var(--border-color); }
.table-dark { background-color: var(--bg-card); border-color: var(--border-color); }
.table-dark > thead > tr > th { background-color: var(--bg-surface); border-color: var(--border-color); color: var(--accent-yellow); font-weight: 600; }
.table-dark > tbody > tr > td { border-color: var(--border-color); color: var(--text-primary); }
.table-dark > tbody > tr:hover { background-color: var(--bg-input); }

/* ── Dropdown ── */
.dropdown-menu { background-color: var(--bg-card); border: 1px solid var(--border-color); box-shadow: var(--shadow-md); }
.dropdown-item { color: var(--text-primary); }
.dropdown-item:hover, .dropdown-item:focus { background-color: var(--bg-input); color: var(--text-primary); }
.dropdown-divider { border-top-color: var(--border-color); }

.blazor-error-boundary { background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjY2LjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121; padding: 1rem 1rem 1rem 3.7rem; color: white; }
.blazor-error-boundary::after { content: "An error has occurred." }
h1:focus { outline: none; }

/* ── Responsive ── */
@media (max-width: 768px) { body { padding-top: calc(56px + var(--announcement-h, 0px)); } .hero { padding: 2rem 1rem; } .price { font-size: 1.5rem; } .product-image { height: 200px; } .toast-container { top: 70px; right: 10px; left: 10px; } .toast { max-width: 100%; } }
@media (max-width: 576px) { .container, .container-fluid { padding: 0 0.875rem; } .hero { padding: 1.5rem 1rem; margin-bottom: 2rem; } .navbar { padding: 0 0.75rem; } .price { font-size: 1.25rem; } .card-body { padding: 1rem; } }

/* ── Utilities ── */
/* `.text-left` / `.text-right` were physical-side utilities that never
   flipped under RTL — they used to mask Arabic-layout bugs as "the
   line just looks wrong." Bootstrap's logical `.text-start` /
   `.text-end` are used everywhere instead, so the legacy aliases were
   removed. Don't reintroduce them; reach for `.text-start` / `.text-end`. */
.text-center { text-align: center; }
.text-muted   { color: var(--text-muted)    !important; }
.text-danger  { color: var(--accent-red)    !important; }
.text-warning { color: var(--accent-yellow) !important; }
.text-success { color: var(--accent-success) !important; }
.text-info    { color: var(--accent-info)    !important; }
.bg-dark  { background-color: var(--bg-card)  !important; color: var(--text-primary) !important; }
.bg-light { background-color: var(--bg-input) !important; color: var(--text-primary) !important; }
.mt-0{margin-top:0}.mt-1{margin-top:.5rem}.mt-2{margin-top:1rem}.mt-3{margin-top:1.5rem}.mt-4{margin-top:2rem}.mt-5{margin-top:3rem}
.mb-0{margin-bottom:0}.mb-1{margin-bottom:.5rem}.mb-2{margin-bottom:1rem}.mb-3{margin-bottom:1.5rem}.mb-4{margin-bottom:2rem}.mb-5{margin-bottom:3rem}
.p-1{padding:.5rem}.p-2{padding:1rem}.p-3{padding:1.5rem}.p-4{padding:2rem}.p-5{padding:3rem}
.d-flex{display:flex}.flex-column{flex-direction:column}.justify-content-between{justify-content:space-between}.justify-content-center{justify-content:center}.align-items-center{align-items:center}
.gap-2{gap:.5rem}.gap-3{gap:1rem}.gap-4{gap:1.5rem}

.spinner-border    { color: var(--accent-yellow); }
.spinner-border-sm { width: 1.5rem; height: 1.5rem; border-width: 0.2em; }
.add-to-cart-success { animation: pulse 0.5s ease; }
@keyframes pulse { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.05); } }
.success-checkmark { color: var(--accent-success); font-weight: bold; display: inline-block; animation: slideIn 0.3s ease; }

/* ── Grids ── */
.products-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(240px, 100%), 1fr)); gap: 1.25rem; }
/* Phones: single column — one product per row so the image and CTA are
   large and readable, matching the col-12 col-sm-6 pattern used by the
   pcard grids elsewhere in the app. */
@media (max-width: 575.98px) { .products-grid { grid-template-columns: 1fr; gap: 1rem; } }

.categories-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(160px, 100%), 1fr)); gap: 1rem; }
/* Category tiles — relative positioning + ::before host a subtle accent
   glow that fades in on hover. The icon itself nudges up slightly so the
   tile feels alive without a noisy lift on the whole card. */
.category-card {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 1.5rem 1rem;
    background-color: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    text-align: center;
    text-decoration: none !important;
    transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
    color: var(--text-primary) !important;
    gap: 0.5rem;
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    isolation: isolate;
}
.category-card::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    background: radial-gradient(120% 80% at 50% 0%, rgba(var(--accent-yellow-rgb), 0.10) 0%, transparent 65%);
    opacity: 0;
    transition: opacity 0.25s ease;
}
.category-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-md);
    border-color: rgba(var(--accent-yellow-rgb), 0.55);
    color: var(--text-primary) !important;
}
.category-card:hover::before { opacity: 1; }
.category-card:hover .category-icon { transform: scale(1.08); }
.category-card:focus-visible {
    outline: 2px solid var(--accent-yellow);
    outline-offset: 3px;
    border-color: rgba(var(--accent-yellow-rgb), 0.55);
}
.category-icon {
    font-size: 2rem;
    color: var(--accent-yellow);
    display: block;
    margin-bottom: 0.25rem;
    transition: transform 0.2s ease;
}
.category-title { font-size: 0.95rem; font-weight: 600; color: var(--text-primary); margin: 0; }

.order-summary-sticky { position: sticky; top: 76px; }

/* Sticky admin table headers — keeps column labels visible while scrolling long
   lists (orders, products, users). Relies on the parent <div.table-responsive>
   being the scroll container. */
.admin-table-sticky thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background-color: var(--bg-header, #343a40);
}
.admin-table-sticky .table-responsive,
.table-responsive.admin-table-sticky {
    max-height: calc(100vh - 220px);
    overflow-y: auto;
}

/* ── Responsive admin table → card stack on mobile ──────────────────────
   Apply class .admin-table-stack to a wrapping div that contains a Bootstrap
   table. Below the md breakpoint the table dissolves into per-row cards with
   "Label: value" rows. Each <td> needs a data-label attribute carrying the
   localized column header so the card layout still tells the user what each
   value means without the row of <th> above it.

   Pure horizontal scroll (the previous fallback) was unusable at 360px:
   the most relevant column (e.g. order ID, customer name) was always
   off-screen by the time the action buttons came into view. Stacked cards
   keep every cell on the visible part of the screen and scale tap targets up
   to the row level. */
@media (max-width: 767.98px) {
    .admin-table-stack table { border: 0; }
    .admin-table-stack thead {
        /* Visually hide but keep the header row in the accessibility tree —
           sr-only equivalent. Removing it entirely would strip column-header
           semantics from every td below. */
        position: absolute;
        width: 1px; height: 1px;
        padding: 0; margin: -1px;
        overflow: hidden; clip: rect(0,0,0,0);
        white-space: nowrap; border: 0;
    }
    .admin-table-stack tr {
        display: block;
        margin-bottom: 0.75rem;
        background: var(--bg-card);
        border: 1px solid var(--border-color);
        border-radius: 10px;
        padding: 0.75rem 1rem;
        box-shadow: var(--shadow-sm);
    }
    .admin-table-stack td {
        display: flex;
        justify-content: space-between;
        gap: 0.75rem;
        padding: 0.4rem 0;
        border: 0;
        border-bottom: 1px solid var(--border-subtle);
        text-align: right;
        min-height: 32px;
        align-items: center;
    }
    .admin-table-stack td:last-child { border-bottom: 0; }
    .admin-table-stack td::before {
        content: attr(data-label);
        font-weight: 600;
        color: var(--text-muted);
        text-align: left;
        flex: 0 0 auto;
        max-width: 45%;
        word-break: break-word;
    }
    /* Cells without a data-label (icons, action buttons) collapse to a
       full-width row aligned to the right so action buttons stay reachable. */
    .admin-table-stack td:not([data-label])::before { content: ""; }
    .admin-table-stack td:not([data-label]) { justify-content: flex-end; }
    /* Sticky header is meaningless once the thead is hidden; release the
       max-height clamp so the page can grow naturally. */
    .admin-table-stack.table-responsive,
    .admin-table-stack .table-responsive {
        max-height: none;
        overflow-y: visible;
    }
}
/* ── Admin Logs detail-modal log-content panes ──
   Replaces Bootstrap's `bg-black text-white` on the <pre> blocks inside
   /admin/logs detail dialog. Bootstrap's bg-black is uniformly black with
   no light-theme variant, so a light-theme admin saw a foreign black slab
   inside an otherwise white modal. These tokens flip with the theme like
   every other surface in the app. .log-pre-error keeps the same surface
   token so the only theme delta is the foreground colour. */
.log-pre {
    background-color: var(--bg-input);
    color: var(--text-primary);
}
.log-pre-error {
    background-color: var(--bg-input);
    color: var(--accent-red);
}

/* Log-detail modal scrim. Replaces an inline `style="background-color:
   rgba(0,0,0,0.6)"` so the modal lives in CSS and can pick up theme tokens
   in the future without HTML edits. The .55 alpha matches confirm-dialog. */
.log-modal-backdrop {
    background-color: rgba(0, 0, 0, 0.55);
}

.admin-modal-backdrop {
    background-color: rgba(0, 0, 0, 0.55);
}

/* Order-status timeline (OrderLookup / Track page). Horizontal ladder of
   "Pending → Paid → Shipped → Delivered" with a filled dot up to the
   current status. The connector line is a ::before on each step except
   the first, so the timeline naturally extends to whatever number of
   statuses the enum holds. */
.order-status-timeline {
    list-style: none;
    padding: 0;
    margin: 0;
    display: flex;
    gap: 0.5rem;
    flex-wrap: wrap;
    align-items: stretch;
}
.order-status-timeline .step {
    flex: 1 1 0;
    min-width: 7rem;
    text-align: center;
    position: relative;
    padding-top: 1.5rem;
    color: var(--text-muted);
    font-size: 0.85rem;
}
.order-status-timeline .step .dot {
    display: inline-block;
    width: 1rem;
    height: 1rem;
    border-radius: 50%;
    background: var(--border-color, #444);
    position: absolute;
    top: 0;
    left: 50%;
    transform: translateX(-50%);
    z-index: 1;
}
.order-status-timeline .step.done .dot { background: var(--accent-red); }
.order-status-timeline .step.done .label { color: var(--text-primary); font-weight: 600; }
.order-status-timeline .step + .step::before {
    content: '';
    position: absolute;
    top: 0.5rem;
    left: -50%;
    right: 50%;
    height: 2px;
    background: var(--border-color, #444);
}
.order-status-timeline .step.done + .step.done::before {
    background: var(--accent-red);
}
@media (max-width: 480px) {
    /* On phones, drop the connectors and stack vertically — a 4-pill
       horizontal row gets too cramped at 360 px. */
    .order-status-timeline { flex-direction: column; }
    .order-status-timeline .step + .step::before { display: none; }
    .order-status-timeline .step { padding-top: 0.25rem; padding-left: 1.5rem; text-align: start; }
    .order-status-timeline .step .dot { left: 0; transform: none; }
}

.summary-row { display: flex; justify-content: space-between; align-items: center; }
/* Larger emphasis line for the grand total — replaces an inline
   style="font-size: 1.15rem" on the checkout summary row. The Cart's
   equivalent .cart-summary-total lives in Cart.razor.css (scoped). */
.summary-row-total { font-size: 1.15rem; }

/* ── Checkout: address-book + payment-method radio cards ──
   Lifted out of an inline <style> in Checkout.razor so the page stays
   focused on markup. Both selectors render the same "tappable card with
   embedded radio" pattern — when selected, the card border picks up the
   accent-red token. */
.saved-address-option,
.payment-method-option {
    display: flex;
    align-items: flex-start;
    border: 1px solid var(--border-color);
    border-radius: 6px;
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}
.saved-address-option { padding: 10px 14px; }
.payment-method-option { padding: 12px 14px; }
.saved-address-option.selected,
.payment-method-option.selected {
    border-color: var(--accent-red);
    background-color: rgba(var(--accent-red-rgb), 0.04);
}
.saved-address-option:hover,
.payment-method-option:hover {
    border-color: var(--accent-red);
}
.saved-address-option input[type="radio"],
.payment-method-option input[type="radio"] { margin-top: 4px; }

/* Cart-only styles (.cart-item-*, .cart-undo-toast, .cart-error-toast,
   @keyframes cart-undo-toast-in) moved to Components/Pages/Cart.razor.css.
   The .qty-* selectors stay here because ItemDetail's quantity stepper
   also uses them. */
.qty-group  { display: flex; align-items: center; border: 1px solid var(--border-color); border-radius: 6px; overflow: hidden; background-color: var(--bg-input); }
.qty-btn    { background: transparent; border: none; color: var(--accent-yellow); padding: 0.3rem 0.6rem; cursor: pointer; font-size: 1rem; transition: background 0.15s ease; line-height: 1; }
.qty-btn:hover { background-color: var(--bg-input-focus); }
.qty-btn:focus-visible { outline: none; box-shadow: 0 0 0 3px rgba(var(--accent-red-rgb), 0.45); border-radius: 4px; }
.qty-input  { width: 44px; text-align: center; background: transparent; border: none; border-left: 1px solid var(--border-color); border-right: 1px solid var(--border-color); color: var(--text-primary); font-size: 0.9rem; padding: 0.3rem 0; -moz-appearance: textfield; }
.qty-input::-webkit-outer-spin-button, .qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* .related-grid (the ItemDetail "Related products" wrapper) lives in
   Components/Pages/ItemDetailParts/ItemRelatedProducts.razor.css. */

/* ========== SHARED ADMIN PAGE STYLES ========== */
.admin-page { color: var(--text-primary); }
.page-header { border-bottom: 2px solid var(--border-color); padding-bottom: 20px; margin-bottom: 1.5rem; }
.admin-page .card { border-radius: 8px; }
.admin-page .card-header { padding: 15px 20px; background-color: var(--bg-surface); }
.admin-page .table thead th { border-color: var(--border-color); font-weight: 600; text-transform: uppercase; font-size: 0.85rem; }
.admin-page .table tbody tr { border-color: var(--border-subtle); }
.admin-page .table tbody tr:hover { background-color: rgba(var(--accent-red-rgb), 0.05); }
.admin-page .table tbody td { padding: 12px 15px; vertical-align: middle; }

/* ── Admin Login ── */
.login-container {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 100vh;
    background: var(--bg-page);
    color: var(--text-primary);
}

.login-card {
    width: 100%;
    max-width: 400px;
    background-color: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    padding: 40px;
    box-shadow: var(--shadow-lg);
}

.login-header {
    text-align: center;
    margin-bottom: 30px;
    padding-bottom: 20px;
    border-bottom: 2px solid var(--accent-red);
}

.login-header h2 {
    font-weight: 600;
    letter-spacing: 1px;
    margin-bottom: 8px;
    color: var(--text-primary);
}

.login-footer {
    border-top: 1px solid var(--border-color);
    padding-top: 15px;
}

/* ========== ADMIN LAYOUT STYLES ========== */

/* ── Layout ── */
.admin-wrapper {
    display: flex;
    min-height: 100vh;
    background: var(--bg-page);
}

/* ── Top Bar ── */
.admin-topbar {
    position: fixed;
    top: 0;
    left: 240px;
    right: 0;
    height: 56px;
    background: var(--bg-surface);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 28px;
    z-index: 999;
    transition: background 0.25s ease, border-color 0.25s ease;
}

.admin-topbar-left {
    display: flex;
    align-items: center;
    gap: 12px;
}

.admin-topbar-store-link {
    display: flex;
    align-items: center;
    gap: 7px;
    color: var(--text-muted);
    text-decoration: none;
    font-size: 0.85rem;
    transition: color 0.15s ease;
}

.admin-topbar-store-link:hover {
    color: var(--accent-red);
    text-decoration: none;
}

.admin-topbar-right {
    display: flex;
    align-items: center;
    gap: 6px;
}

.admin-topbar-divider {
    width: 1px;
    height: 20px;
    background: var(--border-color);
    margin: 0 6px;
}

.admin-topbar-user {
    display: flex;
    align-items: center;
    gap: 7px;
    color: var(--text-secondary);
    font-size: 0.85rem;
    padding: 0 6px;
}

.admin-topbar-user i {
    font-size: 1rem;
    color: var(--text-muted);
}

.admin-topbar-logout {
    display: flex;
    align-items: center;
    gap: 6px;
    background: transparent;
    border: 1px solid var(--border-color);
    color: var(--text-muted);
    padding: 5px 12px;
    border-radius: 6px;
    font-size: 0.82rem;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.admin-topbar-logout:hover {
    background: rgba(var(--accent-red-rgb), 0.08);
    border-color: rgba(var(--accent-red-rgb), 0.4);
    color: var(--accent-red);
}

/* ── Sidebar shell ── */
.admin-sidebar {
    width: 240px;
    min-width: 240px;
    background: var(--bg-surface);
    border-right: 1px solid var(--border-color);
    display: flex;
    flex-direction: column;
    height: 100vh;
    position: fixed;
    left: 0;
    top: 0;
    z-index: 1000;
    overflow: hidden;
    transition: background 0.25s ease, border-color 0.25s ease;
}

/* ── Brand ── */
.sidebar-brand {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 20px 18px;
    border-bottom: 1px solid var(--border-color);
    flex-shrink: 0;
}

.sidebar-brand-icon {
    width: 40px;
    height: 40px;
    background: rgba(var(--accent-red-rgb), 0.12);
    border: 1px solid rgba(var(--accent-red-rgb), 0.35);
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--accent-red);
    font-size: 1.15rem;
    flex-shrink: 0;
}

.sidebar-brand-name {
    color: var(--text-primary);
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.2;
}

.sidebar-brand-sub {
    color: var(--text-muted);
    font-size: 0.62rem;
    text-transform: uppercase;
    letter-spacing: 1.2px;
    margin-top: 2px;
}

/* ── Nav ── */
.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    overflow-x: hidden;
    padding: 8px 0 4px;
}

.sidebar-nav::-webkit-scrollbar { width: 3px; }
.sidebar-nav::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 2px; }

.sidebar-section-label {
    padding: 16px 18px 5px;
    font-size: 0.6rem;
    font-weight: 700;
    letter-spacing: 1.8px;
    text-transform: uppercase;
    color: var(--text-muted);
    user-select: none;
}

/* ── Nav links — global so they pierce NavLink's scope ── */
.sidebar-link {
    display: flex !important;
    align-items: center;
    gap: 11px;
    padding: 10px 18px;
    margin: 2px 10px 2px 0;
    color: var(--text-muted) !important;
    text-decoration: none !important;
    font-size: 0.875rem;
    border-left: 3px solid transparent;
    border-radius: 0 7px 7px 0;
    transition: background 0.15s, color 0.15s, border-color 0.15s;
    cursor: pointer;
}

.sidebar-link i {
    font-size: 1rem;
    min-width: 20px;
    text-align: center;
    flex-shrink: 0;
    opacity: 0.65;
    transition: opacity 0.15s;
}

.sidebar-link span {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar-link:hover {
    color: var(--text-primary) !important;
    background: rgba(var(--accent-red-rgb), 0.07);
    border-left-color: rgba(var(--accent-red-rgb), 0.4);
    text-decoration: none !important;
}

.sidebar-link:hover i {
    opacity: 1;
}

.sidebar-link.active {
    color: var(--accent-red) !important;
    background: rgba(var(--accent-red-rgb), 0.10);
    border-left-color: var(--accent-red);
    font-weight: 600;
}

.sidebar-link.active i {
    color: var(--accent-red);
    opacity: 1;
}

/* ── Footer ── */
.sidebar-footer {
    flex-shrink: 0;
    border-top: 1px solid var(--border-color);
    padding: 0;
}

.sidebar-user {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 14px 18px;
}

.sidebar-avatar {
    width: 34px;
    height: 34px;
    background: var(--bg-input);
    border: 1px solid var(--border-color);
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: var(--text-muted);
    font-size: 1rem;
    flex-shrink: 0;
}

.sidebar-user-info {
    flex: 1;
    min-width: 0;
}

.sidebar-user-name {
    color: var(--text-secondary);
    font-size: 0.82rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    line-height: 1.3;
}

.sidebar-user-role {
    color: var(--accent-red);
    font-size: 0.62rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-top: 2px;
}

/* ── Main content ── */
.admin-main {
    margin-left: 240px;
    flex: 1;
    min-height: 100vh;
    padding: 32px 36px;
    background: var(--bg-page);
    overflow-x: hidden;
    transition: background 0.25s ease;
}

/* ── Responsive ──
   Admin sidebar collapses to a 62px icon-only strip on tablet widths. Every
   rule is SCOPED to `.admin-sidebar` so the selectors don't leak into the
   storefront NavMenu, which also uses `.sidebar-link` / `.sidebar-link span`
   etc. Previously these selectors were unscoped and hid storefront category
   names on tablet/mobile because `.sidebar-link span { display: none }`
   matched both trees. */
@media (max-width: 900px) {
    .admin-sidebar { width: 62px; min-width: 62px; }
    .admin-topbar { left: 62px; padding: 0 16px; }
    .admin-topbar-store-link span,
    .admin-topbar-user span,
    .admin-topbar-logout span { display: none; }
    .admin-main { margin-left: 62px; padding: 20px 16px; }

    .admin-sidebar .sidebar-brand { justify-content: center; padding: 16px; }
    .admin-sidebar .sidebar-brand-name,
    .admin-sidebar .sidebar-brand-sub { display: none; }
    .admin-sidebar .sidebar-brand-icon { width: 36px; height: 36px; }

    .admin-sidebar .sidebar-section-label { display: none; }

    .admin-sidebar .sidebar-link {
        justify-content: center;
        padding: 12px 0;
        margin: 2px 4px 2px 0;
        gap: 0;
        border-radius: 0 6px 6px 0;
    }
    .admin-sidebar .sidebar-link span { display: none; }
    .admin-sidebar .sidebar-link i { font-size: 1.15rem; min-width: auto; }

    .admin-sidebar .sidebar-user { justify-content: center; padding: 12px 0; }
    .admin-sidebar .sidebar-user-info { display: none; }
}

/* ── Site Footer ────────────────────────────────────────────────────────── */

.site-footer {
    background: var(--bg-card);
    border-top: 1px solid var(--border-color);
}

.footer-brand {
    color: var(--text-primary);
    font-weight: 700;
    margin-bottom: 0.75rem;
}

.footer-heading {
    color: var(--text-primary);
    font-weight: 600;
    margin-bottom: 0.75rem;
    text-transform: uppercase;
    font-size: 0.8rem;
    letter-spacing: 0.05em;
}

.footer-links li {
    margin-bottom: 0.35rem;
}

.footer-links a {
    color: var(--text-muted);
    text-decoration: none;
    font-size: 0.9rem;
    transition: color 0.15s;
}

.footer-links a:hover {
    color: var(--accent-yellow);
}

.footer-contact li {
    color: var(--text-muted);
    font-size: 0.9rem;
    margin-bottom: 0.5rem;
}

.footer-contact a {
    color: var(--text-muted);
    text-decoration: none;
}

.footer-contact a:hover {
    color: var(--accent-yellow);
}

.footer-bottom {
    border-top: 1px solid var(--border-color);
}

/* ── Footer newsletter card ──────────────────────────────────────────────
   Promoted from a plain input-group into a small editorial card so the
   subscribe ask reads as an intentional surface, not an afterthought. The
   gold inline-start stripe matches the cart-undo-toast pattern (logical
   property auto-flips under RTL — see Cart.razor.css for the rationale). */
.footer-newsletter {
    background: var(--bg-surface);
    border: 1px solid var(--border-subtle);
    border-inline-start: 3px solid var(--accent-yellow);
    border-radius: 8px;
    padding: 1.15rem 1.4rem;
    margin: 1.5rem 0 0.75rem;
}
.footer-newsletter .footer-heading {
    color: var(--text-primary);
    font-size: 0.95rem;
    letter-spacing: 0;
    text-transform: none;
    font-weight: 700;
    margin-bottom: 0.25rem;
}
.footer-newsletter p {
    margin-bottom: 0;
    line-height: 1.4;
}
.footer-newsletter .input-group .form-control {
    background: var(--bg-input);
    border-color: var(--border-color);
    color: var(--text-primary);
}
.footer-newsletter .input-group .form-control::placeholder {
    color: var(--text-muted);
}
.footer-newsletter .input-group .form-control:focus {
    background: var(--bg-input-focus);
    border-color: var(--accent-yellow);
    box-shadow: 0 0 0 3px rgba(var(--accent-yellow-rgb), 0.25);
}
.footer-newsletter .input-group .btn {
    font-weight: 600;
    letter-spacing: 0.01em;
}
@media (max-width: 575.98px) {
    .footer-newsletter { padding: 1rem 1.1rem; }
}

/* ── Top announcement bar ───────────────────────────────────────────────
   Slim dismissible strip pinned above the fixed-top navbar. The body
   padding-top and navbar `top` both read --announcement-h, which defaults
   to 0 and is bumped to the bar's height ONLY while .announcement-bar is
   present in the DOM. The :has() rule below avoids a JS round-trip for
   visibility (matches the existing `body:has(.mobile-buy-bar)` pattern at
   the top of this file) so the bar's mount / dismiss is purely CSS. z-
   index 1031 keeps it above Bootstrap's default fixed-top stack (1030). */
:root { --announcement-h: 0px; }
:root:has(.announcement-bar) { --announcement-h: 36px; }
@media (max-width: 575.98px) {
    :root:has(.announcement-bar) { --announcement-h: 32px; }
}
.announcement-bar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 1031;
    min-height: 36px;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.75rem;
    padding: 0.4rem 1rem;
    background: linear-gradient(90deg, var(--accent-red) 0%, var(--accent-red-hover) 100%);
    color: var(--on-accent-fg);
    font-size: 0.875rem;
    font-weight: 500;
    line-height: 1.3;
    letter-spacing: 0.01em;
    text-align: center;
    box-shadow: 0 1px 0 rgba(0, 0, 0, 0.15);
}
.announcement-bar-text {
    flex: 0 1 auto;
    min-width: 0;
}
.announcement-bar-dismiss {
    flex: 0 0 auto;
    width: 28px;
    height: 28px;
    border-radius: 999px;
    border: 1px solid rgba(255, 255, 255, 0.30);
    background: transparent;
    color: var(--on-accent-fg);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    cursor: pointer;
    transition: background 0.15s ease, border-color 0.15s ease;
}
.announcement-bar-dismiss:hover,
.announcement-bar-dismiss:focus-visible {
    background: rgba(255, 255, 255, 0.18);
    border-color: rgba(255, 255, 255, 0.65);
}
.announcement-bar-dismiss:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 2px;
}
@media (max-width: 575.98px) {
    .announcement-bar {
        font-size: 0.8rem;
        padding: 0.35rem 0.75rem;
        min-height: 32px;
    }
    .announcement-bar-dismiss { width: 24px; height: 24px; font-size: 0.85rem; }
}

/* ========== AUTH PAGES (Login / Register / ForgotPassword / ResetPassword) ========== */
.auth-shell {
    /* `vh` fallback first; `dvh` for browsers that respect collapsed
       URL bars. Without `dvh`, mobile Firefox / iOS Safari over-size
       the centred card by ~56–80 px and the form gets pushed below
       the visible area. */
    min-height: calc(100vh - 56px);
    min-height: calc(100dvh - 56px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 2rem 1rem;
    background:
        radial-gradient(ellipse at top, rgba(var(--accent-red-rgb), 0.10) 0%, transparent 55%),
        var(--bg-page);
}

.auth-card-wrapper {
    width: 100%;
    max-width: 440px;
}

.auth-card {
    background-color: var(--bg-card);
    border: 1px solid var(--border-color);
    border-radius: 14px;
}

.auth-card:hover { transform: none; box-shadow: var(--shadow-lg); border-color: var(--border-color); }

.auth-icon-circle {
    width: 64px;
    height: 64px;
    border-radius: 50%;
    background: linear-gradient(135deg, rgba(var(--accent-red-rgb), 0.18), rgba(230, 168, 23, 0.12));
    border: 1px solid rgba(var(--accent-red-rgb), 0.35);
    color: var(--accent-yellow);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 1.6rem;
    margin: 0 auto;
    box-shadow: 0 6px 24px rgba(var(--accent-red-rgb), 0.15);
}

[data-theme="light"] .auth-icon-circle {
    background: linear-gradient(135deg, rgba(224, 36, 36, 0.12), rgba(217, 119, 6, 0.10));
    color: var(--accent-red);
}

.auth-divider {
    position: relative;
    text-align: center;
    margin: 1.25rem 0 1rem;
    color: var(--text-muted);
    font-size: 0.8rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.auth-divider::before,
.auth-divider::after {
    content: "";
    position: absolute;
    top: 50%;
    width: calc(50% - 2rem);
    height: 1px;
    background: var(--border-color);
}

.auth-divider::before { left: 0; }
.auth-divider::after  { right: 0; }

.auth-divider span {
    background: var(--bg-card);
    padding: 0 0.75rem;
    position: relative;
}

.auth-link {
    color: var(--accent-yellow);
    font-weight: 600;
    text-decoration: none;
    transition: color 0.15s ease;
}

.auth-link:hover {
    color: var(--accent-yellow-hover);
    text-decoration: underline;
}

.password-input-group {
    position: relative;
    display: flex;
}

.password-input-group .form-control {
    border-top-right-radius: 0;
    border-bottom-right-radius: 0;
}

.password-toggle-btn {
    border-top-left-radius: 0;
    border-bottom-left-radius: 0;
    padding: 0 0.9rem;
    box-shadow: none;
}

.password-toggle-btn:hover { transform: none; box-shadow: none; }

@media (max-width: 420px) {
    .auth-shell { padding: 1rem 0.5rem; }
    .auth-card .card-body { padding: 1.75rem 1.25rem !important; }
    .auth-icon-circle { width: 56px; height: 56px; font-size: 1.4rem; }
}

/* ItemDetail gallery + reviews CSS moved to scoped stylesheets:
     - Components/Pages/ItemDetailParts/ItemGallery.razor.css
     - Components/Pages/ItemDetail.razor.css (review widgets) */

/* ========== ACCESSIBILITY HELPERS ========== */
/* Skip-to-content link. Must be the first interactive element on the page
   so keyboard users can jump straight into <main>. The previous hide
   technique used `position: absolute; top: -48px` to shift the element
   above the viewport, which is brittle on mobile browsers:
     • iOS Safari's rubber-band scroll briefly reveals content above the
       viewport when the user pulls to refresh.
     • Some browsers (Firefox Mobile, Samsung Internet) auto-focus the
       first focusable element on bf-cache restore, triggering :focus and
       slamming the link into view.
     • The link's z-index 1100 is above the fixed-top navbar (1030), so a
       leaking skip link cosmetically eats into the navbar's space.
   The current technique is the WCAG / inclusive-components "visually-
   hidden" pattern: the element is clipped to a 1×1 px rectangle with
   overflow hidden, so even if positioning fails the content is not
   rendered. Restoring on :focus / :focus-visible un-clips and shows the
   pill; tabbing brings it back when the user wants it. */
.skip-link {
    position: absolute;
    top: 0;
    left: 0;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    /* Suppress any colour bleed through the 1px footprint on broken UA. */
    background: transparent;
    color: transparent;
}

.skip-link:focus,
.skip-link:focus-visible {
    /* Un-clip and reveal — only when keyboard / AT focus reaches it. */
    top: 8px;
    left: 8px;
    width: auto;
    height: auto;
    margin: 0;
    padding: 0.5rem 1rem;
    overflow: visible;
    clip: auto;
    clip-path: none;
    white-space: normal;
    z-index: 1100;
    background: var(--accent-red);
    color: #fff;
    border-radius: 6px;
    font-weight: 600;
    text-decoration: none;
    outline: 2px solid #fff;
    outline-offset: 2px;
}

:focus-visible {
    outline: 2px solid var(--accent-yellow);
    outline-offset: 2px;
}

.btn:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible,
.form-check-input:focus-visible {
    outline: 2px solid var(--accent-yellow);
    outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
    .btn:hover,
    .card:hover,
    .pcard:hover,
    .product-card:hover {
        transform: none !important;
    }
}

/* ========== MOBILE TOUCH-TARGET & LAYOUT POLISH ========== */
/* iOS HIG & Android Material both recommend ≥44px touch targets.
   We OR two media queries: pointer: coarse catches real touch devices,
   and max-width: 768px catches small viewports (including DevTools mobile
   emulation, which keeps pointer: fine). The doubled rule means a desktop
   user running at 1440px gets the compact 36px buttons while a mobile
   user — emulated or not — always gets the spec-compliant 44px targets. */
@media (pointer: coarse), (max-width: 768px) {
    .qty-btn {
        min-width: 44px;
        min-height: 44px;
        padding: 0.5rem 0.75rem;
    }
    .cart-item-remove .btn {
        min-width: 44px;
        min-height: 44px;
    }
    .pcard-btn-details,
    .pcard-btn-cart {
        min-height: 44px;
    }
    /* Bootstrap .btn-sm is 31px tall — too small to reliably tap. Bump it
       to the 44px spec while keeping the compact horizontal padding. */
    .btn-sm {
        min-height: 44px;
    }
    .password-toggle-btn {
        min-width: 44px;
        min-height: 44px;
    }
    /* Navbar theme toggle — the 36×36 default reads fine on desktop mouse
       but is under the 44 px spec on touch devices, especially inside the
       mobile collapse menu where it sits next to the wider language button. */
    .btn-theme-toggle {
        width: 44px;
        height: 44px;
    }
    .navbar .nav-link { padding: 0.55rem 0.85rem !important; }
    .sidebar-link { padding: 0.7rem 1rem !important; }
}

/* Tighten cart layout on phones 360–414px wide */
@media (max-width: 414px) {
    .cart-item-row {
        padding: 0.75rem 0.875rem;
        gap: 0.625rem;
    }
    .cart-item-img { width: 64px; }
    .cart-no-img { font-size: 1.25rem; }
    .cart-item-details { flex: 1 1 calc(100% - 80px); }
    .cart-item-details h6 { font-size: 0.925rem; }
    .cart-item-qty { order: 1; }
    .cart-item-subtotal { order: 2; margin-left: auto; }
    .cart-item-remove { order: 3; }
    .breadcrumb { font-size: 0.85rem; }
    h1 { margin-bottom: 0.75rem; }
}

/* Very narrow phones */
@media (max-width: 360px) {
    .cart-item-img { width: 56px; }
    .cart-item-details { flex: 1 1 calc(100% - 72px); }
    .qty-input { width: 36px; }
}

/* Prevent page zoom on iOS when focusing small inputs */
@media (max-width: 767px) {
    .form-control,
    .form-select,
    textarea,
    .dark-input {
        font-size: 16px;
    }
    .qty-input { font-size: 0.95rem; }
}

/* ── Password strength meter + requirement checklist ─────────────────────────
   Used on Register.razor and ResetPassword.razor. The four bars visually map
   to PasswordPolicy.Score (0..4); the requirement list flips each <li> from
   muted/outline-circle to colored/filled-check as each rule passes. Using
   individual color classes (not aria-live) for the bars avoids over-announcing
   on every keystroke while the text label beside them uses aria-live=polite. */
.pwd-strength {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-height: 1.5rem;
}
.pwd-strength-bars {
    display: flex;
    gap: 4px;
    flex: 1 1 auto;
    max-width: 220px;
}
.pwd-strength-seg {
    flex: 1;
    height: 6px;
    border-radius: 3px;
    background: var(--border, #3a3a44);
    transition: background-color 0.2s;
}
.pwd-strength-weak   .pwd-strength-seg.filled { background: var(--strength-weak); }
.pwd-strength-fair   .pwd-strength-seg.filled { background: var(--strength-fair); }
.pwd-strength-good   .pwd-strength-seg.filled { background: var(--strength-good); }
.pwd-strength-strong .pwd-strength-seg.filled { background: var(--strength-strong); }
.pwd-strength-label {
    color: var(--text-muted, #8a8a95);
    white-space: nowrap;
    font-size: 0.8rem;
}
.pwd-strength-weak   .pwd-strength-label strong { color: var(--strength-weak); }
.pwd-strength-fair   .pwd-strength-label strong { color: var(--strength-fair); }
.pwd-strength-good   .pwd-strength-label strong { color: var(--strength-good); }
.pwd-strength-strong .pwd-strength-label strong { color: var(--strength-strong); }

.pwd-rules {
    color: var(--text-muted, #8a8a95);
}
.pwd-rules li {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.1rem 0;
    transition: color 0.2s;
}
.pwd-rules li.ok {
    color: var(--accent-success);
}
.pwd-rules li i {
    font-size: 0.9rem;
    width: 1em;
    flex-shrink: 0;
}

/* ---------------------------------------------------------------
   Sticky filter toolbar on /items — on small screens the sort/filter
   controls scroll away as soon as you start browsing the grid. Pinning
   the toolbar just below the fixed navbar keeps "sort" and "in stock"
   reachable at all times, which matters most when browsing long lists
   on mobile. Desktop keeps the static layout.
   --------------------------------------------------------------- */
.items-toolbar {
    background: var(--bg-card, #fff);
    border-radius: 0.5rem;
    padding: 0.75rem;
}
@media (max-width: 991.98px) {
    .items-toolbar {
        position: sticky;
        /* navbar is fixed-top at ~56px; a small gap keeps the shadow readable */
        top: 60px;
        z-index: 1020;
        box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
        margin-left: -0.5rem;
        margin-right: -0.5rem;
        padding-left: 1rem;
        padding-right: 1rem;
        border-radius: 0;
    }
}

/* Navbar search input responsive max-width — the fixed 240px inline style
   squashed awkwardly on medium viewports; this lets it breathe on wide
   screens and stay compact on narrower ones without hard-coding px. */
.navbar .form-control[type="search"] {
    max-width: 240px;
}
@media (min-width: 1200px) {
    .navbar .form-control[type="search"] {
        max-width: 320px;
    }
}

/* Profile tablist: inactive-tab colour. Earlier code used inline
   `style="color: #fff;"` which rendered invisibly white-on-white in light
   theme. Token-based version respects both themes. */
.nav-tabs .nav-link.profile-tab-inactive {
    color: var(--text-secondary);
}
.nav-tabs .nav-link.profile-tab-inactive:hover {
    color: var(--text-primary);
    border-color: transparent;
}

/* Contact page info labels — previously used Bootstrap's `.text-light`, which
   resolved to near-white in the light theme and was invisible against the
   card background. Token-based so both themes render correctly. */
.contact-info-heading {
    color: var(--text-primary);
    font-weight: 600;
    margin-bottom: 0.25rem;
}

/* Disabled buttons. Bootstrap's default opacity:.65 plus our hover transforms
   gave a confused state where a disabled button still grew on hover. Suppress
   the transform + the box-shadow + the pointer events so isSubmitting state
   is visually unmistakable. (Pass 5 P1-M carry-forward.) */
.btn:disabled,
.btn[disabled] {
    opacity: 0.55;
    cursor: not-allowed;
    transform: none !important;
    box-shadow: none !important;
    pointer-events: none;
}

/* Honor prefers-reduced-motion already covered above; this just nukes the
   hover lift + tinted shadow on disabled custom buttons too. */
.pcard-btn-cart:disabled,
.pcard-btn-details:disabled,
.qty-btn:disabled {
    transform: none !important;
    box-shadow: none !important;
    opacity: 0.55;
}

/* ConfirmDialog modal — themed, focus-trapped replacement for window.confirm.
   Backdrop covers the page; dialog is centered and uses theme tokens so
   light / dark both render correctly. */
.confirm-dialog-backdrop {
    position: fixed; inset: 0;
    background: rgba(0, 0, 0, 0.55);
    z-index: 11000;
}
.confirm-dialog {
    position: fixed; top: 50%; left: 50%;
    transform: translate(-50%, -50%);
    background: var(--bg-card);
    color: var(--text-primary);
    border: 1px solid var(--border-color);
    border-radius: 10px;
    padding: 1.5rem 1.5rem 1.25rem;
    max-width: 460px; width: 92%;
    box-shadow: var(--shadow-lg);
    z-index: 11001;
    outline: none;
}

/* Print stylesheet — receipts (OrderConfirmed), admin order detail (pick
   list) and any other printed view. Hide chrome (nav, footer, sidebar,
   buttons), neutralise dark backgrounds, drop card shadows, prevent
   row-splits across page breaks. */
@media print {
    .navbar, .sidebar, .site-footer, .skip-link, .breadcrumb,
    .btn, .nav-tabs, .alert .btn-close { display: none !important; }
    body {
        background: #fff !important;
        color: #000 !important;
        padding-top: 0 !important;
    }
    a { color: #000 !important; text-decoration: none !important; }
    .card {
        background: #fff !important;
        color: #000 !important;
        border: 1px solid #999 !important;
        box-shadow: none !important;
        break-inside: avoid;
    }
    .text-muted { color: #444 !important; }
    .container, .container-fluid { max-width: 100% !important; }
}

/* =========================================================================
   Profile / account-page primitives
   -------------------------------------------------------------------------
   Replaces a scatter of inline style="background:var(--accent-red);color:#fff"
   and style="border-color:var(--border-color)" declarations in Profile.razor,
   OrderConfirmed.razor, UserOrders.razor and Register.razor. Kept in one
   block so the theme tokens stay the source of truth — change --accent-red
   in :root and every affected button/alert/card follows.
   ========================================================================= */

.alert-success-themed {
    border-color: var(--accent-success);
    background-color: rgba(46, 194, 126, 0.1);
}

.alert-danger-themed {
    border-color: var(--accent-red);
    background-color: rgba(var(--accent-red-rgb), 0.1);
}

.card-themed {
    border-color: var(--border-color);
}

.tablist-themed {
    border-color: var(--border-color);
}

.profile-tab-active {
    border-color: var(--accent-red) !important;
    color: var(--accent-red) !important;
}

.btn-accent {
    background-color: var(--accent-red);
    color: #fff;
    border: none;
}

.btn-accent:hover:not(:disabled) {
    filter: brightness(1.1);
    color: #fff;
}

.btn-muted {
    background-color: var(--bg-muted, #444);
    color: var(--text-primary, #fff);
    border: none;
}

.btn-muted:hover:not(:disabled) {
    filter: brightness(1.1);
    color: var(--text-primary, #fff);
}

.hr-themed {
    border-color: var(--border-color, #444);
}

.address-card {
    border: 1px solid var(--border-color, #444);
    border-radius: 8px;
}

.address-card.is-default {
    border-color: var(--accent-red);
}

.badge-accent {
    background-color: var(--accent-red);
}

.empty-state-icon {
    font-size: 2.5rem;
    opacity: 0.3;
}

/* Larger empty-state icon for full-page no-results screens (Items page,
   no-search-results card). 4rem matches the visual weight users expect on
   a centred "nothing here" page; 2.5rem (used inline) reads as a small
   inline marker. */
.empty-state-icon-lg {
    font-size: 4rem;
}

/* "Big" cart empty icon — was an inline style="font-size: 3rem" on the
   bi-cart-x glyph. Same visual size, theme-token-friendly. */
.empty-state-icon { /* shared baseline above */ }

/* =========================================================================
   Skeleton loaders — used by Items, ItemDetail and UserOrders while data
   is in flight. A soft shimmer via a linear-gradient + keyframes keeps the
   user's eye occupied during the brief ADO.NET round-trip; without this the
   pages blink between empty-state and populated, which reads as a flicker.
   ========================================================================= */

@keyframes sk-shimmer {
    0% { background-position: -400px 0; }
    100% { background-position: 400px 0; }
}

.skeleton {
    display: block;
    background: linear-gradient(90deg,
        rgba(var(--text-primary-rgb, 255,255,255), 0.06) 0%,
        rgba(var(--text-primary-rgb, 255,255,255), 0.14) 50%,
        rgba(var(--text-primary-rgb, 255,255,255), 0.06) 100%);
    background-size: 800px 100%;
    animation: sk-shimmer 1.2s linear infinite;
    border-radius: 6px;
}

.sk-line { height: 0.9rem; margin: 0.4rem 0; }
.sk-line.sk-short { width: 40%; }
.sk-line.sk-med   { width: 65%; }
.sk-line.sk-long  { width: 90%; }
.sk-img           { width: 100%; aspect-ratio: 4 / 3; }
.sk-btn           { height: 2.25rem; width: 100%; }

/* Respect reduced-motion users — hold a static colour instead of animating. */
@media (prefers-reduced-motion: reduce) {
    .skeleton { animation: none; }
}

/* =========================================================================
   Honeypot visually-hidden helper. Off-screen positioning is preferred over
   display:none because some bots skip display:none fields as a heuristic.
   Kept as a class so any future form can reuse it without copy-pasting the
   inline style into the markup.
   ========================================================================= */

.sr-only-input {
    position: absolute;
    left: -10000px;
    top: auto;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* =========================================================================
   Order-detail primitives — shared between OrderConfirmed.razor,
   UserOrders.razor and Admin/OrderDetail.razor so the "thank-you receipt"
   styling stays consistent across the three surfaces a user can see their
   order on (post-checkout, account history, admin view).
   ========================================================================= */

.card-success-themed {
    border-color: var(--accent-success, #28a745);
    border-width: 2px;
}

.card-header-themed {
    border-color: var(--border-color);
}

.order-success-icon {
    color: var(--accent-success, #28a745);
    font-size: 3rem;
    margin-bottom: 1rem;
}

.order-id-display {
    color: var(--accent-yellow, #ffc107);
    font-family: 'Courier New', monospace;
    letter-spacing: 2px;
}

.text-accent       { color: var(--accent-red) !important; }
.text-accent-yellow { color: var(--accent-yellow, #ffc107) !important; }
.text-accent-success { color: var(--accent-success, #28a745) !important; }

.bg-transparent-breadcrumb { background-color: transparent; }

.address-accent-border {
    border-left: 3px solid var(--accent-yellow, #ffc107);
    padding-left: 1rem;
}

.border-top-themed    { border-top: 1px solid var(--border-color); }
.border-bottom-themed { border-bottom: 1px solid var(--border-color); }
.border-divider-dark  { border-top: 1px solid var(--border-color); }
.border-bottom-dark   { border-bottom: 1px solid var(--border-color); }

.total-row {
    font-size: 1.3rem;
}

.badge-pending {
    background-color: var(--accent-yellow, #ffc107);
    color: #000000;
    padding: 0.5rem 1rem;
    font-size: 0.9rem;
}

.empty-state-large-icon {
    font-size: 3rem;
    color: var(--accent-red);
}

.auth-card-narrow {
    max-width: 520px;
}

.hr-dark { border-color: var(--border-color); }

.empty-orders-alert {
    border-color: var(--border-color);
    background-color: var(--bg-card);
}

.order-total-emphasis {
    color: var(--accent-red);
    font-weight: bold;
    font-size: 1.1rem;
}

.table-dark-themed { border-color: var(--border-color); }
.table-th-white { color: var(--text-primary); }

.wishlist-empty-icon { font-size: 4rem; }

/* Cookie consent banner — fixed bottom-center; dismissed to localStorage. */
.cookie-consent-banner {
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: 1rem;
    max-width: min(720px, calc(100vw - 2rem));
    padding: 0.75rem 1rem;
    display: flex;
    gap: 0.75rem;
    align-items: center;
    justify-content: space-between;
    background: var(--bg-surface, #1f1f1f);
    color: var(--text-primary, #eee);
    border: 1px solid var(--border-muted, #444);
    border-radius: 0.5rem;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.35);
    z-index: 1080;
    font-size: 0.9rem;
    line-height: 1.35;
}
.cookie-consent-text { flex: 1; }
.cookie-consent-text a { color: inherit; text-decoration: underline; }

/* ========== DARK THEME: NEUTRALIZE RED-TINTED BACKGROUNDS ==========
   Goal: on dark theme, all background-adjacent surfaces (fills, gradients,
   decorative shadows, radial accents) are pure grayscale. Red is preserved
   ONLY for: text, borders that signal selection/active state, solid CTA
   buttons (.btn-primary, .pcard-btn-cart), and semantic error alerts/toasts.
   Light theme keeps its existing subtle red tints — these overrides are
   [data-theme="dark"] scoped so they don't bleed through. */

/* Hero — neutral border, no red radial. */
[data-theme="dark"] .hero {
    border-color: rgba(255, 255, 255, 0.08);
}
[data-theme="dark"] .hero::before {
    background: radial-gradient(ellipse at 60% 50%, rgba(255, 255, 255, 0.04) 0%, transparent 70%);
}

/* Product card hover — editorial card has no shadow lift; the hover state
   is carried by the bottom hairline (.pcard::after) and the gentle image
   zoom. Dark theme just shifts the border to a subtler bright-on-black
   neutral so the card outline is visible without the brand-red aura. */
[data-theme="dark"] .pcard:hover {
    border-color: rgba(255, 255, 255, 0.22);
}

/* Public-facing sidebar (filter/category list). */
[data-theme="dark"] .sidebar-link:hover {
    background-color: rgba(255, 255, 255, 0.05) !important;
    border-left-color: rgba(255, 255, 255, 0.25);
}
[data-theme="dark"] .sidebar-link.active {
    background-color: rgba(255, 255, 255, 0.08) !important;
}

/* Admin topbar logout button hover — neutral fill, red text retained. */
[data-theme="dark"] .admin-topbar-logout:hover {
    background: rgba(255, 255, 255, 0.06);
    border-color: rgba(255, 255, 255, 0.20);
}

/* Admin sidebar brand icon. */
[data-theme="dark"] .sidebar-brand-icon {
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(255, 255, 255, 0.18);
}

/* Admin table row hover. */
[data-theme="dark"] .admin-page .table tbody tr:hover {
    background-color: rgba(255, 255, 255, 0.04);
}

/* Auth pages — strip the red radial under the card. */
[data-theme="dark"] .auth-shell {
    background: var(--bg-page);
}

/* Auth icon circle — neutral dark well, keep gold icon glyph. */
[data-theme="dark"] .auth-icon-circle {
    background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), rgba(255, 255, 255, 0.03));
    border: 1px solid rgba(255, 255, 255, 0.15);
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45);
}

/* Product-image gallery thumb — keep red border to signal selection,
   swap the red glow for a neutral focus ring. */
[data-theme="dark"] .gallery-thumb.active {
    box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.20);
}

/* =========================================================================
   MOBILE RESPONSIVE POLISH (consolidated)
   -------------------------------------------------------------------------
   Tightens spacing, button sizing, and layout density on narrow viewports.
   Every rule is inside a max-width / coarse-pointer query so the desktop
   experience is untouched. Ordered small-first so earlier blocks can be
   overridden by more-specific narrower blocks.
   ========================================================================= */

/* ── Tablet / small desktop (≤991.98px) ─────────────────────────────────── */
@media (max-width: 991.98px) {
    /* Section headings were sized for desktop; give them a bit less room. */
    .section-heading { margin: 1.75rem 0 1.25rem; }
}

/* ── Phones (≤767.98px) ─────────────────────────────────────────────────── */
@media (max-width: 767.98px) {
    /* Bootstrap container/container-fluid — default 12px gutters look lost
       on a phone. Slightly tighter edges give images and cards more room. */
    .container,
    .container-fluid {
        padding-left: 0.875rem;
        padding-right: 0.875rem;
    }

    /* MainLayout wraps every page in <article class="content px-4">. The
       px-4 (1.5rem) is too generous on phones; override to 0.875rem. */
    .content.px-4 {
        padding-left: 0.875rem !important;
        padding-right: 0.875rem !important;
        padding-top: 0.75rem;
    }

    /* Compact card padding */
    .card-body { padding: 1rem; }

    /* Hero section */
    .hero { padding: 1.75rem 1rem; margin-bottom: 1.75rem; }

    /* h1 margins (top-of-page headings read tall on mobile) */
    h1 { margin-bottom: 0.75rem; line-height: 1.2; }

    /* Breadcrumb */
    .breadcrumb { margin-bottom: 0.75rem; font-size: 0.85rem; }

    /* Page header (admin pages) */
    .page-header { padding-bottom: 0.75rem; margin-bottom: 1rem; }

    /* Tables — ensure smooth horizontal scroll and clear the column-header
       padding that looks cramped on 2-col lists. */
    .table-responsive { -webkit-overflow-scrolling: touch; }
    .admin-page .table tbody td { padding: 10px 10px; }
    .admin-page .table thead th { font-size: 0.78rem; padding: 10px; }

    /* Footer — tighter vertical rhythm on phones */
    .site-footer .row.g-4 { --bs-gutter-y: 1rem; }

    /* Cookie consent wraps on narrow screens instead of squishing */
    .cookie-consent-banner { flex-wrap: wrap; font-size: 0.85rem; }
    .cookie-consent-banner .btn { width: 100%; }

    /* Cart undo/error toasts — allow wrap so long product titles don't squish
       the Undo/Dismiss action into illegibility. */
    .cart-undo-toast,
    .cart-error-toast {
        flex-wrap: wrap;
        row-gap: 0.375rem;
    }

    /* Admin card header with justify-content-between typically holds a title
       and an action button or badge. On phone widths let these wrap so a
       long title and an action button don't truncate or overlap. */
    .admin-page .card-header.d-flex.justify-content-between {
        flex-wrap: wrap;
        gap: 0.5rem;
    }

    /* Admin tables — slightly smaller font so more columns fit before the
       horizontal scroll kicks in. Headers stay on one line for scanning. */
    .admin-page .table { font-size: 0.85rem; }
    .admin-page .table thead th { white-space: nowrap; }
    .admin-page .table tbody td { padding: 9px 10px; }

    /* Admin forms — row columns (col-md-6/4) stack naturally below 768px;
       tighten the form-label + card padding so we see more at once. */
    .admin-page .form-label { margin-bottom: 0.35rem; font-size: 0.9rem; }
    .admin-page .card-body { padding: 1rem; }
    .admin-page .card-header { padding: 0.75rem 1rem; }

    /* Message bubbles (admin OrderDetail + UserOrders) — allow up to 90% of
       the container width on phones so the message text isn't wrapped into
       an awkwardly-narrow column. */
    .message-bubble { max-width: 90% !important; }

    /* Order summary is normally sticky on desktop; remove sticky on mobile
       so it sits at the end of the cart flow naturally. */
    .order-summary-sticky { position: static; top: auto; }

    /* ItemDetail gallery thumbnails smaller to leave room below the fold */
    .gallery-thumb { width: 52px; height: 52px; }
    .gallery-thumbs { gap: 6px; }

    /* ItemDetail price size — clamp() already scales h1, but the plain
       .price class is fixed 1.75rem; knock it down. */
    .price { font-size: 1.4rem; }

    /* Pagination: wrap and tighten so 10+ pages fit without x-scroll */
    .pagination { flex-wrap: wrap; gap: 0.25rem; margin: 1.5rem 0; }
    .pagination .page-link { padding: 0.35rem 0.6rem; font-size: 0.875rem; }

    /* Admin dashboard stat cards — shrink internal padding */
    .stat-card { padding: 14px !important; gap: 12px !important; }
    .stat-icon { width: 44px !important; height: 44px !important; font-size: 1.3rem !important; }
    .stat-value { font-size: 1.4rem !important; }
    .stat-label { font-size: 0.78rem !important; }
}

/* ── Narrow phones (≤575.98px) ──────────────────────────────────────────── */
@media (max-width: 575.98px) {
    /* NavHeaderMenu polish — keep brand from pushing cart/hamburger off-screen.
       The prominent Categories button on the left is the priority tap target,
       so the brand shrinks and ellipsifies first when horizontal space runs
       out. */
    .navbar .navbar-brand {
        max-width: 32vw;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        font-size: 1.05rem;
    }
    .navbar .btn-warning.btn-sm {
        padding: 0.3rem 0.55rem;
        font-size: 0.85rem;
    }
    .navbar { padding: 0 0.625rem; }
    /* Categories button stays ≥44 px tall but loses its horizontal label
       below 420 px (handled by the min-width media query above). */
    .btn-sidebar-toggle { padding: 0 0.55rem; font-size: 1.25rem; }

    /* Profile/Admin nav-tabs: horizontal scroll so they don't wrap into a
       lumpy 2-row block on narrow screens. */
    .nav-tabs {
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: thin;
    }
    .nav-tabs::-webkit-scrollbar { height: 3px; }
    .nav-tabs .nav-link {
        white-space: nowrap;
        flex-shrink: 0;
    }

    /* Admin page-header: stack the title and action button vertically
       so a long title and a full-width "Add new" button don't collide. */
    .admin-page .page-header > .d-flex.justify-content-between,
    .admin-page .page-header > div > .d-flex.justify-content-between {
        flex-direction: column;
        align-items: stretch !important;
        gap: 0.75rem;
    }
    .admin-page .page-header .d-flex.justify-content-between > .btn,
    .admin-page .page-header .d-flex.justify-content-between > a.btn {
        width: 100%;
    }

    /* Items toolbar: labels were small enough but the whole block felt
       cramped. Tighten the gutter so fields breathe. */
    .items-toolbar .row.g-2 { --bs-gutter-y: 0.75rem; }
    .items-toolbar .form-label.small { font-size: 0.75rem; margin-bottom: 0.25rem; }

    /* Filter toolbar Apply/Clear buttons stretch full width for easier tap */
    .items-toolbar .col-12.col-md-3 > .btn { flex: 1 1 auto; }

    /* Modal body padding — confirm dialog is cramped on tiny screens */
    .confirm-dialog { padding: 1.25rem 1rem 1rem; }
}

/* ── Very narrow (≤400px) ───────────────────────────────────────────────── */
@media (max-width: 400px) {
    .container, .container-fluid { padding-left: 0.625rem; padding-right: 0.625rem; }
    .content.px-4 { padding-left: 0.625rem !important; padding-right: 0.625rem !important; }
    .card-body { padding: 0.875rem; }
    .hero { padding: 1.25rem 0.75rem; }
    .hero h1 { font-size: 1.45rem; }
    .hero p { font-size: 0.95rem; margin-bottom: 1.25rem; }
    .navbar .navbar-brand { max-width: 28vw; font-size: 0.95rem; }
    .navbar-brand i { margin-right: 0.2rem !important; }
    /* Cart badge doesn't need to be huge on a 36px tap target */
    .navbar .btn-warning.btn-sm .badge { padding: 0.2rem 0.4rem; font-size: 0.7rem; }
    /* Below 400 px keep the Categories button square (icon-only, no label) —
       the label already hides at 420 px breakpoint; this just tightens the
       horizontal padding so three right-side controls still fit. */
    .btn-sidebar-toggle { padding: 0 0.35rem; min-width: 40px; }
    /* Price emphasis still readable, smaller gap */
    .price { font-size: 1.25rem; }
    /* Toolbar buttons: text may wrap awkwardly; tighten */
    .items-toolbar .btn { padding: 0.4rem 0.55rem; font-size: 0.8rem; }
}

/* ── Storefront NavMenu drawer polish (≤767.98px) ───────────────────────
   On phones the storefront sidebar opens as a 260px off-canvas drawer
   (handled in MainLayout.razor.css). With the drawer at 260px wide and
   a fingertip as the primary pointer, default desktop density (0.5rem
   padding, 0.875rem font) reads cramped and is hard to tap. These rules
   up-size the touch targets and type inside the drawer without touching
   the desktop sidebar. */
@media (max-width: 767.98px) {
    /* Header reads as a proper drawer title on mobile — the desktop
       tight-kerned uppercase label looks like a section divider and users
       scanned past it; sentence-cased larger text lands as "Categories" at
       a glance. */
    .sidebar .sidebar-header {
        padding: 1.1rem 1.25rem 0.95rem;
        font-size: 1rem;
        font-weight: 700;
        letter-spacing: 0.01em;
        text-transform: none;
        color: var(--text-primary);
        background: var(--bg-sidebar);
        border-bottom: 1px solid var(--border-color);
        gap: 0.75rem;
    }
    .sidebar .sidebar-header > span {
        display: inline-flex;
        align-items: center;
        gap: 0.6rem;
    }
    .sidebar .sidebar-header > span > i {
        color: var(--accent-yellow);
        font-size: 1.2rem;
        /* Override the razor's me-2 (Bootstrap 0.5rem) since we're using
           flex `gap` on the span wrapper for consistent icon/text spacing. */
        margin-right: 0 !important;
    }
    /* Close button as a square tap target ≥44 px with a faint hover well
       so the "X" has the same density as the nav items below. */
    .sidebar .sidebar-close-btn {
        font-size: 1.35rem;
        padding: 0;
        margin-right: -0.35rem;
        width: 44px;
        height: 44px;
        border-radius: 10px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }
    .sidebar .sidebar-close-btn:hover,
    .sidebar .sidebar-close-btn:focus-visible {
        background: var(--bg-input);
        color: var(--text-primary);
    }
    .sidebar .sidebar-close-btn:focus-visible {
        outline: 2px solid var(--accent-yellow);
        outline-offset: -2px;
    }
    /* Search sits directly under the header, sticky within the flex column
       (flex layout already keeps it pinned — this just widens the touch
       target and bumps to 16 px so iOS Safari doesn't zoom on focus). */
    .sidebar .sidebar-search {
        padding: 0.85rem 1rem 0.95rem;
        background: var(--bg-sidebar);
        border-bottom: 1px solid var(--border-subtle);
    }
    .sidebar .sidebar-search-input {
        height: 44px;
        font-size: 16px !important;
        border-radius: 8px;
        padding: 0 0.85rem;
    }
    /* Category links: generous padding, large type, 48 px tap height.
       Allow 2-line wrap for long category names instead of ellipsis — the
       320 px drawer still clips longer taxonomy names otherwise. */
    .sidebar .sidebar-link {
        padding: 0.85rem 1.1rem;
        font-size: 1rem;
        gap: 0.9rem;
        min-height: 48px;
        white-space: normal;
        line-height: 1.35;
        border-left-width: 4px;
        border-radius: 0 10px 10px 0;
        margin-right: 0.6rem;
    }
    .sidebar .sidebar-link:hover {
        border-left-color: rgba(var(--accent-red-rgb), 0.45);
    }
    .sidebar .sidebar-link.active {
        border-left-width: 4px;
        font-weight: 600;
    }
    .sidebar .sidebar-icon {
        font-size: 1.2rem;
        width: 1.4rem;
        flex-shrink: 0;
    }
    .sidebar .sidebar-nav {
        padding: 0.4rem 0 1.5rem;
    }
    .sidebar .sidebar-empty {
        padding: 2rem 1rem;
        font-size: 0.92rem;
        color: var(--text-muted);
    }
    .sidebar .sidebar-empty i {
        display: block;
        font-size: 1.6rem;
        margin-bottom: 0.5rem;
        opacity: 0.6;
    }
}

/* ── Admin layout: off-canvas drawer on phones (≤767.98px) ──────────────── */
/* Desktop keeps the 240px fixed sidebar. Tablet (768–900px) still uses the
   icon-only 62px sidebar defined in the existing block above. Below 768px
   we pivot to an off-canvas drawer so the full-text nav is back, and the
   main content uses the entire viewport width. The drawer open state is
   controlled by `.admin-sidebar-open` on `.admin-wrapper` (toggled from
   AdminLayout.razor via a hamburger in the topbar). */
@media (max-width: 767.98px) {
    .admin-sidebar {
        width: 260px !important;
        min-width: 260px !important;
        transform: translateX(-100%);
        transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1),
                    background 0.25s ease,
                    border-color 0.25s ease;
        box-shadow: none;
    }
    .admin-wrapper.admin-sidebar-open .admin-sidebar {
        transform: translateX(0);
        box-shadow: 4px 0 24px rgba(0, 0, 0, 0.5);
    }
    /* Restore full-text nav since drawer is wide */
    .admin-sidebar .sidebar-brand { justify-content: flex-start !important; padding: 18px 16px !important; }
    .admin-sidebar .sidebar-brand-name,
    .admin-sidebar .sidebar-brand-sub { display: block !important; }
    .admin-sidebar .sidebar-section-label { display: block !important; }
    .admin-sidebar .sidebar-link {
        justify-content: flex-start !important;
        padding: 11px 16px !important;
        gap: 12px !important;
        margin: 2px 8px 2px 0 !important;
        border-radius: 0 7px 7px 0 !important;
    }
    .admin-sidebar .sidebar-link span { display: inline !important; }
    .admin-sidebar .sidebar-user { justify-content: flex-start !important; padding: 14px 16px !important; }
    .admin-sidebar .sidebar-user-info { display: block !important; }

    /* Overlay backdrop — only visible when drawer is open, so siblings
       rendered conditionally in Razor (like <div class="admin-sidebar-overlay">)
       appear and disappear on toggle. */
    .admin-sidebar-overlay {
        position: fixed;
        inset: 0;
        background: rgba(0, 0, 0, 0.6);
        backdrop-filter: blur(2px);
        z-index: 998;
        animation: fadeIn 0.2s ease;
    }

    /* Topbar spans full viewport width — no sidebar reserve */
    .admin-topbar {
        left: 0 !important;
        padding: 0 10px !important;
        gap: 6px;
    }
    .admin-topbar-left { gap: 8px; flex-shrink: 1; min-width: 0; }
    .admin-topbar-right { gap: 4px; flex-shrink: 0; }
    .admin-topbar-store-link span { display: none; }
    .admin-topbar-user { display: none; }
    .admin-topbar-divider { display: none; }
    .admin-topbar-logout span { display: none; }
    .admin-topbar-logout { padding: 5px 10px; }
    .admin-topbar .btn-theme-toggle { width: 34px; height: 34px; }

    /* Mobile hamburger toggle button (inserted into the topbar) */
    .admin-mobile-toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        background: transparent;
        border: 1px solid var(--border-color);
        color: var(--text-secondary);
        border-radius: 6px;
        width: 36px;
        height: 36px;
        padding: 0;
        cursor: pointer;
        flex-shrink: 0;
        transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
    }
    .admin-mobile-toggle:hover {
        background: var(--bg-input);
        color: var(--text-primary);
    }
    .admin-mobile-toggle:focus-visible {
        outline: 2px solid var(--accent-yellow);
        outline-offset: 2px;
    }

    /* Main content: no sidebar margin */
    .admin-main {
        margin-left: 0 !important;
        padding: 18px 12px !important;
    }
}

/* Hide the mobile admin toggle on everything above phone sizes — its
   sole purpose is to surface the drawer on screens that don't have the
   permanent sidebar. */
@media (min-width: 768px) {
    .admin-mobile-toggle { display: none !important; }
}

/* ── RTL adjustments (Arabic) ──────────────────────────────────────────
   Bootstrap is loaded as bootstrap.rtl.min.css when the resolved culture
   is Arabic (App.razor swaps the sheet), so Bootstrap utilities flip
   automatically. The rules below patch our own custom CSS where it uses
   physical `margin-left/right`, `border-left/right`, or `text-align`
   instead of logical properties — converting every rule to `*-inline-*`
   would touch ~60 lines and risk visual drift in LTR, so we override
   surgically only where the LTR rule would cause a clearly broken
   appearance under RTL (sidebars, cart layout, price stacks, admin
   sidebar accent stripe). Keep this block near the bottom so it wins
   the cascade without `!important` where possible. */
/* Form controls under RTL: Chrome's user-agent stylesheet renders the
   value of inputs whose content is inherently LTR (email, password,
   tel, url, search, and the autofill rendering of those) anchored to
   the left wall regardless of the surrounding `dir="rtl"`. The
   visible bug was the AdminLogin email/password fields sitting
   left-pinned while every label and button next to them read RTL.
   Force the cursor and value/placeholder to the trailing edge for
   exactly those types — the content itself still flows LTR, only the
   anchor moves. Plain `<input type="text">` and `textarea` already
   respect `dir` correctly via the browser's `text-align: start`
   default, and `<input type="number">` is intentionally left alone
   because numbers are direction-neutral and quantity steppers etc.
   set `text-align: center` deliberately. */
[dir="rtl"] input[type="email"],
[dir="rtl"] input[type="password"],
[dir="rtl"] input[type="tel"],
[dir="rtl"] input[type="url"],
[dir="rtl"] input[type="search"] { text-align: right; }

[dir="rtl"] .form-check-label { margin-left: 0; margin-right: 0.5rem; }
[dir="rtl"] .price-original { margin-right: 0; margin-left: 0.5rem; }
[dir="rtl"] .pcard-btn-cart i,
[dir="rtl"] .pcard-btn-details i { margin-right: 0; margin-left: 0.35rem; }

/* Storefront category sidebar: stripe on the leading edge (left in LTR,
   right in RTL) — flip the border side, then re-emit hover/active
   colours on the new side so the indicator follows the text direction. */
[dir="rtl"] .sidebar-link {
    border-left: 0;
    border-right: 3px solid transparent;
}
[dir="rtl"] .sidebar-link:hover { border-right-color: rgba(var(--accent-red-rgb), 0.35); }
[dir="rtl"] .sidebar-link.active { border-right-color: var(--accent-red); }

/* Product card corner badges: the sale badge anchors to the leading
   edge of the card and the OOS badge to the trailing edge. Under RTL
   "leading" becomes the right side, so swap the two. */
[dir="rtl"] .pcard-sale-badge { left: auto; right: 8px; }
[dir="rtl"] .pcard-oos-badge  { right: auto; left: 8px; }

/* Toast stack: in LTR it sits in the top-right; in RTL it should sit
   in the top-left so it stays in the user's *trailing* corner (the
   logical "out of the way" position relative to text flow). The
   close × inside also flips: float-right under LTR becomes float-left
   under RTL so it lives on the trailing edge of the toast body. */
[dir="rtl"] .toast-container { right: auto; left: 20px; }
@media (max-width: 768px) {
    /* Mobile already pins both edges (right:10px; left:10px) so the
       container spans full width — no override needed there. */
}
[dir="rtl"] .toast-close { float: left; }

/* Profile / order address card: a yellow accent stripe runs down the
   leading edge with content offset by `padding-left`. Mirror both
   under RTL so the stripe stays on the leading side. */
[dir="rtl"] .address-accent-border {
    border-left: 0;
    border-right: 3px solid var(--accent-yellow, #ffc107);
    padding-left: 0;
    padding-right: 1rem;
}

/* `.cart-item-subtotal` is defined in Components/Pages/Cart.razor.css
   (a Blazor scoped stylesheet). An unscoped `[dir="rtl"]` override
   here can't beat the scoped rule because eshop.server.styles.css
   loads after app.css and the two selectors have equal specificity,
   so the override would silently lose. The RTL flip for that class
   lives inside Cart.razor.css with matching scope. Don't reintroduce
   the orphan rule here — it would look right in DevTools and fail in
   the rendered output. */

/* Quantity input vertical dividers: physical left/right become inverted
   under RTL only because the input order itself flips. The borders are
   symmetric (both sides), so no override is strictly needed — but the
   light-theme rule above sets explicit border colors per side. Re-apply
   them symmetrically here so the picker still has visible dividers in
   RTL+light. */
[dir="rtl"][data-theme="light"] .qty-input,
[data-theme="light"] [dir="rtl"] .qty-input {
    border-left-color: #c0c0cc;
    border-right-color: #c0c0cc;
}

/* Admin layout under RTL: the LTR rules pin the sidebar to `left: 0`
   and reserve space with `left/margin-left: 240px` on the topbar and
   main pane. Without flipping every one of those, the sidebar stays on
   the left wall under Arabic and overlays the right-aligned content.
   Mirror them surgically here.

   Sidebar shell: pin to the right wall, move the divider to the
   trailing (left) edge. */
[dir="rtl"] .admin-sidebar {
    left: auto;
    right: 0;
    border-right: 0;
    border-left: 1px solid var(--border-color);
}

/* Sidebar nav links: stripe on the trailing edge (mirrors the
   storefront sidebar pattern above), and flip the gutter + rounded
   corners so the active chip hugs the right wall instead of opening
   a gap on it. */
[dir="rtl"] .admin-sidebar .sidebar-link,
[dir="rtl"] .admin-side-link {
    border-left: 0;
    border-right: 3px solid transparent;
}
[dir="rtl"] .admin-sidebar .sidebar-link {
    margin: 2px 0 2px 10px;
    border-radius: 7px 0 0 7px;
}
[dir="rtl"] .admin-sidebar .sidebar-link.active,
[dir="rtl"] .admin-side-link.active { border-right-color: var(--accent-red); }

/* Desktop (≥901px) — full 240px sidebar. Flip the topbar offset and
   the main-pane margin reserve to the right side. The previous rule
   used `min-width: 992px`, which left a dead zone at 901–991px where
   the desktop sidebar was visible but the RTL margin flip wasn't
   applied. */
@media (min-width: 901px) {
    [dir="rtl"] .admin-topbar { left: 0; right: 240px; }
    [dir="rtl"] .admin-main { margin-left: 0; margin-right: 240px; }
}

/* Tablet (768–900px) — collapsed 62px sidebar. Same flip with the
   smaller reserve, plus tighter chip metrics. */
@media (max-width: 900px) {
    [dir="rtl"] .admin-topbar { left: 0; right: 62px; }
    [dir="rtl"] .admin-main { margin-left: 0; margin-right: 62px; }
    [dir="rtl"] .admin-sidebar .sidebar-link {
        margin: 2px 0 2px 4px;
        border-radius: 6px 0 0 6px;
    }
}

/* Phone (≤767.98px) — off-canvas drawer slides in from the trailing
   edge instead of the leading one. The mobile topbar is already
   `left: 0 !important` and spans the full viewport, so no offset
   flip is needed there. The link metrics inside the drawer carry
   `!important` in the LTR mobile block, so the RTL overrides match. */
@media (max-width: 767.98px) {
    [dir="rtl"] .admin-sidebar { transform: translateX(100%); }
    [dir="rtl"] .admin-wrapper.admin-sidebar-open .admin-sidebar {
        transform: translateX(0);
        box-shadow: -4px 0 24px rgba(0, 0, 0, 0.5);
    }
    [dir="rtl"] .admin-sidebar .sidebar-link {
        margin: 2px 8px 2px 0 !important;
        border-radius: 7px 0 0 7px !important;
    }
}

/* Toast / alert leading-edge accent strip — flip the colored border
   to the trailing side so it appears next to the text-start in RTL. */
[dir="rtl"] .alert-warning,
[dir="rtl"] .toast-warning,
[dir="rtl"] .alert-danger,
[dir="rtl"] .toast-danger {
    border-left: 0;
    border-right-width: 4px;
    border-right-style: solid;
}
[dir="rtl"] .alert-warning,
[dir="rtl"] .toast-warning { border-right-color: var(--accent-yellow, #f5b800); }
[dir="rtl"] .alert-danger,
[dir="rtl"] .toast-danger { border-right-color: var(--validation-error, #dc3545); }

/* Directional Bootstrap Icons under RTL.
   Bootstrap Icons does NOT auto-flip arrows/chevrons based on the
   document's `dir` attribute — `bi-arrow-left` always points left
   regardless of culture. For navigation icons whose meaning is
   "the previous thing in reading order" (back-to-list, pagination
   prev/next, breadcrumb dividers), that produces a button that
   visually contradicts the text next to it under Arabic ("←
   التالي" — arrow points left, label says "next"). Mirror them with
   a horizontal scale so they track text direction.

   The list is the union of every directional arrow/chevron currently
   used in the codebase (audited 2026-04-29: AdminLayout back-to-store,
   pagination on Items / Admin tables, blockquote-style "back to
   category" links, checkout back-to-shop). Add new icons to this
   list when introducing a new directional affordance — `bi-arrow-up`
   / `bi-arrow-down` / fixed-meaning icons (rewind / fast-forward in
   a hypothetical media context) intentionally stay out so they're
   not flipped where direction is semantic, not reading-order. */
[dir="rtl"] .bi-arrow-left,
[dir="rtl"] .bi-arrow-left-circle,
[dir="rtl"] .bi-arrow-left-circle-fill,
[dir="rtl"] .bi-arrow-right,
[dir="rtl"] .bi-arrow-right-circle,
[dir="rtl"] .bi-arrow-right-circle-fill,
[dir="rtl"] .bi-chevron-left,
[dir="rtl"] .bi-chevron-right,
[dir="rtl"] .bi-caret-left,
[dir="rtl"] .bi-caret-left-fill,
[dir="rtl"] .bi-caret-right,
[dir="rtl"] .bi-caret-right-fill {
    display: inline-block;
    transform: scaleX(-1);
}
