﻿/* Hide uncompiled Vue templates */
[v-cloak] {
    display: none !important;
}

.flex-spacer { flex: 1; }

:root {
    --border-radius: 8px;
    /* Shared height for all single-line form controls (input, select, multi-select
       trigger, url-field, search-field) and full-size buttons. Box-sizing:border-box
       on each so this is the true outer height regardless of font/padding. Textareas
       keep min-height. The -sm sibling is the compact variant (.btn-sm toolbars). */
    --control-height: 38px;
    --control-height-sm: 28px;
    /* Medium tier — header chrome / segmented toggles, intentionally shorter than
       the 38px form-aligned controls. Applied via the .btn-md modifier. */
    --control-height-md: 32px;
    --text-color: #333;
    /* Darkened from #6c757d (4.32:1 — FAILED WCAG AA on white) to #5a636c,
       which clears the 4.5:1 AA bar for normal text. Used in 50+ places. */
    --text-light: #5a636c;
    /* Darkened from #bbb (1.92:1 — FAILED AA badly) to #6f6f6f: 5.02:1 on
       --surface and 4.77:1 on --bg-light, so it clears AA in both the inputs
       it sits in. This token is for TEXT only — placeholders, faint labels.
       The faint-glyph uses it used to double as moved to --icon-faint. */
    --text-placeholder: #6f6f6f;
    /* The old #bbb, kept for NON-text glyphs where 1.4.3 does not apply and
       darkening would be a visible design change: the empty-star track, the
       admin cdn-off cue, disabled icon buttons. Don't use it for text. */
    --icon-faint: #bbb;
    --bg-light: #f8f9fa;
    --border-color: #dee2e6;
    --error-color: #dc3545;
    --error-border: #f08585;
    --error-bkg: #efadad;
    /* Red for alerts */
    --pink-highlight: #b60378;
    --pink-highlight-pale: #fdf2f7;
    --blue-highlight: #3b2e9e;
    --blue-highlight-pale: #ecebfb;
    /* Hover steps — keep in sync when re-theming the blue pair:
       -dark = highlight darkened one step; -pale-hover = pale one tint deeper. */
    --blue-highlight-dark: #2c2377;
    --blue-highlight-pale-hover: #d8d5ec;
    --green-highlight: #50aa50;
    --green-highlight-pale: #e9f5e9;
    /* Hover steps for the green family (.btn-green / .btn-green-pale). */
    --green-highlight-dark: #218838;
    --green-highlight-darker: #1e7e34;         /* .btn-green:hover border */
    --green-highlight-pale-hover: #d1e7dd;     /* value-equal to --pill-green-bg by coincidence, kept separate on purpose */
    --green-highlight-pale-hover-text: #146c43;
    /* --green-highlight is a 2.91:1 FAIL as text on white (2.60:1 on the pale),
       so it stays a fill/border colour and every green TEXT use points here
       instead: 6.45:1 on --surface, 5.74:1 on --green-highlight-pale.
       Value-equal to -pale-hover-text above, kept separate on purpose. */
    --green-highlight-text: #146c43;
    --grey-highlight: #6c757d;
    --grey-highlight-pale: #f8f9fa;
    /* Star-rating gold (shop reviews summary stars + bars) */
    --star-gold: #f0a500;

    /* ── Pill / badge palette (7 semantic pairs) ── */
    --pill-green-bg: #d1e7dd;    --pill-green-text: #0f5132;
    --pill-yellow-bg: #fff3cd;   --pill-yellow-text: #856404;
    --pill-red-bg: #f8d7da;      --pill-red-text: #842029;
    --pill-blue-bg: #cfe2ff;     --pill-blue-text: #0a58ca;
    --pill-purple-bg: #e2d9f3;   --pill-purple-text: #6f42c1;
    --pill-pink-bg: #fce4f3;     --pill-pink-text: #b60378;
    /* grey text darkened from #6c757d (3.95:1 on its own bg — FAILED AA) to
       #5a636c = 5.15:1. Value-equal to --text-light, kept separate like the
       other pairs. The other six pairs already clear AA. */
    --pill-grey-bg: #e9ecef;     --pill-grey-text: #5a636c;

    /* Button Colors */
    --btn-primary-bg: var(--pink-highlight);
    --btn-primary-text: #fff;
    --btn-primary-hover: #920260;

    --btn-light-bg: #fdf8df;
    --btn-light-text: #333;
    --btn-light-hover: #fcf0c0;

    /* ── Type scale — ONE ladder, every viewport. Dense and app-like: this is
       the historic scale, banded onto eleven steps.

       There was briefly a second, larger tier for ≤1099px, to keep every step at
       or above a 14px floor because in-app WebViews (Instagram, FB, Messenger)
       can enforce a minimum font size and clamp smaller text upward. It was
       removed: it enlarged type 18–33% for EVERY phone visitor, including
       ordinary Chrome, to defend against a clamp only in-app browsers apply.

       What defends against text being scaled on its own — Chrome for Android's
       Text scaling slider, a desktop font-size or minimum-font-size preference,
       a WebView clamp where one is applied — is the box, not the font value: a
       box that derives its size from its own text grows with it instead of
       letting it out through the side. (Page zoom is not on that list: it
       scales box and text alike, so nothing spills.) See .btn / .discount-badge in
       components.css. That guard works at any type size, so the ladder is free
       to stay dense. */
    --fs-3xs: 0.6rem;    /* ~9.6px — micro tier: counters, corner badges */
    --fs-2xs: 0.7rem;    /* ~11.2px */
    --fs-xs: 0.75rem;    /* 12px */
    --fs-sm: 0.8rem;     /* ~12.8px */
    --fs-base: 0.85rem;  /* ~13.6px — dominant body/control text */
    --fs-md: 0.9rem;     /* ~14.4px */
    --fs-lg: 1rem;       /* 16px */
    --fs-xl: 1.25rem;    /* 20px */
    --fs-2xl: 1.55rem;   /* ~24.8px */
    --fs-3xl: 1.9rem;    /* ~30.4px — display: page/section titles */
    --fs-4xl: 2.4rem;    /* ~38.4px — display: the .tp-stepper numerals */

    /* ── Surface — single source of truth for the white backgrounds behind
       cards, panels, inputs, dropdowns and modals (63 raw #fff today). One
       hook for a future dark theme; --surface-sunken aliases the existing
       recessed grey. Does NOT cover #fff used as button *text* (see
       --btn-*-text) or `white` inside a shadow. */
    --surface: #fff;
    --surface-sunken: var(--bg-light);
    /* The #f5f5f5 grey — the track behind segmented toggles and tab bars
       (.toggle-pills & co.) and everything that reads as the same recessed
       plate: Selections ▸ Placements cards, image/"+N" placeholder tiles,
       disabled selects, the admin table's row rule. Every literal #f5f5f5
       moved onto this token 2026-09-15 — use it, never the hex.
       Deliberately NOT --surface-sunken: #f5f5f5 predates the token pass
       (#233 only tokenised #fff) and is a shade darker than --bg-light, so
       aliasing it would lighten every toggle on the site. */
    --surface-track: #f5f5f5;

    /* ── Radius scale — controls settle on 6px, small/chips on 4px, cards on
       the existing 8px, larger panels on 12px. --border-radius stays as the
       md alias so existing usages keep working. `50%` (circular badges) stays
       literal; pills currently round ad-hoc (16/20px) and are out of scope. */
    --radius-sm: 4px;
    --radius-control: 6px;
    --radius-md: var(--border-radius); /* 8px */
    --radius-lg: 12px;

    /* ── Elevation — collapses ~15 hand-rolled shadows into a 4-step ramp plus
       one named panel shadow. --shadow-panel is the layered dropdown shadow
       the MultiSelect panel floats forms with. Directional drawer/sidebar
       shadows (-2px 0 …) are edge lighting, not elevation — left out on
       purpose. */
    --shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.05);
    --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.1);
    --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.12);
    --shadow-lg: 0 8px 24px rgba(0, 0, 0, 0.16);
    --shadow-panel: 0 10px 28px rgba(0, 0, 0, 0.22), 0 2px 8px rgba(0, 0, 0, 0.12);

    /* ── Z-index ladder — names the load-bearing stacking layers (modal
       system upward), WITHOUT renumbering: the modal stack order matters
       across components (see modal stacking principle). Small local z-indexes
       (badges, close buttons, a panel above its own siblings — 1..50) stay
       literal: they're local stacking, not a global layer. --z-panel (2100)
       is the floating MultiSelect panel that must clear stacked modals;
       --z-popover (3000) is cp-pop / shop-rating-pop; tooltips top out at 9999. */
    --z-sidebar: 900;
    --z-modal-backdrop: 2000;
    --z-modal: 2001;
    --z-modal-stacked-backdrop: 2010;
    --z-modal-stacked: 2011;
    --z-modal-stacked-top-backdrop: 2020;
    --z-modal-stacked-top: 2021;
    --z-panel: 2100;
    --z-popover: 3000;
    --z-tooltip: 9999;
}

