/* ═══════════════════════════════════════════════════════════════════════════
   LITECOIN HORIZON — STORE & PRODUCT PAGES
   Phase 4 redesign · "a phoenix rising"
   ═══════════════════════════════════════════════════════════════════════════

   Loaded by store.php only, via lh_head(['pageCss' => 'store']), AFTER
   style.css. That order is the whole override mechanism: every legacy store
   rule in style.css is matched here at the SAME specificity and wins on source
   order. There are zero `!important` declarations in this file, and there must
   stay zero — the moment one appears, the next person cannot override it
   without a second one.

   THE DIRECTION, AND WHAT IT BUYS US
   Ground is warm ASH (--ink-950 … --ink-850): what a phoenix rises out of. The
   accent is the EMBER → GOLD ramp, and that ramp is the metaphor, not a
   decoration: deep ember at the base, gold at the crown. --flame is a 0deg
   gradient for exactly that reason — anything painted with it reads as rising.

   This solves the problem the store actually has. Most of the catalogue is
   unreleased, and a grid of grey disabled cards reads as an empty shop. Here an
   unreleased piece is not absent, it is NOT YET RISEN:

       ember state (.is-ember)  ember bed under the art, a --flame bar along the
                                base, --flame-soft glowing up out of it, and the
                                label in bright gold. It looks like something
                                about to happen.
       risen state (.is-risen)  clean ash plate, full gold FILL on the pill. The
                                fire is behind it; the piece is out.

   One axis, two states, and "Coming Soon" earns its place on the page.

   COLOUR DIRECTION DOES NOT INVERT. Ember and gold are a FILL carrying ash-dark
   text (--accent-ink), or bright TEXT on the ash ground. Never white on either;
   white on gold is 1.77:1. --ember-700 is 2.59:1 and appears in this file only
   as a gradient stop or a plate — it never carries text.

   --flame AS TEXT. Safe only on the ash ground, where every stop from
   --ember-500 up clears 5:1. On cream the gold stop is 1.81:1, so the
   background-clip treatment is scoped to .phx-invert and .theme-dark, always
   behind an @supports guard, and always with a solid --accent-bright /
   --gold-t colour declared FIRST so a browser without background-clip: text
   renders legible type rather than nothing.

   THEMING. Dark is the default (.theme-dark on <html> unless the visitor chose
   light). Light values are written on the bare selector, dark under
   `.theme-dark …`. Two surfaces are deliberately dark in BOTH themes — the hero
   band and the art plates — because box art is rendered on black and a cream
   plate behind it looks like a printing error. Those carry .phx-invert, which
   re-points the local ember/gold tokens at their on-ash values.
   ═══════════════════════════════════════════════════════════════════════════ */


/* ───────────────────────────────────────────────────────────────────────────
   1. LOCAL TOKEN BRIDGE
   Three locals, all derived from the shared ramp. --gold-t and --ember-t mean
   "gold / ember that is legible on the ground I am currently sitting on"; they
   are the single reason this file never has to ask "is this bit dark?" at each
   call site.
   ─────────────────────────────────────────────────────────────────────────── */
.store-main {
    --gold-t:  var(--accent-deep);                /* #9a2d0c — 7.6:1 on cream */
    --ember-t: var(--ember-600);                  /* #c2410c — 4.65:1 on cream */
    --hair:    var(--border);                     /* hairline rules */
    --plate:   var(--ink-900);                    /* the ash the art sits on */
    --serif:   'Iowan Old Style', 'Palatino Linotype', Palatino, Georgia, 'Times New Roman', serif;
}
.theme-dark .store-main {
    --gold-t:  var(--accent-bright);              /* #ffd774 — 14.29:1 on ash */
    --ember-t: var(--ember-300);                  /* #ff8c3a — 8.51:1 on ash */
    --hair:    var(--ink-700);
}
/* Any always-dark surface, in either theme. */
.phx-invert {
    --gold-t:  var(--accent-bright);
    --ember-t: var(--ember-300);
    --hair:    color-mix(in srgb, var(--ember-400) 26%, transparent);
}


/* ───────────────────────────────────────────────────────────────────────────
   2. PAGE SHELL & BANDS
   main used to carry `style="padding: 2.5rem 2.5rem 3rem"`. Padding moves onto
   the bands so a section can go full-bleed inside .app-window and still keep
   its text on the same measure as its neighbours.
   ─────────────────────────────────────────────────────────────────────────── */
.store-main {
    padding: 0;
    display: block;
}

.phx-wrap {
    /* Named so the sticky filter bar can cancel exactly this much and run
       full-bleed to the wrap's edges. */
    --wrap-pad: clamp(1.25rem, 4vw, 3rem);
    position: relative;
    z-index: 1;
    width: 100%;
    max-width: 1180px;
    margin-inline: auto;
    padding-inline: var(--wrap-pad);
}

.phx-band { padding-block: clamp(var(--space-7), 6vw, var(--space-12)); }
.phx-band-tight { padding-block: var(--space-7); }

/* The ash band is the same in both themes — see the header note. */
.phx-band-dark {
    position: relative;
    background: linear-gradient(0deg, var(--ink-900) 0%, var(--ink-950) 70%);
    color: var(--ink-100);
}
/* The glow rises from the BOTTOM edge of the band. --flame-soft is itself a
   0deg gradient — ember at 0%, transparent by 70% — so it needs no rotating,
   only anchoring to the base. */
.phx-band-dark::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 62%;
    background: var(--flame-soft);
    pointer-events: none;
}

/* The back link sits above the first band and gets its own, tighter rhythm. */
.phx-crumb { padding-block: var(--space-6) 0; }


/* ───────────────────────────────────────────────────────────────────────────
   3. RISING — the reveal
   "Rising" is a direction, so the reveal moves UP and never sideways. The
   .rise class is added by a script in store.php rather than sitting in the
   markup: if that script never runs, nothing is ever hidden in the first place.
   The global prefers-reduced-motion block zeroes --dur-*, and the explicit
   override at the bottom of this section removes the offset too, because a
   zeroed duration alone would still leave an un-observed element at opacity 0.
   ─────────────────────────────────────────────────────────────────────────── */
