/* =====================================================================
   Currency, page stylesheet
   ---------------------------------------------------------------------
   The VinTokens/Vinyls wallet and the decoration shop. Loaded only by
   /currency (see src/routes/currency/index.ts), after the four shell
   sheets. Until August 2026 this page's CSS lived in thirteen disjoint
   regions of the 34k-line global render.css and shipped to every other
   page in the product; it now lives here and nowhere else.

   This file defines no scale of its own. Type comes from --type-*,
   spacing from --space-*, radii from --ds-radius-*, motion from
   --motion-*, colour from the semantic --ds-, --text- and --bg- layer.
   The page owns composition, not values.

   Two rules earn their own note, because breaking either is what made
   this page read as generated rather than designed:

   1. ACCENT CONTAINMENT. Every decoration carries an author-chosen hex
      (--decoration-accent) describing its artwork. That colour is
      allowed inside .shop-card-preview and nowhere else. It used to
      paint the card border, the "LIVE" dot, the selected-state button
      and the body copy, which meant a twelve-item grid rendered in
      twelve competing accents and the one pack whose accent is #111111
      rendered its own chrome invisible. Structure and status are the
      product's job: --ds-border and --ds-accent.

   2. GOLD IS PREMIUM. --premium-gold is a status colour meaning "this
      is the paid tier", shared with /premium and /profile. It was being
      spent on the COMMON rarity chip and on LEGENDARY. Rarity does not
      get gold; only Premium does.
   ===================================================================== */

/* ---------------------------------------------------------------- *
 *  Page rhythm
 * ---------------------------------------------------------------- */

body[data-page="currency"] main > .container {
    max-width: var(--container-max);
}

/* Sections step apart more than the rows inside them do. One value per
   breakpoint, both on the shared scale, not a clamp that resolves to
   arbitrary numbers. */
/* Two steps, assigned by role. One uniform value across six sections is
   the "every section has identical vertical padding" tell: the shelf is
   the reason the page exists and gets air, while the payment blocks are
   variations on one job and sit closer together. */
body[data-page="currency"] {
    --step-major: var(--space-32);
    --step-minor: var(--space-24);
}

@media (min-width: 768px) {
    body[data-page="currency"] {
        --step-major: var(--space-64);
        --step-minor: var(--space-32);
    }
}

/* ---------------------------------------------------------------- *
 *  Shop layout: goods on the left, wallet on the right
 * ---------------------------------------------------------------- */

/* Two columns, on the same spatial system as the Workspace OS shell
   (public/ux/workspaces.css .hq): a 264px rail, a clamp(18,2vw,28)
   gutter, and the rail dropping below the content at 900px. Deliberately
   the same numbers, a rail that is 264px on one surface and 300px on
   another is how a product stops feeling like one product.

   The wallet is a rail rather than a section because you spend from it
   while you shop: the balance used to scroll away above six checkout
   sections, so deciding whether you could afford a 32-VinToken
   decoration meant scrolling back up. */
.shop-layout {
    padding-top: var(--space-24);
    --shop-nav-w: 176px;
    --shop-rail-w: 272px;
    --shop-gap: clamp(18px, 2vw, 28px);
    display: grid;
    grid-template-columns: var(--shop-nav-w) minmax(0, 1fr) var(--shop-rail-w);
    gap: var(--shop-gap);
    align-items: start;
}

/* ---------------------------------------------------------------- *
 *  Left rail: where you are in a long shop
 * ---------------------------------------------------------------- */

/* The page is seven sections deep. This is in-page geography, not a
   second global nav: every entry is an anchor to a section of THIS
   page, so it never competes with the product's top bar for "where do
   global actions live". */
.shop-nav {
    position: sticky;
    top: calc(var(--vd-top-h, 72px) + var(--space-12));
    display: flex;
    flex-direction: column;
    gap: var(--space-24);
    min-width: 0;
}

.shop-nav__group ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.shop-nav__label {
    margin: 0 0 var(--space-8);
    padding-inline: var(--space-8);
    font-size: var(--type-caption);
    font-weight: 600;
    color: var(--text-subtle);
}

.shop-nav__group a {
    display: block;
    padding: var(--space-8);
    border-radius: var(--ds-radius-sm);
    font-size: var(--type-small);
    line-height: 1.3;
    color: var(--text-muted);
    text-decoration: none;
    transition:
        background-color var(--motion-duration-short) var(--motion-ease-standard),
        color var(--motion-duration-short) var(--motion-ease-standard);
}

.shop-nav__group a:hover {
    background: var(--ds-surface);
    color: var(--text-strong);
}

/* The active section is marked by weight and a filled surface, not by
   colour alone, the list still reads correctly in greyscale. */
.shop-nav__group a[aria-current="true"] {
    background: var(--ds-surface);
    color: var(--text-strong);
    font-weight: 600;
}

.shop-nav__promo {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-8);
    padding: var(--space-12);
    border: 1px solid color-mix(in oklab, var(--premium-gold) 32%, transparent);
    border-radius: var(--ds-radius-lg);
}

.shop-nav__promo-title {
    margin: 0;
    font-size: var(--type-small);
    font-weight: 600;
    color: var(--premium-gold);
}

.shop-nav__promo-body {
    margin: 0;
    font-size: var(--type-caption);
    line-height: 1.5;
    color: var(--text-muted);
}

.shop-nav__promo .apply-button,
.shop-nav__promo .ghost-button {
    width: 100%;
    min-height: 36px;
}

/* ---------------------------------------------------------------- *
 *  Hero
 * ---------------------------------------------------------------- */

/* Not a marketing hero: it states what the goods are, gives the two
   things you might want to do, and shows four of the actual products
   running. The imagery IS the merchandise, no rendered object that
   exists nowhere else in the app. */
.shop-hero {
    display: grid;
    grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr);
    gap: var(--space-32);
    align-items: start;
    padding-block: var(--space-16) var(--space-32);
}

/* --type-h2, not --type-hero. This hero sits inside a three-column app
   shell, so its copy column is ~460px: the 56px page-title size wrapped
   the sentence onto four lines and read as a marketing banner. Product
   surfaces run tighter than marketing ones (§32); the page still leads
   with the largest type on screen. */
.shop-hero__copy > h1 {
    margin: 0 0 var(--space-12);
    font-size: var(--type-h2);
    line-height: 1.1;
    letter-spacing: -0.01em;
    text-wrap: balance;
}

.shop-hero__copy > p {
    margin: 0 0 var(--space-24);
    max-width: 52ch;
    font-size: var(--type-lede);
    line-height: 1.55;
    color: var(--text-muted);
}

.shop-hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-8);
    margin-bottom: var(--space-32);
}

.shop-hero__actions .apply-button,
.shop-hero__actions .ghost-button {
    min-height: 44px;
}

/* Three facts, each one a number the page can actually prove: the
   catalogue size, its lowest price, and the billing model. Not a row of
   reassurance badges. */