/* ── Touch tier. Control sizing ONLY. A finger needs a 44px target and a pointer
   does not, which is a fact about the input device — unlike type size, which this
   tier used to override too and should not: that inflated every phone visitor's
   text 18–33% (see the ladder note above). Density and hit area are separate
   decisions, so the labels stay on the one dense ladder while the boxes around
   them grow. rem, so a control can still grow if the platform inflates its label.

   Width alone missed a 1194px iPad; `hover: none` is ANDed with `pointer: coarse`
   so a touchscreen laptop keeps the pointer sizes. */
@media (max-width: 1099px), (hover: none) and (pointer: coarse) {
    :root {
        --control-height: 2.75rem;      /* 44px */
        --control-height-sm: 2.125rem;  /* 34px */
        --control-height-md: 2.5rem;    /* 40px */
    }
}

* {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

/* Disables Blink/WebKit text autosizing, which reflows body copy to its own
   idea of a readable size on narrow viewports. Standard boilerplate, kept for
   that reason alone. It was added believing it fixed a "looks zoomed in" report
   from an in-app browser; it did not — that turned out to be page zoom, which
   this property has no bearing on. */
html {
    -webkit-text-size-adjust: 100%;
    text-size-adjust: 100%;
    /* WebKit/Blink paints a translucent block over whatever was tapped for a
       moment after every touch. It reads as an unfinished UI, and we already
       style :active / :hover ourselves. Deliberately NOT touching outline or
       :focus-visible — that would take the keyboard focus ring with it. */
    -webkit-tap-highlight-color: transparent;
}

button, input, select, textarea, label {
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
}

/* Form controls never show the browser focus ring (black/gold per platform);
   focus is signalled by the brand-pink border instead — the project-wide
   pattern (.tp-input, .form-control, …). Any component .class:focus rule
   outranks this element-level base and can restyle freely. */
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--pink-highlight);
}

body {
    font-family: 'Sofia Sans', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif !important;
    color: var(--text-color);
    background-color: #fff;
    padding-right: 0;
}

::placeholder { color: var(--text-placeholder); opacity: 1; }

/* Generic Link Hover */
a {
    color: var(--blue-highlight);
    text-decoration: none;
    transition: color 0.2s;
}

/* Excluding button anchors is load-bearing. Each button class sets its own
   hover colour inside @media (hover: hover) — FALSE on a touch device, so after
   a tap the browser synthesises :hover, that rule never applies, and this
   generic one won on specificity (a:hover = 0,1,1 beats .btn-primary = 0,1,0),
   painting a pink button's label dark blue on mobile only.

   :where() and NOT :not() — :where() contributes ZERO specificity, so this
   stays exactly (0,1,1). A bare :not([class*="btn"]) would raise it to (0,2,1),
   which then out-ranked every `a.some-card { text-decoration: none }` that had
   been winning on source order, and underlined those cards on hover.

   The @media (hover: hover) wrapper is load-bearing too, and for the mirror of
   the reason above. A tap on a touch device synthesises :hover, so an ungated
   rule here fires on every tap — while each card component's cancelling
   `:hover { text-decoration: none }` is itself gated on (hover: hover) and so
   does NOT. Add-underline ran, remove-underline didn't, and any link-shaped
   card underlined its caption when touched. Gating the underline at the source
   fixes that everywhere at once; the per-component cancels stay because they
   are still what suppresses this rule under a real pointer. */