.rise {
    opacity: 0;
    transform: translateY(20px);
    transition: opacity var(--dur-slow) var(--ease-out),
                transform var(--dur-slow) var(--ease-out);
}
.rise.is-in {
    opacity: 1;
    transform: none;
}
/* Small, ordered groups stagger; the 54-card figurine grid deliberately does
   not — a staggered reveal of fifty tiles reads as a slow page, not as craft. */
.rise-stagger > * { transition-delay: calc(var(--rise-i, 0) * 70ms); }

@media (prefers-reduced-motion: reduce) {
    .rise, .rise.is-in { opacity: 1; transform: none; }
}


/* ───────────────────────────────────────────────────────────────────────────
   4. EDITORIAL PRIMITIVES — eyebrow, display headline, lede, pullquote
   ─────────────────────────────────────────────────────────────────────────── */
.phx-eyebrow {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0 0 var(--space-4);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--gold-t);
}
/* The circled index, banked with a little ember. currentColor on the ring so it
   always agrees with its own label. */
.phx-num {
    display: inline-grid;
    place-items: center;
    inline-size: 1.9em;
    block-size: 1.9em;
    border: 1px solid currentColor;
    border-radius: var(--radius-pill);
    font-size: 0.95em;
    letter-spacing: 0;
    font-variant-numeric: tabular-nums;
}
.phx-invert .phx-num,
.phx-band-dark .phx-num { background: var(--flame-soft); }

.phx-display {
    margin: 0;
    font-size: var(--fs-2xl);
    font-family: var(--font-display);
    font-weight: 400;   /* Anton ships one weight */
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    text-transform: uppercase;
    text-wrap: balance;
}
/* The house signature: the final word of the headline is the flame. The solid
   colour is declared here and unconditionally — the gradient treatment below is
   an enhancement layered on top of legible type, never a replacement for it.
   The universal `* { font-family: Inter }` reset in style.css has specificity 0,
   so this single class beats it without a fight. */
.phx-display em {
    font-family: var(--serif);
    font-style: italic;
    font-weight: var(--fw-normal);
    text-transform: none;
    letter-spacing: 0;
    color: var(--gold-t);
}
/* Scoped to the ash ground only. On cream the gold stop of --flame measures
   1.81:1, so the gradient must not be let anywhere near a light section. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .phx-invert .phx-display em,
    .phx-band-dark .phx-display em,
    .theme-dark .phx-display em {
        background-image: var(--flame);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
    }
}

.phx-lede {
    margin: var(--space-4) 0 0;
    max-width: 56ch;
    font-size: var(--fs-md);
    line-height: var(--lh-base);
    color: var(--text-2);
}
.phx-invert .phx-lede,
.phx-band-dark .phx-lede { color: var(--ink-300); }

/* Pullquote. The rule is a rising flame rather than a flat gold bar, drawn as a
   positioned pseudo-element so it stays on the logical inline-start edge. The
   figurine blurbs are flavour prose, so on a product page the description IS
   the pullquote. */
.phx-pull {
    position: relative;
    margin: var(--space-5) 0;
    padding-inline-start: var(--space-5);
    font-size: var(--fs-md);
    line-height: var(--lh-base);
    color: var(--text-2);
}
.phx-pull::before {
    content: '';
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 3px;
    border-radius: var(--radius-pill);
    background-image: var(--flame);
}

.phx-meta-line {
    margin: var(--space-3) 0 0;
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    line-height: var(--lh-base);
    color: var(--text-3);
}


/* ───────────────────────────────────────────────────────────────────────────
   5. ACTIONS — one flame fill, one plain arrow link
   The primary CTA is painted with --flame itself: ember at its base, gold at
   its crown. --accent-ink on the darkest stop (--ember-500) measures 4.83:1,
   which clears AA for the button's bold small-caps label; on the gold end it is
   ~10:1. background-color is declared first so a browser that drops the image
   still gets a solid gold fill rather than a transparent button.
   ─────────────────────────────────────────────────────────────────────────── */
.phx-cta,
.btn-buy {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 48px;
    width: auto;
    padding: 0 var(--space-6);
    border: 1px solid var(--accent);
    border-radius: var(--radius-sm);
    background-color: var(--accent);
    background-image: var(--flame);
    color: var(--text-on-accent);
    font-size: var(--fs-xs);
    font-weight: var(--fw-black);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    transition: background-color var(--dur-base) var(--ease),
                transform var(--dur-fast) var(--ease);
}
/* Hover flares to full gold — the risen end of the ramp. */
.phx-cta:hover,
.btn-buy:hover {
    background-image: none;
    background-color: var(--accent-bright);
    border-color: var(--accent-bright);
}
.phx-cta:active,
.btn-buy:active { transform: translateY(1px); }

/* Disabled add-to-cart (soap kit under 4 bars). Reads as "not yet", not as
   broken: the ember survives as an outline. */
.btn-buy.is-disabled,
.btn-buy:disabled {
    background-image: none;
    background-color: transparent;
    border-color: var(--hair);
    color: var(--text-3);
    cursor: not-allowed;
}
.btn-buy.is-disabled:hover,
.btn-buy:disabled:hover { background-color: transparent; border-color: var(--hair); }

/* The "Secondary →" text link. No box, no fill — it must never compete with
   the flame CTA. */
.phx-link,
.product-readlink,
.btn-crypto {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    color: var(--gold-t);
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease);
}
.phx-link:hover,
.product-readlink:hover,
.btn-crypto:hover { color: var(--ember-t); text-decoration: underline; text-underline-offset: 4px; }

/* Not yet risen. NOT a grey disabled slab — an ember-outlined plate with the
   glow banked at its base, so it reads as something still coming up. Still
   genuinely disabled. */
.btn-comingsoon {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    width: auto;
    min-height: 48px;
    padding: 0 var(--space-6);
    overflow: hidden;
    border: 1px solid var(--ember-t);
    border-radius: var(--radius-sm);
    background-color: transparent;
    color: var(--gold-t);
    font-size: var(--fs-xs);
    font-weight: var(--fw-black);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    cursor: not-allowed;
}
.btn-comingsoon::before {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 70%;
    background: var(--flame-soft);
    pointer-events: none;
}

.store-back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    margin: 0;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
    transition: color var(--dur-fast) var(--ease);
}
.store-back:hover { color: var(--gold-t); }


/* ───────────────────────────────────────────────────────────────────────────
   6. STATUS PILLS — risen vs ember
   is-live = full gold FILL + ash text: the piece is out.
   is-soon = ember outline + bright gold label: not yet risen.
   One rule covers every ground because both colours resolve through the local
   --gold-t / --ember-t.
   ─────────────────────────────────────────────────────────────────────────── */
