*,*::before,*::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }
body {
    font-family: var(--font-main);
    color: var(--clr-text);
    background: #f8fafc;
    line-height: 1.6;
    min-height: 100vh;
    overflow-x: hidden;
    /* Sticky-footer column. The page's <main> takes the leftover height
       (see .yalo-main), so the footer sits at the bottom of short pages
       without any element having to guess a viewport subtraction.
       Fixed/absolute children (overlay, toast, drawers, admin bar) are
       out of flow and unaffected. */
    display: flex;
    flex-direction: column;
}
/* Any page-level landmark grows to fill — covers the 404 page and any
   future route without each one re-declaring the rule. */
body > main { flex: 1 0 auto; min-width: 0; }
a { color: inherit; text-decoration: none; }
img { max-width: 100%; display: block; }
button { cursor: pointer; font-family: inherit; }
input, select, textarea { font-family: inherit; }
.container { max-width: 1280px; margin: 0 auto; padding: 0 16px; }
@media (min-width: 640px)  { .container { padding: 0 24px; } }
@media (min-width: 1024px) { .container { padding: 0 32px; } }

/* ── Utility ─────────────────────────── */
.flex { display: flex; }
.hidden { display: none !important; }

/* ── Buttons ─────────────────────────── */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 20px;
    border-radius: var(--btn-radius);
    font-weight: 700;
    font-size: 14px;
    border: none;
    cursor: pointer;
    transition: var(--transition);
    white-space: nowrap;
    text-decoration: none;
    line-height: 1;
}
.btn-primary {
    background: var(--clr-primary);
    color: #fff;
    box-shadow: 0 4px 14px rgba(var(--clr-primary-rgb),.35);
}
.btn-primary:hover { filter: brightness(1.08); transform: translateY(-1px); }
/* ── Secondary CTA (v5.81 Theme System) ───────────────────────
   Used for "Add to Cart" actions, the mobile bottom-nav cart
   button, and the filter/options toggle — see render_core_styles
   token block for --clr-secondary-*. */
.btn-secondary {
    background: var(--clr-secondary);
    color: #fff;
    box-shadow: 0 4px 14px rgba(var(--clr-secondary-rgb),.35);
}
.btn-secondary:hover { filter: brightness(1.08); transform: translateY(-1px); }
.btn-outline {
    background: transparent;
    color: var(--clr-primary);
    border: 2px solid var(--clr-primary);
}
.btn-outline:hover { background: var(--clr-primary-50); }
.btn-ghost { background: transparent; color: var(--clr-text-2); }
.btn-ghost:hover { background: var(--clr-surface-2); }
.btn-sm { padding: 7px 14px; font-size: 13px; border-radius: var(--btn-radius); }
.btn-lg { padding: 14px 28px; font-size: 15px; border-radius: var(--btn-radius); }
.btn:disabled { opacity: .6; cursor: not-allowed; pointer-events: none; }

/* ── Badge ──────────────────────────── */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 10px;
    border-radius: 999px;
    font-size: 11px;
    font-weight: 700;
    white-space: nowrap;
}
.badge-primary { background: var(--clr-primary-100); color: var(--clr-primary); }

/* ── Card ──────────────────────────── */
.card {
    background: var(--clr-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow);
    overflow: hidden;
}