@media (hover: hover) {
    a:hover:where(:not([class*="btn"])) {
        color: var(--blue-highlight-dark);
        text-decoration: underline;
    }
}

/* Anchors styled as buttons never underline on hover */
a[class*="btn"]:hover {
    text-decoration: none;
}

/* Menu items (desktop header nav, mobile bottom nav, mobile menu panel) never
   underline. .menu-item's own text-decoration:none is (0,1,0) and loses to the
   generic a:hover above (0,1,1); this :hover selector (0,2,0) wins it back. */
.nav-item:hover,
.bottom-nav-item:hover,
.menu-item:hover {
    text-decoration: none;
}

/* =========================================
   Layout & Container
   ========================================= */
.main-layout {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-start;
    max-width: 1200px;
    width: 100%;
    box-sizing: border-box;
    margin: 0 auto;
    justify-content: center;
    gap: 16px;
    padding-top: 0;
    padding-left: 10px;
    padding-right: 10px;
}

.app-content {
    /* Default mobile width handled by media query */
    padding: 16px;
    max-width: 600px;
    margin: 0 auto;
}

/* Desktop Defaults */
@media (min-width: 1201px) {
    .app-content {
        width: 470px;
        min-width: 470px;
        max-width: 470px;
        flex: 0 0 470px;
        margin: 0;
        padding: 0;
    }

    .app-content>section,
    .app-content .product-card,
    .app-content .search-section,
    .app-content .filter-section {
        width: 100%;
        max-width: none;
        margin-left: 0;
        margin-right: 0;
    }

}

/* Mobile Defaults */
@media (max-width: 1200px) {
    .main-layout {
        display: block;
        width: 100%;
        padding: 0;
    }
}

/* =========================================
   Header Styles
   ========================================= */
.app-header {
    background: #fff;
    width: 100%;
    position: sticky;
    top: 0;
    z-index: 1020;
    border-bottom: 1px solid var(--border-color);
}

.header-container {
    display: flex;
    justify-content: space-between;
    align-items: center;
    width: calc(100% - 32px);
    max-width: 1200px;
    margin: 0 auto;
    padding: 7px 0;
}

@media (min-width: 1201px) {
    .app-header .header-container {
        max-width: 1200px;
        padding: 0 10px;
        /* A floor: the row holds the search field, whose own height is in rem and
           does grow when text is scaled on its own. A fixed 56px would have it
           overflow the bar. */
        min-height: 56px;
    }
}

.header-left {
    display: flex;
    align-items: center;
    gap: 15px;
}

.menu-btn {
    background-color: #ffffff;
    border: 1px solid #e0e0e0;
    cursor: pointer;
    width: 36px;
    height: 36px;
    border-radius: 8px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    padding-left: 7px;
    gap: 3px;
    flex-shrink: 0;
    transition: background-color 0.2s, border-color 0.2s;
}

.menu-btn:hover {
    background-color: var(--bg-light);
    border-color: #e2e6ea;
}

.menu-btn span {
    display: block;
    height: 2px;
    background-color: #222;
    border-radius: 2px;
}

.menu-btn span:nth-child(1) {
    width: 18px;
}

.menu-btn span:nth-child(2) {
    width: 10px;
}

.menu-btn span:nth-child(3) {
    width: 18px;
}

.logo-img {
    height: 43px;
    width: auto;
    display: block;
}

@media (max-width: 600px) {
    .logo-img {
        height: 47px;
    }
}

.header-right {
    display: flex;
    align-items: center;
    margin-left: auto;
    justify-content: flex-end;
    width: auto;
    gap: 10px;
}

.notification-icon {
    margin: 0;
}


.notification-icon {
    position: relative;
    color: var(--pink-highlight);
    display: flex;
    align-items: center;
    cursor: pointer;
    margin-right: 15px;
    /* Adjust spacing as needed */
}

.notification-icon svg {
    transform: scale(1.15);
}

/* Header counters (bell unread, Saved) — the look is the filter trigger's
   count: `badge badge-pink badge-sm` + the pink outline (components.css,
   `.btn-neutral .badge, .notif-count`). This only places it. On an icon it
   sits centred on the icon's top-right corner so a 2-digit count covers
   only that corner; in a menu row it follows the label. */
.notification-icon .notif-count,
.nav-icon-sq .notif-count {
    position: absolute;
    top: 0;
    right: 0;
    transform: translate(50%, -50%);
}
.menu-item .notif-count { margin-left: 6px; }
/* Bottom nav: its 22px icons sit tight under a short label, so the counter
   drops to the micro tier there; everywhere else it keeps the filter size. */
.bottom-nav .notif-count,
.header-nav .notif-count { font-size: var(--fs-3xs); padding: 0 4px; }
/* Desktop nav icons sit only 7px below the header's top edge, so the default
   -50% lift pushed the counter off the top of the page (measured top -2.5px).
   A shallower lift keeps it inside the header, still on the icon's corner. */
.header-nav .notif-count { transform: translate(50%, -35%); }
.nav-icon-sq { position: relative; display: inline-flex; }

/* Profile arrow hidden below 1100px — there the avatar is not a dropdown
   trigger but a shortcut to /saved (TheHeader.handleProfileClick); the
   hamburger beside it carries the account menu. */
.profile-desktop-only {
    display: none !important;
}
@media (min-width: 1100px) {
    .profile-arrow-badge.profile-desktop-only {
        display: flex !important;
    }
}

/* Dropdown always hidden on mobile; .is-open (TheHeader) shows it on desktop */
@media (max-width: 1099px) {
    .profile-dropdown {
        display: none !important;
    }
}

/* User Profile & Dropdown */
.user-profile {
    position: relative;
    cursor: pointer;
}

.profile-wrapper {
    position: relative;
    /* block, not inline-block: an inline-block reserves a text line's
       descender under the photo (the .user-profile box measured 39px around a
       34px image), which lifted the avatar 2.5px off the header's centre line. */
    display: block;
}

.profile-img {
    width: 34px;
    height: 34px;
    border-radius: 50%;
    object-fit: cover;
    display: block;
    border: 1px solid #ddd;
}