.phx-status {
    display: inline-flex;
    align-items: center;
    gap: 0.45em;
    padding: 0.36em 0.75em;
    border: 1px solid transparent;
    border-radius: var(--radius-pill);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    white-space: nowrap;
}
.phx-status.is-live {
    background-color: var(--accent);
    color: var(--text-on-accent);
}
.phx-status.is-soon {
    border-color: var(--ember-t);
    background-color: color-mix(in srgb, var(--ember-700) 42%, transparent);
    color: var(--gold-t);
}
.phx-status.is-out { border-color: var(--hair); color: var(--text-3); }


/* ───────────────────────────────────────────────────────────────────────────
   7. NOTICE BANNERS — promo, preview, checkout success
   All three were inline-styled cyan. They keep their role="status".
   ─────────────────────────────────────────────────────────────────────────── */
.phx-notices { padding-block: var(--space-6) 0; display: grid; gap: var(--space-3); }

.phx-notice,
.preview-banner,
.checkout-success {
    position: relative;
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    margin: 0;
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
    color: var(--text-1);
    font-size: var(--fs-sm);
    line-height: var(--lh-base);
}
.theme-dark .phx-notice,
.theme-dark .preview-banner,
.theme-dark .checkout-success { background: var(--surface-2); }

/* The leading edge is a rising flame rather than a flat rule. */
.phx-notice::before,
.preview-banner::before {
    content: '';
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 3px;
    border-start-start-radius: var(--radius-sm);
    border-end-start-radius: var(--radius-sm);
    background-image: var(--flame);
}
.phx-notice strong { font-weight: var(--fw-black); }
.phx-notice-quiet { color: var(--text-3); }

.checkout-success { border-inline-start: 3px solid #16a34a; }
.checkout-success .cs-icon {
    flex: 0 0 auto;
    display: grid;
    place-items: center;
    width: 26px; height: 26px;
    border-radius: var(--radius-pill);
    background: #16a34a;
    color: var(--white);
    font-weight: var(--fw-black);
}


/* ───────────────────────────────────────────────────────────────────────────
   8. LANDING — hero, stat strip, category tiles
   ─────────────────────────────────────────────────────────────────────────── */
.phx-hero { overflow: hidden; }
.phx-hero .phx-display { font-size: var(--fs-3xl); color: var(--white); }

.phx-hero-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-5);
    margin-top: var(--space-6);
}

/* Stat strip — the credibility row. The numerals take the flame; they are the
   one place on the page where the gradient is doing arithmetic, and they sit on
   the ash band where every stop clears 5:1. */
.phx-stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
    gap: var(--space-5);
    margin-top: var(--space-8);
    padding-top: var(--space-6);
    border-top: 1px solid var(--hair);
    list-style: none;
}
.phx-stat-n {
    display: block;
    font-size: var(--fs-xl);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    color: var(--accent-bright);
    font-variant-numeric: tabular-nums;
}
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
    .phx-invert .phx-stat-n,
    .phx-band-dark .phx-stat-n {
        background-image: var(--flame);
        -webkit-background-clip: text;
        background-clip: text;
        color: transparent;
    }
}
.phx-stat-k {
    display: block;
    margin-top: var(--space-1);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--ink-300);
}

/* Category tiles. Overrides .category-grid / .category-tile / .cat-* wholesale.
   A tile whose category has not launched carries .is-ember and gets the ember
   bed; a live one carries .is-risen and stays clean ash. */
.category-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 280px), 1fr));
    gap: var(--space-5);
    margin-top: var(--space-6);
}
.category-tile {
    position: relative;
    display: block;
    overflow: hidden;
    aspect-ratio: 4 / 5;
    border: 1px solid var(--hair);
    border-radius: var(--radius-md);
    background: var(--plate);
    color: var(--ink-100);
    box-shadow: none;
    transition: border-color var(--dur-base) var(--ease), transform var(--dur-base) var(--ease-out);
}
.category-tile:hover {
    transform: translateY(-4px);
    border-color: var(--ember-400);
    box-shadow: var(--shadow-3);
}
.category-tile img {
    position: absolute;
    inset: 0;
    width: 100%; height: 100%;
    object-fit: cover;
    transition: transform var(--dur-slow) var(--ease-out);
}
.category-tile:hover img { transform: scale(1.03); }
.cat-noart {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-size: var(--fs-3xl);
    background: linear-gradient(0deg, var(--ink-800) 0%, var(--ink-950) 100%);
}
.cat-overlay {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-5);
    background: linear-gradient(to top,
        var(--ink-950) 0%,
        color-mix(in srgb, var(--ink-950) 74%, transparent) 42%,
        transparent 80%);
}
/* Not yet risen: the fire is still under it. */
.category-tile.is-ember .cat-overlay {
    background:
        var(--flame-soft),
        linear-gradient(to top,
            var(--ink-950) 0%,
            color-mix(in srgb, var(--ink-950) 74%, transparent) 42%,
            transparent 80%);
}
.category-tile.is-ember::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 3px;
    background-image: var(--flame);
}
.cat-label {
    font-size: var(--fs-lg);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    text-transform: uppercase;
    color: var(--white);
}
.cat-count {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--ink-300);
}
.cat-cta {
    margin-top: var(--space-1);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-black);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--accent-bright);
}
.cat-status { align-self: flex-start; }


/* ───────────────────────────────────────────────────────────────────────────
   9. CATEGORY HEADER
   ─────────────────────────────────────────────────────────────────────────── */
.phx-head {
    /* Above the sticky filter bar in the next band: both are .phx-wrap stacking
       contexts, and this one's share popover has to open over it. */
    z-index: 3;
    padding-block: var(--space-7) var(--space-5);
}
.phx-head .page-title {
    margin: 0;
    font-size: var(--fs-2xl);
    font-family: var(--font-display);
    font-weight: 400;   /* Anton ships one weight */
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    text-transform: uppercase;
}
.phx-head .page-subtitle {
    margin: var(--space-3) 0 0;
    max-width: 60ch;
    font-size: var(--fs-base);
    line-height: var(--lh-base);
    color: var(--text-2);
}
.phx-head-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    margin-top: var(--space-5);
}
.cat-share { margin: 0; }


