/* =============================================================
   Storefront theme — "Minimal"

   A quiet, high-contrast design system: white surfaces, one thin
   rule instead of a shadow, near-black for anything clickable, and
   colour reserved for the two things a shopper scans for — the
   price drop and the rating.

   Load order: bootstrap.min.css → app.css → theme.css → the inline
   :root block in layouts/header.php (admin-chosen colours, so it
   must come last and win).

   Sections
     1  Tokens
     2  Base & typography
     3  Buttons
     4  Forms
     5  Cards & surfaces
     6  Badges, pills, ratings
     7  Header
     8  Footer
     9  Cart FAB · tab bar · toast · back-to-top
     10 Product card
     11 Sections & page headers
   ============================================================= */

/* =============================================================
   1. TOKENS
   The --c-* colours are re-declared by header.php from Site
   Settings; the values here are the fallback for a fresh install
   and the reference for what each one is for.
   ============================================================= */

:root {
    --font-sans: "Roboto", "Hind Siliguri", system-ui, -apple-system, "Segoe UI", sans-serif;
    --font-bn: "Hind Siliguri", "Roboto", system-ui, sans-serif;

    /* Ink — three weights is enough for a minimal page.
       --c-heading is the brand's "black"; --c-body the reading grey. */
    --c-heading: #454545;
    --c-body: #7e7e7e;
    --c-muted: #a3a3a3;

    /* Surfaces */
    --c-white: #ffffff;
    --c-light: #f8f8f8;
    --c-surface: #f1f1f1;
    --c-line: #e6e6e6;
    --c-line-strong: #d4d4d4;

    /* Brand */
    --c-primary: #e9a54f;
    /* CTAs, header bars, anything solid */
    --c-primary-d: #d18e35;
    --c-primary-rgb: 233, 165, 79;

    /* Text that sits ON the primary fill — every button, badge and pill
       the brand colour fills, hover states included. White by request.
       Worth knowing: on the gold this is about 2:1, so if the primary is
       ever changed to something paler, its lettering gets harder to read
       rather than adjusting itself. Change it here, in one place. */
    --c-on-primary: #ffffff;

    /* The dark counterpart to the gold: top bar, toasts, badges. */
    --c-secondary: #454545;
    --c-secondary-d: #2e2e2e;

    --c-accent: #e9a54f;
    /* links, focus, active filter */
    --c-accent-rgb: 233, 165, 79;
    --c-highlight: #e9a54f;
    /* rating stars */

    /* The copyright band under the footer. Its own token rather than
       --c-primary because header.php re-declares --c-primary from Site
       Settings — the bar is part of the footer's design, not a CTA — and
       it has its own picker (Admin → Brand colours → Footer bar).
       Lettered in the dark ink for the same reason as --c-on-primary. */
    --c-footer-bar: #e9a54f;
    --c-on-footer-bar: #454545;

    /* State */
    --c-success: #059669;
    --c-danger: #dc2626;
    /* sale price, discount badge */

    /* Geometry — small radii read as "considered", large as "playful". */
    --r-sm: 6px;
    --r-md: 8px;
    --r-lg: 12px;
    --r-pill: 999px;

    /* One shadow, used sparingly. Minimal pages lean on borders. */
    --shadow-sm: 0 1px 2px rgba(69, 69, 69, .06);
    --shadow-md: 0 4px 12px rgba(69, 69, 69, .10);
    --shadow-lg: 0 12px 32px rgba(69, 69, 69, .14);

    /* Height of the sticky bar only (row 2). The top bar and the
       category bar scroll away, so they are not counted here — the
       sticky sidebars in app.css offset against this. */
    --header-h: 72px;
    --container-max: 1240px;

    --ease: cubic-bezier(.4, 0, .2, 1);

    /* Bootstrap follows the same palette instead of shipping its own. */
    --bs-primary: var(--c-primary);
    --bs-primary-rgb: var(--c-primary-rgb);
    --bs-body-color: var(--c-body);
    --bs-body-bg: var(--c-white);
    --bs-border-color: var(--c-line);
    --bs-border-radius: var(--r-md);
    --bs-link-color: var(--c-heading);
    --bs-link-hover-color: var(--c-accent);
}

/* =============================================================
   2. BASE & TYPOGRAPHY
   ============================================================= */

* {
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
}

body {
    font-family: var(--font-sans);
    font-size: 15px;
    line-height: 1.65;
    color: var(--c-body);
    background: var(--c-white);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Bangla runs a touch larger than Latin at the same point size looks
   right — the script has taller counters and less x-height contrast. */
:lang(bn) {
    font-family: var(--font-bn);
}

.container {
    max-width: var(--container-max);
}

h1,
h2,
h3,
h4,
h5,
h6,
.h1,
.h2,
.h3,
.h4,
.h5,
.h6 {
    color: var(--c-heading);
    font-weight: 650;
    letter-spacing: -.015em;
    line-height: 1.3;
}

h1,
.h1 {
    font-size: clamp(1.9rem, 1.4rem + 2vw, 2.75rem);
}

h2,
.h2 {
    font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2rem);
}

h3,
.h3 {
    font-size: clamp(1.25rem, 1.1rem + .6vw, 1.5rem);
}

h4,
.h4 {
    font-size: 1.15rem;
}

h5,
.h5 {
    font-size: 1rem;
}

h6,
.h6 {
    font-size: .9rem;
}

.display-5 {
    font-weight: 700;
    letter-spacing: -.03em;
}

.lead {
    font-size: 1.05rem;
    color: var(--c-body);
    line-height: 1.7;
}

p {
    margin-bottom: 1rem;
}

a {
    color: var(--c-heading);
    text-decoration: none;
    transition: color .15s var(--ease);
}

a:hover {
    color: var(--c-accent);
}

/* Links inside body copy stay underlined — in a page this quiet an
   unmarked link is invisible. */
.prose a,
.page-body a {
    color: var(--c-accent);
    text-decoration: underline;
    text-underline-offset: .18em;
    text-decoration-thickness: 1px;
}

.text-muted {
    color: var(--c-muted) !important;
}

small,
.small {
    font-size: .8125rem;
}

hr {
    border-color: var(--c-line);
    opacity: 1;
}

/* Prices — tabular figures so a column of them lines up. */
.price,
.price-old,
.money {
    font-variant-numeric: tabular-nums;
    letter-spacing: -.01em;
}

::selection {
    background: rgba(var(--c-accent-rgb), .16);
}

/* The honeypot from App\Services\SpamGuard. It must exist in the DOM for a
   bot to fill in, and must never be visible to a person — off-screen rather
   than display:none, because some bots skip hidden fields. Every form that
   calls SpamGuard::fields() depends on this rule. */
.spam-trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* One focus style everywhere, keyboard only. */
:focus-visible {
    outline: 2px solid var(--c-accent);
    outline-offset: 2px;
    border-radius: var(--r-sm);
}

.site-main {
    min-height: 50vh;
}

/* =============================================================
   3. BUTTONS
   Two real buttons: solid (the action) and outline (everything
   else). Bootstrap's colour variants are remapped rather than
   replaced, so .btn-primary keeps working in every view.
   ============================================================= */

.btn {
    --bs-btn-padding-y: .625rem;
    --bs-btn-padding-x: 1.25rem;
    --bs-btn-font-size: .9375rem;
    --bs-btn-border-radius: var(--r-md);
    font-weight: 550;
    letter-spacing: -.005em;
    transition: background-color .16s var(--ease), border-color .16s var(--ease),
        color .16s var(--ease), transform .16s var(--ease);
}

.btn:active {
    transform: translateY(1px);
}

.btn-lg {
    --bs-btn-padding-y: .8125rem;
    --bs-btn-padding-x: 1.75rem;
    --bs-btn-font-size: 1rem;
}

.btn-sm {
    --bs-btn-padding-y: .375rem;
    --bs-btn-padding-x: .875rem;
    --bs-btn-font-size: .8125rem;
    --bs-btn-border-radius: var(--r-sm);
}

.btn-primary {
    --bs-btn-bg: var(--c-primary);
    --bs-btn-border-color: var(--c-primary);
    --bs-btn-color: var(--c-on-primary);
    --bs-btn-hover-bg: var(--c-primary-d);
    --bs-btn-hover-border-color: var(--c-primary-d);
    --bs-btn-hover-color: var(--c-on-primary);
    --bs-btn-active-bg: var(--c-primary-d);
    --bs-btn-active-border-color: var(--c-primary-d);
    --bs-btn-active-color: var(--c-on-primary);
    --bs-btn-disabled-bg: var(--c-line-strong);
    --bs-btn-disabled-border-color: var(--c-line-strong);
    --bs-btn-disabled-color: var(--c-white);
}

/* The dark button — for the second action in a pair, where a second
   gold fill would leave neither reading as "the" action. */
.btn-dark {
    --bs-btn-bg: var(--c-secondary);
    --bs-btn-border-color: var(--c-secondary);
    --bs-btn-color: #fff;
    --bs-btn-hover-bg: var(--c-secondary-d);
    --bs-btn-hover-border-color: var(--c-secondary-d);
    --bs-btn-active-bg: var(--c-secondary-d);
    --bs-btn-active-border-color: var(--c-secondary-d);
}

/* For the dark bands only: an outline button in the page's normal ink
   would vanish on charcoal, so this one is drawn in the surface colour
   and fills to solid white on hover. */
.btn-ghost {
    --bs-btn-color: #fff;
    --bs-btn-border-color: rgba(255, 255, 255, .35);
    --bs-btn-bg: transparent;
    --bs-btn-hover-bg: #fff;
    --bs-btn-hover-border-color: #fff;
    --bs-btn-hover-color: var(--c-heading);
    --bs-btn-active-bg: #fff;
    --bs-btn-active-border-color: #fff;
    --bs-btn-active-color: var(--c-heading);
}