/* Mobile header: the avatar matches the 36px menu button beside it. */
@media (max-width: 1099px) {
    .profile-img { width: 36px; height: 36px; }
}

.profile-arrow-badge {
    position: absolute;
    bottom: -2px;
    right: -2px;
    width: 14px;
    height: 14px;
    background-color: #fff;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2);
}

.profile-arrow-badge svg {
    width: 10px;
    height: 10px;
    stroke: var(--text-light);
    transition: transform 0.2s ease;
}
/* Menu open → the chevron points up. */
.user-profile.active .profile-arrow-badge svg { transform: rotate(180deg); }

/* Header logo link */
.header-logo-link {
    display: flex;
    align-items: center;
    text-decoration: none;
}

/* Guest links (not logged in) */
.header-guest-links {
    display: flex;
    gap: 8px;
    align-items: center;
}

/* Profile dropdown username */
/* Account-menu identity row: name + optional role badge. */
.dropdown-identity {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    border-bottom: 1px solid var(--border-color);
}

.dropdown-identity .badge { flex-shrink: 0; }

.dropdown-username {
    flex: 1 1 auto;
    min-width: 0;
    font-size: var(--fs-base);
    color: var(--text-light);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Rendered but invisible while closed, so opening can animate: it fades in and
   drops 6px into place; closing fades out a touch faster. visibility waits for
   the fade before hiding (the delayed 0s step), and `inert` (TheHeader) keeps
   the closed menu's links out of the tab order. */
.profile-dropdown {
    display: block;
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px);
    pointer-events: none;
    transition: opacity 0.14s ease-in, transform 0.14s ease-in, visibility 0s linear 0.14s;
    position: absolute;
    top: 50px;
    right: 0;
    min-width: 180px;
    width: max-content;
    max-width: 280px;
    background: #fff;
    border-radius: 8px;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    border: 1px solid var(--border-color);
    z-index: 2005;
    padding: 8px 0;
}

.profile-dropdown.is-open {
    opacity: 1;
    visibility: visible;
    transform: none;
    pointer-events: auto;
    transition: opacity 0.2s ease-out, transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1), visibility 0s;
}

/* :is(a, button) — Log out is an action, not a destination, so it is a
   <button> while its three neighbours stay links. Same UA-reset reasoning as
   .menu-item: each added property is inert on the <a>s. */
.profile-dropdown :is(a, button) {
    display: block;
    width: 100%;
    padding: 8px 16px;
    border: none;
    background: none;
    color: var(--text-color);
    text-align: left;
    font-family: inherit;
    font-size: var(--fs-md);
    text-decoration: none;
    cursor: pointer;
    transition: background 0.2s;
}

.profile-dropdown :is(a, button):hover {
    background: var(--bg-light);
    text-decoration: none;
}
/* A row with a counter (Saved, Notifications): the pill sits at the right
   end of the row, the label keeps the left edge the other rows use. */
.profile-dropdown .dropdown-item-count {
    display: flex;
    align-items: center;
    gap: 12px;
}
.profile-dropdown .dropdown-item-count .notif-count { margin-left: auto; }


/* (Removed dead .header-btn / .btn-log-in / .btn-sign-up / .mobile-login-btn —
   were used only by the no-op public/index.html.) */

/* =========================================
   Footer Styles
   ========================================= */
/* Global footer disclaimer — lives INSIDE .app-footer, above the container's top
   rule, so it inherits the dark background instead of carrying its own band.
   CMS-managed + translatable (admin Pages → Disclaimer); rendered by TheFooter. */
.footer-disclaimer {
    /* Same value .footer-links restates: 5.01:1 on the footer background. */
    color: #869198;
    font-size: var(--fs-base);
    line-height: 1.55;
    text-align: left;
}
/* Gutters mirror .footer-container so the text lines up with the social icons. */
.footer-disclaimer-inner {
    width: calc(100% - 60px);
    max-width: 1150px;
    margin: 0 auto;
    padding: 0 15px 20px;
}
.footer-disclaimer p { margin: 0; }

.app-footer {
    background: #0d2331;
    margin-top: 40px;
    padding: 30px 0;
}

.footer-container {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 9px;
    width: calc(100% - 60px);
    max-width: 1150px;
    margin: 0 auto;
    padding: 20px 15px;
    color: white;
    border-top: 1px solid rgba(134, 145, 152, 0.5);
    border-bottom: 1px solid rgba(134, 145, 152, 0.5);
}

.footer-icons {
    display: flex;
    gap: 15px;
}

.footer-links {
    display: flex;
    gap: 15px;
    font-size: var(--fs-md);
    flex-wrap: wrap;
    justify-content: center;
    /* Same value the links below restate, so the "|" separator matches them
       instead of carrying its own inline #666 — that was 2.81:1 on the footer,
       where this is 5.01:1. */
    color: #869198;
}

.footer-links a,
.footer-link-btn {
    color: #869198;
    text-decoration: none;
    transition: color 0.2s;
}

.footer-links a:hover,
.footer-link-btn:hover {
    color: #ffffff;
}

/* "Cookie settings" — a <button> (it runs a script, it goes nowhere) that must
   read as one more footer link: native chrome off, the link's type inherited,
   and the cookie icon laid in front of the label. */
.footer-link-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    cursor: pointer;
}

/* Footer country/market switcher (i18n Phase 2) — relocated from the header,
   under the Terms/Privacy links. Styled for the dark footer; the dropdown opens
   UPWARD because the footer sits at the bottom of the page. Larger than the
   header language pill: a globe icon + the full country name. */