.shop-hero__facts {
    list-style: none;
    margin: 0;
    padding: var(--space-16) 0 0;
    border-top: 1px solid var(--ds-border);
    /* A grid, so the third fact cannot wrap onto its own line and read
       as a stray heading. */
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-16);
}

.shop-hero__facts li {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.shop-hero__facts strong {
    font-family: var(--font-display);
    font-size: var(--type-h3);
    font-weight: 600;
    line-height: 1.1;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

.shop-hero__facts span {
    font-size: var(--type-caption);
    color: var(--text-subtle);
}

.shop-hero__art {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-12);
}

/* The tiles reuse .shop-card-preview so the hero and the cards run the
   same effect pipeline at two sizes. */
.shop-hero__art .shop-hero__tile {
    height: 148px;
    border-radius: var(--ds-radius-lg);
}



.shop-layout__main {
    min-width: 0;
}

.shop-wallet {
    position: sticky;
    /* Clears the fixed top bar, using the shell's own measurement. */
    top: calc(var(--vd-top-h, 72px) + var(--space-12));
    display: flex;
    flex-direction: column;
    gap: var(--space-12);
    min-width: 0;
}

/* The rail is the one place on this page that uses a filled surface:
   it is chrome you spend from, not content you read. Sections in the
   main column stay flat and are separated by rules. */
.wallet-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-12);
    padding: var(--space-16);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
    background: var(--ds-surface);
}

.wallet-card__title {
    margin: 0;
    font-size: var(--type-small);
    font-weight: 600;
    letter-spacing: normal;
    text-transform: none;
    color: var(--text-muted);
}

/* Which decoration is live where. Both apply targets exist on the
   profile, so both are stated, the rail used to name only the
   marketplace one, which read as "the other one isn't a thing". */
.wallet-applied {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
}

.wallet-applied > div {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-8);
}

.wallet-applied dt {
    font-size: var(--type-caption);
    color: var(--text-subtle);
}

.wallet-applied dd {
    margin: 0;
    font-size: var(--type-small);
    font-weight: 500;
    color: var(--text-strong);
    text-align: right;
    overflow-wrap: anywhere;
}

/* Rail-scale pack tiles: the same Stripe packs as the main column, at
   the density a 288px rail can hold. A variant of one component, not a
   second checkout, they even share the click handler. */
.wallet-packs {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.wallet-pack {
    width: 100%;
    display: grid;
    grid-template-columns: auto 1fr auto;
    align-items: baseline;
    gap: var(--space-8);
    padding: var(--space-8);
    min-height: 40px;
    border: 1px solid transparent;
    border-radius: var(--ds-radius-sm);
    background: transparent;
    color: inherit;
    text-align: left;
    cursor: pointer;
    font: inherit;
    transition:
        background-color var(--motion-duration-short) var(--motion-ease-standard),
        border-color var(--motion-duration-short) var(--motion-ease-standard);
}

.wallet-pack:hover {
    background: var(--ds-surface-hover);
    border-color: var(--ds-border);
}

.wallet-pack__tokens {
    font-family: var(--font-display);
    font-size: var(--type-body);
    font-weight: 600;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

.wallet-pack__unit {
    font-size: var(--type-caption);
    color: var(--text-subtle);
}

.wallet-pack__price {
    font-size: var(--type-small);
    font-weight: 600;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

/* The count is the fact you scan for; the sentence under it explains
   what it means. Same value-over-label relationship as the balances. */
.wallet-owned {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--type-h3);
    font-weight: 600;
    line-height: 1.1;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

.wallet-card__note {
    margin: 0;
    font-size: var(--type-caption);
    line-height: 1.5;
    color: var(--text-subtle);
}

.wallet-card .apply-button,
.wallet-card .ghost-button {
    min-height: 40px;
}

/* The wallet header. Balances are the reason the page exists, so they
   are the largest thing on it, but they are numbers in a row, not
   three "stat cards" with borders, washes and a top rule. */
.currency-wallet {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-16);
    margin: 0;
}

.currency-balance {
    display: flex;
    flex-direction: column-reverse; /* value reads first, label explains it */
    gap: var(--space-4);
}

.currency-balance > dt {
    font-size: var(--type-small);
    color: var(--text-muted);
}

/* The number outranks its own label. Before this pass the values were
   rendered dimmer than the words beside them. */
.currency-balance > dd {
    margin: 0;
    display: flex;
    align-items: center;
    gap: var(--space-4);
    font-family: var(--font-display);
    font-size: var(--type-h3);
    font-weight: 600;
    line-height: 1.1;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
    overflow-wrap: anywhere;
}

.currency-section {
    padding-block: var(--step-major);
    border-bottom: 1px solid var(--ds-border);
}

/* The three payment blocks are one job in three forms, so they read as a
   group: closer together, and only the last one closes with a rule. */
#buy-vintokens,
#premium,
#buy-robux,
#other-methods {
    padding-block: var(--step-minor);
}

#buy-vintokens,
#premium,
#buy-robux {
    border-bottom: 0;
}

.currency-section:last-of-type {
    border-bottom: 0;
}

.currency-section > h2,
.currency-section .shop-section-header h2 {
    margin: 0 0 var(--space-8);
    font-size: var(--type-h2);
    line-height: 1.15;
}

.currency-section > .subtle,
.currency-section .shop-section-header .subtle {
    margin: 0 0 var(--space-24);
    max-width: 68ch;
    font-size: var(--type-lede);
    line-height: 1.55;
    color: var(--text-muted);
}

.shop-hud-row {
    display: flex;
    align-items: center;
    gap: var(--space-12);
    flex-wrap: wrap;
}

/* ---------------------------------------------------------------- *
 *  Shop sections
 * ---------------------------------------------------------------- */

.shop-sections {
    display: flex;
    flex-direction: column;
    gap: var(--space-48);
}

/* A collection is a heading and a grid. It was a panel with a border, a
   shadow and a three-layer gradient wash, sitting inside another panel,
   around cards that carry their own border and shadow, four outlines
   in 40px. Spacing separates it now. */
.shop-section {
    position: relative;
}

.shop-section-header {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-16);
    flex-wrap: wrap;
    margin-bottom: var(--space-16);
}

.shop-section-header h3 {
    margin: 0;
    font-size: var(--type-h3);
    font-weight: 600;
    letter-spacing: normal; /* was 0.16em uppercase, a label, not a title */
    text-transform: none;
    line-height: 1.2;
}

.shop-section-subtle {
    margin: var(--space-4) 0 0;
    max-width: 68ch;
    font-size: var(--type-small);
    line-height: 1.5;
    color: var(--text-muted);
}

/* ---------------------------------------------------------------- *
 *  Shop grid + card
 * ---------------------------------------------------------------- */

/* auto-fill, so a collection holding one item does not stretch that
   item across the full row while the collection beside it shows four.
   Both grids on this page now share one track definition.

   The 200px floor is set against the real middle column (~710px inside
   the 1280 container, once the two rails are subtracted), so a
   collection reads as three compact items rather than two oversized
   ones. A shop should look like a shelf. */
