/* ================================================================
   PRODUCT CARD — shared component
   Used on the shop grid, homepage "Just Landed", related products,
   recently-viewed, and cart cross-sell. Loaded sitewide (small,
   cheap, and needed in enough places that conditional-loading it
   per page type isn't worth the bookkeeping).
   ================================================================ */

.product-card {
    background: var(--color-surface);
    border-radius: var(--r-xl);
    overflow: hidden;
    box-shadow: var(--shadow-sm);
    transition: box-shadow var(--t-base), transform var(--t-spring);
    display: flex;
    flex-direction: column;
}

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

.product-card:active {
    transform: translateY(0) scale(0.98);
    transition: transform var(--t-press);
}

/* ── Media (photo + badge + wishlist) ───────────────────────────── */

.product-card__media {
    position: relative;
}

.product-card__img-link {
    display: block;
}

.product-card__img-wrap {
    position: relative;
    aspect-ratio: 1 / 1;
    overflow: hidden;
    /* Plain white, not the usual light-gray surface — the background-
       removal pass leaves a faint white-ish fringe around some cutouts
       (most source photos were shot on a white backdrop to begin with),
       and that fringe all but disappears against true white instead of
       showing as a visible halo against gray. */
    background: var(--color-white);
}

.product-card__img {
    width: 100%;
    height: 100%;
    /* cover, not contain — source photos come from many different
       suppliers with wildly inconsistent margins/whitespace around the
       watch, so "shrink to fit the whole photo" made the watch itself
       render at a different apparent size on every card. Cropping to
       fill the square instead means every photo gets center-cropped by
       the same rule, which reads as consistent even though the crop
       amount technically differs per source photo — the watch face ends
       up close to frame most of the time since it's usually the visual
       center of the shot. */
    object-fit: cover;
    object-position: center;
    padding: var(--sp-2);
    transition: transform var(--t-slow);
}

.product-card:hover .product-card__img { transform: scale(1.04); }

.product-card__badge {
    position: absolute;
    top: var(--sp-3);
    left: var(--sp-3);
    background: var(--color-navy);
    color: var(--color-white);
    font-size: var(--text-xs);
    font-weight: var(--fw-bold);
    padding: 3px var(--sp-2);
    border-radius: var(--r-md);
    line-height: 1.4;
    pointer-events: none;
}

/* Wishlist + compare share one "icon chip" recipe — same size, same
   translucent white circle over the photo, same stroke icon — so the
   two read as one consistent pair instead of two different-looking
   buttons that happen to be near each other. */
.product-card__wishlist,
.product-card__compare {
    /* !important on the wishlist rule — the wcboost-wishlist plugin
       ships its own base CSS for .wcboost-wishlist-button (padding/
       border/background), which this button also carries so the
       plugin's existing sitewide add/remove JS picks it up. Same
       defensive pattern already used in product-single.css's reskin
       of this plugin's single-product button. */
    position: absolute !important;
    right: var(--sp-3) !important;
    left: auto !important;
    bottom: auto !important;
    width: 32px !important;
    height: 32px !important;
    padding: 0 !important;
    margin: 0 !important;
    border: none !important;
    display: flex !important;
    align-items: center;
    justify-content: center;
    background: rgba(255, 255, 255, 0.85) !important;
    backdrop-filter: blur(4px);
    color: var(--color-navy) !important;
    border-radius: var(--r-full) !important;
    box-shadow: var(--shadow-sm);
    cursor: pointer;
    text-decoration: none !important;
    transition: color var(--t-fast), background var(--t-fast), border-color var(--t-fast), transform var(--t-spring);
}

.product-card__wishlist { top: var(--sp-3) !important; }
.product-card__compare  { top: calc(var(--sp-3) + 32px + var(--sp-2)) !important; }

/* The wcboost-wishlist plugin's own CSS gives its icon wrapper a fixed,
   non-square 16x24px block box, which is what was pushing the heart
   off-center — and something in the plugin's own rules (never fully
   pinned down — plausibly its own float/inline-block behavior on this
   span winning a cascade tie) kept fighting flex centering even after
   squaring the box off. Absolute-positioning the icon dead center of
   the button sidesteps that fight entirely instead of continuing to
   patch the flex approach. */