/* ───────────────────────────────────────────────────────────────────────────
   10. COIN CHIPS — the community filter
   Per-coin colour bar on the leading edge, coin art as a plate, name, count.
   --ltc / --btc / --doge are other projects' identities and are not ours to
   restyle, so they survive the palette change untouched. --coin is keyed off
   the data-community attribute the filter JS already reads, so the colour and
   the filter can never drift apart.
   ─────────────────────────────────────────────────────────────────────────── */
.community-cards {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 158px), 1fr));
    gap: var(--space-3);
    margin: 0 0 var(--space-5);
}

.community-card {
    /* Fallback for the three communities that have no shared brand token —
       Bellscoin, Pepecoin and Buckazoids are not Litecoin/Bitcoin/Dogecoin and
       have no entry in the token layer, so their bar colour is declared here,
       scoped to this component, rather than smuggled into :root. */
    --coin: var(--ink-400);
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    width: auto;
    aspect-ratio: auto;
    overflow: hidden;
    padding: var(--space-3) var(--space-4) var(--space-3) var(--space-5);
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    box-shadow: none;
    cursor: pointer;
    text-align: left;
    transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.theme-dark .community-card { background: var(--surface-2); }

/* The colour bar. */
.community-card::before {
    content: '';
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 4px;
    background: var(--coin);
}
.community-card[data-community="Litecoin"]   { --coin: var(--ltc); }
.community-card[data-community="Bitcoin"]    { --coin: var(--btc); }
.community-card[data-community="Dogecoin"]   { --coin: var(--doge); }
.community-card[data-community="Bellscoin"]  { --coin: #d4a017; }
.community-card[data-community="Pepecoin"]   { --coin: #4f9d3a; }
.community-card[data-community="Buckazoids"] { --coin: #7b5ea7; }

.community-card:hover { border-color: var(--coin); }
.community-card.active {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
    box-shadow: none;
}

/* "All communities" — the default view. It carries no data-community on
   purpose: that attribute is a frozen contract at exactly 54 occurrences (48
   cards + the six real community buttons) and the harness counts them. */
.community-card.is-all { --coin: var(--accent); }

/* The coin art. style.css absolutely positions this to fill the old square
   tile, so both properties have to be unwound here. */
.community-card > img,
.community-card > picture,
.community-card > .cc-glyph {
    position: static;
    flex: 0 0 auto;
    width: 36px; height: 36px;
    border-radius: var(--radius-pill);
    object-fit: cover;
    background: var(--plate);
}
.community-card > picture > img { width: 36px; height: 36px; border-radius: var(--radius-pill); object-fit: cover; }
.community-card > .cc-glyph {
    display: grid;
    place-items: center;
    font-size: var(--fs-base);
    font-weight: var(--fw-black);
    color: var(--accent-bright);
    background: var(--plate);
}
.community-card > .cc-glyph-all {
    background-color: transparent;
    background-image: var(--flame);
}

.cc-overlay {
    position: static;
    display: block;
    inset: auto;
    padding: 0;
    background: none;
    min-width: 0;
}
.cc-name {
    font-size: var(--fs-sm);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    color: var(--text-1);
}
.cc-count {
    margin-top: 2px;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
}
.community-card.active .cc-count { color: var(--gold-t); }


/* ───────────────────────────────────────────────────────────────────────────
   11. THE FIGURINE BROWSER — sticky bar, active chips, grouped grid
   ───────────────────────────────────────────────────────────────────────────

   48 items behind three stacked chip rows was the problem: nothing told you
   where you were, and narrowing meant combining three separate controls in your
   head. This is one sticky bar (search · genre · collection · sort · size), the
   live state echoed back as removable chips, and a grid grouped by community
   with sticky headers so scrolling has landmarks.

   Everything here is a VIEW concern. All 48 cards are in the server-rendered
   HTML; these controls only set `hidden` on them.
   ─────────────────────────────────────────────────────────────────────────── */

/* Sticky needs a non-scroll-container ancestor chain, and style.css puts
   `overflow: hidden` on .app-window to clip its rounded corners — which turns it
   into a scroll container and silently kills every `position: sticky` inside it.
   `clip` on the inline axis keeps the corner clipping without creating that
   scroll container; `visible` on the block axis is what lets the filter bar and
   the product-page media column actually stick. Paired with `clip` rather than
   `hidden`, `visible` is a legal computed value and stays as specified. A
   browser too old for `overflow: clip` drops the declaration and falls back to
   style.css's `hidden` — no sticky, but nothing broken. */
.app-window { overflow: clip visible; }

.fig-bar {
    position: sticky;
    top: 0;
    z-index: 30;
    /* Full-bleed to the wrap's edges so cards scroll UNDER the bar rather than
       out beside it. Mirrors .phx-wrap's own inline padding exactly. */
    margin-inline: calc(-1 * var(--wrap-pad));
    margin-block: 0 var(--space-7);
    padding: var(--space-4) var(--wrap-pad);
    background: var(--surface-2);
    border-bottom: 1px solid var(--hair);
    box-shadow: var(--shadow-2);
}

.fig-bar-controls {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-3) var(--space-4);
}

.fig-field {
    position: relative;
    display: inline-flex;
    flex-direction: column;
    gap: 5px;
    min-width: 0;
}
.fig-field[hidden] { display: none; }
.fig-field-search { flex: 1 1 240px; }
.fig-field-label {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
}

.fig-input,
.fig-select {
    height: 42px;
    max-width: 100%;
    padding: 0 var(--space-3);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background-color: var(--surface-1);
    color: var(--text-1);
    font-family: inherit;
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    transition: border-color var(--dur-fast) var(--ease);
}
.fig-input::placeholder { color: var(--text-3); }
.fig-input:hover,
.fig-select:hover { border-color: var(--ember-400); }
.fig-input:focus,
.fig-select:focus { border-color: var(--accent); }
.fig-select {
    appearance: none;
    padding-inline-end: var(--space-6);
    cursor: pointer;
}
/* The native option list follows color-scheme, not our tokens, so a dark page
   would otherwise drop a white menu on top of it. */
.theme-dark .fig-select { color-scheme: dark; }
/* Custom chevron. Drawn on the field rather than the select so no extra wrapper
   element is needed; the select is the last child at a fixed height, so this
   lands on it in every field that has one. */
.fig-field:has(select)::after {
    content: '';
    position: absolute;
    inset-inline-end: var(--space-3);
    bottom: 18px;
    width: 0; height: 0;
    border-inline: 4px solid transparent;
    border-top: 5px solid var(--text-3);
    pointer-events: none;
}

.fig-density {
    display: inline-flex;
    height: 42px;
    overflow: hidden;
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
}
.fig-density-btn {
    padding: 0 var(--space-4);
    border: 0;
    background-color: var(--surface-1);
    color: var(--text-3);
    font-family: inherit;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.fig-density-btn:hover { color: var(--text-1); }
.fig-density-btn.is-on {
    background-color: var(--accent);
    color: var(--text-on-accent);
}

.fig-bar-state {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-3);
    margin-top: var(--space-4);
}

/* The result count — the page's aria-live region for the grid. */
.phx-result {
    margin: 0;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
    white-space: nowrap;
}
.phx-result b { color: var(--gold-t); font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }

/* Active filters, one removable chip each: the current state is readable at a
   glance and undoable a facet at a time, which a set of scattered controls
   never is. */
.fig-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.fig-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.5em;
    min-height: 32px;
    padding: 0 var(--space-3);
    border: 1px solid var(--accent);
    border-radius: var(--radius-pill);
    background-color: color-mix(in srgb, var(--accent) 14%, transparent);
    color: var(--text-1);
    font-family: inherit;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease), background-color var(--dur-fast) var(--ease);
}
.fig-chip:hover { border-color: var(--ember-400); background-color: color-mix(in srgb, var(--ember-400) 18%, transparent); }
.fig-chip-x { color: var(--ember-t); font-weight: var(--fw-black); }

.fig-clear {
    margin-inline-start: auto;
    padding: 0;
    min-height: 32px;
    border: 0;
    background: none;
    color: var(--gold-t);
    font-family: inherit;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    text-decoration: underline;
    text-underline-offset: 4px;
    cursor: pointer;
}
.fig-clear[hidden] { display: none; }
.fig-clear:hover { color: var(--ember-t); }

/* ── The grouped grid ──────────────────────────────────────────────────────
   Community sections with sticky headers. The colour bar is the same per-coin
   token the filter chips use, so the header and the chip that selects it are
   visibly the same thing. */
.fig-groups { display: grid; gap: var(--space-10); }

.fig-group { --coin: var(--ink-400); }
.fig-group[hidden] { display: none; }
.fig-group[data-group="Litecoin"]   { --coin: var(--ltc); }
.fig-group[data-group="Bitcoin"]    { --coin: var(--btc); }
.fig-group[data-group="Dogecoin"]   { --coin: var(--doge); }
.fig-group[data-group="Bellscoin"]  { --coin: #d4a017; }
.fig-group[data-group="Pepecoin"]   { --coin: #4f9d3a; }
.fig-group[data-group="Buckazoids"] { --coin: #7b5ea7; }

.fig-group-head {
    position: sticky;
    /* Directly beneath the filter bar. --fig-bar-h is measured by the page
       script, because the bar's height changes when its controls wrap. */
    top: var(--fig-bar-h, 96px);
    z-index: 20;
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    margin: 0 0 var(--space-5);
    padding: var(--space-3) 0 var(--space-3) var(--space-4);
    background: var(--surface-1);
    border-bottom: 1px solid var(--hair);
    font-size: var(--fs-md);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    text-transform: uppercase;
}
.fig-group-head::before {
    content: '';
    position: absolute;
    inset-block: var(--space-2);
    inset-inline-start: 0;
    width: 4px;
    border-radius: var(--radius-pill);
    background: var(--coin);
}
.fig-group-name { color: var(--text-1); }
.fig-group-count {
    margin-inline-start: auto;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
}

/* .product-card sets display:flex in this file, which beats the UA sheet's
   `[hidden] { display: none }` — without this, a filtered-out card stays on
   screen. Same for every other element the script toggles. */
.fig-card[hidden] { display: none; }

/* ── Compact density ───────────────────────────────────────────────────────
   The actual complaint was "hard to get through 48 of these", so the fastest
   fix is more of them on screen: a smaller tile, no blurb, no per-card CTA. */
.fig-groups.is-compact .store-grid {
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 168px), 1fr));
    gap: var(--space-5) var(--space-3);
}
.fig-groups.is-compact .product-desc,
.fig-groups.is-compact .card-ship-note,
.fig-groups.is-compact .product-readlink { display: none; }
.fig-groups.is-compact .product-info { gap: var(--space-1); padding-top: var(--space-3); }
.fig-groups.is-compact .product-name { font-size: var(--fs-sm); }
.fig-groups.is-compact .phx-card-foot { margin-top: var(--space-2); padding-top: var(--space-2); }

.fig-empty {
    display: grid;
    justify-items: center;
    gap: var(--space-5);
    padding: var(--space-12) 0;
    text-align: center;
}
.fig-empty[hidden] { display: none; }
.fig-empty-text { margin: 0; font-size: var(--fs-md); color: var(--text-3); }


/* ────────────────────────────────────────────────────────────────────
───────
   12. PRODUCT GRID + CARD
   The wrapper stays `<div class="product-card …">` with data-community /
   data-genre / data-collection on the opening tag: the regression harness
   splits the page on that exact string and reads the facets out of the first
   400 characters of each chunk. Renaming the class or reordering it would make
   the whole facet contract unreadable.
   ─────────────────────────────────────────────────────────────────────────── */
.store-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 258px), 1fr));
    gap: var(--space-6) var(--space-5);
    margin-top: 0;
}