.btn-outline-primary {
    --bs-btn-color: var(--c-heading);
    --bs-btn-border-color: var(--c-line-strong);
    --bs-btn-bg: transparent;
    --bs-btn-hover-bg: var(--c-primary);
    --bs-btn-hover-border-color: var(--c-primary);
    --bs-btn-hover-color: var(--c-on-primary);
    --bs-btn-active-bg: var(--c-primary);
    --bs-btn-active-border-color: var(--c-primary);
    --bs-btn-active-color: var(--c-on-primary);
}

.btn-light {
    --bs-btn-bg: var(--c-light);
    --bs-btn-border-color: var(--c-line);
    --bs-btn-color: var(--c-heading);
    --bs-btn-hover-bg: var(--c-surface);
    --bs-btn-hover-border-color: var(--c-line-strong);
}

.btn-danger {
    --bs-btn-bg: var(--c-danger);
    --bs-btn-border-color: var(--c-danger);
}

.btn-success {
    --bs-btn-bg: var(--c-success);
    --bs-btn-border-color: var(--c-success);
}

/* In flight. app.js adds this while an AJAX post is running so a second
   click can't queue a second add-to-cart. */
.is-busy {
    pointer-events: none;
    opacity: .65;
}

/* A text button — used where a link would be too quiet but a bordered
   button too loud (e.g. "সব দেখুন" beside a section heading). */
.btn-quiet {
    display: inline-flex;
    align-items: center;
    gap: .375rem;
    padding: .375rem 0;
    font-size: .9rem;
    font-weight: 550;
    color: var(--c-heading);
    background: none;
    border: 0;
    border-bottom: 1px solid var(--c-line-strong);
    border-radius: 0;
    transition: border-color .16s var(--ease), color .16s var(--ease);
}

.btn-quiet:hover {
    color: var(--c-accent);
    border-bottom-color: currentColor;
}

/* =============================================================
   4. FORMS
   ============================================================= */

.form-control,
.form-select {
    padding: .625rem .875rem;
    font-size: .9375rem;
    color: var(--c-heading);
    background-color: var(--c-white);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
    transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}

.form-control::placeholder {
    color: var(--c-muted);
}

.form-control:focus,
.form-select:focus {
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px rgba(var(--c-accent-rgb), .12);
}

.form-control.is-invalid,
.form-select.is-invalid {
    border-color: var(--c-danger);
}

.form-label {
    margin-bottom: .375rem;
    font-size: .875rem;
    font-weight: 550;
    color: var(--c-heading);
}

.form-text {
    font-size: .8125rem;
    color: var(--c-muted);
}

.invalid-feedback {
    font-size: .8125rem;
}

.form-check-input {
    border-color: var(--c-line-strong);
}

.form-check-input:checked {
    background-color: var(--c-primary);
    border-color: var(--c-primary);
}

.form-check-input:focus {
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px rgba(var(--c-accent-rgb), .12);
}

.input-group>.form-control:focus {
    z-index: 3;
}

/* =============================================================
   5. CARDS & SURFACES
   A border, not a shadow. The shadow arrives only on hover, and
   only where hovering means something.
   ============================================================= */

.card {
    --bs-card-border-color: var(--c-line);
    --bs-card-border-radius: var(--r-lg);
    --bs-card-bg: var(--c-white);
    box-shadow: none;
}

/* The workhorse panel: settings blocks, order summaries, filter cards. */
.panel {
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    padding: 1.5rem;
}

.panel--flush {
    padding: 0;
}

.panel--tint {
    background: var(--c-light);
}

.panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding-bottom: 1rem;
    margin-bottom: 1.25rem;
    border-bottom: 1px solid var(--c-line);
}

.panel__head h2,
.panel__head h3,
.panel__head h5 {
    margin: 0;
    font-size: 1.0625rem;
}

/* Full-bleed tinted band between white sections. */
.band {
    background: var(--c-light);
    border-block: 1px solid var(--c-line);
}

.alert {
    --bs-alert-border-radius: var(--r-md);
    border-width: 1px;
    font-size: .9rem;
}

.alert-success {
    --bs-alert-color: #065f46;
    --bs-alert-bg: #ecfdf5;
    --bs-alert-border-color: #a7f3d0;
}

.alert-danger {
    --bs-alert-color: #991b1b;
    --bs-alert-bg: #fef2f2;
    --bs-alert-border-color: #fecaca;
}

.alert-info {
    --bs-alert-color: #1e40af;
    --bs-alert-bg: #eff6ff;
    --bs-alert-border-color: #bfdbfe;
}

.breadcrumb {
    --bs-breadcrumb-font-size: .8125rem;
    --bs-breadcrumb-divider-color: var(--c-muted);
    --bs-breadcrumb-item-active-color: var(--c-muted);
    margin-bottom: 0;
}

.breadcrumb a {
    color: var(--c-body);
}

/* =============================================================
   6. BADGES, PILLS, RATINGS
   ============================================================= */

.badge {
    padding: .3125rem .5rem;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .01em;
    border-radius: var(--r-sm);
}

/* Discount. The one place red is allowed to shout. Top right, which is
   where a shopper scanning a grid of cards looks for the number. */
.badge-sale {
    position: absolute;
    top: .875rem;
    right: .875rem;
    z-index: 2;
    padding: .3125rem .6875rem;
    font-size: .6875rem;
    font-weight: 700;
    color: #fff;
    background: var(--c-danger);
    border-radius: var(--r-pill);
}

.badge-soft {
    color: var(--c-body);
    background: var(--c-surface);
}

.badge-stock-in {
    color: var(--c-success);
    background: #ecfdf5;
}

.badge-stock-out {
    color: var(--c-danger);
    background: #fef2f2;
}

/* Rating stars — amber, and the only warm colour on a product card. */
.stars {
    display: inline-flex;
    gap: 1px;
    color: var(--c-highlight);
    font-size: .8125rem;
    line-height: 1;
}

.stars .bi {
    display: block;
}

.card-rating {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .375rem;
}

/* The count inherits the body's line-height, which is taller than the
   stars beside it — enough to push the title of a reviewed product a few
   pixels below its neighbours' in the same row. */
.card-rating small {
    line-height: 1;
}

/* An unreviewed product still shows the row, so the cards line up — but
   its stars are drawn in the rule colour rather than the gold, which
   would otherwise read as a rating nobody gave. */
.card-rating:not(:has(small)) .stars {
    color: var(--c-line-strong);
}

/* =============================================================
   7. HEADER
   Three stacked rows:

     1  top bar  — welcome line, e-mail, hotline, quick links
     2  main bar — logo · links · account · cart      (sticky)
     3  cat bar  — "সকল ক্যাটাগরি" menu + the big search field

   Only row 2 is sticky: keeping all three pinned would eat a third
   of a laptop screen, and --header-h (which the sticky sidebars in
   app.css offset against) stays honest.

   Below lg rows 1 and 3 shed everything but the search field, and
   the link list moves into the panel opened from the bottom tab
   bar's "মেনু" key — within reach of a thumb.
   ============================================================= */

/* ---- Row 1: top bar ---- */

.topbar {
    font-size: .8325rem;
    color: var(--c-body);
    background: var(--c-white);
    border-bottom: 1px solid var(--c-line);
}

.topbar__inner {
    display: flex;
    align-items: center;
    gap: 1.75rem;
    min-height: 42px;
}

.topbar__msg {
    margin: 0;
}

.topbar__meta,
.topbar__links {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    margin: 0;
    padding: 0;
    list-style: none;
}

/* Pushed to the far edge, whatever is or isn't to its left. */
.topbar__links {
    margin-left: auto;
    gap: 1.125rem;
}

.topbar a {
    color: var(--c-body);
}

.topbar a:hover {
    color: var(--c-primary-d);
}

.topbar .bi {
    margin-right: .375rem;
    color: var(--c-primary);
}

/* ---- Row 2: main bar ---- */

.site-nav {
    --bs-navbar-padding-y: 0;
    min-height: var(--header-h);
    background: rgba(255, 255, 255, .88);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    transition: box-shadow .2s var(--ease), background-color .2s var(--ease);
}

.site-nav.scrolled {
    background: rgba(255, 255, 255, .96);
    box-shadow: var(--shadow-sm);
}

.site-nav .container {
    display: flex;
    align-items: center;
    gap: 1rem;
    min-height: var(--header-h);
}

/* ---- Brand mark ---- */

.brand-logo {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    flex: none;
    color: var(--c-heading);
}

.brand-logo:hover {
    color: var(--c-heading);
    opacity: .75;
}

.brand-mark {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    font-size: 1.125rem;
    color: var(--c-on-primary);
    background: var(--c-primary);
    border-radius: var(--r-md);
}

.brand-text {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -.02em;
}

.brand-img {
    max-height: 40px;
    width: auto;
}

/* ---- Nav links ---- */

/* Bootstrap gives .navbar-collapse flex-grow:1, which would leave the
   links hard against the logo. Sized to content with auto margins
   instead, the list floats between the logo and the account/cart
   block — the arrangement in the reference layout. */
@media (min-width: 992px) {
    .site-nav .navbar-collapse {
        flex-grow: 0;
        flex-basis: auto;
        margin-inline: auto;
    }
}

.site-nav .navbar-nav {
    gap: .25rem;
}

.site-nav .nav-link {
    text-transform: uppercase;
    position: relative;
    padding: .5rem .875rem;
    font-size: 0.9rem;
    font-weight: 700;
    color: var(--c-secondary-d);
    border-radius: var(--r-sm);
    transition: color .15s var(--ease), background-color .15s var(--ease);
}

.site-nav .nav-link:hover,
.site-nav .nav-link:focus {
    color: var(--c-heading);
    background: var(--c-light);
}

.site-nav .nav-link.is-active {
    color: var(--c-primary);
    font-weight: 600;
}