.footer-switches { display: flex; align-items: center; justify-content: center; gap: 10px; flex-wrap: wrap; }
.footer-country-switch { position: relative; display: inline-flex; }
.footer-country-trigger {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 9px 16px;
    font-size: var(--fs-md);
    color: #cdd5da;
    background: rgba(255, 255, 255, 0.06);
    border: 1px solid rgba(134, 145, 152, 0.5);
    border-radius: 999px;
    cursor: pointer;
    transition: background 0.2s, color 0.2s, border-color 0.2s;
}
.footer-country-trigger:hover { color: #fff; background: rgba(255, 255, 255, 0.12); border-color: #cdd5da; }
.footer-country-label { font-weight: 600; }
.footer-country-backdrop { position: fixed; inset: 0; z-index: 99; }
.footer-country-menu {
    position: absolute;
    bottom: calc(100% + 8px);
    left: 50%;
    transform: translateX(-50%);
    z-index: 100;
    min-width: 220px;
    padding: 6px;
    background: #fff;
    border: 1px solid var(--border-color);
    border-radius: 10px;
    box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.footer-country-option {
    padding: 11px 14px;
    border-radius: 7px;
    font-size: var(--fs-md);
    color: var(--text-color);
    cursor: pointer;
    white-space: nowrap;
}
.footer-country-option:hover { background: var(--bg-light); }
.footer-country-option.active { color: var(--pink-highlight); font-weight: 600; }

@media (min-width: 1201px) {
    .footer-container {
        padding-left: 10px;
        padding-right: 10px;
        flex-direction: row;
        justify-content: space-between;
        align-items: center;
        gap: 0;
    }
}

/* Navigation Menu Modal (Global) */
.menu-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 2000;
    opacity: 0;
    transition: opacity 0.3s ease;
}

.menu-overlay.active {
    display: block;
    animation: fadeIn 0.3s ease forwards;
}

/* 380px, up from 300: the "Last seen" strip is three 84px cells and two 14px
   gaps = 280px, and it has to clear the panel's 20px gutter with enough left
   over for a fourth cell to peek and say "this scrolls". At 300px it was three
   cells and a 20px sliver, which reads as a clipped row, not a scroller.

   The TWO limits bind on different devices, so both had to move: max-width
   governs actual phones (90% → 324px on a 360px handset, 351px on a 390px one)
   while the px width only takes over past ~422px of viewport, i.e. large
   phones and the tablet range this panel still serves below 1201px. A small
   phone is therefore close to its ceiling already — the remaining 10% is the
   strip of page behind, which is both the tap-to-close target and the signal
   that this is a panel and not a new screen; spending it would buy ~30px and
   cost the affordance.

   Not wider still: at ~400px the fourth cell stops peeking and fits exactly
   (cells land at 20/118/216/314 and the last ends at 398), so the row looks
   complete and nothing says it scrolls. 380 is the widest value that still
   peeks. Measured peek: 66px at the 380 cap, 38px at 390, and — the honest
   limit — 10px on a 360px handset, where the panel tops out at 324 and three
   84px cells plus a gap already reach 314. No panel width in reach fixes that
   one; only a narrower cell would, and .thumb-strip-item's 84px is shared with
   the star strips. left must stay past -width, or the closed panel's edge
   shows. */
.menu-modal {
    position: fixed;
    top: 0;
    left: -400px;
    width: 380px;
    max-width: 90%;
    height: 100%;
    background: #fff;
    box-shadow: 2px 0 10px rgba(0, 0, 0, 0.2);
    z-index: 2001;
    transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    display: flex;
    flex-direction: column;
}

.menu-modal.active {
    left: 0;
}

/* ── App-like bottom navigation (mobile only) ───────────────────────────
   Fixed to the viewport bottom like native-app tab bars (icon on top,
   label under). z-index 1500 keeps it ABOVE page content but BELOW the
   mobile menu (2000/2001) and every modal/overlay (2000+), so dialogs
   always cover it. body.has-bottom-nav (set by TheHeader) reserves space
   under the page so content and footer never hide behind the bar. */
.bottom-nav {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    display: flex;
    align-items: stretch;
    background: #fff;
    border-top: 1px solid #f0f0f0;
    border-radius: 20px 20px 0 0;   /* same top rounding as the bottom-up modals */
    box-shadow: 0 -3px 14px rgba(0, 0, 0, 0.14);
    padding-bottom: env(safe-area-inset-bottom);
    z-index: 1500;
}

.bottom-nav-item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    padding: 12px 2px 10px;
    background: none;
    border: none;
    cursor: pointer;
    font-family: inherit;
    font-size: var(--fs-xs);
    font-weight: 600;
    /* 1.15, not 1: at the 14px floor "Горещи продукти" is the one label that
       wraps to two lines, and line-height:1 set them solid. */
    line-height: 1.15;
    text-align: center;
    color: var(--text-light);
    text-decoration: none;
}

.bottom-nav-item.active {
    color: var(--pink-highlight);
}

body.has-bottom-nav {
    padding-bottom: calc(64px + env(safe-area-inset-bottom));
}

@media (min-width: 1100px) {
    .bottom-nav {
        display: none;
    }

    body.has-bottom-nav {
        padding-bottom: 0;
    }
}

/* Modal shell — Mobile (bottom sheet, default personality)
   Sheet sizes to its content between the dvh bounds (dvh, not vh, so the
   mobile URL bar can't push the sheet off-screen). safe-area keeps the
   footer above the iPhone home indicator (0 on desktop). The shell is the
   single .modal-wrapper class (was .notification-modal.modal-content) and
   stays display:none until .active — that toggle absorbed the former
   .modal-wrapper-flex helper. */
.modal-wrapper {
    position: fixed;
    top: auto;
    bottom: 0;
    left: 0;
    width: 100%;
    max-width: 100%;
    height: auto;
    min-height: 40dvh;
    max-height: 85dvh;
    border-radius: 20px 20px 0 0;
    background: #fff;
    box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.2);
    z-index: 2001;
    animation: slideUp 0.3s ease forwards;
    display: none;
    flex-direction: column;
    padding-bottom: env(safe-area-inset-bottom);
}

.modal-wrapper.active { display: flex; }

/* Sheet grabber — the native-style drag handle. A real element (not a
   pseudo) so it can own touch-action and receive the drag; the visible pill
   is its ::after. A full-width strip gives a generous grab target while the
   pill stays small and centered. Sheets only: hidden on desktop variants and
   admin's force-centered dialogs (see below). touch-action:none hands the
   vertical gesture to modal-sheet-drag.js instead of the browser's scroll. */
.modal-grabber {
    flex-shrink: 0;
    height: 20px;
    display: flex;
    align-items: center;
    justify-content: center;
    touch-action: none;
    cursor: grab;
}