.shop-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: var(--space-12);
}

.shop-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-12);
    padding: var(--space-16);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
    background: var(--ds-surface);
    /* One structural cue. The card used to carry two borders, an inset
       highlight and a 60px drop shadow at once. */
    box-shadow: none;
    transition:
        border-color var(--motion-duration-short) var(--motion-ease-standard),
        background-color var(--motion-duration-short) var(--motion-ease-standard);
}

.shop-card:hover {
    border-color: var(--ds-border-strong);
    background: var(--ds-surface-hover);
    /* No lift. The design system set transform:none for the card family;
       this component was the only one disagreeing. */
    transform: none;
}

.shop-card-top {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.shop-card h4 {
    margin: 0;
    font-family: var(--font-display);
    font-size: var(--type-body);
    font-weight: 600;
    letter-spacing: normal;
    line-height: 1.3;
    color: var(--text-strong);
}

.shop-card p {
    margin: 0;
    font-size: var(--type-small);
    line-height: 1.5;
    color: var(--text-muted);
}

/* Price and state share the row directly above the actions: the two
   facts you need to decide. Price is the decision, so it is the larger
   of the two and the only one at full contrast. */
.shop-card-meta {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-8);
    flex-wrap: wrap;
    margin-top: auto;
    padding-top: var(--space-4);
}

.shop-card-price {
    display: inline-flex;
    align-items: center;
    gap: var(--space-4);
    font-size: var(--type-lede);
    font-weight: 600;
    line-height: 1.2;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

/* Gold marks the paid tier, matching /premium and /profile. It is the
   only gold on this page, rarity does not get it. */
.shop-card-price.is-premium {
    font-size: var(--type-body);
    color: var(--premium-gold);
}

.shop-card-price small {
    font-size: var(--type-caption);
    font-weight: 500;
    color: var(--text-muted);
}

/* Rarity: one neutral treatment. It used to be four coloured pills,
   including gold on COMMON, which read as loot-box tiering and put
   five accents in one viewport. */
.shop-card-rarity {
    font-size: var(--type-caption);
    font-weight: 500;
    color: var(--text-subtle);
}

/* State is a status, so it takes the product's status geometry (pill)
   and the product's semantic tones. `owned` is success, `locked` is the
   paid tier, and nothing here is danger. */
.shop-card-status {
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-4) var(--space-8);
    border: 1px solid var(--ds-border);
    border-radius: 999px; /* pill: a compact status, the one place it fits */
    background: transparent;
    font-size: var(--type-caption);
    font-weight: 500;
    letter-spacing: normal;
    text-transform: none;
    color: var(--text-muted);
}

.shop-card-status[data-tone="owned"] {
    border-color: color-mix(in oklab, var(--ds-accent) 45%, transparent);
    color: var(--ds-accent);
}

.shop-card-status[data-tone="premium"] {
    border-color: color-mix(in oklab, var(--premium-gold) 45%, transparent);
    color: var(--premium-gold);
}

/* Two independent apply targets, each a toggle that shows whether the
   decoration is live there. Pressed state is carried by aria-pressed and
   by weight plus fill, never by colour alone. */
.shop-card-targets {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-4);
    font-size: var(--type-caption);
    color: var(--text-subtle);
}

.shop-card-targets__label {
    flex-basis: 100%;
}

.shop-card-price.is-owned {
    font-size: var(--type-body);
    color: var(--ds-accent);
}

.shop-card-target {
    padding: var(--space-4) var(--space-8);
    min-height: 32px;
    border: 1px solid var(--ds-border);
    border-radius: 999px;
    background: transparent;
    color: var(--text-muted);
    font: inherit;
    font-size: var(--type-caption);
    cursor: pointer;
    transition:
        background-color var(--motion-duration-short) var(--motion-ease-standard),
        border-color var(--motion-duration-short) var(--motion-ease-standard),
        color var(--motion-duration-short) var(--motion-ease-standard);
}

.shop-card-target:hover {
    border-color: var(--ds-border-strong);
    color: var(--text-strong);
}

.shop-card-target[aria-pressed="true"] {
    border-color: color-mix(in oklab, var(--ds-accent) 45%, transparent);
    background: color-mix(in oklab, var(--ds-accent) 12%, transparent);
    color: var(--ds-accent);
    font-weight: 600;
}

.shop-card-actions {
    display: flex;
    gap: var(--space-8);
    flex-wrap: wrap;
}

/* The buy action fills the row; preview sits beside it at its natural
   width. Previously three controls competed, one of which was a
   permanently disabled primary reading "Premium only" next to a ghost
   button reading "Premium only". */
.shop-card-actions .apply-button {
    flex: 1 1 auto;
    min-height: 40px;
}

.shop-card-actions .ghost-button {
    flex: 0 0 auto;
    min-height: 40px;
}

.shop-card-actions .ghost-button.previewing {
    border-color: var(--ds-accent);
    color: var(--ds-accent);
}

/* Defence in depth. The card no longer carries `.decorated`, but if it
   ever does again, the decoration must not reach the frame or the copy:
   that is what produced twelve competing card colours and gave two
   cards orange and cyan body text. */
.shop-card::after,
.shop-card::before {
    content: none;
}

.shop-card h4 {
    color: var(--text-strong);
}

.shop-card p {
    color: var(--text-muted);
}

/* ---------------------------------------------------------------- *
 *  Purchase surfaces (Stripe packs, Premium, Robux packs)
 * ---------------------------------------------------------------- */

/* One card design for "pick a thing and pay for it", shared by all
   three checkout rails. There were four. */
.buy-card {
    display: flex;
    flex-direction: column;
    gap: var(--space-8);
    padding: var(--space-16);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
    background: var(--ds-surface);
}

.buy-card > h4 {
    margin: 0;
    font-size: var(--type-body);
    font-weight: 600;
    line-height: 1.3;
    color: var(--text-strong);
}

.buy-card > p {
    margin: 0;
    font-size: var(--type-small);
    line-height: 1.5;
    color: var(--text-muted);
}

/* The money is the point of a checkout card, so it is the biggest thing
   on it. It used to be rendered with the component literally named
   "status": 11.2px uppercase grey metadata, smaller and dimmer than the
   sentence above it. */
/* A merchandising label ("Popular", "Best value"), not a status. It used
   to render with .shop-card-status[data-tone="owned"], borrowing the
   success colour to say something that has nothing to do with success,
   the same semantic borrowing the first audit flagged on this page. */
.buy-card-badge {
    align-self: flex-start;
    padding: var(--space-4) var(--space-8);
    border: 1px solid var(--ds-border);
    border-radius: 999px;
    font-size: var(--type-caption);
    font-weight: 500;
    color: var(--text-muted);
}

.buy-card-price {
    margin-top: auto;
    padding-top: var(--space-8);
    display: flex;
    align-items: baseline;
    gap: var(--space-8);
    flex-wrap: wrap;
}

