/* =========================================================================
   NOBP 504 — merch page
   -------------------------------------------------------------------------
   Layered on top of nobp.css, which already owns the header, buttons, footer
   and the colour/type tokens. Nothing here redefines a token: --gold,
   --cream, --font-display and the rest all come from there, so the shop
   cannot drift away from the rest of the station.

   Gold is a focus colour on black here, never a fill. It appears on borders,
   prices, one CTA and the active filter — and nowhere else.
   ========================================================================= */

/* ── hero ─────────────────────────────────────────────────────────────── */
.merch-hero {
  position: relative;
  /* The header is sticky, so the first thing in the hero sits UNDER it unless
     the header's own height is added to the top padding. Without this the
     eyebrow line was clipped in half on every desktop width. */
  padding-top: calc(var(--header-h) + clamp(28px, 5vh, 70px));
  padding-bottom: clamp(36px, 7vh, 88px);
  overflow: hidden;
  background:
    radial-gradient(ellipse 70% 90% at 78% 40%, rgba(217, 166, 46, 0.11), transparent 62%),
    linear-gradient(180deg, #080807 0%, var(--black) 100%);
  border-bottom: 1px solid var(--line-soft);
}

/* A faint speaker grille, drawn rather than loaded — two repeating gradients
   cost nothing and never 404. Very low contrast on purpose: it should read as
   texture at arm's length, not as a pattern. */
.merch-hero::before {
  content: '';
  position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-image:
    repeating-linear-gradient(0deg, rgba(247,240,223,0.028) 0 1px, transparent 1px 5px),
    repeating-linear-gradient(90deg, rgba(247,240,223,0.018) 0 1px, transparent 1px 5px);
  mask-image: radial-gradient(ellipse 80% 70% at 50% 50%, #000 30%, transparent 78%);
}

.merch-hero-inner {
  position: relative; z-index: 1;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(28px, 5vw, 64px);
  align-items: center;
}

.merch-eyebrow {
  margin: 0 0 14px;
  font-family: var(--font-ui); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.24em;
  font-size: 11px; color: var(--gold);
}

/* FEEL DAT BEAT! — the slogan, spelled exactly, never autocorrected.
   clamp()'s upper bound is deliberately large: this is the loudest thing on
   the page and it should be the first thing read from across a room. */
.merch-headline {
  margin: 0;
  font-family: var(--font-display); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.01em; line-height: 0.92;
  font-size: clamp(50px, 9vw, 116px);
  color: var(--cream);
  text-wrap: balance;
}
.merch-headline .bang { color: var(--gold-bright); }

.merch-sub {
  margin: 18px 0 0;
  font-family: var(--font-display); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.08em;
  font-size: clamp(14px, 1.6vw, 19px);
  color: var(--muted);
}

.merch-cta-row { margin-top: clamp(22px, 3.4vh, 34px); }

.merch-notes {
  list-style: none;
  margin: clamp(26px, 4vh, 40px) 0 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: 10px 26px;
}
.merch-notes li {
  font-family: var(--font-ui); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.16em;
  font-size: 10px; color: var(--muted);
  display: flex; align-items: center; gap: 9px;
}
.merch-notes li::before {
  content: ''; width: 5px; height: 5px; border-radius: 50%;
  background: var(--gold-deep); flex: none;
}

/* The hero composition. With no photography yet this is a composed stack of
   the real logo on a lit plinth rather than a grey box — it has to look like
   an intentional brand slide, because that is what the owner will see. */
.merch-showcase {
  position: relative;
  aspect-ratio: 4 / 3;
  border-radius: var(--r-xl);
  border: 1px solid var(--line-soft);
  background:
    radial-gradient(ellipse 60% 44% at 50% 80%, rgba(217,166,46,0.20), transparent 68%),
    linear-gradient(170deg, #14130f 0%, #0a0a09 58%, #070706 100%);
  display: grid; place-items: center;
  overflow: hidden;
  box-shadow: inset 0 1px 0 rgba(247,240,223,0.05), 0 30px 80px rgba(0,0,0,0.6);
}
.merch-showcase img {
  width: min(62%, 420px); height: auto; display: block;
  filter: drop-shadow(0 18px 36px rgba(0,0,0,0.75));
}
.merch-showcase figcaption {
  position: absolute; left: 0; right: 0; bottom: 14px;
  font-family: var(--font-ui); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.2em;
  font-size: 10px; color: var(--muted); text-align: center;
}

/* ── filter rail ──────────────────────────────────────────────────────── */
.merch-toolbar {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 18px; flex-wrap: wrap;
  padding-block: clamp(24px, 4vh, 40px) 14px;
}
.merch-toolbar h2 {
  margin: 0; font-family: var(--font-display); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.05em;
  font-size: clamp(19px, 2.3vw, 27px); color: var(--cream);
}
.merch-count {
  margin: 0; font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.merch-filters {
  display: flex; gap: 10px;
  padding-bottom: 6px;
  /* Horizontal rail on small screens, with the scrollbar hidden — the row is
     obviously swipeable from the cut-off chip at the edge, and a visible bar
     under five buttons looks like a mistake. */
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.merch-filters::-webkit-scrollbar { display: none; }

.merch-filter {
  flex: 0 0 auto;
  padding: 10px 18px;
  min-height: 44px;                       /* touch target floor */
  border-radius: 999px;
  border: 1px solid var(--line-soft);
  background: transparent;
  color: var(--muted);
  font-family: var(--font-display); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.12em; font-size: 12px;
  transition: color 180ms var(--ease), border-color 180ms var(--ease),
              background-color 180ms var(--ease);
}
.merch-filter:hover { color: var(--cream); border-color: var(--gold-deep); }
.merch-filter[aria-selected="true"] {
  color: var(--black); background: var(--gold); border-color: var(--gold);
  font-weight: 600;
}

/* ── grid ─────────────────────────────────────────────────────────────── */
.merch-grid {
  list-style: none; margin: 0; padding: 0 0 clamp(44px, 7vw, 76px);
  display: grid; gap: clamp(16px, 2vw, 26px);
  grid-template-columns: 1fr;
}

.merch-card {
  position: relative;
  display: flex; flex-direction: column;
  border: 1px solid var(--line-soft);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
  transition: transform 200ms var(--ease), border-color 200ms var(--ease),
              box-shadow 200ms var(--ease);
}
.merch-card:hover,
.merch-card:focus-within {
  transform: translateY(-4px);
  border-color: var(--gold-deep);
  box-shadow: 0 18px 44px rgba(0, 0, 0, 0.55);
}

.merch-badge {
  position: absolute; top: 12px; left: 12px; z-index: 2;
  padding: 5px 11px; border-radius: 999px;
  background: var(--gold); color: var(--black);
  font-family: var(--font-ui); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.14em; font-size: 9.5px;
}

.merch-fav {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 44px; height: 44px; display: grid; place-items: center;
  border: 0; border-radius: 50%; background: rgba(5,5,5,0.55);
  backdrop-filter: blur(8px);
  color: var(--muted);
  transition: color 180ms var(--ease), background-color 180ms var(--ease);
}
.merch-fav svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.merch-fav:hover { color: var(--cream); background: rgba(5,5,5,0.75); }
.merch-fav[aria-pressed="true"] { color: var(--gold-bright); }
.merch-fav[aria-pressed="true"] svg { fill: currentColor; stroke: currentColor; }

/* The product shot. Fixed aspect box + contain, so every render — a tall
   hoodie, a round rug, a wide speaker — occupies the same footprint and
   dropping in a real PNG never reflows the card. */
.merch-shot {
  position: relative;
  aspect-ratio: 1 / 1;
  background:
    radial-gradient(ellipse 58% 40% at 50% 78%, rgba(217,166,46,0.13), transparent 70%),
    linear-gradient(168deg, #16151180 0%, #0c0c0b 60%, #090908 100%);
  display: grid; place-items: center;
  padding: 8%;
}
.merch-shot img[data-merch-img] {
  width: 100%; height: 100%;
  object-fit: contain;
  display: block;
  filter: drop-shadow(0 14px 26px rgba(0,0,0,0.7));
}

/* The missing-render state. Today this is what most cards show, so it is
   designed rather than tolerated: the station mark on its own plinth with a
   plain line of text. Never a broken-image glyph, never a question mark. */
.merch-shot-fallback {
  position: absolute; inset: 0;
  display: none;
  flex-direction: column; align-items: center; justify-content: center; gap: 14px;
  padding: 12%;
  text-align: center;
}
.merch-shot-fallback img { width: min(64%, 190px); height: auto; opacity: 0.5; }
.merch-shot-fallback span {
  font-family: var(--font-ui); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.18em;
  font-size: 9.5px; color: var(--muted);
}
.merch-shot[data-fallback="true"] img[data-merch-img] { display: none; }
.merch-shot[data-fallback="true"] .merch-shot-fallback { display: flex; }

.merch-body {
  padding: 15px 16px 17px;
  display: flex; flex-direction: column; gap: 5px;
  flex: 1;
}
.merch-name {
  margin: 0; font-family: var(--font-display); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.04em;
  font-size: 15px; color: var(--cream); line-height: 1.22;
  /* EVERY CARD RESERVES TWO LINES FOR THE NAME.
     "NOBP 504 Heavyweight Hoodie" and "NOBP 504 Varsity Jacket" wrap; "NOBP
     504 Area Rug" and "NOBP 504 Ceramic Mug" do not. Left alone, that pushed
     the tagline, the price and the button to a different height on every
     card, so a row read as a ragged staircase instead of a row. Reserving
     the taller of the two cases puts every price on the same line and every
     button at the same height, whatever the product is called. */
  min-height: calc(2 * 1.22em);
}
.merch-line {
  margin: 0; font-size: 12.5px; color: var(--muted);
  /* Same reasoning, one line: the taglines are all short, but reserving the
     line keeps a card whose tagline is ever missing from collapsing. */
  min-height: 1.5em;
}
.merch-price {
  margin: 6px 0 12px;
  display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap;
  font-family: var(--font-display); font-weight: 600;
  font-size: 19px; color: var(--gold-bright);
  font-variant-numeric: tabular-nums;
}
/* Says "mock" on every single card, so the disclaimer travels with the price
   even in a screenshot of one product. */
.merch-mock {
  font-family: var(--font-ui); font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.14em;
  font-size: 9px; color: var(--muted);
}
.merch-add { margin-top: auto; width: 100%; min-height: 44px; }
.merch-add:active { transform: translateY(1px) scale(0.99); }

/* ── mock cart toast ──────────────────────────────────────────────────── */
.cart-toast {
  position: fixed; z-index: 60;
  left: 50%; bottom: calc(20px + var(--safe-b));
  transform: translate(-50%, 140%);
  width: min(420px, calc(100% - 32px));
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 14px;
  background: var(--glass-strong);
  backdrop-filter: blur(18px);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: 0 20px 50px rgba(0,0,0,0.6);
  opacity: 0;
  transition: transform 220ms var(--ease), opacity 220ms var(--ease);
}
.cart-toast[data-open="true"] { transform: translate(-50%, 0); opacity: 1; }
.cart-toast p { margin: 0; font-size: 13px; color: var(--cream); }
.cart-toast .toast-note {
  margin-top: 3px; font-size: 11px; color: var(--muted);
}
.cart-toast .toast-close {
  margin-left: auto; flex: none;
  /* 44 square, not the 30 it looks like. The glyph stays small; the target
     does not. Measured at six viewports — anything under 44 fails the brief's
     floor and, more to the point, gets missed by a thumb. */
  width: 44px; height: 44px; border: 0; border-radius: 50%;
  display: grid; place-items: center;
  background: transparent; color: var(--muted); font-size: 19px; line-height: 1;
}
.cart-toast .toast-close:hover { color: var(--cream); }

/* THE SHARED ICON BUTTONS ARE 40x40 IN nobp.css — four pixels short.
   Rather than change that file and move every icon on every page of the site
   without being asked, the target is grown here with a pseudo-element: the
   button still LOOKS 40px, and the area that responds to a thumb is 44.
   Worth raising site-wide later; noted rather than done quietly. */
.site-header .icon-btn { position: relative; }
.site-header .icon-btn::after {
  content: '';
  position: absolute; top: 50%; left: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}

/* ── header additions ─────────────────────────────────────────────────── */
.cart-btn { position: relative; }
/* `display:grid` beats the browser's own `[hidden] { display:none }`, so the
   empty-cart badge sat there showing a gold "0" even though the JS had set
   the hidden attribute. Restating it at higher specificity is the fix. */
.cart-count[hidden] { display: none; }
.cart-count {
  position: absolute; top: -3px; right: -3px;
  min-width: 17px; height: 17px; padding: 0 4px;
  display: grid; place-items: center;
  border-radius: 999px; background: var(--gold); color: var(--black);
  font-family: var(--font-ui); font-weight: 700; font-size: 10px;
  font-variant-numeric: tabular-nums;
}

/* A compact Bounce Button in the header — same artwork and same state machine
   as the big one on the home page, at a size that belongs in a 66px bar. */
.bounce--mini {
  position: relative;
  width: 46px; height: 46px; flex: none;
  border: 0; padding: 0; background: transparent; border-radius: 50%;
  touch-action: manipulation;
  transition: transform 160ms var(--ease), filter 160ms var(--ease);
}
.bounce--mini img { width: 100%; height: 100%; display: block; }
.bounce--mini [data-bounce-rings],
.bounce--mini [data-bounce-arc] { position: absolute; inset: 0; pointer-events: none; }
.bounce--mini:hover { transform: scale(1.06); filter: brightness(1.08); }
.bounce--mini:active { transform: scale(0.96); }
.bounce--mini[data-state="playing"] {
  filter: drop-shadow(0 0 12px rgba(244, 202, 98, 0.45));
}
.bounce--mini[data-state="off-air"] { filter: saturate(.55) brightness(.72); }
.bounce--mini:focus-visible { outline: 3px solid var(--gold-bright); outline-offset: 4px; }

.shop-live-chip {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 11px 5px 8px;
  border: 1px solid var(--gold-deep); border-radius: 999px;
  color: var(--gold);
  font-family: var(--font-display); font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.12em; font-size: 10.5px;
  white-space: nowrap;
}
.shop-live-chip .dot {
  width: 6px; height: 6px; border-radius: 50%; background: #5d5648;
}
.shop-live-chip[data-on="true"] { color: var(--gold-bright); border-color: var(--gold); }
.shop-live-chip[data-on="true"] .dot { background: var(--gold-bright); }

/* The plain-language line under the mini button, for anyone who does not
   already know what a "Bounce Button" is. */
.shop-bounce-cue {
  margin: 0; font-size: 10px; color: var(--muted);
  text-transform: uppercase; letter-spacing: 0.14em;
}

.shop-disclaimer {
  margin: 0 0 clamp(20px, 3vh, 30px);
  padding: 11px 14px;
  border: 1px dashed var(--line-soft);
  border-radius: var(--r-sm);
  font-size: 12.5px; color: var(--muted);
}
.shop-disclaimer b { color: var(--cream); font-weight: 600; }

/* ── responsive ───────────────────────────────────────────────────────── */
@media (min-width: 560px) {
  .merch-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 768px) {
  .merch-showcase { aspect-ratio: 5 / 4; }
}
@media (min-width: 1024px) {
  .merch-hero-inner { grid-template-columns: 1.05fr 1fr; }
  .merch-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (min-width: 1280px) {
  .merch-grid { grid-template-columns: repeat(4, 1fr); }
  .merch-name { font-size: 16px; }
}

@media (prefers-reduced-motion: reduce) {
  .merch-card, .merch-add, .bounce--mini, .cart-toast, .merch-filter {
    transition: none !important;
  }
  .merch-card:hover, .merch-card:focus-within { transform: none; }
  .cart-toast { transform: translate(-50%, 0); }
  .cart-toast[data-open="false"] { display: none; }
}