.product-card {
    display: flex;
    flex-direction: column;
    overflow: visible;
    border: 0;
    border-radius: 0;
    background: none;
    transition: none;
}
.product-card:hover { transform: none; box-shadow: none; }

/* The art plate. Always ash — box art is rendered on black. */
.phx-art {
    position: relative;
    display: block;
    overflow: hidden;
    aspect-ratio: 2 / 3;
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: linear-gradient(0deg, var(--ink-850) 0%, var(--ink-950) 100%);
    transition: border-color var(--dur-base) var(--ease);
}
.phx-art img {
    position: relative;
    width: 100%; height: 100%;
    object-fit: contain;
    display: block;
    transition: transform var(--dur-slow) var(--ease-out);
}
.phx-art.is-cover img { object-fit: cover; }

/* RISEN — the piece is out. Clean ash, no fire under it. */
.phx-art.is-risen { background: linear-gradient(0deg, var(--ink-850) 0%, var(--ink-950) 100%); }

/* EMBER — not yet risen. An ember bed at the base, --flame-soft glowing up
   through the art, and a --flame bar along the bottom edge: the fire it has not
   come out of yet. --ember-700 is 2.59:1 and appears here as a plate only; no
   text is ever placed on it. */
.phx-art.is-ember {
    background: linear-gradient(0deg,
        var(--ember-700) 0%,
        color-mix(in srgb, var(--ember-700) 40%, var(--ink-900)) 14%,
        var(--ink-900) 42%,
        var(--ink-950) 100%);
}
.phx-art.is-ember::before {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 72%;
    background: var(--flame-soft);
    pointer-events: none;
    z-index: 1;
}
.phx-art.is-ember::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 3px;
    background-image: var(--flame);
    z-index: 2;
}