/* A sub-category inside the phone menu panel: stepped in and set lighter,
   so the list reads as a tree rather than one flat run of names. */
.site-nav .nav-link--sub {
    padding-left: 1.75rem;
    font-size: .8125rem;
    font-weight: 600;
    text-transform: none;
    color: var(--c-body);
}

/* ---- Account & cart ---- */

.header-actions {
    display: flex;
    align-items: center;
    gap: 1.5rem;
    flex: none;
    margin-left: auto;
}

/* One shape for both: a gold glyph, then two stacked lines of text —
   a quiet label over the thing that actually changes (the name, the
   cart total). */
.header-user,
.header-cart {
    display: flex;
    align-items: center;
    gap: .625rem;
    color: var(--c-heading);
    white-space: nowrap;
}

.header-user:hover,
.header-cart:hover {
    color: var(--c-heading);
}

.header-user__ic,
.header-cart__ic {
    position: relative;
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    flex: none;
    font-size: 1.625rem;
    line-height: 1;
    color: var(--c-primary);
    transition: color .15s var(--ease);
}

.header-user:hover .header-user__ic,
.header-cart:hover .header-cart__ic {
    color: var(--c-primary-d);
}

.header-user__txt,
.header-cart__txt {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.header-user__hi,
.header-cart__label {
    font-size: .75rem;
    color: var(--c-body);
}

.header-user__do,
.header-cart__total {
    font-size: .875rem;
    font-weight: 650;
}

.header-cart__total {
    color: var(--c-primary-d);
    font-variant-numeric: tabular-nums;
}

/* Dark, not gold: at 11px the count has to survive being small, and
   the gold badge would sit on a gold bag. */
.header-cart__count {
    position: absolute;
    right: -4px;
    bottom: -2px;
    min-width: 18px;
    padding: 0 4px;
    font-size: .6875rem;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    color: #fff;
    background: var(--c-secondary);
    border: 2px solid var(--c-white);
    border-radius: var(--r-pill);
    box-sizing: content-box;
}

/* Same pop as the floating cart — app.js adds .is-bumped to every
   element carrying data-cart-fab, this one included. */
.header-cart.is-bumped .header-cart__ic {
    animation: fab-bump .4s var(--ease);
}

/* ---- Row 3: category bar + search ---- */

.catbar {
    background: var(--c-white);
}

.catbar__inner {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding-block: .875rem;
}

.catbar__cats {
    flex: none;
}

.cat-btn {
    display: flex;
    align-items: center;
    gap: .625rem;
    width: 265px;
    padding: .8125rem 1.125rem;
    font-size: .9375rem;
    font-weight: 650;
    letter-spacing: -.01em;
    color: var(--c-white);
    background: var(--c-primary);
    border: 0;
    border-radius: 3px;
    transition: background-color .16s var(--ease);
}

.cat-btn:hover,
.cat-btn[aria-expanded="true"] {
    color: var(--c-on-primary);
    background: var(--c-primary-d);
}

.cat-btn__caret {
    margin-left: auto;
    font-size: .8125rem;
    transition: transform .2s var(--ease);
}

.cat-btn[aria-expanded="true"] .cat-btn__caret {
    transform: rotate(180deg);
}

.cat-menu {
    width: 265px;
    padding: .375rem;
    margin-top: .375rem;
}

.cat-menu .dropdown-item {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem .75rem;
    border-radius: var(--r-sm);
}

.cat-menu .dropdown-item .bi {
    font-size: .875rem;
    color: var(--c-primary);
}

/* ---- Sub-categories ----
   A flyout to the right of the parent's row. Bootstrap has no nested menu,
   so this is plain CSS: shown while the pointer is over the parent row or
   the panel itself, and kept reachable from the keyboard with :focus-within.
   The parent row stays a link of its own — the arrow only says there is
   more underneath. */

.cat-menu__item {
    position: relative;
}

.cat-menu__more {
    margin-left: auto;
    font-size: .75rem !important;
    opacity: .7;
}

.cat-menu__sub {
    position: absolute;
    top: -.375rem;
    left: 100%;
    z-index: 10;
    display: none;
    min-width: 210px;
    margin: 0;
    padding: .375rem;
    list-style: none;
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-lg);
}

.cat-menu__item:hover > .cat-menu__sub,
.cat-menu__item:focus-within > .cat-menu__sub {
    display: block;
}

.cat-menu__sub .dropdown-item {
    padding: .5rem .75rem;
    border-radius: var(--r-sm);
}

/* Not enough room to fly out sideways — the sub-categories sit inline
   under their parent instead, one step in. */
@media (max-width: 1199.98px) {
    .cat-menu__sub {
        position: static;
        display: block;
        min-width: 0;
        margin: 0 0 .25rem .75rem;
        padding: 0 0 0 .5rem;
        border: 0;
        border-left: 2px solid var(--c-line);
        border-radius: 0;
        box-shadow: none;
    }

    .cat-menu__more { display: none; }
}

/* flex-basis 0, not auto: a wrapping flex container decides where to break
   from each item's *hypothetical* size before any shrinking happens, so with
   `auto` the field claims its intrinsic width and pushes the button onto a
   second row on a phone. From a zero basis it takes whatever is left, and
   min-width:0 lets it shrink past the input's own intrinsic width too. */
.header-search {
    display: flex;
    align-items: stretch;
    flex: 1 1 0;
    min-width: 0;
    background: var(--c-white);
    border: 1px solid var(--c-line-strong);
    border-radius: 3px;
    overflow: hidden;
    transition: border-color .16s var(--ease), box-shadow .16s var(--ease);
}

.header-search:focus-within {
    border-color: var(--c-primary);
    box-shadow: 0 0 0 3px rgba(var(--c-accent-rgb), .22);
}

.header-search__q {
    flex: 1 1 0;
    min-width: 0;
    padding: .75rem 1rem;
    font-size: .9375rem;
    color: var(--c-heading);
    background: transparent;
    border: 0;
    outline: 0;
}

.header-search__q::placeholder {
    color: var(--c-muted);
}

/* Native appearance off so the control matches the rest of the bar;
   the caret is painted back on as a background image. */
.header-search__cat {
    flex: none;
    width: 175px;
    padding: .75rem 2rem .75rem .875rem;
    font-size: .875rem;
    color: var(--c-body);
    background-color: transparent;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%237e7e7e'%3E%3Cpath d='M8 11.2 3.3 6.5l1.1-1.1L8 9l3.6-3.6 1.1 1.1z'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .75rem center;
    background-size: 13px;
    border: 0;
    border-left: 1px solid var(--c-line);
    outline: 0;
    cursor: pointer;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
}

.header-search__btn {
    flex: none;
    padding: .75rem 1.875rem;
    font-size: .875rem;
    font-weight: 700;
    letter-spacing: .01em;
    color: var(--c-white);
    background: var(--c-primary);
    border: 0;
    transition: background-color .16s var(--ease);
}

.header-search__btn:hover {
    background: var(--c-primary-d);
}

.dropdown-menu {
    --bs-dropdown-border-color: var(--c-line);
    --bs-dropdown-border-radius: var(--r-md);
    --bs-dropdown-link-color: var(--c-body);
    --bs-dropdown-link-hover-bg: var(--c-light);
    --bs-dropdown-link-active-bg: var(--c-light);
    --bs-dropdown-link-active-color: var(--c-heading);
    --bs-dropdown-font-size: .9rem;
    box-shadow: var(--shadow-md);
}

/* On phones the collapse is a panel below the bar, opened from the
   tab bar's "মেনু" button rather than a burger in the header. The
   account and cart keep their glyphs but drop their labels, and the
   category menu goes with them — its list is in that same panel. */
@media (max-width: 991.98px) {
    .site-nav .container {
        flex-wrap: wrap;
        padding-block: .75rem;
    }

    .navbar-collapse {
        order: 3;
        width: 100%;
        margin-top: .75rem;
        padding-top: .75rem;
        border-top: 1px solid var(--c-line);
    }

    .site-nav .nav-link {
        padding: .625rem .5rem;
    }

    .header-actions {
        gap: 1rem;
    }

    .header-user__txt,
    .header-cart__txt {
        display: none;
    }

    .catbar__inner {
        padding-block: .625rem;
    }

    .catbar__cats {
        display: none;
    }

    .header-search__cat {
        display: none;
    }

    .header-search__btn {
        padding-inline: 1.125rem;
    }
}

/* Narrow phones: the word "সার্চ" costs more width than it earns. */
@media (max-width: 575.98px) {
    .header-search__btn {
        padding-inline: .875rem;
    }

    .header-search__btn span {
        display: none;
    }
}

/* =============================================================
   8. FOOTER
   ============================================================= */

.site-footer {
    margin-top: 4rem;
    color: var(--c-body);
    background: var(--c-white);
    border-top: 1px solid var(--c-line);
}

/* A page whose last section is a full-bleed band can set .is-flush on
   <main> so the two meet with no white strip between them. Only the gap
   goes — the rule stays, because it is what separates the footer from the
   band above it once they are touching. */
.site-main.is-flush+.site-footer {
    margin-top: 0;
}

/* ---- Newsletter band ---- */

.footer-news {
    background: var(--c-light);
    border-bottom: 1px solid var(--c-line);
}

.footer-news__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding-block: 2.5rem;
}

.footer-news__copy h5 {
    margin-bottom: .25rem;
    font-size: 1.125rem;
}

.footer-news__copy p {
    margin: 0;
    font-size: .9rem;
    color: var(--c-muted);
}

.footer-news__form {
    flex: 1 1 340px;
    max-width: 460px;
}

.newsletter__form {
    display: flex;
    gap: .5rem;
}

.newsletter__form input[type="email"] {
    flex: 1;
    padding: .625rem .875rem;
    font: inherit;
    font-size: .9375rem;
    color: var(--c-heading);
    background: var(--c-white);
    border: 1px solid var(--c-line-strong);
    border-radius: var(--r-md);
}