.buy-card-price > strong {
    font-family: var(--font-display);
    font-size: var(--type-h3);
    font-weight: 600;
    line-height: 1.1;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

/* What you receive, stated once, as supporting text, not as a bordered
   box that reads like a disabled input. */
.buy-card-grant {
    display: inline-flex;
    align-items: center;
    gap: var(--space-4);
    font-size: var(--type-small);
    color: var(--text-muted);
}

.buy-card .apply-button,
.buy-card .ghost-button {
    margin-top: var(--space-8);
    min-height: 40px;
}

/* ---------------------------------------------------------------- *
 *  Page head, converter, notes, alternatives
 * ---------------------------------------------------------------- */

.currency-page-head {
    padding-block: 0 var(--space-8);
}

.currency-page-head > h1 {
    margin: 0 0 var(--space-8);
    font-size: var(--type-hero);
    line-height: 1.05;
}

.currency-page-head > .subtle {
    margin: 0;
    max-width: 62ch;
    font-size: var(--type-lede);
    line-height: 1.55;
    color: var(--text-muted);
}

/* The only form on the page that moves currency: it gets a real label,
   a hint bound to the input via aria-describedby, and a max. It had a
   placeholder standing in for a label and its help text sat after the
   submit button. */
.currency-convert {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-8);
}

.currency-convert__field {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    font-size: var(--type-small);
    color: var(--text-muted);
}

.currency-convert__field input {
    width: 100%;
    min-width: 0;
}

.currency-convert__hint {
    flex-basis: 100%;
    margin: 0;
    font-size: var(--type-caption);
    color: var(--text-subtle);
}

/* Caveats and tax breakdowns: supporting text, one size below body. */
.currency-note {
    margin: var(--space-8) 0 0;
    font-size: var(--type-caption);
    line-height: 1.5;
    color: var(--text-subtle);
}

/* Two manual payment routes. Neither completes a purchase here, so
   neither gets the primary button that the Stripe and Robux cards use,
   they used to be two full-width primaries side by side. */
.currency-alt {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: var(--space-16);
}

.currency-alt > li {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
    padding: var(--space-16);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
}

.currency-alt > li > strong {
    font-size: var(--type-body);
    font-weight: 600;
    color: var(--text-strong);
}

.currency-alt > li > span {
    font-size: var(--type-small);
    line-height: 1.5;
    color: var(--text-muted);
}

.currency-alt > li > .ghost-button {
    margin-top: var(--space-8);
}

/* Robux packs: rows, not a third card grid. Each states what you get,
   what it costs, and why it costs more than the card price. */
.buy-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
    overflow: hidden;
}

.buy-row {
    display: grid;
    grid-template-columns: minmax(0, 8rem) minmax(0, 8rem) minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-8) var(--space-16);
    padding: var(--space-12) var(--space-16);
}

.buy-row + .buy-row {
    border-top: 1px solid var(--ds-border);
}

.buy-row__grant,
.buy-row__price {
    font-family: var(--font-display);
    font-size: var(--type-body);
    font-weight: 600;
    color: var(--text-strong);
    font-variant-numeric: tabular-nums;
}

.buy-row__grant small,
.buy-row__price small {
    font-family: var(--font-sans);
    font-size: var(--type-caption);
    font-weight: 500;
    color: var(--text-muted);
}

.buy-row__note {
    font-size: var(--type-caption);
    color: var(--text-subtle);
}

.buy-row .ghost-button {
    min-height: 36px;
}

@media (max-width: 720px) {
    .buy-row {
        grid-template-columns: minmax(0, 1fr) auto;
    }

    .buy-row__note {
        grid-column: 1 / -1;
    }
}

/* ---------------------------------------------------------------- *
 *  Currency glyphs
 * ---------------------------------------------------------------- */

.vt-coin,
.vy-coin {
    display: inline-block;
    width: 1em;
    height: 1em;
    vertical-align: -0.125em;
    background-repeat: no-repeat;
    background-position: center;
    background-size: contain;
    flex-shrink: 0;
}

.vt-coin {
    background-image: url("/assets/brand/vintokens.svg");
}

.vy-coin {
    background-image: url("/assets/brand/vinyls.svg");
}

/* ---------------------------------------------------------------- *
 *  Status messages
 * ---------------------------------------------------------------- */

/* The page has four status regions. All four used to render identical
   muted grey because the JS wrote raw hex to inline style, which loses
   to the design system's !important. They take data-tone now, like
   every other .form-status in the product. */
.currency-status {
    margin: var(--space-12) 0 0;
    font-size: var(--type-small);
    line-height: 1.5;
}

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

/* Below 900 the rail cannot hold its width, so the layout goes to one
   column and the wallet moves ABOVE the goods: on a phone the first
   question is still "what can I afford", and the answer should not be
   six sections down. Same breakpoint the Workspace OS rail uses. */
/* Three columns need roughly 1200px before the middle one starts
   squeezing the cards. Below that the in-page nav goes first: the
   wallet is the one you spend from, the nav is a convenience. */
@media (max-width: 1200px) {
    .shop-layout {
        grid-template-columns: minmax(0, 1fr) var(--shop-rail-w);
    }

    .shop-nav {
        display: none;
    }
}

@media (max-width: 1040px) {
    .shop-hero {
        grid-template-columns: minmax(0, 1fr);
        gap: var(--space-24);
    }

    /* Four tiles in a row read as a filmstrip rather than a stack of
       squares once the hero is full-width. */
    .shop-hero__art {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }

    .shop-hero__art .shop-hero__tile {
        height: 96px;
    }
}

@media (max-width: 900px) {
    /* The shell pads `main` by 56px and `.container` by another 64px on
       narrow viewports, so the page opened on 120px of empty screen
       before anything. Scoped to this page rather than changing the
       shell, which every other surface shares. */
    /* The rail shell's own rule is
       `body[data-nav-shell="rail"] #app-shell main > .container`, which
       carries an id, a plain page-scoped selector loses to it. Matching
       that shape and adding the page attribute is the minimum needed to
       win, and it keeps the override provably local to /currency. */
    body[data-page="currency"][data-nav-shell="rail"] #app-shell main > .container {
        padding-top: var(--space-16);
    }

    /* `display: contents` dissolves the rail so its cards become items of
       the page's own column. That lets the balance sit above the goods
       while the two secondary wallet tasks fall below them, a phone
       reader wants "what can I afford" first and "trade my Vinyls" last,
       and those two answers are not adjacent. */
    .shop-layout {
        display: flex;
        flex-direction: column;
        gap: var(--space-24);
        padding-top: var(--space-8);
    }

    .shop-wallet {
        display: contents;
    }

    .shop-layout__main {
        order: 0;
    }

    .wallet-card {
        order: 1;
    }

    /* Balance only: compact, above the shop. */
    .wallet-card--balance {
        order: -1;
        flex-direction: row;
        flex-wrap: wrap;
        align-items: center;
        gap: var(--space-12) var(--space-16);
    }

    .wallet-card--balance .wallet-card__title {
        flex-basis: 100%;
    }

    .wallet-card--balance .currency-wallet {
        flex: 1 1 auto;
        gap: var(--space-16);
    }

    .wallet-card--balance .apply-button {
        flex: 0 0 auto;
    }
}