.product-media-link { display: block; }
.product-media-link:hover .phx-art { border-color: var(--ember-400); }
.product-media-link:hover .phx-art img { transform: scale(1.035); }

/* Edition index, top-left, and status pill, top-right. Above the ember wash. */
.phx-art-index {
    position: absolute;
    top: var(--space-3);
    left: var(--space-3);
    z-index: 3;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-black);
    letter-spacing: var(--tracking-label);
    color: var(--accent-bright);
    font-variant-numeric: tabular-nums;
}
.phx-art-status {
    position: absolute;
    top: var(--space-3);
    right: var(--space-3);
    z-index: 3;
}

.product-info {
    display: flex;
    flex-direction: column;
    flex: 1;
    gap: var(--space-2);
    padding: var(--space-4) 0 0;
}
.product-name-link { display: block; color: inherit; }
.product-name {
    margin: 0;
    font-size: var(--fs-base);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    text-transform: uppercase;
}
.product-name-link:hover .product-name { color: var(--gold-t); }
.product-sub {
    margin: 0;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--gold-t);
}
.product-desc {
    margin: var(--space-1) 0 0;
    flex: 1;
    font-size: var(--fs-sm);
    line-height: var(--lh-base);
    color: var(--text-3);
    /* Four lines, then ellipsis. The blurbs run from one line to eight, and an
       eight-line card drags every neighbour in its row down with it. */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
}
.phx-card-foot {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-3);
    padding-top: var(--space-3);
    border-top: 1px solid var(--hair);
}
.product-price {
    margin: 0;
    font-size: var(--fs-base);
    font-weight: var(--fw-black);
    letter-spacing: var(--tracking-display);
    color: var(--text-1);
    font-variant-numeric: tabular-nums;
}
.product-price span { font-size: var(--fs-2xs); font-weight: var(--fw-bold); color: var(--text-3); }

.card-ship {
    margin: 0;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: 0.03em;
    color: var(--text-3);
    text-transform: none;
}
.card-ship.is-low { color: var(--ember-t); }

.product-card .product-readlink { margin-top: var(--space-1); }
.product-card .btn-comingsoon { align-self: flex-start; margin-top: var(--space-2); }


/* ───────────────────────────────────────────────────────────────────────────
   13. PRODUCT DETAIL
   ─────────────────────────────────────────────────────────────────────────── */
.product-detail {
    display: grid;
    grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
    gap: clamp(var(--space-6), 5vw, var(--space-10));
    align-items: start;
    margin-top: 0;
}
.detail-media { position: sticky; top: var(--space-5); width: 100%; }

/* Renamed from .detail-main so it does not inherit style.css's
   `.theme-dark .detail-main { background: #0f172a }`, which is a two-class
   selector this file could otherwise only beat by repeating .theme-dark. */
.phx-frame {
    position: relative;
    overflow: hidden;
    border: 1px solid var(--hair);
    border-radius: var(--radius-md);
    background: linear-gradient(0deg, var(--ink-850) 0%, var(--ink-950) 100%);
}
.phx-frame img { display: block; width: 100%; height: auto; }
.phx-frame .product-placeholder {
    aspect-ratio: 2 / 3;
    display: grid;
    place-items: center;
    font-size: var(--fs-3xl);
    background: none;
}
/* An unreleased product's hero gets the same ember bed as its grid tile, so the
   two states stay legible as one system. */
.phx-frame.is-ember::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 46%;
    background: var(--flame-soft);
    pointer-events: none;
}

.detail-thumbs {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
}
.detail-thumb {
    width: 62px; height: 62px;
    padding: 0;
    overflow: hidden;
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: var(--plate);
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease);
}
.detail-thumb:hover { border-color: var(--ember-400); }
.detail-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.detail-info { min-width: 0; }
.detail-info h1 {
    margin: 0;
    font-size: var(--fs-2xl);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    text-transform: uppercase;
    text-wrap: balance;
}
.detail-sub {
    margin: var(--space-2) 0 0;
    font-family: var(--serif);
    font-style: italic;
    font-size: var(--fs-lg);
    font-weight: var(--fw-normal);
    line-height: var(--lh-snug);
    letter-spacing: 0;
    text-transform: none;
    color: var(--gold-t);
}
.detail-desc { color: var(--text-2); }

/* Spec table. */
.detail-meta {
    list-style: none;
    margin: var(--space-6) 0;
    padding: 0;
    border-top: 1px solid var(--hair);
}
.detail-meta li {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: var(--space-4);
    align-items: baseline;
    padding: var(--space-3) 0;
    border-bottom: 1px solid var(--hair);
    font-size: var(--fs-sm);
}
.detail-meta li .k {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
}
.detail-meta li .v { font-weight: var(--fw-bold); text-align: right; color: var(--text-1); }
.detail-meta li .v.is-live { color: var(--ember-t); }

.detail-note {
    margin: var(--space-5) 0 0;
    font-size: var(--fs-xs);
    line-height: var(--lh-base);
    color: var(--text-3);
}
.ship-note {
    margin: var(--space-4) 0 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    font-size: var(--fs-xs);
    font-weight: var(--fw-medium);
    color: var(--text-3);
}


/* ───────────────────────────────────────────────────────────────────────────
   14. BUY PANEL
   The flame is banked along the BOTTOM edge and glows upward through the panel,
   so the money control is the hottest thing on the page. No overflow: hidden
   here — the share popover is a child and would be clipped by it.
   ─────────────────────────────────────────────────────────────────────────── */