.product-card__wishlist .wcboost-wishlist-button__icon {
    position: absolute !important;
    top: 50% !important;
    left: 50% !important;
    transform: translate(-50%, -50%) !important;
    width: 16px !important;
    height: 16px !important;
    display: block !important;
}

.product-card__wishlist svg,
.product-card__compare svg {
    /* display:block — an inline (the SVG default) reserves baseline
       descender space below the glyph even though there's no text,
       which is what was actually pushing the heart down/off-center
       within its own already-centered wrapper. */
    display: block;
    width: 16px;
    height: 16px;
}

/* Stroke-only at rest — including the plugin's OWN swapped-in icon.
   wcboost-wishlist replaces this button's inner SVG with its own icon
   (a different shape, no fill="none" of its own) as soon as its JS
   runs — left alone, that icon defaults to a solid black heart per
   plain SVG fill behavior. Forcing fill/stroke here on the *svg
   element itself* (not just our own authored markup) makes whatever
   path the plugin swaps in render as the same outline weight as
   compare, so the two read as one pair regardless of which one
   currently owns the DOM. The .added heart below is the one
   deliberate exception — see that rule. */
.product-card__wishlist svg,
.product-card__wishlist.loading svg,
.product-card__compare svg {
    fill: none !important;
    stroke: currentColor !important;
    stroke-width: 2px !important;
}

.product-card__wishlist:hover,
.product-card__compare:hover {
    color: var(--color-blue) !important;
    transform: scale(1.08);
}

.product-card__wishlist:active,
.product-card__compare:active { transform: scale(0.94); }

.product-card__wishlist.loading { color: var(--color-text-muted) !important; }

/* Added/confirmed — an unmistakable, universally-understood "yes,
   saved" signal: the heart genuinely fills solid red (same red used
   for out-of-stock/price-warning elsewhere), not just an outline
   color change like everything else on this card. */
.product-card__wishlist.added {
    color: #c0392b !important;
    background: var(--color-white) !important;
}

.product-card__wishlist.added svg {
    fill: currentColor !important;
    stroke: currentColor !important;
}

/* Same idea for compare — a solid dark fill is the clearest "this one
   is selected" signal, distinct from its own outline resting state.
   !important to beat the shared resting-state rule above, which sets
   its own !important background for the translucent-white chip look. */
.product-card__compare.is-added {
    color: var(--color-white) !important;
    background: var(--color-navy) !important;
}

.product-card__compare.is-shake {
    animation: compare-shake 400ms var(--ease-out);
}

@keyframes compare-shake {
    0%, 100% { transform: translateX(0); }
    25%      { transform: translateX(-3px); }
    75%      { transform: translateX(3px); }
}

/* ── Body (brand + model + rating) ──────────────────────────────── */

.product-card__body {
    padding: var(--sp-3) var(--sp-4) var(--sp-4);
    display: flex;
    flex-direction: column;
    gap: var(--sp-2);
    flex: 1;
    color: inherit;
}

/* Cards that carry a separate .product-card__footer (price + cart button
   live there instead) don't need the body's own bottom padding — the
   footer supplies its own. Kept as a modifier rather than changing
   .product-card__body itself, which several other renderers (homepage
   rails, related products, recently-viewed, cart cross-sell) still use
   without a footer, relying on this padding for their own bottom gutter. */
.product-card--has-footer .product-card__body {
    padding-bottom: 0;
}

.product-card__brand {
    font-size: var(--text-xs);
    font-weight: var(--fw-bold);
    color: var(--color-blue);
    text-transform: uppercase;
    letter-spacing: 0.06em;
}

.product-card__name {
    font-size: var(--text-sm);
    font-weight: var(--fw-semi);
    color: var(--color-navy);
    line-height: var(--lh-tight);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* Model number leads on the shop grid card — bigger and bolder than the
   shared .product-card__name default used elsewhere. */
.product-card__name--model {
    font-size: var(--text-base);
    font-weight: var(--fw-bold);
}

.product-card__rating {
    display: flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-xs);
    color: var(--color-text-muted);
}

.product-card__rating .star-rating {
    font-size: 0.75rem;
    color: var(--color-blue);
}