.newsletter__form input[type="email"]:focus {
    outline: 0;
    border-color: var(--c-accent);
    box-shadow: 0 0 0 3px rgba(var(--c-accent-rgb), .12);
}

.newsletter__form button {
    flex: none;
    padding: .625rem 1.25rem;
    font: inherit;
    font-size: .9375rem;
    font-weight: 550;
    color: var(--c-on-primary);
    background: var(--c-primary);
    border: 0;
    border-radius: var(--r-md);
    transition: background-color .16s var(--ease);
}

.newsletter__form button:hover {
    background: var(--c-primary-d);
}

.newsletter__msg {
    margin: .625rem 0 0;
    font-size: .8125rem;
}

.newsletter__msg.is-ok {
    color: var(--c-success);
}

.newsletter__msg.is-error {
    color: var(--c-danger);
}

/* ---- Columns ----
   Contact · menu · policies · hours. The contact column carries the
   about line as well, so it gets the extra width; the hours column is
   a touch wider than the link lists because its rows are sentences. */

.footer-grid {
    display: grid;
    grid-template-columns: 1.7fr 1fr 1.1fr 1.2fr;
    gap: 2.5rem 3rem;
    padding-block: 4rem 3.5rem;
}

.footer-col h5 {
    margin-bottom: 1.375rem;
    font-size: 1.0625rem;
    font-weight: 600;
    letter-spacing: .01em;
    text-transform: uppercase;
    color: var(--c-heading);
}

.footer-about {
    max-width: 42ch;
    margin-bottom: 1.5rem;
    font-size: .9rem;
    color: var(--c-body);
}

.footer-links,
.footer-contact,
.footer-hours {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: .875rem;
    font-size: .9rem;
}

.footer-links a {
    color: var(--c-body);
}

.footer-links a:hover {
    color: var(--c-heading);
}

.footer-contact li {
    display: flex;
    align-items: flex-start;
    gap: .625rem;
}

.footer-contact .bi {
    margin-top: .15em;
    font-size: 1rem;
    color: var(--c-muted);
}

.footer-contact a {
    color: var(--c-body);
}

/* The delivery line is the one thing in this column worth a second
   glance, so it is the only bold text in the footer body. */
.footer-hours__note {
    margin: 1rem 0 0;
    font-size: .9rem;
    font-weight: 650;
    color: var(--c-heading);
}

/* ---- Bottom bar ----
   A solid gold band under the columns, holding the copyright, the
   payment marks and the social links. Lettered in the dark ink, not
   white: white on this gold is about 2:1 and unreadable — see the note
   on --c-on-primary in the tokens. */

.footer-bottom {
    background: var(--c-footer-bar);
}

.footer-bottom__inner {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1rem 1.5rem;
    padding-block: 1.125rem;
    font-size: .8125rem;
    color: var(--c-white);
}

.footer-copy {
    flex: 1 1 auto;
}

.footer-credit {
    opacity: .85;
}

.footer-credit::before {
    content: " · ";
}

/* Payment marks — white chips, each mark drawn as text (or two rings
   for Mastercard) so the bar loads no images. */

.footer-pay {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .4375rem;
}

.pay-ic {
    display: grid;
    place-items: center;
    min-width: 44px;
    height: 28px;
    padding-inline: .4375rem;
    font-size: .625rem;
    font-weight: 800;
    line-height: 1;
    letter-spacing: .01em;
    white-space: nowrap;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 1px 2px rgba(16, 24, 40, .18);
}

.pay-ic--visa {
    font-size: .75rem;
    font-style: italic;
    color: #1a1f71;
}

.pay-ic--amex {
    color: #2e77bc;
}

.pay-ic--paypal {
    font-size: .6875rem;
    color: #003087;
}

.pay-ic--bkash {
    font-size: .6875rem;
    color: #e2136e;
}

.pay-ic--nagad {
    font-size: .6875rem;
    color: #ee1c25;
}

.pay-ic--rocket {
    font-size: .6875rem;
    color: #8c3494;
}

.pay-ic--mastercard {
    padding-inline: .25rem;
}

/* Social sits on the coloured bar now, so the circles are outlined in
   the bar's own ink and fill on hover rather than sitting on a plate. */

.footer-social {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.social-ic {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    font-size: .875rem;
    color: var(--c-white);
    background: transparent;
    border: 1px solid var(--c-white);
    border-radius: var(--r-pill);
    transition: color .16s var(--ease), background-color .16s var(--ease);
}

.social-ic:hover {
    color: var(--c-primary);
    background: var(--c-white);
}

/* ---- Responsive ---- */

@media (max-width: 991.98px) {
    .footer-grid {
        grid-template-columns: 1fr 1fr;
        gap: 2.25rem 2rem;
        padding-block: 3rem 2.5rem;
    }

    /* The contact column is the tall one — let it take the full row so
       the three short columns line up underneath it. */
    .footer-col--brand {
        grid-column: 1 / -1;
    }
}

@media (max-width: 767.98px) {
    .footer-news__inner {
        padding-block: 2rem;
    }

    .newsletter__form {
        flex-direction: column;
    }

    .footer-bottom__inner {
        justify-content: center;
        text-align: center;
    }

    .footer-copy {
        flex-basis: 100%;
    }

    .footer-pay,
    .footer-social {
        justify-content: center;
    }
}

@media (max-width: 575.98px) {
    .footer-grid {
        grid-template-columns: 1fr;
        gap: 2rem;
        padding-block: 2.5rem;
    }

    .footer-col--brand {
        grid-column: auto;
    }
}

/* =============================================================
   9. CART FAB · TAB BAR · TOAST · BACK-TO-TOP
   ============================================================= */

.cart-fab {
    position: fixed;
    right: 1.5rem;
    bottom: 1.5rem;
    z-index: 1030;
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    padding: .625rem 1rem .625rem .75rem;
    color: var(--c-on-primary);
    background: var(--c-primary);
    border-radius: var(--r-pill);
    box-shadow: var(--shadow-lg);
    transition: transform .2s var(--ease), opacity .2s var(--ease);
}

.cart-fab:hover {
    color: var(--c-on-primary);
    transform: translateY(-2px);
}

.cart-fab.is-empty {
    opacity: 0;
    pointer-events: none;
    transform: translateY(8px);
}

.cart-fab__icon {
    position: relative;
    display: grid;
    place-items: center;
    font-size: 1.1875rem;
}

.cart-fab__count {
    position: absolute;
    top: -6px;
    right: -10px;
    min-width: 18px;
    padding: 0 4px;
    font-size: .6875rem;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    color: #fff;
    background: var(--c-secondary);
    border-radius: var(--r-pill);
}

.cart-fab__total {
    font-size: .9rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
}

/* Pop when the count changes — restarted from JS by removing and
   re-adding the class. */
.cart-fab.is-bumped .cart-fab__icon {
    animation: fab-bump .4s var(--ease);
}

@keyframes fab-bump {
    0% {
        transform: scale(1);
    }

    35% {
        transform: scale(1.22);
    }

    100% {
        transform: scale(1);
    }
}

/* ---- Bottom tab bar (phones) ---- */

.tabbar {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 1035;
    display: grid;
    grid-template-columns: repeat(5, 1fr);
    align-items: center;
    background: rgba(255, 255, 255, .94);
    backdrop-filter: saturate(180%) blur(12px);
    -webkit-backdrop-filter: saturate(180%) blur(12px);
    border-top: 1px solid var(--c-line);
    padding-bottom: env(safe-area-inset-bottom);
}

.tabbar__item {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 2px;
    padding: .5rem 0 .4375rem;
    font-size: .6875rem;
    font-weight: 500;
    color: var(--c-muted);
    background: none;
    border: 0;
    transition: color .15s var(--ease);
}

.tabbar__item .bi {
    font-size: 1.1875rem;
    line-height: 1;
}

.tabbar__item.is-active,
.tabbar__item[aria-expanded="true"] {
    color: var(--c-heading);
}

.tabbar__gap {
    display: block;
}

/* On phones the FAB becomes the raised middle key of the tab bar. */
@media (max-width: 991.98px) {
    body {
        padding-bottom: 64px;
    }

    .cart-fab {
        right: 50%;
        bottom: 14px;
        transform: translateX(50%);
        flex-direction: column;
        gap: 0;
        width: 58px;
        height: 58px;
        padding: 0;
        justify-content: center;
        z-index: 1036;
    }

    .cart-fab:hover,
    .cart-fab.is-empty {
        transform: translateX(50%);
    }

    .cart-fab.is-empty {
        opacity: 1;
        pointer-events: auto;
    }

    .cart-fab__total {
        display: none;
    }

    .cart-fab.is-empty .cart-fab__count {
        display: none;
    }
}

/* From lg up the cart lives in the header, so the floating one would
   be a second control for the same thing. */
@media (min-width: 992px) {

    .tabbar,
    .cart-fab {
        display: none;
    }
}

/* ---- Toast ---- */

.cart-toast {
    position: fixed;
    left: 50%;
    bottom: 5.5rem;
    z-index: 1080;
    transform: translate(-50%, 12px);
    max-width: min(90vw, 380px);
    padding: .75rem 1.125rem;
    font-size: .875rem;
    color: #fff;
    background: var(--c-heading);
    border-radius: var(--r-md);
    box-shadow: var(--shadow-lg);
    opacity: 0;
    visibility: hidden;
    transition: opacity .2s var(--ease), transform .2s var(--ease), visibility .2s;
}

.cart-toast.is-open {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
}

.cart-toast.is-error {
    background: var(--c-danger);
}

/* ---- Back to top ---- */

/* Shown and hidden by a class rather than jQuery's fadeIn, which drives
   `display` from a timer — a transition on opacity and position hands the
   whole thing to the compositor and lets the button drift in. */
#toTop {
    position: fixed;
    right: 1.5rem;
    bottom: 5.5rem;
    z-index: 1029;
    display: grid;
    place-items: center;
    width: 46px;
    height: 46px;
    padding: 0;
    font-size: .9375rem;
    color: var(--c-heading);
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    box-shadow: 0 2px 6px rgba(69, 69, 69, .10),
                0 10px 24px -10px rgba(69, 69, 69, .30);
    opacity: 0;
    visibility: hidden;
    transform: translateY(10px) scale(.94);
    transition: opacity .25s var(--ease), transform .25s var(--ease),
                visibility .25s, color .18s var(--ease),
                background-color .18s var(--ease), border-color .18s var(--ease);
}