.modal-grabber::after {
    content: '';
    width: 36px;
    height: 4px;
    border-radius: 999px;
    background: #ddd;
}

@keyframes fadeIn {
    from { opacity: 0; }
    to   { opacity: 1; }
}

@keyframes slideUp {
    from {
        transform: translateY(100%);
    }

    to {
        transform: translateY(0);
    }
}

@keyframes slideRight {
    from {
        transform: translateX(100%);
    }

    to {
        transform: translateX(0);
    }
}

/* Centered dialogs rise + settle instead of popping in. The resting state is
   now margin-centered (no transform), so this animates a transient transform
   that's dropped on settle — leaving the dialog crisp on the pixel grid. */
@keyframes dialogIn {
    from {
        opacity: 0;
        transform: translateY(8px) scale(0.97);
    }
}

/* Modal motion is decorative — honor the OS-level opt-out */
@media (prefers-reduced-motion: reduce) {
    .modal-wrapper,
    .modal-overlay {
        animation: none !important;
        transition: none !important;
    }
}

/* Desktop Notification Override — right sidebar */
@media (min-width: 1201px) {

    .modal-wrapper {
        top: 0;
        bottom: 0;
        left: auto;
        right: 0;
        width: 400px;
        max-width: 100%;
        height: 100%;
        max-height: 100%;
        border-radius: 0;
        box-shadow: -2px 0 10px rgba(0, 0, 0, 0.2);
        animation: slideRight 0.3s ease forwards;
    }

    /* No sheet grabber on desktop variants */
    .modal-grabber { display: none; }

    /* Centered dialog variant — add .modal-dialog to customClass to opt in.
       Height standard (same values as admin.css's force-centered override):
       hug the content between a modest floor and the dvh ceiling. */
    /* Center with inset:0 + margin:auto, NOT translate(-50%,-50%). A persistent
       transform promotes the dialog to its own GPU layer; with an odd content
       height the -50% resolves to a half-pixel and the whole layer — every 1px
       border — rasterizes blurry. Margin-centering rests on the pixel grid with
       no layer, so borders stay crisp. height:fit-content preserves the
       hug-content sizing the old height:auto gave. */
    .modal-wrapper.modal-dialog {
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        margin: auto;
        width: 90%;
        max-width: 660px;
        height: fit-content;
        min-height: 160px;
        max-height: min(90dvh, 100dvh - 80px);
        border-radius: 16px;
        animation: dialogIn 0.18s ease-out;
        box-shadow: 0 8px 32px rgba(0, 0, 0, 0.18);
        /* Clip to the radius — a footer-less body's scrollbar would square
           off the bottom corners. */
        overflow: hidden;
    }

    /* Width tiers: sm 420 / default 660 / wide 840. Sole definition — the
       admin.css mirror this comment used to point at was deleted in #215.
       Scoped to ≥1201px on purpose: below that the shell is the full-width
       bottom sheet and must not be capped. */
    .modal-wrapper.modal-dialog.modal-dialog-sm   { max-width: 420px; }
    .modal-wrapper.modal-dialog.modal-dialog-wide { max-width: 840px; }

    /* Dialogs hug content — no forced body height */
    .modal-wrapper.modal-dialog .modal-body {
        min-height: unset;
        flex: 0 1 auto;
    }

    /* Tabbed dialogs opt into ONE stable frame: hug-content sizing would
       resize + re-center the modal on every tab switch. Body stretches so
       the footer stays pinned to the bottom edge. */
    .modal-wrapper.modal-dialog.modal-dialog-tall { height: min(600px, 85dvh); }
    .modal-wrapper.modal-dialog.modal-dialog-tall .modal-body { flex: 1 1 auto; }
}

/* The same promise below the dialog tier. Here the shell is a bottom sheet
   sized `height:auto` between 40dvh and 85dvh, so a tabbed modal still moves
   its top edge when a sparse tab follows a dense one — the exact jump the rule
   above exists to stop, just anchored from the other side.

   A floor, not a fixed height: the sheet must stay free to grow to 85dvh for a
   dense tab. min() is load-bearing — a bare `min-height: 550px` would BEAT the
   85dvh max-height (min-height always wins that conflict) and produce a sheet
   taller than the window on a short viewport.

   Replaces the `@media (min-width: 641px) { .modal-shop-settings .modal-body }`
   floor retired in #257, which left 641–1200 with neither treatment. Scoped to
   -tall so all four tabbed dialogs get it, not just shop settings, and keyed to
   the existing 1200/1201 boundary so it adds no new breakpoint. */
@media (max-width: 1200px) {
    .modal-wrapper.modal-dialog.modal-dialog-tall { min-height: min(550px, 85dvh); }
}

.modal-overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 2000;
    opacity: 0;
    visibility: hidden;
    transition: opacity 0.3s ease, visibility 0.3s;
}

/* Scroll lock — while ANY modal overlay is active the document doesn't
   scroll. Every modal in the project (raw markup and Modal.js alike)
   renders .modal-overlay.active when open, so this one rule covers all
   of them, including future ones. scrollbar-gutter keeps the page from
   jumping sideways when the scrollbar disappears. */
html { scrollbar-gutter: stable; }
body:has(.modal-overlay.active) { overflow: hidden; }

/* Desktop: blur the page under the dim for depth (skipped on mobile —
   backdrop-filter paint cost is real on low-end devices) */
@media (min-width: 1201px) {
    .modal-overlay { backdrop-filter: blur(3px); }
}

/* Overlay visibility hangs solely on .active — set by both raw inline markup
   ("modal-overlay active") and the Modal.js component (it toggles .active via
   isOpen). A dead general-sibling fallback (.modal-wrapper~.modal-overlay),
   left over from a removed NotificationModal, used to also live here; it lit
   every teleported overlay following a wrapper, so it's gone. */
.modal-overlay.active {
    opacity: 1;
    visibility: visible;
    /* The opacity transition can't run on v-if mounts (both classes arrive
       together) — the animation covers that first paint */
    animation: fadeIn 0.25s ease;
}

.menu-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    padding: 20px;
    border-bottom: 1px solid var(--border-color);
}