/* ── Footer (price + quick add-to-cart) ─────────────────────────── */

.product-card__footer {
    padding: 0 var(--sp-4) var(--sp-4);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-2);
}

/* Stacked — the crossed-out regular price sits small above the real
   (sale) price, both left-aligned, rather than side by side. Two prices
   inline used to fight the cart button for width on a narrow phone card,
   where the button would get squeezed against/past the card's edge;
   stacking keeps the whole price block to one price's width instead of
   two, which is what actually freed up the room. */
.product-card__price {
    margin-top: auto;
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
}

.product-card__price--current,
.product-card__price--sale,
.product-card__price--regular {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.product-card__price--current {
    font-size: var(--text-md);
    font-weight: var(--fw-bold);
    color: var(--color-navy);
}

.product-card__price--sale {
    font-size: var(--text-md);
    font-weight: var(--fw-bold);
    color: var(--color-blue-dark);
    order: 2;
}

.product-card__price--regular {
    font-size: var(--text-xs);
    color: var(--color-gray-500);
    text-decoration: line-through;
    order: 1;
}

/* Solid dark fill, deliberately the odd one out — wishlist/compare stay
   outline (a toggle you can take back), this is the one-tap purchase
   action, and keeping it visually heavier is what makes it read as
   the card's actual primary action instead of a third peer icon. */
.product-card__cart-btn {
    flex-shrink: 0;
    width: 38px;
    height: 38px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--color-navy);
    color: var(--color-white);
    border-radius: var(--r-full);
    transition: background var(--t-fast), transform var(--t-spring), color var(--t-fast);
}

@media (max-width: 479px) {
    .product-card__price--current,
    .product-card__price--sale {
        font-size: var(--text-xs);
    }

    .product-card__cart-btn {
        width: 32px;
        height: 32px;
    }
}

.product-card__cart-btn:hover { background: var(--color-blue); }
.product-card__cart-btn:active { transform: scale(0.92); }

.product-card__cart-btn-icon {
    display: flex;
    align-items: center;
    justify-content: center;
}

.product-card__cart-btn.is-loading {
    background: var(--color-blue-light);
    pointer-events: none;
}

.product-card__cart-btn.is-loading .product-card__cart-btn-icon {
    animation: cart-btn-pulse 0.9s ease-in-out infinite;
}

.product-card__cart-btn.is-added {
    background: var(--color-blue);
    animation: cart-btn-pop var(--t-spring);
}

@keyframes cart-btn-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.4; }
}

@keyframes cart-btn-pop {
    0%   { transform: scale(0.85); }
    60%  { transform: scale(1.12); }
    100% { transform: scale(1); }
}

/* Variable / out-of-stock products route to the product page instead of
   one-tap adding — same footprint, an outline instead of a solid fill so
   it reads as "go choose" rather than "done in one tap". */
.product-card__cart-btn--link {
    background: var(--color-surface);
    color: var(--color-navy);
    border: 1.5px solid var(--color-border);
}

.product-card__cart-btn--link:hover {
    background: var(--color-navy);
    color: var(--color-white);
    border-color: var(--color-navy);
}

/* ── Skeleton loading (shared shimmer) ──────────────────────────── */

@keyframes shimmer {
    0%   { background-position: -200% center; }
    100% { background-position:  200% center; }
}

.skeleton-shimmer {
    background: linear-gradient(
        90deg,
        var(--color-gray-200) 25%,
        var(--color-gray-100) 50%,
        var(--color-gray-200) 75%
    );
    background-size: 200% 100%;
    animation: shimmer 1.4s ease-in-out infinite;
}

.product-card--skeleton {
    pointer-events: none;
}

.product-card--skeleton .product-card__img-wrap {
    aspect-ratio: 1 / 1;
}

.skeleton-line {
    height: 12px;
    border-radius: var(--r-sm);
    background: linear-gradient(
        90deg,
        var(--color-gray-200) 25%,
        var(--color-gray-100) 50%,
        var(--color-gray-200) 75%
    );
    background-size: 200% 100%;
    animation: shimmer 1.4s ease-in-out infinite;
}

.skeleton-line--name  { width: 80%; }
.skeleton-line--price { width: 45%; margin-top: var(--sp-1); }