#toTop.is-visible {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
}

#toTop:hover {
    color: var(--c-on-primary);
    background: var(--c-primary);
    border-color: var(--c-primary);
}

/* The chevron rides up a little on hover — the direction the button goes. */
#toTop .bi {
    position: relative;
    z-index: 1;
    line-height: 1;
    transition: transform .18s var(--ease);
}

#toTop:hover .bi {
    transform: translateY(-2px);
}

/* ---- Scroll progress ring ---- */

.to-top__ring {
    position: absolute;
    inset: -1px;               /* sits over the border, not inside it */
    width: 46px;
    height: 46px;
    /* Start the sweep at twelve o'clock instead of three. */
    transform: rotate(-90deg);
    overflow: visible;
}

.to-top__ring circle {
    fill: none;
    stroke-width: 2;
}

/* A faint full circle behind the arc, so the ring reads as a gauge that is
   part-filled rather than a stray stroke floating off one side. */
.to-top__track {
    stroke: var(--c-line);
    transition: stroke .18s var(--ease);
}

#toTop:hover .to-top__track {
    stroke: rgba(255, 255, 255, .35);
}

.to-top__bar {
    stroke: var(--c-primary);
    stroke-linecap: round;
    /* 2πr for r=20. app.js moves the offset from this down to 0. */
    stroke-dasharray: 125.66;
    stroke-dashoffset: 125.66;
    transition: stroke-dashoffset .12s linear, stroke .18s var(--ease);
}

#toTop:hover .to-top__bar {
    stroke: var(--c-on-primary);
}

@media (max-width: 991.98px) {
    #toTop {
        bottom: 5.25rem;
        right: 1rem;
    }

    .cart-toast {
        bottom: 5.25rem;
    }
}

/* =============================================================
   10. PRODUCT CARD
   Square image, thin rule, everything else in one column so a row
   of cards has its titles, prices and buttons on the same lines.
   ============================================================= */

/* One tone the whole way down — the theme's background grey behind the
   photograph as well as behind the wording. No border either: the card
   reads against the white page by tone alone, and lifts on hover. */
.product-card {
    position: relative;
    overflow: hidden;
    border: 0;
    border-radius: 1rem;
    background: var(--c-light);
    text-align: center;
    transition: box-shadow .18s var(--ease), transform .18s var(--ease);
}

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

.product-card .position-relative {
    padding: 1rem 0.8rem 0.5rem;
    background: var(--c-light);
}

/* The corners and the clipping belong to this box rather than to the
   picture: one rounded shape to paint instead of a rounded image that has
   to be re-rounded whenever anything about it changes. */
.product-card__shot {
    display: block;
    overflow: hidden;
    border-radius: var(--r-md);
}

/* No hover zoom — the card's own lift is the whole of the hover now. */
.product-card .card-img-top {
    display: block;
    aspect-ratio: 1 / 1;
    width: 100%;
    object-fit: cover;
    background: var(--c-light);
}

.product-card .card-body {
    padding: 1.125rem 1rem 1.15rem;
    background: var(--c-light);
}

.product-title {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: .9rem;
    font-weight: 600;
    line-height: 1.5;
    color: var(--c-heading);
    min-height: 2.7em;
}

.product-title:hover {
    color: var(--c-primary-d);
}

/* Sale price and the struck-through original always share one line —
   two cards fit across a phone and the original must not be pushed
   out of sight. */
.price-row {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    justify-content: center;
    gap: .5rem;
}

.price {
    font-size: 1.0625rem;
    font-weight: 700;
    color: var(--c-heading);
}

.price-old {
    font-size: .8125rem;
    color: var(--c-muted);
    text-decoration: line-through;
}

/* On sale the live price turns gold. The red is spent on the discount
   badge, which is the louder of the two signals. */
.product-card .price-row:has(.price-old) .price {
    color: var(--c-primary);
}

/* ---- The card's own button ----
   White, pill, small uppercase type: over a grey panel a solid fill would
   be the loudest thing on a whole row of cards. It fills with the gold on
   hover, inherited from .btn-outline-primary. */
.btn-card {
    --bs-btn-bg: var(--c-white);
    --bs-btn-border-color: var(--c-line-strong);
    --bs-btn-padding-y: .5625rem;
    --bs-btn-font-size: .6875rem;
    --bs-btn-border-radius: var(--r-pill);
    font-weight: 700;
    letter-spacing: .09em;
    text-transform: uppercase;
}

/* ---- Wishlist heart ---- */

.wish-form {
    margin: 0;
}

.wish-btn {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    font-size: .9375rem;
    color: var(--c-body);
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    transition: color .16s var(--ease), border-color .16s var(--ease),
        transform .16s var(--ease);
}

.wish-btn:hover {
    color: var(--c-danger);
    border-color: var(--c-danger);
}

.wish-btn.is-saved {
    color: var(--c-danger);
    border-color: var(--c-danger);
}

.wish-btn:active {
    transform: scale(.92);
}

/* A small pop when the heart fills — added by app.js, and restarted the
   same way the cart badge's bump is. */
.wish-btn.is-popped .bi {
    animation: heart-pop .32s var(--ease);
}

@keyframes heart-pop {
    0% {
        transform: scale(1);
    }

    45% {
        transform: scale(1.35);
    }

    100% {
        transform: scale(1);
    }
}

/* Left, because the discount badge now owns the right corner and the two
   would sit on top of each other. */
.wish-btn--float {
    position: absolute;
    top: .875rem;
    left: .875rem;
    z-index: 2;
    box-shadow: var(--shadow-sm);
}

/* Product page: a square key sitting in the row with WhatsApp and Call,
   so the three secondary actions read as one group. */
.wish-btn--wide {
    width: 46px;
    height: 46px;
    font-size: 1.0625rem;
    border-radius: var(--r-md);
}

/* Narrow phones: two cards leave ~150px each, so tighten everything. */
@media (max-width: 575.98px) {
    .product-card .position-relative {
        padding: .75rem;
    }

    .product-card .card-body {
        padding: 0 .625rem .875rem;
    }

    .product-title {
        font-size: .8125rem;
    }

    .price {
        font-size: .9375rem;
    }

    /* The uppercase label needs the tracking dropped before it will fit
       across a card this narrow. */
    .btn-card {
        --bs-btn-padding-x: .375rem;
        --bs-btn-font-size: .625rem;
        letter-spacing: .05em;
    }
}

/* Cards appended by "load more" fade in rather than appearing. */
.is-appended {
    animation: card-in .35s var(--ease) both;
}

@keyframes card-in {
    from {
        opacity: 0;
        transform: translateY(8px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* =============================================================
   11. SECTIONS & PAGE HEADERS
   ============================================================= */

.section {
    padding-block: 4rem;
}

.section--tight {
    padding-block: 2.5rem;
}

.section-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 2rem;
}

.section-title {
    margin: 0;
    font-size: clamp(1.25rem, 1.1rem + .6vw, 1.625rem);
    font-weight: 650;
    letter-spacing: -.02em;
}

.section-sub {
    margin: .25rem 0 0;
    font-size: .9rem;
    color: var(--c-muted);
}

/* Page header for the inner pages — a rule, not a coloured banner. */
.page-head {
    padding-block: 2.5rem 2rem;
    border-bottom: 1px solid var(--c-line);
    margin-bottom: 2.5rem;
}

.page-head h1 {
    margin: .5rem 0 0;
}

.page-head p {
    margin: .5rem 0 0;
    color: var(--c-muted);
}

@media (max-width: 767.98px) {
    .section {
        padding-block: 2.5rem;
    }

    .section-head {
        margin-bottom: 1.25rem;
    }
}

/* =============================================================
   12. HOMEPAGE

   A stack of full-bleed bands that alternate light and dark, so the
   page has a rhythm instead of one long scroll of white. The gold is
   the constant across all of them — it only reads as gold against
   something deep, which is why the hero, the deal and the numbers are
   charcoal rather than another white card.

   Everything here is prefixed .hp- so the homepage can be restyled
   without touching a class the catalogue or the account pages share.

   Bands
     ·  shared furniture (headings, eyebrow, badges)
     1  hero            6  product tabs
     2  promo cards     7  numbers
     3  service strip   8  testimonials
     4  categories      9  brands
     5  weekly deal    10  closing CTA
   ============================================================= */

/* ---- Shared band furniture ---- */

.hp-section {
    padding-block: 4.5rem;
    background: var(--c-white);
}

.hp-section--tint {
    background: var(--c-light);
}

/* ---- Ruled heading ----
   A title on a hairline that runs the width of the container, with a short
   thick tab under the words themselves. Quieter than the eyebrow-and-sub
   block the other bands use — it belongs above a plain row of items rather
   than a section that needs introducing. */
.hp-rule-head {
    position: relative;
    margin-bottom: 2.75rem;
    padding-bottom: .875rem;
    border-bottom: 1px solid var(--c-line);
}

.hp-rule-head::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -2px;
    /* sits on the hairline, not above it */
    width: 84px;
    height: 3px;
    background: var(--c-primary);
    border-radius: 2px;
}

.hp-rule-head__title {
    margin: 0;
    font-size: clamp(1.25rem, 1.1rem + .7vw, 1.625rem);
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--c-heading);
}