.buy-box {
    position: relative;
    margin: 0 0 var(--space-4);
    padding: var(--space-6);
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
}
.theme-dark .buy-box { background: var(--surface-2); }
.buy-box::before {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 3px;
    border-end-start-radius: var(--radius-sm);
    border-end-end-radius: var(--radius-sm);
    background-image: var(--flame);
}
.buy-box::after {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 55%;
    background: var(--flame-soft);
    pointer-events: none;
}
.buy-box > * { position: relative; z-index: 1; }

.opt-label {
    margin: 0 0 var(--space-3);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
}
.opt-pills {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
    gap: var(--space-3);
    margin: 0 0 var(--space-5);
}
.opt-pill {
    position: relative;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: var(--space-3);
    row-gap: 2px;
    min-height: 64px;
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.theme-dark .opt-pill { background: var(--surface-1); }
.opt-pill:hover { border-color: var(--ember-400); }
.opt-pill.active {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}

/* THE ACCESSIBILITY FIX ON THIS PAGE.
   These radios were `opacity: 0` with no visible focus state at all, so a
   keyboard user choosing between a $100 and a $140 option saw literally
   nothing move as they arrowed between them. Two changes:
     1. a real, visible radio dot (.opt-dot) that reflects :checked, so the
        selection is legible without relying on the JS .active class;
     2. the global focus ring, re-projected from the (still visually hidden but
        still focusable) input onto the label the user can actually see. */
.opt-pill input {
    position: absolute;
    width: 1px; height: 1px;
    margin: 0;
    opacity: 0;
    pointer-events: none;
}
.opt-pill:has(input:focus-visible) {
    outline: 3px solid var(--focus-ring);
    outline-offset: 2px;
}
.opt-dot {
    grid-row: 1 / span 2;
    display: grid;
    place-items: center;
    width: 18px; height: 18px;
    border: 2px solid var(--border-strong);
    border-radius: var(--radius-pill);
    transition: border-color var(--dur-fast) var(--ease);
}
.opt-dot::after {
    content: '';
    width: 8px; height: 8px;
    border-radius: var(--radius-pill);
    background-color: var(--accent);
    transform: scale(0);
    transition: transform var(--dur-fast) var(--ease-out);
}
.opt-pill:has(input:checked) .opt-dot { border-color: var(--accent); }
.opt-pill:has(input:checked) .opt-dot::after { transform: scale(1); }

.opt-title {
    grid-column: 2;
    font-size: var(--fs-sm);
    font-weight: var(--fw-bold);
    color: var(--text-1);
}
.opt-title em {
    font-style: normal;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    color: var(--ember-t);
}
.opt-price {
    grid-column: 2;
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
}

.detail-price {
    display: flex;
    align-items: baseline;
    gap: var(--space-3);
    margin: 0 0 var(--space-5);
    font-size: var(--fs-xl);
    font-weight: var(--fw-black);
    letter-spacing: var(--tracking-display);
    color: var(--text-1);
    font-variant-numeric: tabular-nums;
}
.detail-price span,
.price-unit {
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
}

.detail-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4);
    max-width: none;
}
.detail-actions .btn-buy,
.detail-actions .btn-comingsoon { width: auto; }

/* aria-live confirmation for add-to-cart. The button label already flips to
   "Added ✓", but a label change on a control the user has just activated is not
   a reliable announcement, and there was no live region anywhere on the site. */
.buy-feedback {
    margin: var(--space-4) 0 0;
    min-height: 1.2em;
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    letter-spacing: 0.02em;
    color: var(--gold-t);
}
.buy-feedback:empty { margin-top: 0; }

.opt-hint {
    margin: var(--space-4) 0 0;
    font-size: var(--fs-xs);
    line-height: var(--lh-base);
    color: var(--text-3);
}


/* ───────────────────────────────────────────────────────────────────────────
   15. SHARE CONTROL
   Structure is fixed by the LHShare script at the foot of store.php; only the
   skin changes here.
   ─────────────────────────────────────────────────────────────────────────── */
.share-wrap { position: relative; display: inline-block; }
.btn-share {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: 0 var(--space-4);
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-3);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    white-space: nowrap;
    cursor: pointer;
    transition: color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}
.btn-share:hover,
.share-wrap.is-open .btn-share { color: var(--gold-t); border-color: var(--ember-t); }

.share-pop {
    position: absolute;
    top: calc(100% + var(--space-2));
    left: 0;
    z-index: 40;
    display: flex;
    flex-direction: column;
    min-width: 184px;
    padding: var(--space-1);
    border: 1px solid var(--border-strong);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    box-shadow: var(--shadow-3);
}
.share-pop[hidden] { display: none; }
.share-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-height: 40px;
    padding: 0 var(--space-3);
    border: 0;
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-1);
    text-align: left;
    font-size: var(--fs-xs);
    font-weight: var(--fw-bold);
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.share-item b { width: 1.15em; text-align: center; font-size: var(--fs-sm); line-height: 1; }
.share-item:hover { background: var(--surface-2); color: var(--gold-t); }
.share-item.copied { color: var(--gold-t); }


/* ───────────────────────────────────────────────────────────────────────────
   16. SOAP 4-PACK KIT
   ─────────────────────────────────────────────────────────────────────────── */
.soap-kit {
    position: relative;
    padding: var(--space-6);
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: var(--surface-2);
}
.theme-dark .soap-kit { background: var(--surface-2); }
.soap-kit::before {
    content: '';
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 3px;
    border-end-start-radius: var(--radius-sm);
    border-end-end-radius: var(--radius-sm);
    background-image: var(--flame);
}
.soap-kit > * { position: relative; z-index: 1; }

.soap-kit-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-5);
    margin-bottom: var(--space-5);
}
.soap-kit-title {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-black);
    line-height: var(--lh-tight);
    letter-spacing: var(--tracking-display);
    text-transform: uppercase;
}
.soap-kit-sub { margin: var(--space-2) 0 0; font-size: var(--fs-sm); color: var(--text-3); }
.soap-kit-price {
    font-size: var(--fs-xl);
    font-weight: var(--fw-black);
    letter-spacing: var(--tracking-display);
    color: var(--gold-t);
    white-space: nowrap;
    font-variant-numeric: tabular-nums;
}
.soap-kit-price span {
    display: block;
    text-align: right;
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
}