/* 768 used to force every grid to one column, which turned a 768px
   tablet into a stack of 688px-wide cards and buttons and added ~3,300px
   of page for 32px of lost width. The grids already auto-fill from a
   200px floor, so they are left alone here and only collapse when the
   floor genuinely stops fitting. */
@media (max-width: 600px) {
    .shop-grid,
    .currency-alt {
        grid-template-columns: 1fr;
    }

    .shop-card-actions .apply-button,
    .shop-card-actions .ghost-button {
        flex: 1 1 100%;
    }
}

@media (max-width: 768px) {

    /* Section titles step down one level so the page title still leads. */
    .currency-section > h2,
    .currency-section .shop-section-header h2 {
        font-size: var(--type-h3);
    }

    .shop-hero__copy > h1 {
        font-size: var(--type-h2);
    }

    .currency-section > .subtle,
    .shop-hero__copy > p {
        font-size: var(--type-body);
    }

    .shop-hero {
        padding-block: var(--space-8) var(--space-24);
    }

    .shop-hero__actions {
        margin-bottom: var(--space-24);
    }

    .shop-hero__actions .apply-button,
    .shop-hero__actions .ghost-button {
        flex: 1 1 100%;
    }

    .shop-hero__facts {
        gap: var(--space-24);
    }

    /* Two rows of two beats a four-across filmstrip of 70px slivers. */
    .shop-hero__art {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (max-width: 480px) {
    .shop-card-meta {
        gap: var(--space-4);
    }

    /* One wallet card per row; two 130px columns of numbers is not a
       layout, it is a collision waiting for a five-digit balance. */
    .shop-wallet {
        grid-template-columns: 1fr;
    }

    .shop-section-header {
        gap: var(--space-8);
    }

    /* Touch targets: the shell's buttons are 40px, which is the floor
       here too, nothing in the shop gets smaller on the smallest
       screen, where it matters most. */
    .shop-card-actions .apply-button,
    .shop-card-actions .ghost-button,
    .wallet-card .apply-button,
    .wallet-card .ghost-button,
    .buy-card .apply-button,
    .buy-card .ghost-button {
        min-height: 44px;
    }
}


/* =====================================================================
   Decoration artwork
   ---------------------------------------------------------------------
   Below this line is the live-preview stage and the purchase
   celebration overlay: the decorations themselves, which ARE the
   product being sold, plus the WebGL/CSS effect scaffolding that
   public/ux/shell/decoration-fx.js drives. It is preserved from the
   pre-August-2026 stylesheet rather than re-authored, because changing
   it would change the goods, not the interface around them.

   The one correction applied here: --decoration-accent no longer
   reaches the LIVE indicator or the selected-state button, and the
   dead [data-motion] branches are gone (every pack ships a `preview`,
   so the motion fallback never rendered).
   ===================================================================== */


.shop-card-preview  {
        position: relative;
        height: 116px;
        border-radius: 12px;
        overflow: hidden;
        border: 1px solid rgba(255, 255, 255, 0.1);
        background: rgba(4, 6, 12, 0.6);
        isolation: isolate;
        box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
    }

.shop-card-preview__bg  {
        position: absolute;
        inset: -14%;
        background: var(--decoration-bg, transparent);
        background-size: 150% 150%;
        background-repeat: no-repeat;
        filter: saturate(1.16) contrast(1.06);
        transform: translate3d(0, 0, 0);
        will-change: transform, background-position;
    }

.shop-card-preview__sheen  {
        position: absolute;
        inset: 0;
        background: linear-gradient(
            115deg,
            transparent 0%,
            transparent 38%,
            rgba(255, 255, 255, 0.22) 50%,
            transparent 62%,
            transparent 100%
        );
        transform: translateX(-120%);
        opacity: 0.9;
        mix-blend-mode: screen;
    }

.shop-card-preview__label  {
        position: absolute;
        left: 12px;
        bottom: 10px;
        z-index: 2;
        font-size: 0.62rem;
        letter-spacing: 0.22em;
        text-transform: uppercase;
        color: rgba(255, 255, 255, 0.82);
        text-shadow: 0 2px 8px rgba(0, 0, 0, 0.6);
    }


@media (prefers-reduced-motion: no-preference) {
        .shop-card:hover .shop-card-preview__sheen {
            animation: vshopSheen 0.9s var(--motion-ease-decelerate);
        }
}

.vshop-pop  {
        position: fixed;
        inset: 0;
        z-index: 1400;
        display: grid;
        place-items: center;
        padding: 24px;
        box-sizing: border-box;
        /* The purchase dialog is the only way to complete a decoration
           purchase. Without these it overflowed a small or landscape
           phone with no way to reach the confirm button. */
        overflow-y: auto;
        overscroll-behavior: contain;
        opacity: 0;
        visibility: hidden;
        pointer-events: none;
        transition:
            opacity var(--motion-duration-medium) var(--motion-ease-standard),
            visibility 0s linear var(--motion-duration-medium);
    }

.vshop-pop[data-open="true"]  {
        opacity: 1;
        visibility: visible;
        pointer-events: auto;
        transition:
            opacity var(--motion-duration-medium) var(--motion-ease-standard),
            visibility 0s linear 0s;
    }

.vshop-pop__scrim  {
        position: absolute;
        inset: 0;
        background:
            radial-gradient(130% 120% at 50% -10%, rgba(14, 18, 28, 0.72), transparent 60%),
            rgba(3, 5, 11, 0.86);
        backdrop-filter: blur(7px);
    }

.vshop-pop__card  {
    max-height: calc(100dvh - var(--space-48));
    overflow-y: auto;
        position: relative;
        width: min(430px, 100%);
        border-radius: 24px;
        padding: 30px 26px 24px;
        text-align: center;
        background: linear-gradient(168deg, rgba(21, 25, 36, 0.99), rgba(11, 14, 21, 0.99));
        border: 1px solid rgba(255, 255, 255, 0.14);
        box-shadow:
            0 50px 130px rgba(2, 5, 14, 0.74),
            inset 0 1px 0 rgba(255, 255, 255, 0.09);
        transform: translateY(22px) scale(0.94);
        opacity: 0;
        overflow: hidden;
        transition:
            transform var(--motion-duration-long) var(--motion-ease-decelerate),
            opacity var(--motion-duration-medium) var(--motion-ease-standard);
    }

.vshop-pop[data-open="true"] .vshop-pop__card  {
        transform: translateY(0) scale(1);
        opacity: 1;
    }

.vshop-pop__card::before  {
        content: "";
        position: absolute;
        inset: 0 0 auto 0;
        height: 4px;
        background: linear-gradient(
            90deg,
            transparent,
            var(--vshop-accent, #5fe3c5),
            transparent
        );
        opacity: 0.9;
    }

.vshop-pop__card::after  {
        content: "";
        position: absolute;
        inset: -40% -30% auto -30%;
        height: 220px;
        background: radial-gradient(
            60% 100% at 50% 0%,
            color-mix(in srgb, var(--vshop-accent, #5fe3c5) 26%, transparent),
            transparent 70%
        );
        pointer-events: none;
        z-index: 0;
    }

.vshop-pop__card > *  {
        position: relative;
        z-index: 1;
    }

.vshop-pop__orb  {
        width: 72px;
        height: 72px;
        margin: 4px auto 16px;
        border-radius: 999px;
        display: grid;
        place-items: center;
        font-size: 1.9rem;
        color: #041018;
        background:
            radial-gradient(circle at 35% 28%, rgba(255, 255, 255, 0.9), transparent 60%),
            linear-gradient(160deg, var(--vshop-accent, #5fe3c5), color-mix(in srgb, var(--vshop-accent, #5fe3c5) 55%, #0a0d14));
        box-shadow:
            0 0 0 6px color-mix(in srgb, var(--vshop-accent, #5fe3c5) 14%, transparent),
            0 18px 40px color-mix(in srgb, var(--vshop-accent, #5fe3c5) 30%, transparent);
    }

.vshop-pop[data-open="true"] .vshop-pop__orb  {
        animation: vshopOrbPop 0.6s var(--motion-ease-decelerate) both;
    }

.vshop-pop__orb--sphere  {
        --s-hot: #cfe8ff;
        --s-mid: #5fb8ff;
        --s-deep: #143a6e;
        --s-rim: #050a14;
        width: 150px;
        height: 150px;
        margin: 10px auto 24px;
        position: relative;
        overflow: hidden;
        border-radius: 999px;
        color: transparent;
        background:
            radial-gradient(circle at 32% 27%,
                var(--s-hot) 0%,
                var(--s-mid) 34%,
                var(--s-deep) 70%,
                var(--s-rim) 100%);
        box-shadow:
            inset -9px -11px 26px rgba(0, 0, 0, 0.5),
            inset 9px 10px 22px rgba(255, 255, 255, 0.14),
            0 0 0 5px color-mix(in srgb, var(--decoration-accent, #5fe3c5) 14%, transparent),
            0 22px 52px color-mix(in srgb, var(--decoration-accent, #5fe3c5) 36%, transparent);
    }

.vshop-pop__orb--sphere[data-preview="ember"]  {
        --s-hot: #ffe39a; --s-mid: #ff7a1f; --s-deep: #8f2600; --s-rim: #1c0602;
    }

.vshop-pop__orb--sphere[data-preview="galaxy"]  {
        --s-hot: #d9c9ff; --s-mid: #6f53ff; --s-deep: #241a63; --s-rim: #06030f;
    }

.vshop-pop__orb--sphere[data-preview="aurora"]  {
        --s-hot: #c7ffe6; --s-mid: #3fd6a6; --s-deep: #155a4b; --s-rim: #04120d;
    }

.vshop-pop__orb--sphere[data-preview="arcade"]  {
        --s-hot: #ffd0f3; --s-mid: #ff5ed0; --s-deep: #6e1a63; --s-rim: #12041a;
    }

.vshop-pop__sphere-tilt  {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        border-radius: inherit;
        opacity: 0.72;
        mix-blend-mode: screen;
        -webkit-mask-image: radial-gradient(circle at 50% 47%, #000 0%, #000 46%, transparent 76%);
        mask-image: radial-gradient(circle at 50% 47%, #000 0%, #000 46%, transparent 76%);
        transform-style: preserve-3d;
        transition: transform 120ms linear;
        will-change: transform;
    }

.vshop-pop__orb--sphere canvas.fx-canvas,
    .vshop-pop__sphere-tilt > *  {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        display: block;
        border-radius: inherit;
    }

.vshop-pop__orb--sphere::after  {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        pointer-events: none;
        z-index: 2;
        background: radial-gradient(
            26% 20% at var(--hx, 30%) var(--hy, 24%),
            rgba(255, 255, 255, 0.96),
            rgba(255, 255, 255, 0.16) 38%,
            transparent 56%
        );
        mix-blend-mode: screen;
    }

.vshop-pop__orb--sphere::before  {
        content: "";
        position: absolute;
        inset: 0;
        border-radius: inherit;
        pointer-events: none;
        z-index: 1;
        background: radial-gradient(
            circle at 72% 76%,
            color-mix(in srgb, var(--s-mid) 50%, transparent) 0%,
            transparent 34%
        );
        mix-blend-mode: screen;
        opacity: 0.5;
    }

.vshop-pop__orb--ball  {
        width: 164px;
        height: 164px;
        margin: 10px auto 24px;
        position: relative;
        background: none;
        box-shadow: none;
        overflow: visible;
        perspective: 760px;
    }

.vshop-pop__orb--ball::before  {
        content: "";
        position: absolute;
        inset: -16%;
        border-radius: 999px;
        background: radial-gradient(
            circle at 50% 48%,
            color-mix(in srgb, var(--decoration-accent, #5fe3c5) 52%, transparent),
            transparent 66%
        );
        filter: blur(16px);
        opacity: 0.85;
        z-index: 0;
    }

.vshop-pop__ball-tilt  {
        position: relative;
        z-index: 1;
        width: 100%;
        height: 100%;
        transform-style: preserve-3d;
        transition: transform 120ms linear;
        will-change: transform;
    }

.vshop-pop__orb--ball canvas.fx-canvas  {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
        display: block;
        filter: drop-shadow(0 18px 34px rgba(0, 0, 0, 0.5));
    }

.vshop-pop__orb--ball::after  {
        content: "";
        position: absolute;
        inset: 8%;
        border-radius: 999px;
        pointer-events: none;
        z-index: 2;
        background: radial-gradient(
            22% 18% at var(--hx, 32%) var(--hy, 24%),
            rgba(255, 255, 255, 0.85),
            rgba(255, 255, 255, 0.12) 40%,
            transparent 58%
        );
        mix-blend-mode: screen;
        opacity: 0.9;
    }

.vshop-pop__orb--ball:not(.is-tracking) .vshop-pop__ball-tilt  {
        animation: vshopBallFloat 7s ease-in-out infinite;
    }

.vshop-pop__orb--ball.is-tracking .vshop-pop__ball-tilt  {
        animation: none;
    }

@media (prefers-reduced-motion: reduce) {.vshop-pop__orb--ball .vshop-pop__ball-tilt  {
            animation: none;
            transition: none;
            transform: none;
        }
}

.vshop-pop__orb--leaf  {
        width: 150px;
        height: 150px;
        margin: 10px auto 24px;
        position: relative;
        background: none;
        box-shadow: none;
        overflow: visible;
    }

.vshop-pop__orb--leaf::before  {
        content: "";
        position: absolute;
        inset: -8%;
        border-radius: 999px;
        background: radial-gradient(
            circle at 50% 48%,
            color-mix(in srgb, var(--decoration-accent, #ff9d4d) 42%, transparent),
            transparent 66%
        );
        filter: blur(9px);
        z-index: 0;
    }

.vshop-pop__leaf-tilt  {
        position: relative;
        z-index: 1;
        width: 100%;
        height: 100%;
        transition: transform 120ms linear;
        will-change: transform;
    }

.vshop-pop__leaf  {
        position: relative;
        z-index: 1;
        display: block;
        width: 100%;
        height: 100%;
        background: url("/assets/decorations/fall/leaf-3.png") center / 74% no-repeat;
        image-rendering: pixelated;
        filter: drop-shadow(0 8px 12px rgba(0, 0, 0, 0.5));
        animation: vshopLeafFloat 4.6s ease-in-out infinite;
    }

.vshop-pop__orb--star  {
        width: 168px;
        height: 168px;
        margin: 10px auto 24px;
        background: none;
        box-shadow: none;
        overflow: visible;
        perspective: 900px;
    }

.vshop-pop__orb--star::before  {
        content: "";
        position: absolute;
        inset: -16%;
        border-radius: 999px;
        background: radial-gradient(
            circle at 50% 46%,
            color-mix(in srgb, var(--vshop-accent, #5fe3c5) 50%, transparent),
            transparent 66%
        );
        filter: blur(7px);
        z-index: 0;
    }

.vshop-pop__star3d  {
        position: relative;
        z-index: 1;
        width: 168px;
        height: 168px;
        transform-style: preserve-3d;
        transform: rotateX(8deg) rotateY(-12deg);
        transition: transform 120ms var(--motion-ease-standard);
        will-change: transform;
    }

.vshop-pop__star-fx  {
        position: absolute;
        inset: 0;
        clip-path: polygon(
            50% 1%, 61.8% 35.5%, 98% 35.5%, 68.2% 57.5%,
            79.4% 91%, 50% 70%, 20.6% 91%, 31.8% 57.5%,
            2% 35.5%, 38.2% 35.5%
        );
        transform: translateZ(8px);
        overflow: hidden;
        background: #05060c;
        backface-visibility: hidden;
    }

.vshop-pop__star-fx > *  {
        position: absolute;
        inset: 0;
        width: 100%;
        height: 100%;
    }

.vshop-pop__star-fx canvas.fx-canvas  { display: block; }

.vshop-pop__star-fx .vfall  { inset: 0; }

.vshop-pop__star3d[data-fx="true"] .vshop-pop__star-shine  {
        background: radial-gradient(54% 48% at 33% 24%, rgba(255, 255, 255, 0.6), transparent 60%);
        mix-blend-mode: screen;
        opacity: 0.85;
    }

.vshop-pop__orb--star:not(.is-tracking) .vshop-pop__star3d  {
        animation: vshopStarFloat 7s ease-in-out infinite;
    }

.vshop-pop__orb--star.is-tracking .vshop-pop__star3d  {
        animation: none;
        transition: transform 120ms linear;
    }

.vshop-pop__star-layer,
    .vshop-pop__star-shine  {
        position: absolute;
        inset: 0;
        clip-path: polygon(
            50% 1%, 61.8% 35.5%, 98% 35.5%, 68.2% 57.5%,
            79.4% 91%, 50% 70%, 20.6% 91%, 31.8% 57.5%,
            2% 35.5%, 38.2% 35.5%
        );
        backface-visibility: hidden;
    }

.vshop-pop__star-layer  {
        transform: translateZ(calc((4.5 - var(--i, 0)) * 1.7px));
        background: color-mix(
            in srgb,
            var(--vshop-accent, #5fe3c5) calc((10 - var(--i, 0)) * 9%),
            #04060c
        );
    }

.vshop-pop__star-shine  {
        transform: translateZ(8.4px);
        background:
            radial-gradient(58% 52% at 34% 26%, rgba(255, 255, 255, 0.95), transparent 58%),
            linear-gradient(
                152deg,
                color-mix(in srgb, var(--vshop-accent, #5fe3c5) 78%, #ffffff),
                color-mix(in srgb, var(--vshop-accent, #5fe3c5) 64%, #05070d)
            );
    }

@media (prefers-reduced-motion: reduce) {.vshop-pop__orb--star .vshop-pop__star3d  {
            animation: none;
            transition: none;
            transform: rotateX(8deg) rotateY(-10deg);
        }
}

.vshop-pop__title  {
        margin: 0 0 8px;
        font-family: var(--font-display);
        font-size: 1.32rem;
        letter-spacing: 0.01em;
        color: var(--text-strong);
    }

.vshop-pop__body  {
        margin: 0 auto 18px;
        max-width: 340px;
        font-size: 0.92rem;
        line-height: 1.55;
        color: rgba(242, 242, 242, 0.74);
    }

.vshop-pop__body strong  { color: #fff; }

.vshop-pop__perks  {
        display: flex;
        flex-direction: column;
        gap: 8px;
        margin: 16px auto 0;
        max-width: 280px;
        text-align: left;
    }

.vshop-pop__perks > span  {
        position: relative;
        padding-left: 22px;
        font-size: 0.86rem;
        line-height: 1.4;
        color: rgba(242, 242, 242, 0.84);
    }

.vshop-pop__perks > span::before  {
        content: "";
        position: absolute;
        left: 3px;
        top: 0.42em;
        width: 8px;
        height: 8px;
        border-radius: 2px;
        transform: rotate(45deg);
        background: var(--vshop-accent, #5fe3c5);
        box-shadow: 0 0 9px color-mix(in srgb, var(--vshop-accent, #5fe3c5) 60%, transparent);
    }

.vshop-pop__meta  {
        display: flex;
        flex-wrap: wrap;
        gap: 10px;
        justify-content: center;
        margin-bottom: 20px;
    }

.vshop-pop__chip  {
        display: inline-flex;
        flex-direction: column;
        align-items: center;
        gap: 2px;
        min-width: 92px;
        padding: 10px 14px;
        border-radius: 12px;
        background: rgba(255, 255, 255, 0.05);
        border: 1px solid rgba(255, 255, 255, 0.1);
    }

.vshop-pop__chip span  {
        font-size: 0.62rem;
        letter-spacing: 0.16em;
        text-transform: uppercase;
        color: rgba(242, 242, 242, 0.55);
    }

.vshop-pop__chip strong  {
        font-size: 1.05rem;
        color: var(--text-strong);
    }

.vshop-pop__chip[data-tone="short"] strong  { color: #ff7a7a; }

.vshop-pop__chip[data-tone="good"] strong  { color: #5fe3c5; }

.vshop-pop__meter  {
        height: 8px;
        border-radius: 999px;
        background: rgba(255, 255, 255, 0.08);
        overflow: hidden;
        margin: 0 auto 18px;
        max-width: 320px;
    }

.vshop-pop__meter > i  {
        display: block;
        height: 100%;
        width: var(--vshop-fill, 0%);
        border-radius: 999px;
        background: linear-gradient(90deg, #f3c061, #ff7a7a);
        transition: width var(--motion-duration-long) var(--motion-ease-decelerate);
    }

.vshop-pop__actions  {
        display: flex;
        gap: 10px;
        justify-content: center;
        flex-wrap: wrap;
    }

.vshop-pop__actions .apply-button,
    .vshop-pop__actions .ghost-button  {
        flex: 1 1 auto;
        min-width: 130px;
        justify-content: center;
    }

.vshop-pop__dismiss  {
        position: absolute;
        top: 14px;
        right: 14px;
        z-index: 2;
        width: 32px;
        height: 32px;
        padding: 0;
        border-radius: 999px;
        border: 1px solid rgba(255, 255, 255, 0.12);
        background: rgba(255, 255, 255, 0.06);
        color: rgba(242, 242, 242, 0.7);
        font-size: 0;
        line-height: 0;
        font-family: inherit;
        cursor: pointer;
        display: grid;
        place-items: center;
        transition:
            background var(--motion-duration-short) var(--motion-ease-standard),
            border-color var(--motion-duration-short) var(--motion-ease-standard),
            color var(--motion-duration-short) var(--motion-ease-standard);
    }

.vshop-pop__dismiss::before,
    .vshop-pop__dismiss::after  {
        content: "";
        grid-area: 1 / 1;
        width: 13px;
        height: 1.6px;
        border-radius: 2px;
        background: currentColor;
    }

.vshop-pop__dismiss::before  { transform: rotate(45deg); }

.vshop-pop__dismiss::after  { transform: rotate(-45deg); }

.vshop-pop__dismiss:hover  {
        background: rgba(255, 255, 255, 0.14);
        border-color: rgba(255, 255, 255, 0.24);
        color: #fff;
    }

.vshop-pop__preview  {
        height: 92px;
        border-radius: 16px;
        margin: 0 0 18px;
        overflow: hidden;
        position: relative;
        border: 1px solid rgba(255, 255, 255, 0.12);
    }

.vshop-pop__preview > i  {
        position: absolute;
        inset: -12%;
        background: var(--decoration-bg, transparent);
        background-size: 150% 150%;
        filter: saturate(1.15);
    }

.vshop-burst  {
        position: fixed;
        inset: 0;
        z-index: 1500;
        pointer-events: none;
        overflow: hidden;
    }

.vshop-burst > i  {
        position: absolute;
        top: var(--y, 50%);
        left: var(--x, 50%);
        width: 11px;
        height: 11px;
        border-radius: 4px;
        background: var(--c, #5fe3c5);
        opacity: 0;
        transform: translate(-50%, -50%);
        will-change: transform, opacity;
    }

.vshop-burst > i.vshop-leaf  {
        width: 24px;
        height: 24px;
        border-radius: 0;
        background-color: transparent;
        background-repeat: no-repeat;
        background-position: center;
        background-size: contain;
        image-rendering: pixelated;
        filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.45));
    }

.vshop-burst[data-run="true"] > i  {
        animation: vshopConfetti 1100ms var(--motion-ease-decelerate) forwards;
        animation-delay: var(--d, 0ms);
    }

.vshop-coin  {
        position: fixed;
        z-index: 1500;
        pointer-events: none;
        font-size: 1.4rem;
        opacity: 0;
        transform: translate(-50%, -50%);
        filter: drop-shadow(0 6px 12px rgba(0, 0, 0, 0.5));
    }

.vshop-coin[data-run="true"]  {
        animation: vshopCoinFly 760ms var(--motion-ease-decelerate) forwards;
    }

@media (prefers-reduced-motion: reduce) {.vshop-burst > i,
        .vshop-coin  { display: none; }
}

.vshop-shock  {
        position: fixed;
        z-index: 1499;
        pointer-events: none;
        width: 44px;
        height: 44px;
        border-radius: 999px;
        border: 2px solid var(--c, #5fe3c5);
        transform: translate(-50%, -50%) scale(0.3);
        opacity: 0;
    }

.vshop-shock[data-run="true"]  {
        animation: vshopShock 720ms var(--motion-ease-decelerate) forwards;
    }

.vt-coin,
    .vy-coin  {
        display: inline-block;
        width: 1em;
        height: 1em;
        vertical-align: -0.16em;
        margin-right: 0.34em;
        flex-shrink: 0;
        background-repeat: no-repeat;
        background-position: center;
        background-size: contain;
    }

.vt-coin  { background-image: url("/assets/brand/vintokens.svg"); }

.vy-coin  { background-image: url("/assets/brand/vinyls.svg"); }

.stat-card strong .vt-coin,
    .stat-card strong .vy-coin  {
        width: 0.84em;
        height: 0.84em;
        margin-right: 0.28em;
        filter: drop-shadow(0 4px 10px rgba(0, 0, 0, 0.4));
    }

.shop-card-price .vt-coin  { margin-right: 0.28em; }

.shop-card-preview--terminal  { background: #04070a; }

.shop-card-preview--arcade  {
        background:
            radial-gradient(circle at 50% 42%, rgba(255, 141, 232, 0.14), transparent 70%),
            #0b0710;
    }

.shop-card-preview--fx  { background: #05060c; }

.shop-card-preview[data-preview="galaxy"] { background: #04030a; }

.shop-card-preview[data-preview="ember"] {
        background: linear-gradient(180deg, #1a0805 0%, #0d0504 100%);
    }

.shop-card-preview[data-preview="aurora"] {
        background:
            radial-gradient(120% 70% at 50% 100%, rgba(40, 90, 80, 0.25), transparent 70%),
            linear-gradient(180deg, #03040f 0%, #050b14 55%, #02050a 100%);
    }

.shop-card-preview[data-preview="aurora"]::before {
        content: "";
        position: absolute;
        inset: 0;
        background-size: 120px 120px, 90px 90px;
        background-image:
            radial-gradient(1.2px 1.2px at 18px 16px, rgba(255, 255, 255, 0.85), transparent 60%),
            radial-gradient(1px 1px at 64px 44px, rgba(255, 255, 255, 0.6), transparent 60%);
        opacity: 0.7;
    }

.shop-card-preview[data-preview="ocean"] {
        background: linear-gradient(180deg, #0a3454 0%, #06203a 48%, #030b1c 100%);
    }

.shop-card-preview[data-preview="holo"] {
        background:
            radial-gradient(90% 70% at 30% 20%, rgba(143, 246, 255, 0.10), transparent 70%),
            linear-gradient(150deg, #0a0c16 0%, #0c0a18 60%, #060810 100%);
    }

/* The grid packs (Noirline, Ivory Inverse) ship their artwork as a CSS
   background: ink-black with white tracers, and its inverse, porcelain
   with black ink. The stage used to be pinned to a dark gradient and
   the WebGL tracer layer is additive, so Ivory Inverse, the one pack
   whose whole concept is inversion, rendered as a solid black
   rectangle. Showing the decoration's own background means the preview
   shows the thing being sold. */
.shop-card-preview[data-preview="grid"] {
        background: var(--decoration-bg, linear-gradient(160deg, #07090f 0%, #05060c 100%));
        background-size: cover;
    }