.hp-rule-head__sub {
    max-width: 56ch;
    margin: .375rem 0 0;
    font-size: .9375rem;
    color: var(--c-body);
}

.hp-head {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: space-between;
    gap: 1.5rem;
    margin-bottom: 2.5rem;
}

.hp-head--center {
    justify-content: center;
    text-align: center;
}

/* On the dark bands the same heading block has to survive on charcoal. */
.hp-head--invert .hp-title {
    color: #fff;
}

.hp-head--invert .hp-sub {
    color: rgba(255, 255, 255, .62);
}

/* The little gold rule before a section label. Set as a pseudo-element
   rather than a border so it inherits the label's colour and needs no
   second declaration on the dark bands. */
.hp-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    margin-bottom: .625rem;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .16em;
    text-transform: uppercase;
    color: var(--c-primary);
}

.hp-eyebrow::before {
    content: "";
    width: 26px;
    height: 2px;
    background: currentColor;
    border-radius: 2px;
}

.hp-head--center .hp-eyebrow::after {
    content: "";
    width: 26px;
    height: 2px;
    background: currentColor;
    border-radius: 2px;
}

.hp-title {
    margin: 0;
    font-size: clamp(1.6rem, 1.2rem + 1.6vw, 2.35rem);
    font-weight: 700;
    letter-spacing: -.03em;
    line-height: 1.2;
}

.hp-sub {
    max-width: 56ch;
    margin: .625rem 0 0;
    color: var(--c-body);
}

.hp-head--center .hp-sub {
    margin-inline: auto;
}

.hp-link {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-size: .9375rem;
    font-weight: 600;
    color: var(--c-heading);
    padding-bottom: .25rem;
    border-bottom: 2px solid var(--c-primary);
    transition: color .16s var(--ease), gap .16s var(--ease);
}

.hp-link:hover {
    color: var(--c-primary-d);
    gap: .75rem;
}

/* Button row — shared with the About page, which pre-dates this file. */
.hero-actions {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
}

/* ---- 1. Hero ----
   A full-bleed banner per slide with the copy laid over its left third.
   Both stock banners (and most banners a shop will upload) put the goods
   right of centre and leave the left open, so the words go there and the
   photograph is never covered where it matters.

   The banner is left as shot. There is only the faintest wash on the left
   edge — enough to stop dark type sitting directly on a dark object, not
   enough to read as an overlay — and the copy carries a white text-shadow
   instead, which lifts the letters off whatever is behind them without
   putting a panel over the photograph. */

.hp-hero {
    position: relative;
    overflow: hidden;
    background: var(--c-light);
}

.hp-hero__slide {
    position: relative;
    display: flex;
    align-items: center;
    min-height: clamp(420px, 44vw, 620px);
}

.hp-hero__media {
    position: absolute;
    inset: 0;
    overflow: hidden;
}

.hp-hero__media img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    object-position: center right;
    /* keep the goods in frame when it crops */
}

.hp-hero__media::after {
    content: "";
    position: absolute;
    inset: 0;
    /* Barely there, and gone by the middle of the frame — it only takes
       the edge off a dark object landing under the type. Several stops
       rather than two so the falloff never shows as a visible edge. */
    background: linear-gradient(90deg,
            rgba(255, 255, 255, .42) 0%,
            rgba(255, 255, 255, .28) 22%,
            rgba(255, 255, 255, .12) 38%,
            rgba(255, 255, 255, 0) 52%);
}

.hp-hero .container {
    position: relative;
    z-index: 1;
}

.hp-hero__copy {
    max-width: 34rem;
    padding-block: 3rem;
    /* Does the work the overlay used to. Three layers, because one will
       not cover both cases: a tight 1px ring that separates the glyph
       edges where the photograph is busy (bookshelves, foliage), and two
       softer ones that lift the whole block off a mid-tone. Over the light
       parts of a banner none of it is visible. */
    text-shadow:
        0 0 1px rgba(255, 255, 255, .95),
        0 1px 4px rgba(255, 255, 255, .9),
        0 0 20px rgba(255, 255, 255, .75);
}

/* The product or collection name, above the headline. */
.hp-hero__name {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin-bottom: 1rem;
    font-size: .8125rem;
    font-weight: 700;
    letter-spacing: .18em;
    text-transform: uppercase;
    color: var(--c-primary-d);
}

.hp-hero__name::before {
    content: "";
    width: 34px;
    height: 2px;
    background: currentColor;
}

.hp-hero__title {
    margin: 0 0 1rem;
    font-size: clamp(2rem, 1.3rem + 2.9vw, 3.5rem);
    font-weight: 700;
    letter-spacing: -.04em;
    line-height: 1.08;
    color: var(--c-heading);
}

/* Darker and a touch heavier than body copy elsewhere: this is the
   smallest thing on the band and it sits on a photograph, not a flat
   wash, so it needs the extra weight to hold together. */
.hp-hero__text {
    max-width: 44ch;
    margin-bottom: 2rem;
    font-size: 1.0625rem;
    font-weight: 500;
    line-height: 1.75;
    color: var(--c-heading);
}

/* ---- Hero button ----
   Outline only, no fill: over a photograph a solid block reads as a
   sticker. The pill shape and the brand ink keep it deliberate, and it
   fills with the gold on hover so the click target still confirms
   itself. */
.hp-hero__btn {
    display: inline-flex;
    align-items: center;
    gap: .625rem;
    padding: .875rem 2rem;
    font-size: 1rem;
    font-weight: 650;
    color: var(--c-heading);
    background: transparent;
    border: 2px solid var(--c-heading);
    border-radius: var(--r-pill);
    transition: color .18s var(--ease), background-color .18s var(--ease),
        border-color .18s var(--ease), gap .18s var(--ease);
}

.hp-hero__btn:hover,
.hp-hero__btn:focus-visible {
    color: var(--c-on-primary);
    background: var(--c-primary);
    border-color: var(--c-primary);
    gap: .875rem;
}

/* The halo belongs to the words, not to a bordered button sitting on
   its own background. */
.hp-hero__btn,
.hp-hero__btn:hover {
    text-shadow: none;
}

.hp-hero__btn .bi {
    font-size: .9375rem;
}

/* ---- Hero motion ----
   Bootstrap removes and re-adds these classes as the carousel turns, so
   keying the animations off them replays the sequence on every slide with
   no JavaScript. Each line starts a little further down the stack, which
   reads as the block assembling itself.

   Two selectors, not one. .active alone would be enough, except Bootstrap
   only adds it once the crossfade has *finished* — which leaves the new
   banner sitting wordless for the length of the fade. The incoming slide
   also carries .carousel-item-next.carousel-item-start for the duration of
   that fade, and the outgoing one never does (it keeps .active instead),
   so matching it starts the copy moving with the picture.

   The reduced-motion block at the end of the file shows the copy outright
   and holds the banner still. */

@keyframes hero-rise {
    from {
        opacity: 0;
        transform: translateY(24px);
    }

    to {
        opacity: 1;
        transform: none;
    }
}

/* Slow push in on the photograph. 12s and 6% — enough to feel alive,
   not enough to notice as movement. */
@keyframes hero-pan {
    from {
        transform: scale(1);
    }

    to {
        transform: scale(1.06);
    }
}

.hp-hero__name,
.hp-hero__title,
.hp-hero__text,
.hp-hero__copy .hero-actions {
    opacity: 0;
}

:is(.carousel-item.active, .carousel-item-next.carousel-item-start) :is(.hp-hero__name,
    .hp-hero__title,
    .hp-hero__text,
    .hp-hero__copy .hero-actions) {
    animation: hero-rise .7s var(--ease) both;
}

:is(.carousel-item.active, .carousel-item-next.carousel-item-start) .hp-hero__name {
    animation-delay: .05s;
}

:is(.carousel-item.active, .carousel-item-next.carousel-item-start) .hp-hero__title {
    animation-delay: .17s;
}

:is(.carousel-item.active, .carousel-item-next.carousel-item-start) .hp-hero__text {
    animation-delay: .29s;
}

:is(.carousel-item.active, .carousel-item-next.carousel-item-start) .hp-hero__copy .hero-actions {
    animation-delay: .41s;
}

:is(.carousel-item.active, .carousel-item-next.carousel-item-start) .hp-hero__media img {
    animation: hero-pan 12s linear both;
}

/* ---- Hero dots ----
   Over the picture rather than under it, on the container's left edge so
   they line up with the copy above them. */
.hp-hero .carousel-indicators {
    position: absolute;
    right: auto;
    bottom: 2rem;
    left: 50%;
    z-index: 2;
    justify-content: flex-start;
    width: min(100% - 3rem, var(--container-max));
    margin: 0;
    padding: 0;
    transform: translateX(-50%);
}

.hp-hero .carousel-indicators button {
    width: 26px;
    height: 3px;
    margin-inline: 0 .375rem;
    background: rgba(69, 69, 69, .28);
    border: 0;
    border-radius: var(--r-pill);
    opacity: 1;
    transition: width .25s var(--ease), background-color .25s var(--ease);
}

.hp-hero .carousel-indicators button.active {
    width: 44px;
    background: var(--c-primary);
}

/* ---- 2. Service strip ----
   A flat white band: four promises, a plain glyph each, split by thin
   rules. No tiles, no shadow, no hover — it is a statement of terms, not
   something to click, and anything decorative here competes with the
   hero above it. The band sits on the tinted page so the plate reads. */

.hp-usp {
    padding-block: 3rem;
    background: var(--c-light);
}

.hp-usp__row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-sm);
}

/* Centred inside its own cell, so the four groups sit evenly across the
   band instead of hugging the left of each column. */
.hp-usp__item {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    padding: 1.5rem 1.25rem;
}

/* One hairline between cells, drawn on the left of every cell but the
   first — so it survives any number of features. */