.soap-ship-banner {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin: 0 0 var(--space-5);
    padding: var(--space-3) var(--space-4);
    border: 0;
    border-radius: 0;
    background: none;
    font-size: var(--fs-sm);
    line-height: var(--lh-base);
    color: var(--text-2);
}
.soap-ship-banner::before {
    content: '';
    position: absolute;
    inset-block: 0;
    inset-inline-start: 0;
    width: 3px;
    border-radius: var(--radius-pill);
    background-image: var(--flame);
}
.soap-ship-banner strong { font-weight: var(--fw-black); color: var(--text-1); }

.soap-scents {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
    gap: var(--space-3);
}
.scent {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: var(--surface-1);
    transition: border-color var(--dur-fast) var(--ease), background var(--dur-fast) var(--ease);
}
.theme-dark .scent { background: var(--surface-1); }
.scent.picked {
    border-color: var(--accent);
    background: color-mix(in srgb, var(--accent) 12%, transparent);
}
.scent-detail-link { display: flex; align-items: center; gap: var(--space-3); min-width: 0; color: inherit; }
.scent-detail-link:hover .scent-name { color: var(--gold-t); }
.scent-img {
    flex: 0 0 auto;
    width: 46px; height: 46px;
    border-radius: var(--radius-sm);
    object-fit: cover;
    background: var(--plate);
}
.scent-placeholder { display: inline-grid; place-items: center; font-size: var(--fs-lg); }
.scent-info { display: block; min-width: 0; }
.scent-name { display: block; font-size: var(--fs-sm); font-weight: var(--fw-bold); color: var(--text-1); }
.scent-sub { display: block; margin-top: 2px; font-size: var(--fs-2xs); color: var(--text-3); }

.scent-qty { display: flex; align-items: center; gap: var(--space-2); flex: 0 0 auto; }
.qbtn {
    display: grid;
    place-items: center;
    width: 34px; height: 34px;
    border: 1px solid var(--hair);
    border-radius: var(--radius-sm);
    background: transparent;
    color: var(--text-1);
    font-size: var(--fs-md);
    font-weight: var(--fw-bold);
    line-height: 1;
    cursor: pointer;
    transition: border-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.qbtn:hover:not(:disabled) { border-color: var(--ember-400); color: var(--gold-t); }
.qbtn:disabled { opacity: 0.35; cursor: not-allowed; }
.qval { min-width: 18px; text-align: center; font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }

/* Progress toward 4 bars — the kit's own little flame, filling upward in
   colour as it fills across. --kit-fill is written by the kit script. */
.kit-progress {
    height: 4px;
    margin: var(--space-6) 0 var(--space-4);
    background: var(--hair);
    border-radius: var(--radius-pill);
    overflow: hidden;
}
.kit-progress span {
    display: block;
    height: 100%;
    width: var(--kit-fill, 0%);
    background-image: var(--flame);
    transition: width var(--dur-base) var(--ease-out);
}
.soap-kit-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
    margin-top: 0;
}
.soap-kit-count {
    font-size: var(--fs-2xs);
    font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-label);
    text-transform: uppercase;
    color: var(--text-3);
}
.soap-kit-count b { color: var(--gold-t); font-weight: var(--fw-black); font-variant-numeric: tabular-nums; }
.soap-kit-foot .btn-buy,
.soap-kit-foot .btn-comingsoon { width: auto; min-width: 220px; }


/* ───────────────────────────────────────────────────────────────────────────
   17. NOT FOUND
   The route still sends a real 404 and a robots noindex; this is only its face.
   ─────────────────────────────────────────────────────────────────────────── */
.store-notice {
    display: block;
    margin: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    color: var(--text-2);
    font-size: var(--fs-base);
    font-weight: var(--fw-normal);
    line-height: var(--lh-base);
}
.store-notice strong { color: var(--text-1); font-weight: var(--fw-black); }
.store-notice .store-notice-icon {
    display: block;
    width: auto; height: auto;
    margin-bottom: var(--space-4);
    border-radius: 0;
    background: none;
    color: var(--gold-t);
    font-family: var(--serif);
    font-style: italic;
    font-size: var(--fs-3xl);
    font-weight: var(--fw-normal);
    line-height: var(--lh-tight);
}
.phx-404-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-5);
    margin-top: var(--space-6);
}


/* ───────────────────────────────────────────────────────────────────────────
   18. RESPONSIVE
   ─────────────────────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
    .product-detail { grid-template-columns: 1fr; }
    .detail-media { position: static; max-width: 420px; }
}

@media (max-width: 640px) {
    .store-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 150px), 1fr)); gap: var(--space-5) var(--space-3); }
    .product-desc { -webkit-line-clamp: 3; line-clamp: 3; }
    .community-cards { grid-template-columns: repeat(auto-fill, minmax(min(100%, 142px), 1fr)); }
    /* The bar is the one thing that must stay reachable over 48 cards, so on a
       phone it sheds its labels and its own padding rather than its stickiness.
       Search stays full width; the four selects go two-up. */
    .fig-bar { padding-block: var(--space-3); margin-block-end: var(--space-6); }
    .fig-bar-controls { gap: var(--space-2) var(--space-3); }
    .fig-field-search { flex: 1 1 100%; }
    .fig-field { flex: 1 1 calc(50% - var(--space-3)); }
    .fig-select, .fig-density { width: 100%; }
    .fig-density-btn { flex: 1; padding: 0 var(--space-2); }
    .fig-bar-state { margin-top: var(--space-3); }
    .fig-clear { margin-inline-start: 0; }
    .fig-groups { gap: var(--space-8); }
    .fig-groups.is-compact .store-grid { grid-template-columns: repeat(auto-fill, minmax(min(100%, 128px), 1fr)); }
    .soap-kit, .buy-box { padding: var(--space-5) var(--space-4); }
    .soap-kit-head { flex-direction: column; }
    .soap-kit-price { text-align: left; }
    .soap-kit-price span { text-align: left; }
    .soap-kit-foot .btn-buy, .soap-kit-foot .btn-comingsoon { width: 100%; min-width: 0; }
    /* The action row can push Share to the right edge, so its popover opens
       leftward rather than off-screen. */
    .detail-actions .share-pop { left: auto; right: 0; }
}