.menu-header h3 {
    margin: 0;
    font-size: var(--fs-xl);
    /* hug the glyphs so align-items:center centers the visible text
       against the close X, not the taller default line box */
    line-height: 1;
    color: var(--pink-highlight);
}

/* Logged-in: the panel title becomes the account identity — name (brand
   pink) + role pill. min-width:0 lets a long name ellipsis instead of
   shoving the close button. */
.menu-identity {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

.menu-identity-name {
    min-width: 0;          /* let the name ellipsis instead of shoving the badge */
    flex: 0 1 auto;        /* shrink the name, never the badge */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.menu-identity .badge { flex-shrink: 0; }

.menu-close {
    background: none;
    border: none;
    cursor: pointer;
    color: var(--text-light);
    padding: 0;
    display: flex;
    align-items: center;
    flex-shrink: 0;
    transition: transform 0.3s ease, color 0.2s;
}

.menu-close:hover {
    color: var(--text-color);
    transform: rotate(90deg);
}

.menu-content {
    flex: 1;
    overflow-y: auto;
    padding-top: 0;
}

/* ── "Last seen" strip ───────────────────────────────────────────────────
   The viewer's recently engaged products, at the FOOT of the open menu, below
   the navigation and account rows. Same set as the homepage's "Continue where
   you left off" rail and the personalised Hot Products tab, read from that
   tab's config (TheHeader.js) so an admin moving the selection moves all
   three. Signed out it never renders — the endpoint can only answer with an
   empty list.

   Mobile-only for free: .menu-btn is display:none from 1201px, so nothing can
   open this panel on a desktop viewport. No media query of its own.

   Cells are the shared .thumb-strip block (components.css); only the section
   chrome lives here. The TRACK carries the panel's 20px gutter as its own
   inline padding instead of inheriting it from the section, so the last cell
   can scroll clear to the panel edge rather than stopping a gutter short. */
.menu-recent {
    padding: 14px 0 12px;
    /* Rule on the LEADING edge now that the section sits last — it separates
       the strip from the account rows above it; a bottom rule would draw a
       stray line along the end of the panel with nothing under it. */
    border-top: 1px solid var(--border-color);
}

.menu-recent .thumb-strip {
    padding-left: 20px;
    padding-right: 20px;
    /* Captions are gone here, so nothing needs the base 14px of breathing
       room between cells; the pictures read as one row at 8px. */
    gap: 8px;
}

/* Without a caption the cell has no reason to be wider than the thumb it
   centres — the shared 84px would otherwise hold the pictures 24px apart on
   top of the gap. Shrink to the thumb and let `gap` alone set the spacing. */
.menu-recent .thumb-strip-item {
    width: 60px;
    gap: 0;
}

/* Whole product, not a crop. The shared cell defaults to `cover` because its
   other host shows list/collection COVERS, where filling the circle matters
   more than the edges. A product photo is the opposite: cover shaves the sides
   off a tall bottle and the top off a wide appliance, and with no caption left
   the picture is the only thing naming the row. Same pairing the product cards
   use (.product-img): contain over --surface, so the letterboxing reads as the
   frame's interior rather than a grey band. */
.menu-recent .thumb-strip-img {
    object-fit: contain;
    background: var(--surface);
}

/* Not uppercased: the text is the SELECTION's own title, which an admin writes
   in four languages and decorates with an emoji — shouting a 25-character
   Cyrillic phrase in caps wraps it and mangles the glyph. Bold in the body
   colour is what separates it from the .menu-item rows above without that.

   --fs-lg is the .menu-item size: a section heading over 60px thumbs that read
   smaller than the rows above it would look like a caption, not a heading. */
.menu-recent-label {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0 0 10px;
    padding: 0 20px;
    font-size: var(--fs-lg);
    font-weight: 700;
    color: var(--text-color);
}

/* The emoji the admin typed at the END of the title, moved to the front (see
   splitRecentTitle in TheHeader.js). Slightly larger than the text because an
   emoji glyph sits optically small at the same size, and line-height:1 so the
   taller glyph can't stretch the heading's line box. */
.menu-recent-icon {
    font-size: 1.15em;
    line-height: 1;
    flex-shrink: 0;
}

/* Worn by both real navigation links and the two rows that are actions
   (Notifications, Log out) and so render as <button>. The width/border/
   background/text-align/font-family block undoes the UA button defaults;
   every one of them is a no-op on an <a>, which is what lets the two share
   this rule and stay pixel-identical. */
.menu-item {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 12px 20px;
    border: none;
    background: none;
    color: var(--text-color);
    text-align: left;
    text-decoration: none;
    font-family: inherit;
    font-size: var(--fs-lg);
    font-weight: normal;
    cursor: pointer;
    transition: background-color 0.2s;
    gap: 15px;
}

.menu-item:hover,
.menu-item.active {
    background-color: #f0f2f5;
    color: var(--text-color);
}

.menu-icon {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
}

.menu-separator {
    height: 1px;
    background-color: #d1d5db;
    margin: 8px 0;
    width: 100%;
    border: none;
    display: block;
}

.mobile-only-nav {
    display: flex !important;
}

@media (min-width: 1201px) {
    .mobile-only-nav {
        display: none !important;
    }
}

@media (min-width: 1100px) {
    body {
        padding-right: 0;
    }

    .main-layout {
        display: flex;
        flex-wrap: nowrap;
        align-items: flex-start;
        max-width: 1070px;
        width: 100%;
        box-sizing: border-box;
        margin: 0 auto;
        justify-content: center;
        gap: 15px;
        padding-top: 0;
        padding-left: 0;
        padding-right: 0;
    }

    /* No .app-content width here. The 470px column belongs to the desktop grid
       in pages/browse.css, which sizes it from its own 1fr track. */

    /* Sidebar Wrapper (Desktop) */
    .sidebar-wrapper {
        display: block;
        flex: 0 1 auto;
        /* Allow shrinking if needed */
        position: relative;
        /* Ensure context */
        height: auto;
        /* Allow height to fit sidebar */
        min-width: 0;
        /* Enable flex shrinking below auto content size */
    }

    .sidebar-wrapper .modal-overlay {
        display: none !important;
    }

    /* Target specific sidebars if they had flex/width settings */


    /* Target specific sidebars if they had flex/width settings */
    /* Assuming celebrities/hot-products sidebars have their own widths defined elsewhere in original css */
    /* Let's verify if they need to be flex items. 
       Usually sidebars are static width. 
       If wrapper surrounds them, Wrapper needs that width or flex property. 
    */


    .app-header .header-container {
        max-width: 1200px;
        margin: 0 auto;
        width: 100%;
        min-height: 56px;
        padding: 0 10px;
    }

    /* Scoped Override for Index Page (2 Columns) */
    .page-index .celebrities-sidebar {
        display: none !important;
    }

    .page-index .app-content {
        flex: 0 0 695px !important;
        width: 695px !important;
        max-width: 695px !important;
        min-width: 695px !important;
    }
}

/* Header Description */
.header-description {
    margin: 3px 0 0 0;
    font-size: var(--fs-base);
    color: var(--text-light);
    font-weight: 400;
    line-height: 1.2;
}

/* Adjust header strength and sizing */


/* Mobile Adjustments for Header Description */
@media (max-width: 1200px) {
    .header-description {
        font-size: var(--fs-sm);
        color: var(--text-light);
    }
}

/* Utility Classes for Icons (replacing inline style) */
.header-title-row {
    display: flex;
    gap: 12px;
    align-items: center;
    flex: 1;
    padding-left: 0;
}

/* Visibility Utilities (Appended Fix) */
.desktop-only {
    display: none !important;
}

@media (min-width: 1100px) {
    .mobile-only {
        display: none !important;
    }

    .desktop-only {
        display: inline-flex !important;
    }

    .menu-btn {
        display: none !important;
    }
}

/* Navigation Styles (Missing Fix) */
.header-nav {
    display: none;
}

@media (min-width: 1100px) {
    .header-nav {
        display: flex !important;
        gap: 30px;
        margin-left: 30px;
        align-items: center;
    }

    .nav-item {
        display: flex;
        flex-direction: column;
        align-items: center;
        text-decoration: none;
        color: #222;
        font-size: var(--fs-sm);
        font-weight: 600;
        gap: 2px;
    }

    /* Current page — same pattern as .bottom-nav-item.active (mobile). */
    .nav-item.active {
        color: var(--pink-highlight);
    }

    /* Group divider between consumer destinations and operator tools.
       Experimental/revertable — remove together with the .nav-divider
       element in TheHeader.js to restore the flat rail. */
    .nav-divider {
        align-self: stretch;
        width: 1px;
        background: var(--border-color);
    }

    .nav-icon-sq {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 24px;
        height: 24px;
    }

    .nav-icon-sq svg {
        width: 24px;
        height: 24px;
    }

    /* Slimmer icon strokes in the desktop navbar only — overrides the
       stroke-width presentation attribute AppIcon sets on the svg root;
       every other AppIcon usage keeps its default weight. */
    .nav-icon-sq svg,
    .nav-icon-sq svg * {
        stroke-width: 1.5;
    }
}

/* Sidebar Bottom-Up Transition Override (Mobile) */
@media (max-width: 1200px) {

    .celebrities-sidebar,
    .hot-products-sidebar {
        /* Reset existing positioning */
        top: auto !important;
        left: 0 !important;
        right: 0 !important;
        bottom: 0 !important;

        position: fixed !important;
        width: 100% !important;
        max-width: 100% !important;
        height: auto !important;
        max-height: 80vh !important;

        border-radius: 20px 20px 0 0 !important;
        background: #fff;
        box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.2);
        z-index: 2001;

        /* Transition */
        transform: translateY(100%);
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;

        display: flex;
        flex-direction: column;
    }

    .celebrities-sidebar.active,
    .hot-products-sidebar.active {
        transform: translateY(0) !important;
    }

    /* Ensure content inside the sheet-sidebars handles scroll. Scoped to
       the sidebars: the same content classes render inside Modal.js
       overlays, where .modal-body owns the scrolling. */
    .celebrities-sidebar .celebrities-content,
    .hot-products-sidebar .hot-products-content {
        overflow-y: auto;
        padding-bottom: 20px;
    }
}

/* =========================================
   Scroll-to-top Button (Global)
   ========================================= */
.scroll-top-btn {
    position: fixed;
    bottom: 28px;
    right: 28px;
    width: 42px;
    height: 42px;
    border-radius: 50%;
    background: var(--pink-highlight);
    color: #fff;
    border: none;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 10px rgba(182, 3, 120, 0.35);
    transition: background 0.15s, transform 0.15s, opacity 0.25s, visibility 0.25s;
    z-index: 900;
    opacity: 0;
    /* pointer-events alone only stops the mouse — the button stayed in the tab
       order, so keyboard users focused an invisible control (WCAG 2.4.11).
       visibility takes it out of both, and transitions rather than snapping
       because it is listed above alongside opacity. */
    visibility: hidden;
    pointer-events: none;
}

.scroll-top-btn.visible {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
}

/* Keep the back-to-top button above the mobile bottom nav bar. */
body.has-bottom-nav .scroll-top-btn {
    bottom: calc(86px + env(safe-area-inset-bottom));
}

@media (min-width: 1100px) {
    body.has-bottom-nav .scroll-top-btn {
        bottom: 28px;   /* no bottom nav on desktop */
    }
}

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

/* ── Motion opt-out ──────────────────────────────────────────────────────
   Every animated rule in this file, behind the one query that asks. This
   sheet already carried the pattern for a handful of components; the rest
   were simply never enumerated. Listed selector by selector rather than as
   a `*` reset, because a universal reset only beats these rules with
   !important, which this project does not add.
   ──────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
    a,
    .menu-btn,
    .profile-dropdown,
    .profile-dropdown.is-open,
    .profile-dropdown :is(a, button),
    .profile-arrow-badge svg,
    .footer-links a,
    .footer-link-btn,
    .footer-country-trigger,
    .menu-overlay,
    .menu-overlay.active,
    .menu-modal,
    .modal-wrapper.modal-dialog,
    .modal-overlay.active,
    .menu-close,
    .menu-item,
    .celebrities-sidebar, .hot-products-sidebar,
    .scroll-top-btn {
        transition: none;
        animation: none;
    }
}