.hp-usp__item+.hp-usp__item::before {
    content: "";
    position: absolute;
    top: 18%;
    bottom: 18%;
    left: 0;
    width: 1px;
    background: var(--c-line);
}

.hp-usp__ic {
    flex: none;
    font-size: 2.125rem;
    line-height: 1;
    color: var(--c-body);
}

.hp-usp__text strong {
    display: block;
    margin-bottom: .125rem;
    font-size: .8125rem;
    font-weight: 700;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--c-heading);
    line-height: 1.35;
}

.hp-usp__text span {
    display: block;
    font-size: .8125rem;
    line-height: 1.4;
    color: var(--c-muted);
}

/* ---- 3. Categories ---- */

/* No card, no plate, no crop: the photograph sits straight on the page
   and the name goes under it. object-fit: contain rather than cover, so a
   product is shown whole the way a catalogue shot should be — cropping it
   into a circle was hiding half of what the tile is advertising. */
/* Columns are capped at 190px rather than left as 1fr. A 1fr track takes
   whatever space is going, so a shop with three categories got three
   enormous pictures instead of three normal ones; with a ceiling on the
   track the tiles keep their size at any count. Packed to the start, so a
   short row begins on the same left edge as the heading above it rather
   than floating in the middle of the page. */
/* 1fr columns, so the row always reaches both edges of the container —
   capping the track instead left a ragged gap on the right once the tiles
   had taken their fixed width. The size of the picture is held steady by
   .hp-cat__shot below rather than by the column, which is what kept a
   short row from ballooning; a wider column now just gives each tile more
   air around it. */
.hp-cats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 2rem 1.25rem;
}

.hp-cat {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.125rem;
    width: 100%;
    text-align: center;
}

/* A flat height, not aspect-ratio. As a flex item this box carries
   min-height: auto, so a portrait photograph could push it taller than the
   ratio asked for and drop that one caption below the rest of the row. A
   stated height cannot be argued with: every picture area is identical, so
   every caption sits on the same line.

   max-width does the same job across the row: the column stretches to fill
   the container, but the picture inside it never grows past 175px square,
   so three categories are drawn at exactly the size six are. */
.hp-cat__shot {
    display: grid;
    place-items: center;
    width: 100%;
    max-width: 175px;
    height: 175px;
    flex: none;
    margin-inline: auto;
    font-size: 2.25rem;
    color: var(--c-line-strong);
}

/* width/height 100%, not max-width/max-height. With only a maximum, an
   image smaller than the box was left at its own size while a large one
   was scaled down to fit — which is why a row of uploads came out at all
   different sizes. Filling the box scales every picture, up or down, to
   the same square; object-fit: contain keeps each one's proportions, so
   nothing is stretched or cropped to get there. */
.hp-cat__shot img {
    width: 100%;
    height: 100%;
    /* A grid item refuses to shrink below its own content by default, which
       let a tall picture push the square taller and knock that tile's
       caption out of line with the rest of the row. */
    min-width: 0;
    min-height: 0;
    object-fit: contain;
    transition: transform .3s var(--ease);
}

.hp-cat:hover .hp-cat__shot img {
    transform: scale(1.06);
}

.hp-cat__name {
    font-size: .9375rem;
    font-weight: 600;
    color: var(--c-heading);
    transition: color .16s var(--ease);
}

.hp-cat:hover .hp-cat__name {
    color: var(--c-primary-d);
}

/* ---- 4. Weekly deal ---- */

/* The band sits on the page's own soft grey rather than a dark slab. The
   separation now comes from the cards being white against it, which is
   quieter and lets the product photographs — shot on white — sit on the
   card without a visible seam. */
.hp-deal {
    padding-block: 4.5rem;
    background: var(--c-light);
}

/* ---- Countdown ----
   Solid dark tiles: the only genuinely urgent thing on a calm band, and
   the one element that should catch the eye before the price does. */
.hp-count {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.hp-count__box {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 66px;
    padding: .625rem .5rem;
    color: var(--c-white);
    background: var(--c-primary);
    border-radius: var(--r-md);
}

.hp-count__box b {
    font-size: 1.375rem;
    font-weight: 700;
    line-height: 1.1;
    color: var(--c-white);
    font-variant-numeric: tabular-nums;
}

.hp-count__box small {
    font-size: .6875rem;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--c-white);
}

.hp-count__date {
    align-self: center;
    font-size: .875rem;
    color: var(--c-body);
}

/* stretch, not start: the card and the list are two halves of one block,
   and letting each take its own height left their bottom edges a few
   pixels apart — close enough to look like a mistake rather than a
   choice. */
.hp-deal__grid {
    display: grid;
    grid-template-columns: 1.35fr 1fr;
    gap: 1.5rem;
    align-items: stretch;
}

.hp-deal__hero {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 2rem;
    padding: 1.75rem;
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
}

.hp-deal__shot {
    position: relative;
    display: block;
    aspect-ratio: 1;
    overflow: hidden;
    background: var(--c-light);
    border-radius: var(--r-md);
}

.hp-deal__shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .35s var(--ease);
}

.hp-deal__hero:hover .hp-deal__shot img {
    transform: scale(1.04);
}

.hp-deal__off {
    position: absolute;
    top: .75rem;
    left: .75rem;
    padding: .25rem .625rem;
    font-size: .75rem;
    font-weight: 700;
    color: var(--c-white);
    background: var(--c-primary);
    border-radius: var(--r-pill);
}

.hp-deal__body {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: .875rem;
}

.hp-deal__name {
    font-size: 1.375rem;
    font-weight: 700;
    line-height: 1.3;
    color: var(--c-heading);
}

.hp-deal__name:hover {
    color: var(--c-primary-d);
}

.hp-deal__text {
    margin: 0;
    font-size: .9375rem;
    color: var(--c-body);
}

.hp-deal__prices {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: .75rem;
}

.hp-deal__now {
    font-size: 1.875rem;
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--c-heading);
    font-variant-numeric: tabular-nums;
}

.hp-deal__was {
    color: var(--c-muted);
    text-decoration: line-through;
}

.hp-deal__save {
    padding: .25rem .75rem;
    font-size: .75rem;
    font-weight: 650;
    color: var(--c-primary-d);
    background: rgba(var(--c-accent-rgb), .16);
    border-radius: var(--r-pill);
}

/* Barely rounded, unlike the pill buttons elsewhere — the deal card is
   the most "shopfront" thing on the page and a square-ish key suits it.
   The lettering is white from --c-on-primary, so nothing to set here. */
.hp-deal__body .btn-primary {
    --bs-btn-border-radius: 3px;
}

/* Red, not gold: this is the one line meant to make someone hurry, and
   the gold is already carrying the price and the badge. */
.hp-deal__stock {
    display: flex;
    align-items: center;
    gap: .375rem;
    margin: 0;
    font-size: .875rem;
    font-weight: 600;
    color: var(--c-danger);
}

/* The runners-up: compact rows, so five of them cost the height one more
   card would. align-self: start keeps the column its natural height rather
   than stretching — the card beside it is the one that grows to match, so
   four rows line up with the card's bottom edge while two simply stack at
   the top instead of being flung to opposite corners. */
.hp-deal__list {
    display: flex;
    flex-direction: column;
    align-self: start;
    gap: .75rem;
}

.hp-mini {
    display: flex;
    align-items: center;
    gap: .875rem;
    padding: .75rem;
    color: var(--c-heading);
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-md);
    transition: border-color .16s var(--ease), box-shadow .16s var(--ease),
        transform .16s var(--ease);
}

.hp-mini:hover {
    color: var(--c-heading);
    border-color: var(--c-primary);
    box-shadow: var(--shadow-sm);
    transform: translateX(3px);
}

.hp-mini__img {
    flex: none;
    width: 58px;
    height: 58px;
    object-fit: cover;
    background: var(--c-light);
    border-radius: var(--r-sm);
}

.hp-mini__body {
    display: flex;
    flex-direction: column;
    gap: .125rem;
    flex: 1;
    min-width: 0;
    /* lets the name ellipsis instead of pushing */
}

.hp-mini__name {
    font-size: .875rem;
    font-weight: 600;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.hp-mini__prices b {
    color: var(--c-heading);
    font-variant-numeric: tabular-nums;
}

.hp-mini__prices s {
    margin-left: .375rem;
    font-size: .8125rem;
    color: var(--c-muted);
}

.hp-mini__off {
    flex: none;
    padding: .1875rem .5rem;
    font-size: .75rem;
    font-weight: 700;
    color: var(--c-white);
    background: var(--c-primary);
    border-radius: var(--r-pill);
}

/* ---- 5. Product tabs ---- */

.hp-tabs {
    display: flex;
    flex-wrap: wrap;
    gap: .375rem;
    margin: 0;
    padding: 0;
    list-style: none;
    border: 0;
}

.hp-tab {
    padding: .5rem 1.125rem;
    font-size: .9rem;
    font-weight: 600;
    color: var(--c-body);
    background: var(--c-light);
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    transition: color .16s var(--ease), background-color .16s var(--ease), border-color .16s var(--ease);
}

.hp-tab:hover {
    color: var(--c-heading);
    border-color: var(--c-line-strong);
}

.hp-tab.active {
    color: var(--c-white);
    background: var(--c-primary);
    border-color: var(--c-primary);
}

.hp-more {
    margin-top: 2.5rem;
    text-align: center;
}

/* ---- 6. Numbers ---- */

.hp-stats {
    padding-block: 3.25rem;
    color: #fff;
    background: var(--c-primary);
}

.hp-stats__row {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    text-align: center;
}

.hp-stat+.hp-stat {
    border-left: 1px solid rgba(255, 255, 255, 0.26);
}

.hp-stat b {
    display: block;
    font-size: clamp(1.75rem, 1.4rem + 1.2vw, 2.5rem);
    font-weight: 700;
    letter-spacing: -.02em;
    color: var(--c-white);
}

.hp-stat span {
    font-size: .875rem;
    color: var(--c-white);
}

/* ---- 7. Testimonials ---- */

/* ---- Slider ----
   A real scroll container rather than a carousel that moves things with
   transforms: swipe, trackpad, shift-wheel and keyboard all work without
   a line of code, and the arrows just call scrollBy(). */
.hp-slider {
    position: relative;
}

.hp-reviews {
    display: flex;
    gap: 1.5rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-behavior: smooth;
    /* Room for the cards' shadow, which a clipped track would shear off. */
    padding: .5rem .25rem 1rem;
    margin-inline: -.25rem;
    /* The arrows are the affordance here; a scrollbar under the cards
       would only add a second one. */
    scrollbar-width: none;
    -ms-overflow-style: none;
}

.hp-reviews::-webkit-scrollbar {
    display: none;
}

/* Three across, with the gaps taken out of the share first so the third
   card ends flush with the container rather than half off the edge. */
.hp-review {
    flex: 0 0 calc((100% - 3rem) / 3);
    scroll-snap-align: start;
    position: relative;
    margin: 0;
    padding: 2rem 1.75rem 1.5rem;
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow-sm);
}