/* ── Skeleton Loading ───────────────── */
@keyframes skeleton-pulse {
    0%, 100% { opacity: 1; }
    50%       { opacity: .5; }
}
.skeleton {
    background: linear-gradient(90deg, #f1f5f9 25%, #e2e8f0 50%, #f1f5f9 75%);
    background-size: 200% 100%;
    animation: skeleton-slide 1.4s ease infinite;
    border-radius: var(--radius-sm);
}
@keyframes skeleton-slide {
    0%   { background-position: 200% 0; }
    100% { background-position: -200% 0; }
}

/* ── Toast (v5.83 — Modern Notification System) ────────────────
   Top-center, professional card style with icon badge per type,
   slide-down entrance / fade+slide-up exit. No emoji — icons only
   via Font Awesome. Supports success / error / warning / info. */
#yalo-toast {
    position: fixed;
    /* v5.83 fix: on mobile the toast must appear BELOW the sticky
       mobile header (and topbar/admin quick-bar, if present), not
       above/over it. --mob-sticky-top already accounts for the
       topbar + admin quick-bar, and --mob-hdr-h is the mobile
       header's own height — stacking the toast right under that
       point keeps it fully visible and never overlaps the header. */
    top: calc(var(--mob-sticky-top) + var(--mob-hdr-h) + 10px);
    /* v5.82 fix: centering must use a PHYSICAL offset (left), not a
       logical one (inset-inline-start). In RTL, inset-inline-start
       resolves to "right: 50%" while translateX(-50%) still shifts
       physically left — pushing the whole toast off-screen on the
       left edge on mobile. "left: 50%" + translateX(-50%) is the
       standard, direction-agnostic centering trick and now centers
       the toast correctly in both LTR and RTL. */
    left: 50%;
    transform: translateX(-50%);
    z-index: 9999;
    display: flex;
    flex-direction: column;
    gap: 10px;
    align-items: stretch;
    pointer-events: none;
    width: calc(100% - 24px);
    max-width: 380px;
}
@media (min-width: 768px) {
    /* Desktop: no mobile header in the stack — sit just under the
       fixed/sticky desktop header instead. */
    #yalo-toast { top: calc(var(--header-h) + 12px); }
}
.toast-item {
    display: flex;
    align-items: center;
    gap: 12px;
    width: 100%;
    background: var(--clr-surface);
    color: var(--clr-text);
    padding: 13px 16px;
    border-radius: var(--radius-card);
    font-size: 13.5px;
    font-weight: 700;
    line-height: 1.45;
    box-shadow: var(--shadow-xl);
    border: 1px solid var(--clr-border);
    animation: toast-slide-down .35s cubic-bezier(.34,1.56,.64,1) forwards;
    pointer-events: auto;
}
.toast-item .toast-icon {
    flex-shrink: 0;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 14px;
}
.toast-item .toast-msg { flex: 1; min-width: 0; }
.toast-item.toast-success .toast-icon { background: #dcfce7; color: #16a34a; }
.toast-item.toast-error   .toast-icon { background: #fee2e2; color: #dc2626; }
.toast-item.toast-warning .toast-icon { background: #fef3c7; color: #d97706; }
.toast-item.toast-info    .toast-icon { background: #dbeafe; color: #2563eb; }
.toast-item.toast-out { animation: toast-fade-up .28s ease forwards; }
@keyframes toast-slide-down {
    from { opacity: 0; transform: translateY(-22px) scale(.96); }
    to   { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes toast-fade-up {
    from { opacity: 1; transform: translateY(0); }
    to   { opacity: 0; transform: translateY(-14px); }
}
body.dark-mode .toast-item { background: var(--clr-surface); border-color: var(--clr-border); box-shadow: var(--shadow-xl), 0 0 0 1px rgba(255,255,255,.04); }

/* ── Overlay ────────────────────────── */
.yalo-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,.45);
    backdrop-filter: blur(var(--yalo-overlay-blur));
    -webkit-backdrop-filter: blur(var(--yalo-overlay-blur));
    z-index: 400;
    opacity: 0;
    pointer-events: none;
    transition: opacity .3s;
}
.yalo-overlay.open { opacity: 1; pointer-events: auto; }

/* ── Drawer (Mobile Sidebar) ─────────── */
.yalo-drawer {
    position: fixed;
    top: 0;
    inset-inline-start: 0;
    width: var(--yalo-drawer-w);
    height: 100%;
    background: var(--clr-surface);
    z-index: 500;
    transform: translateX(-100%);
    transition: var(--yalo-drawer-transition);
    overflow-y: auto;
    overscroll-behavior: contain;
    display: flex;
    flex-direction: column;
}
[dir="rtl"] .yalo-drawer { transform: translateX(100%); }
.yalo-drawer.open { transform: translateX(0); }

/* ── Page scroll lock ───────────────── */
body.scroll-lock { overflow: hidden; }

/* ── Pagination ─────────────────────── */
.pagination {
    display: flex;
    gap: 6px;
    align-items: center;
    justify-content: center;
    flex-wrap: wrap;
}
.page-btn {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-sm);
    border: 1.5px solid var(--clr-border);
    background: var(--clr-surface);
    font-weight: 700;
    font-size: 14px;
    color: var(--clr-text-2);
    transition: var(--transition);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    text-decoration: none;
}
.page-btn:hover, .page-btn.active {
    background: var(--clr-primary);
    color: #fff;
    border-color: var(--clr-primary);
}
.page-btn:disabled { opacity: .4; cursor: default; }

/* ── Forms ──────────────────────────── */
.form-group { margin-bottom: 16px; }
.form-label {
    display: block;
    font-size: 13px;
    font-weight: 700;
    color: var(--clr-text-2);
    margin-bottom: 6px;
}
.form-label .req { color: #ef4444; margin-inline-start: 2px; }
.form-control {
    width: 100%;
    padding: 11px 18px;
    border: 1.5px solid var(--clr-border);
    border-radius: var(--field-radius);
    font-size: 14px;
    font-family: var(--font-main);
    color: var(--clr-text);
    background: var(--clr-surface);
    transition: border-color .2s, box-shadow .2s;
    outline: none;
    appearance: none;
}
.form-control:focus {
    border-color: var(--clr-primary);
    box-shadow: 0 0 0 3px rgba(var(--clr-primary-rgb), .12);
}
.form-control.error { border-color: #ef4444; }
.form-error { font-size: 12px; color: #ef4444; margin-top: 4px; display: none; }
.form-control.error + .form-error { display: block; }
select.form-control {
    cursor: pointer;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' stroke='%2394a3b8' stroke-width='2' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: left 14px center;
    padding-left: 38px;
}
[dir="rtl"] select.form-control {
    background-position: right 14px center;
    padding-left: 14px;
    padding-right: 38px;
}

/* ── Range input ─────────────────────── */
.range-input { -webkit-appearance: none; width: 100%; height: 4px; border-radius: 999px; background: var(--clr-border); outline: none; }
.range-input::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; border-radius: 50%; background: var(--clr-primary); cursor: pointer; }

/* ── Main wrapper (accounts for header + bottomnav) ─────── */
/* The footer is pushed to the bottom of short pages by the body flex
   column declared above, NOT by a min-height on this element.
   min-height guessed the free space from a fixed subtraction, so it
   over-reserved on every page that also renders a topbar or a
   breadcrumb strip, and — worse — any page that opened <main> twice
   reserved a SECOND viewport of blank space between the two. `flex:1`
   measures the real leftover height instead of assuming it, which is
   exact for one main and still gap-free for any number of them. */
.yalo-main {
    flex: 1 0 auto;
    min-width: 0;
    /* The real occupied height, not the nav's own — see --bottomnav-total
       in views/layout.php. Using --bottomnav-h alone left the last 16px of
       clearance eaten by the home-indicator inset on notched phones. */
    padding-bottom: calc(var(--bottomnav-total, var(--bottomnav-h)) + 16px);
}
@media (min-width: 768px) {
    .yalo-main { padding-bottom: 0; }
}

/* ── Product Grid ───────────────────── */
.products-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 12px;
}
@media (min-width: 640px)  { .products-grid { grid-template-columns: repeat(3, 1fr); gap: 16px; } }
@media (min-width: 1024px) { .products-grid { grid-template-columns: repeat(4, 1fr); gap: 20px; } }
@media (min-width: 1280px) { .products-grid { grid-template-columns: repeat(5, 1fr); } }

/* ── Product Card ───────────────────── */
.product-card {
    background: var(--clr-surface);
    border-radius: var(--radius-lg);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    border: 1.5px solid rgba(0,0,0,.2);
    transition: box-shadow var(--transition), border-color var(--transition), transform var(--transition);
    position: relative;
    display: flex;
    flex-direction: column;
}
.product-card:hover {
    box-shadow: var(--shadow-lg);
    border-color: var(--clr-primary-200);
    transform: translateY(-3px);
}
body.dark-mode .product-card {
    border-color: rgba(255,255,255,.08);
}
body.dark-mode .product-card:hover {
    border-color: var(--clr-primary-200);
}
.product-card__img {
    position: relative;
    overflow: hidden;
    aspect-ratio: 1/1;
    background: var(--clr-surface-2);
}
.product-card__img img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform .4s cubic-bezier(.4,0,.2,1);
}
.product-card:hover .product-card__img img { transform: scale(1.06); }
.product-card__wishlist {
    position: absolute;
    top: 10px;
    inset-inline-end: 10px;
    z-index: 2;
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: rgba(255,255,255,.9);
    backdrop-filter: blur(8px);
    border: none;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: var(--transition);
    color: var(--clr-text-3);
    font-size: 13px;
}
.product-card__wishlist:hover,
.product-card__wishlist.active { color: #ef4444; background: #fff; }
.product-card__body {
    padding: 12px;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.product-card__name {
    font-size: 13.5px;
    font-weight: 700;
    color: var(--clr-text);
    line-height: 1.4;
    /* Always occupy exactly 2 lines — short titles leave blank below */
    min-height: calc(1.4em * 2);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* ── Product card: category overlay chip — image inline-start top corner ── */
/* Uses CSS logical properties → RTL/LTR unified, zero JS direction detection */
.pc-cat-overlay {
    position: absolute;
    top: 0;
    inset-inline-start: 0;
    z-index: 3;
    pointer-events: none;
    line-height: 0;
    font-size: 0;
}
.pc-cat-chip {
    display: block;
    padding: 5px 10px;
    background: var(--clr-primary);
    color: #fff;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .02em;
    white-space: nowrap;
    line-height: 1;
    pointer-events: auto;
    text-decoration: none;
    /* Sharp top-inline-start corner, rounded opposite corner — logical radius */
    border-end-end-radius: var(--radius);
}
.pc-cat-chip--link {
    cursor: pointer;
    transition: background .15s ease, opacity .15s ease;
}
.pc-cat-chip--link:hover {
    background: var(--clr-primary-700, #4338ca);
    opacity: .95;
}

/* ══════════════════════════════════════════════════════════════
   YALOSHOP — Unified Badge System v6.90
   Shared by product cards (pc-badge-slide-*) and PDP (same markup).

   Shape: border-end-end-radius + border-start-start-radius
   → bottom-inline-end corner rounded + top-inline-start corner rounded
   → RTL/LTR logical, zero JS direction detection.

   Slide behaviour (2+ badges) — explicit JS-driven state machine,
   NOT percentage-based @keyframes (that approach broke for 3+ badges
   — see v6.90 changelog). Exactly one of three classes is present on
   each item, toggled by JS in render_global_js → initBadgeSlideshow():
   • .is-waiting → parked above the slot, invisible, no transition
   • .is-active  → slid into place, fully visible (the only state
                   considered "shown")
   • .is-leaving → sliding further down, fading out
   The JS engine only allows the next badge to enter AFTER the current
   badge's exit transition reports transitionend — by construction two
   badges can never both be opaque at once, for any badge count.

   Shimmer: a swept highlight over EVERY badge pill, applied uniformly
   regardless of badge type (promo or discount) — one consistent
   visual language. Applied via ::after pseudo-element, zero DOM
   addition.
══════════════════════════════════════════════════════════════ */

/* ── Shared badge pill shape ──────────────────────────────── */
.yalo-badge-pill {
    display: block;
    padding: 6px 13px;
    font-size: 11px;
    font-weight: 800;
    white-space: nowrap;
    letter-spacing: .03em;
    line-height: 1.1;
    position: relative;
    overflow: hidden;
    /* RTL-safe dual-corner rounding:
       top-inline-start = flat (flush to card edge)
       bottom-inline-end = rounded (decorative)
       top-inline-end   = flat
       bottom-inline-start = rounded (decorative) */
    border-start-start-radius: var(--radius);
    border-end-end-radius: var(--radius);
    border-start-end-radius: 0;
    border-end-start-radius: 0;
}

/* ── Shimmer sweep — ::after on EVERY badge pill, unified ─── */
.yalo-badge-pill::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
        105deg,
        transparent 35%,
        rgba(255,255,255,.45) 50%,
        transparent 65%
    );
    transform: translateX(-120%);
    animation: yalo-badge-shimmer 2.8s ease-in-out infinite;
    pointer-events: none;
}
[dir="rtl"] .yalo-badge-pill::after {
    transform: translateX(120%);
    animation: yalo-badge-shimmer-rtl 2.8s ease-in-out infinite;
}
@keyframes yalo-badge-shimmer {
    0%, 30% { transform: translateX(-120%); }
    55%     { transform: translateX(120%);  }
    100%    { transform: translateX(120%);  }
}
@keyframes yalo-badge-shimmer-rtl {
    0%, 30% { transform: translateX(120%);  }
    55%     { transform: translateX(-120%); }
    100%    { transform: translateX(-120%); }
}

/* ── Card badge slideshow wrapper ────────────────────────── */
.pc-badge-slide-wrap {
    position: relative;
    height: 27px;
    overflow: hidden;           /* clips the vertical slide motion */
    display: flex;
    align-items: center;
}

/* Empty placeholder keeps layout height when no badges */
.pc-badge-slide-empty {
    display: block;
    height: 27px;
}

/* Each slide item: hidden by default, parked above the slot.
   No transition on the base rule — the transition only applies once
   .js-ready is present (see .is-active / .is-leaving below), so a
   slow first paint can never show a stray slide animation. */
.pc-badge-slide-item {
    position: absolute;
    inset-inline-start: 0;
    opacity: 0;
    transform: translateY(-130%);
    pointer-events: none;
    will-change: transform, opacity;
    /* Inherit pill shape from .yalo-badge-pill applied in PHP */
}

/* Single badge — always fully visible, shimmer active, no motion */
.pc-badge-slide-wrap:not([data-badge-count]) .pc-badge-slide-item {
    position: relative;
    opacity: 1;
    transform: none;
    pointer-events: auto;
}

/* Pre-init fallback: first badge (.is-active) shows before JS fires —
   plain and static, no slide offset, so there is never a "popping
   in from above" flash on first paint. */
.pc-badge-slide-wrap[data-badge-count] .pc-badge-slide-item.is-active {
    opacity: 1;
    transform: none;
}

/* ── State machine (after .js-ready, driven entirely by JS) ───────
   Exactly one of these three classes is present on each item at any
   given moment — the JS engine never lets two items be in a visible
   (opacity > 0) state at the same time, by construction:

   .is-waiting → parked above the slot, instant (no transition) —
                 nothing to see, so no animation is wasted on it.
   .is-active  → slid into place, fully visible. Entering this state
                 FROM .is-waiting is what produces the slide-down-in.
   .is-leaving → slides further down and fades out. Entering this
                 state FROM .is-active is what produces the
                 slide-down-out. Only after this transition completes
                 (transitionend, JS-driven) is the NEXT badge allowed
                 to become .is-active — this is the actual no-overlap
                 guarantee, enforced in JS rather than in CSS timing.
────────────────────────────────────────────────────────────────── */
.pc-badge-slide-wrap.js-ready .pc-badge-slide-item.is-waiting {
    transition: none;
    opacity: 0;
    transform: translateY(-130%);
}
.pc-badge-slide-wrap.js-ready .pc-badge-slide-item.is-active {
    transition: transform .42s cubic-bezier(.22,.8,.36,1),
                opacity   .42s cubic-bezier(.22,.8,.36,1);
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}
.pc-badge-slide-wrap.js-ready .pc-badge-slide-item.is-leaving {
    transition: transform .42s cubic-bezier(.5,0,.75,.4),
                opacity   .42s cubic-bezier(.5,0,.75,.4);
    opacity: 0;
    transform: translateY(130%);
    pointer-events: none;
}

/* Reduced-motion: instant switch, no slide, no shimmer — the JS
   engine still drives the same state machine, but with the
   transitions stripped out below it just snaps between states. */
@media (prefers-reduced-motion: reduce) {
    .yalo-badge-pill::after {
        display: none;
    }
    .pc-badge-slide-wrap.js-ready .pc-badge-slide-item.is-active,
    .pc-badge-slide-wrap.js-ready .pc-badge-slide-item.is-leaving {
        transition: none;
    }
}
/* Price text direction is NOT forced here. format_price() (core/helpers.php)
   already embeds the correct bidi control marks (LRM around the digits) so
   the currency symbol lands in the right place in every locale. Forcing
   direction:ltr on this element overrides that and misplaces "دج" in RTL
   (ar) layouts — see .pdp-price-current in views/product.php, which is the
   single source of truth this card now matches. */
.product-card__price {
    font-size: 15px;
    font-weight: 900;
    color: var(--clr-price);
}
.product-card__compare {
    font-size: 12px;
    color: var(--clr-text-3);
    text-decoration: line-through;
}
.product-card__footer {
    padding: 0 12px 12px;
}
.product-card__actions {
    display: flex;
    gap: 6px;
}
.product-card__actions .btn { flex: 1; font-size: 12.5px; padding: 8px 10px; }
.product-card__stock {
    font-size: 11px;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: 4px;
}
.product-card__stock.out { color: #dc2626; }
.product-card__stock::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

/* ── Pricing row — margin-top:auto anchors it after badges ── */
.product-card__pricing {
    display: flex;
    align-items: center;
    gap: 6px;
    flex-wrap: wrap;
    margin-top: auto;
}

/* ── RFQ price microcopy — shown in catalog mode where a price would be.
   Real text (never a hidden price), theme-aware, so the card communicates
   "request pricing" clearly to shoppers AND search engines. ── */
.product-card__price-request {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    font-size: 12.5px;
    font-weight: 700;
    color: var(--clr-primary);
    background: var(--clr-primary-50, color-mix(in srgb, var(--clr-primary) 10%, transparent));
    padding: 3px 10px;
    border-radius: 999px;
    line-height: 1.5;
}
.product-card__price-request i { font-size: 10.5px; opacity: .85; }
body.dark-mode .product-card__price-request {
    background: color-mix(in srgb, var(--clr-primary) 22%, transparent);
    color: #fff;
}

/* ── Discount chip — used in PDP promo-badges area (cards use slideshow) ── */
.pc-discount-chip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    padding: 2px 8px;
    background: #cb0000;
    color: #fff;
    font-size: 11px;
    font-weight: 800;
    letter-spacing: .02em;
    white-space: nowrap;
    line-height: 1.5;
}

/* ── Card rating row (v7.97) — star + numeric average, sits on its own
   line directly ABOVE the price so the pricing row keeps its
   margin-top:auto anchoring and nothing else on the card shifts. ── */
.product-card__rating {
    display: flex;
    align-items: center;
    gap: 6px;
    margin-top: auto;
    min-height: 15px;
}
.product-card__rating + .product-card__pricing { margin-top: 4px; }
.product-card__rating-count {
    font-size: 11px;
    font-weight: 700;
    color: var(--clr-text-3);
    font-variant-numeric: tabular-nums;
}

/* ══════════════════════════════════════════════════════════════
   Dark Mode — theme tokens
   ──────────────────────────────────────────────────────────────
   Moved here from header.css in v9.19. These are the tokens for the
   ENTIRE dark theme, so they have to be declared in the bundle that
   every page loads, not in the one that only pages with a site
   header load. A page without the header (the Ads Campaign landing
   page) previously got `body.dark-mode` from the anti-FOUC script
   and no palette to satisfy it.

   Safe to move: `body.dark-mode` (0,0,1,1) outranks the `body`
   rule at the top of this file (0,0,0,1), so the outcome does not
   depend on which comes first — and custom properties resolve where
   they are USED, never where they are declared, so nothing that
   consumes them can shift either.
   ══════════════════════════════════════════════════════════════ */
body.dark-mode {
    /* v9.07 — CENTRAL fix for native-widget contrast. Everything the
       operating system paints itself (the open list of a <select>, date
       and colour pickers, scrollbars, form autofill) ignores our CSS and
       defaults to a LIGHT palette, producing white-on-white popups over
       the dark theme. Declaring the colour scheme once here makes every
       native control on every page follow the theme — the root fix, so
       no individual control has to remember to opt in. */
    color-scheme: dark;
    /* A shadow tinted with slate is invisible on a slate surface — dark
       mode needs a genuinely black, slightly stronger one to read at all. */
    --cta-hover-shadow: 0 2px 10px rgba(0, 0, 0, .45);
    --clr-surface:   #1e293b;
    --clr-surface-2: #0f172a;
    --clr-border:    #334155;
    --clr-text:      #f1f5f9;
    --clr-text-2:    #94a3b8;
    --clr-text-3:    #64748b;
    background:      #0f172a;
}