.hp-review__mark {
    position: absolute;
    top: 1rem;
    right: 1.25rem;
    font-size: 2.75rem;
    line-height: 1;
    color: rgba(var(--c-accent-rgb), .22);
}

.hp-review blockquote {
    margin: .875rem 0 1.25rem;
}

.hp-review blockquote strong {
    display: block;
    margin-bottom: .375rem;
    color: var(--c-heading);
}

.hp-review blockquote p {
    margin: 0;
    color: var(--c-body);
}

.hp-review__by {
    display: flex;
    align-items: center;
    gap: .75rem;
    padding-top: 1rem;
    border-top: 1px solid var(--c-line);
}

.hp-review__by img {
    flex: none;
    width: 44px;
    height: 44px;
    object-fit: cover;
    background: var(--c-light);
    border-radius: var(--r-md);
}

.hp-review__by b {
    display: block;
    font-size: .9rem;
    color: var(--c-heading);
}

.hp-review__by a {
    font-size: .8125rem;
    color: var(--c-muted);
}

.hp-review__by a:hover {
    color: var(--c-primary-d);
}

/* ---- Slider arrows ----
   Outside the track on a wide screen, so they never sit over a card.
   app.js sets [hidden] on both when there is nothing to scroll to, and
   .is-off on whichever end has been reached. */
.hp-slider__btn {
    position: absolute;
    top: 50%;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    font-size: 1.125rem;
    color: var(--c-heading);
    background: var(--c-white);
    border: 1px solid var(--c-line);
    border-radius: var(--r-pill);
    box-shadow: var(--shadow-sm);
    transform: translateY(-50%);
    transition: color .16s var(--ease), background-color .16s var(--ease),
                border-color .16s var(--ease), opacity .16s var(--ease);
}

.hp-slider__btn--prev { left: -22px; }
.hp-slider__btn--next { right: -22px; }

.hp-slider__btn:hover {
    color: var(--c-on-primary);
    background: var(--c-primary);
    border-color: var(--c-primary);
}

/* Dimmed rather than removed at the ends: a control that disappears
   makes the row jump, and its absence is harder to read than its
   being greyed out. */
.hp-slider__btn.is-off {
    opacity: .35;
    pointer-events: none;
}

/* ---- Responsive ---- */

@media (max-width: 991.98px) {

    .hp-section,
    .hp-deal {
        padding-block: 3rem;
    }

    /* The scrim has to cover more of a narrower banner for the copy to
       stay readable, and the picture is cropped harder — so hold the
       goods a little further into frame. */
    .hp-hero__slide {
        min-height: clamp(400px, 62vw, 520px);
    }

    .hp-hero__media img {
        object-position: 72% center;
    }

    .hp-hero__media::after {
        background: linear-gradient(90deg,
                rgba(255, 255, 255, .5) 0%,
                rgba(255, 255, 255, .34) 40%,
                rgba(255, 255, 255, .14) 62%,
                rgba(255, 255, 255, 0) 82%);
    }

    .hp-hero__copy {
        max-width: 30rem;
        padding-block: 2.5rem 4rem;
    }

    .hp-hero .carousel-indicators {
        bottom: 1.25rem;
    }

    .hp-usp__row {
        grid-template-columns: repeat(2, 1fr);
    }

    /* Two per row: only the right-hand cell of each pair keeps a divider,
       and the first row gets a rule under it. */
    .hp-usp__item:nth-child(2n+1)::before {
        display: none;
    }

    .hp-usp__item:nth-child(-n+2) {
        border-bottom: 1px solid var(--c-line);
    }

    .hp-deal__grid {
        grid-template-columns: 1fr;
    }

    .hp-stats__row {
        grid-template-columns: repeat(2, 1fr);
        gap: 1.75rem 0;
    }

    .hp-stat:nth-child(odd) {
        border-left: 0;
    }

    /* Two cards a view, and the arrows come inside the track — there is no
       longer room in the margin for them to sit outside it. */
    .hp-review {
        flex-basis: calc((100% - 1.5rem) / 2);
    }

    .hp-slider__btn--prev { left: 2px; }
    .hp-slider__btn--next { right: 2px; }
}

@media (max-width: 767.98px) {
    .hp-head {
        margin-bottom: 1.75rem;
    }

    /* One card a view on a phone, with the next one peeking in so it is
       obvious there is more to the side. */
    .hp-review {
        flex-basis: 85%;
        padding: 1.75rem 1.5rem 1.25rem;
    }

    /* The arrows move below the track rather than disappearing: a thumb
       can swipe, but a visible control is what tells someone they can.
       Over a card this narrow they would cover the words. */
    .hp-slider {
        padding-bottom: 3.75rem;
    }

    .hp-slider__btn {
        top: auto;
        bottom: 0;
        width: 40px;
        height: 40px;
        transform: none;
    }

    .hp-slider__btn--prev {
        left: calc(50% - 46px);
        right: auto;
    }

    .hp-slider__btn--next {
        right: calc(50% - 46px);
        left: auto;
    }

    /* On a phone the banner cannot carry both the picture and the words
       side by side, so the scrim covers it completely and the photograph
       becomes a texture behind the copy rather than the subject of it. */
    /* A phone has no room to put the words beside the picture, so here the
       wash does have to carry the whole frame — but it stays lighter than
       the panel it replaced, and the text-shadow takes up the slack. */
    .hp-hero__media::after {
        background: linear-gradient(90deg,
                rgba(255, 255, 255, .72) 0%,
                rgba(255, 255, 255, .62) 60%,
                rgba(255, 255, 255, .5) 100%);
    }

    .hp-hero__copy {
        max-width: none;
    }

    /* Three across on a phone rather than auto-fitting down to two huge
       ones — these are pictures to scan, not to study. */
    .hp-cats {
        grid-template-columns: repeat(3, 1fr);
        gap: 1.5rem .75rem;
    }

    .hp-cat {
        gap: .75rem;
    }

    .hp-cat__shot {
        max-width: 120px;
        height: 120px;
    }

    .hp-cat__name {
        font-size: .8125rem;
    }

    .hp-rule-head {
        margin-bottom: 1.75rem;
    }

    .hp-deal__hero {
        grid-template-columns: 1fr;
    }

    .hp-count__box {
        min-width: 56px;
        padding: .5rem .375rem;
    }

    .hp-count__box b {
        font-size: 1.125rem;
    }

}

@media (max-width: 575.98px) {
    .hp-usp__row {
        grid-template-columns: 1fr;
    }

    /* Stacked: the vertical hairlines give way to horizontal ones, and the
       rows align left — centring four different text lengths one above the
       other leaves the icons in a ragged column. */
    .hp-usp__item::before {
        display: none;
    }

    .hp-usp__item {
        justify-content: flex-start;
        padding: 1.25rem 1.375rem;
        border-bottom: 1px solid var(--c-line);
    }

    .hp-usp__item:last-child {
        border-bottom: 0;
    }

}

/* =============================================================
   13. SCROLL REVEAL

   Blocks fade and rise as they come into view, on every page.

   The hidden state is gated on .has-reveal, which assets/js/app.js puts
   on <html> only once it has an IntersectionObserver to un-hide things
   with. Without that gate a browser with JavaScript off — or one that
   errored before the script ran — would show a permanently blank page,
   which is a steep price for a decorative effect.
   ============================================================= */

.has-reveal [data-reveal] {
    opacity: 0;
    transform: translateY(26px);
}

/* Only the transition is declared here; the properties above are the
   from-state. Transforms are cheap to animate — no layout is involved —
   so a long list of revealing cards stays smooth. */
[data-reveal] {
    transition: opacity .65s var(--ease), transform .65s var(--ease);
    transition-delay: var(--reveal-delay, 0ms);
    will-change: opacity, transform;
}

.has-reveal [data-reveal].is-in {
    opacity: 1;
    transform: none;
    will-change: auto;
}

/* Motion is a preference. Honour the system setting. */
@media (prefers-reduced-motion: reduce) {

    *,
    *::before,
    *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
        scroll-behavior: auto !important;
    }

    /* The hero copy starts hidden and is revealed by its animation, so
       cutting the duration alone would leave it flashing into place.
       Show it outright instead, and hold the banner still. */
    .hp-hero__name,
    .hp-hero__title,
    .hp-hero__text,
    .hp-hero__copy .hero-actions {
        opacity: 1;
    }

    :is(.carousel-item.active, .carousel-item-next.carousel-item-start) .hp-hero__media img {
        animation: none;
        transform: none;
    }

    /* Same reasoning for the scroll reveal: shorten the transition and the
       blocks would still start invisible and snap in. Show them outright. */
    .has-reveal [data-reveal] {
        opacity: 1;
        transform: none;
    }
}