/* ============================================================
   INCOME EXECUTIVE — storefront styles

   Brand read, from the client's own channel (Dividend Executive,
   @dividendexecutive, 12K subs / 452 videos): dividend + options
   income, the wheel strategy, "cash printing machine", a stated goal
   of $5,000/mo dividends + $5,000/mo options. Audience is retail
   traders chasing passive income and early retirement.

   Palette:
     ink      near-black trading-terminal ground
     gold     wealth / "executive" — the primary accent
     profit   up-candle green, used for prices and gains
     loss     down-candle red, used sparingly as counterpoint
   Gold does the luxury work; green and red do the markets work.
   Everything else stays out of the way.
   ============================================================ */

:root {
  --ink: #06070a;
  --ink-1: #0c0e13;
  --ink-2: #12151c;
  --ink-3: #1a1f28;
  --line: #262c38;
  --line-gold: rgba(212, 175, 55, 0.28);

  --text: #eef1f6;
  --muted: #98a2b3;
  --dim: #6b7480;

  --gold: #e8c14f;
  --gold-hi: #fff0b8;
  --gold-lo: #a8801f;
  --gold-ink: #120e04;

  --profit: #17d67c;
  --profit-dim: #0e8f53;
  --loss: #ff4d5a;

  --radius: 12px;
  --radius-lg: 18px;
  --max: 1180px;

  --font: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --display: "Georgia", "Times New Roman", serif;

  --gold-grad: linear-gradient(135deg, var(--gold-lo) 0%, var(--gold) 38%, var(--gold-hi) 52%, var(--gold) 66%, var(--gold-lo) 100%);
}

* { box-sizing: border-box; }

/* The `hidden` attribute is only a UA-stylesheet display:none, so any author
   rule setting display beats it — which left the cart drawer stuck open. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  font-family: var(--font);
  background: var(--ink);
  color: var(--text);
  line-height: 1.6;
  overflow-x: hidden;
}

img { max-width: 100%; display: block; }
a { color: inherit; }
.muted { color: var(--muted); }
.error { color: var(--loss); font-size: 0.9rem; }

/* Gold accent text.

   This deliberately does NOT use the `background-clip: text` + transparent
   colour trick, which is the usual way to do gradient text. That approach
   was tried here and abandoned: it has a catastrophic failure mode. If the
   background does not paint for any reason, the text keeps its transparent
   fill and the headline disappears completely — which is exactly what
   happened on this site (the word "DIVIDENDS" and every stat number
   rendered invisible in production while the surrounding text was fine).

   A headline that is occasionally flat instead of foiled is a cosmetic
   nitpick. A headline that is occasionally *invisible* is a broken page.
   So the text is a solid colour that cannot fail, and the richness comes
   from a glow behind it.

   The gradient still does its job on BUTTONS, where it paints a real box
   and cannot take the label with it if it fails. */
.gold-text {
  color: var(--gold);
  text-shadow: 0 0 26px rgba(232, 193, 79, 0.3);
}

/* ---------- ticker tape ---------- */

.ticker {
  position: relative;
  overflow: hidden;
  background: var(--ink-1);
  border-bottom: 1px solid var(--line);
  padding: 0.5rem 0;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  white-space: nowrap;
  /* Fade the ends so the loop seam never reads as a hard cut. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.ticker-track {
  display: inline-flex;
  gap: 2.2rem;
  padding-left: 2.2rem;
  animation: tape 42s linear infinite;
}
/* Two identical tracks sit end to end; translating one full track width and
   looping is what makes it seamless. */
@keyframes tape {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.tick { display: inline-flex; align-items: center; gap: 0.45rem; }
.tick-sym { color: var(--text); }
.tick-up { color: var(--profit); }
.tick-down { color: var(--loss); }

/* ---------- header ---------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 30;
  backdrop-filter: blur(14px);
  background: rgba(6, 7, 10, 0.82);
  border-bottom: 1px solid var(--line);
}
.site-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 1.25rem;
  max-width: var(--max);
  margin: 0 auto;
}

.brand {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  font-family: var(--display);
  font-size: 1.2rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
  text-transform: uppercase;
}
.brand-mark { color: var(--text); }
.brand-sub {
  font-family: var(--font);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  color: var(--gold);
  text-transform: uppercase;
}

.site-nav { display: flex; align-items: center; gap: 1.4rem; }
.site-nav a {
  text-decoration: none;
  color: var(--muted);
  font-size: 0.88rem;
  font-weight: 500;
  transition: color 0.15s ease;
}
.site-nav a:hover { color: var(--gold); }

.cart-button {
  background: transparent;
  border: 1px solid var(--line-gold);
  color: var(--text);
  border-radius: 999px;
  padding: 0.45rem 1rem;
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.cart-button:hover {
  border-color: var(--gold);
  box-shadow: 0 0 0 3px rgba(232, 193, 79, 0.1);
}

.cart-count {
  display: inline-grid;
  place-items: center;
  min-width: 1.45em;
  height: 1.45em;
  margin-left: 0.45rem;
  border-radius: 999px;
  background: var(--gold);
  color: var(--gold-ink);
  font-weight: 800;
  font-size: 0.7rem;
}

/* ---------- hero ---------- */

.hero {
  position: relative;
  overflow: hidden;
  padding: 5.5rem 1.25rem 4rem;
  text-align: center;
}

/* Gold bloom behind the headline. */
.hero::before {
  content: "";
  position: absolute;
  top: -30%;
  left: 50%;
  transform: translateX(-50%);
  width: min(900px, 120%);
  aspect-ratio: 1;
  background: radial-gradient(circle, rgba(232, 193, 79, 0.13) 0%, transparent 62%);
  pointer-events: none;
}

/* Faint candlestick chart along the bottom of the hero. Decorative only —
   the values are drawn, not data, so it is aria-hidden in the markup. */
.hero-chart {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 190px;
  width: 100%;
  opacity: 0.5;
  pointer-events: none;
}

.hero-inner { position: relative; max-width: 900px; margin: 0 auto; }

/* The X banner, reused verbatim. Fixed 3:1 so the reserved space matches the
   image and nothing below it jumps while it loads. The gold hairline in the
   artwork already frames it, so the border here is only a faint seat. */
.hero-banner {
  width: 100%;
  max-width: 760px;
  height: auto;
  aspect-ratio: 3 / 1;
  margin: 0 auto 1.8rem;
  border-radius: var(--radius);
  border: 1px solid var(--line);
  object-fit: cover;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 1.4rem;
  padding: 0.35rem 0.9rem;
  border: 1px solid var(--line-gold);
  border-radius: 999px;
  background: rgba(232, 193, 79, 0.06);
  color: var(--gold);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
}
.eyebrow::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--profit);
  box-shadow: 0 0 0 3px rgba(23, 214, 124, 0.2);
}

.hero h1 {
  font-family: var(--display);
  font-size: clamp(2.2rem, 6.2vw, 4.2rem);
  line-height: 1.05;
  letter-spacing: -0.02em;
  margin: 0 auto 1.1rem;
  max-width: 15ch;
  text-transform: uppercase;
  /* Without this the last line orphans a single word ("IT") under a very
     wide headline. Balance evens the line lengths; browsers without it
     just wrap normally. */
  text-wrap: balance;
}

.lede {
  color: var(--muted);
  font-size: clamp(1rem, 1.6vw, 1.12rem);
  max-width: 54ch;
  margin: 0 auto 2rem;
}

.hero-cta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.85rem;
  justify-content: center;
  margin-bottom: 3.2rem;
}

/* ---------- stat band ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1px;
  max-width: 720px;
  margin: 0 auto;
  padding: 0;
  list-style: none;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.stats li {
  background: var(--ink-1);
  padding: 1.1rem 0.9rem;
  text-align: center;
}
.stat-num {
  display: block;
  font-family: var(--display);
  font-size: 1.7rem;
  font-weight: 700;
  line-height: 1.1;
}
.stat-label {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--dim);
}

/* Phones. There are exactly three tiles, and `auto-fit` with a 150px minimum
   fits two of them across a 375px screen, so Subscribers and Videos sat side
   by side and "Monthly options goal" dropped onto a second row alone, stuck
   in the left column with dead space beside it. Forcing three narrow columns
   instead just wraps the longest label onto three lines and stretches every
   tile to match.

   One column per tile, with the number and the label on the same baseline,
   reads as a deliberate list rather than a broken grid, and it is shorter
   than three stacked blocks so the links hub still sits near the fold.
   Keep the count at three if you add a stat, or revisit this. */
@media (max-width: 560px) {
  .stats {
    grid-template-columns: 1fr;
  }

  .stats li {
    display: flex;
    align-items: baseline;
    justify-content: center;
    gap: 0.55rem;
    padding: 0.85rem 1rem;
  }

  .stat-num {
    font-size: 1.45rem;
  }

  .stat-label {
    margin-top: 0;
    font-size: 0.66rem;
  }
}

/* ---------- buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  border: 1px solid var(--line);
  background: var(--ink-2);
  color: var(--text);
  padding: 0.8rem 1.6rem;
  border-radius: var(--radius);
  text-decoration: none;
  font: inherit;
  font-size: 0.92rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  cursor: pointer;
  transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease;
}
.btn:hover { border-color: var(--gold); transform: translateY(-1px); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; transform: none; }

/* Solid gold FIRST, gradient layered on top. Same reasoning as .gold-text:
   this button has dark text, so if only a gradient carried the gold and it
   failed to paint, the label would be dark-on-dark and unreadable. */
.btn-primary {
  position: relative;
  overflow: hidden;
  background-color: var(--gold);
  background-image: var(--gold-grad);
  background-size: 200% 100%;
  border-color: transparent;
  color: var(--gold-ink);
  box-shadow: 0 8px 30px -10px rgba(232, 193, 79, 0.55);
  transition: background-position 0.35s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.btn-primary:hover {
  background-position: 100% 50%;
  box-shadow: 0 12px 38px -10px rgba(232, 193, 79, 0.75);
}

.btn-block { width: 100%; }

.btn-add {
  width: 100%;
  margin-top: 0.85rem;
  padding: 0.7rem 1rem;
  font-size: 0.85rem;
}
.btn-add:hover { box-shadow: 0 0 0 3px rgba(232, 193, 79, 0.12); }

/* Resting shimmer on every Add to cart button (2026-09-03, Sam's ask: "the
   same glimmer as the photo coming soon tile"). Same @keyframes sheen, same
   105deg band, same 2.8s ease-in-out, so the shop reads as one system.

   Three things differ from the tile version, all forced by the surface:

   1. `::before`, not `::after`. .btn-add.is-added::after is the diagonal
      corner-to-corner glimmer that confirms an add, and the two must be able
      to run at once without one clobbering the other.
   2. Near-white rather than the tile's 255,240,184 gold. That warm gold is
      what makes the band visible on a near-black empty tile; on the gold
      button it is the same colour as the surface and disappears. White at a
      low alpha is what reads as light crossing metal here.
   3. Weaker peak (0.28 against the tile's 0.2 on a much darker ground). The
      button is bright already; a strong band washes the dark label out and
      hurts contrast on the one control that has to stay readable.

   .btn-primary already sets position:relative and overflow:hidden, so the
   band is clipped to the button and nothing bleeds into the card.

   Reduced motion needs no special case: the global rule collapses the
   duration and the band parks at translateX(100%), off the button entirely,
   the same way the tile sheen was written to. ⚠️ Sam runs with reduced motion
   ON, so he will not see this on his own machine. */
.btn-add::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  background: linear-gradient(
    105deg,
    transparent 30%,
    rgba(255, 255, 255, 0.1) 42%,
    rgba(255, 255, 255, 0.28) 50%,
    rgba(255, 255, 255, 0.1) 58%,
    transparent 70%
  );
  transform: translateX(-100%);
  animation: sheen 2.8s ease-in-out infinite;
}

/* The label must sit above the band, or the sweep passes in front of the text
   instead of across the metal behind it. This is why script.js wraps the label
   in .btn-add-label rather than leaving it a bare text node. */
.btn-add-label { position: relative; z-index: 2; }

/* Stagger the row. Three buttons sweeping in lockstep reads as a page-wide
   loading state; offset, it reads as light moving across separate objects.
   Nth-child on the card, not the button, because each button is alone in its
   own card. */
.card:nth-child(3n + 2) .btn-add::before { animation-delay: 0.45s; }
.card:nth-child(3n + 3) .btn-add::before { animation-delay: 0.9s; }

/* Add confirmation: the label flips to "+1" and a glimmer travels diagonally
   across the gold bar from the bottom left corner to the top right, then it
   returns to "Add to cart".

   Why its own keyframes rather than the shared @keyframes sheen: sheen is a
   flat horizontal wipe, which is right for the empty photo tiles but reads as
   a loading shimmer on a button. A 45deg band moving corner to corner reads
   as light catching metal, which is what the gold is doing.

   `inset: -60%` oversizes the layer so the band is fully off the button at
   both ends of the travel and no edge of the gradient is ever visible inside
   it. .btn-primary already sets position:relative and overflow:hidden, which
   is what clips the overshoot. */
.btn-add.is-added::after {
  content: "";
  position: absolute;
  inset: -60%;
  pointer-events: none;
  /* Same warm band as the empty photo tiles: the 255,240,184 gold rather than
     flat white, and the same soft three-stop falloff so it reads as light
     moving over the surface instead of a white bar crossing it. Brighter at
     the peak than the tiles are, because this fires once over an opaque gold
     button rather than looping over a dark one. Angled at 45deg so the band
     travels corner to corner. */
  background: linear-gradient(
    45deg,
    transparent 30%,
    rgba(255, 240, 184, 0.18) 42%,
    rgba(255, 255, 245, 0.62) 50%,
    rgba(255, 240, 184, 0.18) 58%,
    transparent 70%
  );
  animation: glimmer 0.9s ease-in-out;
}

/* Bottom left to top right. Both ends put the band fully off the button, so
   nothing is visible before or after the sweep and there is no edge to catch.
   `ease-in-out` matches the tiles: it eases in, crosses, and eases out. */
@keyframes glimmer {
  from { transform: translate(-85%, 85%); }
  to   { transform: translate(85%, -85%); }
}

.icon-btn {
  background: none;
  border: 1px solid var(--line);
  color: var(--text);
  border-radius: 7px;
  width: 2rem;
  height: 2rem;
  cursor: pointer;
  font: inherit;
  line-height: 1;
  transition: border-color 0.15s ease;
}
.icon-btn:hover { border-color: var(--gold); }

/* ---------- section furniture ---------- */

.section {
  max-width: var(--max);
  margin: 0 auto;
  padding: 4rem 1.25rem;
}

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.6rem;
}
.section-head h2 {
  font-family: var(--display);
  font-size: clamp(1.7rem, 3.6vw, 2.4rem);
  letter-spacing: -0.01em;
  text-transform: uppercase;
  margin: 0;
}

.zone-picker {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-size: 0.85rem;
  color: var(--muted);
}

select {
  background: var(--ink-2);
  color: var(--text);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 0.5rem 0.7rem;
  font: inherit;
  font-size: 0.88rem;
}
select:focus-visible { outline: 2px solid var(--gold); outline-offset: 1px; }

.zone-note {
  color: var(--dim);
  font-size: 0.82rem;
  margin: 0 0 2rem;
  max-width: 64ch;
}

/* ---------- three brand marks ---------- */

.brands {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background: var(--ink-1);
}
.brands-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 2.4rem 1.25rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 1.5rem;
}

.brand-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.55rem;
  text-decoration: none;
  text-align: center;
}
.brand-tile img {
  width: 96px;
  height: 96px;
  object-fit: contain;
  border-radius: 50%;
  /* The money bag mark is black line art on a transparent ground, so on this
     near-black page its outlines would vanish. A light disc behind ALL three
     keeps them consistent and keeps that one legible. The astronaut and the
     coin were drawn on black, so they get a dark disc instead via the
     modifier below. */
  background: #f4f5f7;
  padding: 10px;
  transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.brand-tile:hover img {
  transform: translateY(-3px);
  box-shadow: 0 14px 30px -14px rgba(232, 193, 79, 0.5);
}
/* Marks that already carry their own black ground. */
.brand-tile img[src*="darkminer"],
.brand-tile img[src*="income-executives"] {
  background: #000;
  padding: 0;
  border: 1px solid var(--line-gold);
}

.brand-tile-name {
  font-family: var(--display);
  font-size: 0.98rem;
  font-weight: 700;
  color: var(--text);
}
.brand-tile-sub {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
}

/* ---------- links hub ---------- */

.section-sub {
  color: var(--muted);
  margin: 0 0 2rem;
  max-width: 60ch;
}

.links-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1rem;
}

.link-card {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 1.35rem 1.4rem;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--ink-2) 0%, var(--ink-1) 100%);
  text-decoration: none;
  transition: transform 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
}
.link-card:hover {
  transform: translateY(-3px);
  border-color: var(--line-gold);
  box-shadow: 0 20px 44px -24px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(232, 193, 79, 0.1);
}

/* The channel is the point of the page, so it gets the gold edge. */
.link-card.is-primary {
  border-color: var(--line-gold);
  background:
    radial-gradient(ellipse at 15% 0%, rgba(232, 193, 79, 0.1), transparent 60%),
    linear-gradient(180deg, var(--ink-2) 0%, var(--ink-1) 100%);
}

.link-card-label {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--gold);
}
.link-card-name {
  font-family: var(--display);
  font-size: 1.2rem;
  font-weight: 700;
  color: var(--text);
  letter-spacing: -0.01em;
}
.link-card-desc {
  font-size: 0.88rem;
  color: var(--muted);
  line-height: 1.5;
}
.link-card-meta {
  margin-top: 0.35rem;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--dim);
}

/* Required, not decorative: a referral link to a brokerage is a material
   connection and has to be disclosed where people can actually see it. */
.disclosure {
  margin: 1.5rem 0 0;
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--dim);
  max-width: 70ch;
}

/* ---------- product grid ---------- */

.grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
  gap: 1.6rem;
}

.card {
  position: relative;
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: linear-gradient(180deg, var(--ink-2) 0%, var(--ink-1) 100%);
  display: flex;
  flex-direction: column;
  transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
}
.card:hover {
  transform: translateY(-4px);
  border-color: var(--line-gold);
  box-shadow: 0 22px 50px -22px rgba(0, 0, 0, 0.9), 0 0 0 1px rgba(232, 193, 79, 0.1);
}

.card-media {
  position: relative;
  aspect-ratio: 4 / 5;
  background: var(--ink-3);
  overflow: hidden;
}
/* Photo stacks above the label, so a real image hides the placeholder text. */
.card-media img {
  position: relative;
  z-index: 1;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Front/back swap for apparel with a back print. The back sits on top at
   zero opacity so it is already decoded by the time anyone hovers; fading a
   loaded image is instant, swapping a src is not. Hidden entirely from
   touch devices via the hover media query, where there is no hover to
   trigger it and the extra image would just be weight. */
.card-photo-back {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: 0;
  transition: opacity 0.25s ease;
}
@media (hover: hover) {
  /* Scoped to the photo box, not the whole card (2026-09-03, Sam's call):
     the front shows while the cursor is on the shirt and the tile falls back
     to the back print the moment it leaves, instead of staying flipped while
     the cursor sits over the title and the buy button. */
  .card-media:hover .card-photo-back { opacity: 1; }
}
@media (hover: none) {
  .card-photo-back { display: none; }
}
.card-media.is-placeholder {
  background:
    radial-gradient(circle at 50% 40%, rgba(232, 193, 79, 0.1), transparent 62%),
    var(--ink-3);
}

/* Gold sheen sweeping left to right across an empty photo slot.

   Done as a translating overlay rather than an animated background-position
   (which is how it was written first). Two reasons: transform animates on the
   compositor instead of repainting, and separating the sweep from the base
   background means the band can be made bright enough to actually see without
   also washing out the tile underneath it. The first version was so low
   contrast against the new darker palette that it read as no animation at all.

   Sits above the "photo coming soon" label on purpose, so the sheen passes
   over the text. It is translucent, so the label stays readable. */
.card-media.is-placeholder::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    105deg,
    transparent 30%,
    rgba(255, 240, 184, 0.07) 42%,
    rgba(255, 240, 184, 0.2) 50%,
    rgba(255, 240, 184, 0.07) 58%,
    transparent 70%
  );
  transform: translateX(-100%);
  animation: sheen 2.8s ease-in-out infinite;
}

/* Ends off-canvas to the right, so when the reduced-motion rule collapses the
   duration the band simply parks out of sight instead of freezing mid-tile. */
@keyframes sheen {
  to { transform: translateX(100%); }
}

.card-media-label {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  padding: 1rem;
  text-align: center;
}
.card-media-label-name {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--muted);
  line-height: 1.35;
}
.card-media-label-note {
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--gold);
  opacity: 0.8;
}

.card-body { padding: 1.15rem; display: flex; flex-direction: column; flex: 1; }
.card-body h3 {
  margin: 0 0 0.3rem;
  font-size: 1rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}
.card-desc { font-size: 0.85rem; color: var(--muted); margin: 0 0 0.9rem; flex: 1; }

.price {
  font-family: var(--display);
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--profit);
  margin: 0;
}

/* Both share a min-height so a card with a size picker and a card without
   one keep their price and button on the same line across the grid. */
.variant,
.variant-spacer {
  margin-top: 0.8rem;
  min-height: 62px;
}
.variant {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dim);
}

/* "SIZE" on the left, "Size guide" on the right, sharing one line so the
   picker below is not pushed down and cards keep matching heights. */
.variant-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.variant-guide {
  color: var(--gold);
  text-decoration: underline;
  text-underline-offset: 2px;
  letter-spacing: 0.06em;
}

.variant-guide:hover { text-decoration-thickness: 2px; }

/* ---------- creator strip ---------- */

.creator {
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  background:
    radial-gradient(ellipse at 20% 0%, rgba(232, 193, 79, 0.07), transparent 55%),
    radial-gradient(ellipse at 85% 100%, rgba(23, 214, 124, 0.06), transparent 55%),
    var(--ink-1);
}
.creator-inner {
  max-width: var(--max);
  margin: 0 auto;
  padding: 3.5rem 1.25rem;
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.4rem;
  text-align: center;
  justify-items: center;
}
.creator h2 {
  font-family: var(--display);
  font-size: clamp(1.6rem, 3.4vw, 2.2rem);
  text-transform: uppercase;
  margin: 0;
}
.creator p { color: var(--muted); max-width: 58ch; margin: 0; }
.creator-links { display: flex; flex-wrap: wrap; gap: 0.8rem; justify-content: center; }
/* Says out loud that the VIP button leads to a paid signup. A merch shopper
   clicking "community" does not expect a checkout. */
.creator-note {
  margin: 0.25rem 0 0;
  font-size: 0.78rem;
  color: var(--dim);
}

/* ---------- cart drawer ---------- */

.backdrop {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.72);
  backdrop-filter: blur(3px);
  z-index: 40;
}

.cart {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: min(430px, 100%);
  background: var(--ink-1);
  border-left: 1px solid var(--line-gold);
  z-index: 50;
  display: flex;
  flex-direction: column;
  box-shadow: -30px 0 70px -30px rgba(0, 0, 0, 0.95);
}

.cart-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 1.15rem 1.35rem;
  border-bottom: 1px solid var(--line);
}
.cart-head h2 {
  margin: 0;
  font-family: var(--display);
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.cart-lines { flex: 1; overflow-y: auto; padding: 1rem 1.35rem; }

.cart-line {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.9rem 0;
  border-bottom: 1px solid var(--line);
  font-size: 0.9rem;
}
.cart-line-price { white-space: nowrap; font-weight: 700; color: var(--profit); }
.qty { display: flex; align-items: center; gap: 0.7rem; margin-top: 0.55rem; }

.cart-foot { border-top: 1px solid var(--line); padding: 1.1rem 1.35rem 1.35rem; }

.totals { margin: 0 0 0.85rem; }
.totals > div {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  font-size: 0.92rem;
  padding: 0.2rem 0;
}
.totals dt { color: var(--muted); }
.totals dd { margin: 0; font-weight: 700; }

.fineprint { font-size: 0.74rem; color: var(--dim); margin: 0 0 0.85rem; line-height: 1.5; }

/* ---------- content pages ---------- */

.page { max-width: 70ch; margin: 0 auto; padding: 3.5rem 1.25rem 5rem; }
/* Centred to match every heading on the home page. Body copy underneath
   stays left aligned on purpose: a centred paragraph gives the eye no fixed
   left edge to return to and is measurably harder to read at this width. */
.page h1 {
  font-family: var(--display);
  font-size: clamp(2rem, 5vw, 2.9rem);
  text-transform: uppercase;
  letter-spacing: -0.01em;
  text-align: center;
}
.page h2 {
  font-family: var(--display);
  margin-top: 2.4rem;
  text-transform: uppercase;
  font-size: 1.25rem;
  letter-spacing: 0.02em;
  color: var(--gold);
}
.page table { width: 100%; border-collapse: collapse; margin: 1rem 0; font-size: 0.9rem; }
.page th, .page td { text-align: left; padding: 0.6rem 0.65rem; border-bottom: 1px solid var(--line); }
.page th { color: var(--dim); font-weight: 700; font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; }

.notice {
  border: 1px solid var(--line-gold);
  border-left: 3px solid var(--gold);
  border-radius: var(--radius);
  padding: 1rem 1.15rem;
  background: rgba(232, 193, 79, 0.05);
  font-size: 0.9rem;
}

/* ---------- footer ---------- */

.site-foot {
  border-top: 1px solid var(--line);
  padding: 2.5rem 1.25rem 3.5rem;
  max-width: var(--max);
  margin: 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  align-items: center;
  color: var(--dim);
  font-size: 0.85rem;
}
.site-foot nav { display: flex; gap: 1.3rem; flex-wrap: wrap; }
.site-foot a { text-decoration: none; transition: color 0.15s ease; }
.site-foot a:hover { color: var(--gold); }

/* ---------- a11y ---------- */

:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }
  /* Without this the tape sits mid-slide with a visible seam. */
  .ticker-track { transform: none !important; }
}

/* ─────────────────────────────── FAQ ─────────────────────────────── */

.faq { border-top: 1px solid var(--line); }

.faq-list {
  max-width: 46rem;
  margin: 2.5rem auto 0;
  display: grid;
  gap: 0.6rem;
}

.faq-item {
  background: #0b0d13;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  transition: border-color 0.22s ease;
}

.faq-item:hover,
.faq-item[open] { border-color: var(--line-gold); }

.faq-item summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.25rem;
  padding: 1.1rem 1.35rem;
  font-weight: 600;
  font-size: 1rem;
  line-height: 1.45;
  color: var(--text);
}

/* Safari draws its own triangle without this. */
.faq-item summary::-webkit-details-marker { display: none; }

/* Chevron as its own flex child rather than an absolutely positioned ::after,
   so a long question can never run underneath it. */
.faq-item summary::after {
  content: '';
  flex: 0 0 auto;
  width: 9px;
  height: 9px;
  border-right: 2px solid var(--gold);
  border-bottom: 2px solid var(--gold);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform 0.26s ease;
}

.faq-item[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }

.faq-item summary:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: -2px;
  border-radius: var(--radius);
}

.faq-body {
  padding: 0 1.35rem 1.3rem;
  border-top: 1px solid var(--line);
  padding-top: 1rem;
}

.faq-body p {
  color: var(--muted);
  font-size: 0.94rem;
  line-height: 1.72;
}

.faq-body p + p { margin-top: 0.75rem; }

.faq-body a {
  color: var(--gold);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* ──────────────────── STICKY MOBILE CTA ──────────────────── */

/* Desktop keeps the header nav and its cart button in view, so the bar is
   mobile only. script.js adds .is-visible. */
.sticky-cta { display: none; }

@media (max-width: 760px) {
  .sticky-cta {
    display: flex;
    gap: 0.55rem;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    /* Above the sticky header (30), below the cart backdrop (40) so the
       open drawer covers it. */
    z-index: 35;
    padding: 0.65rem 0.9rem calc(0.65rem + env(safe-area-inset-bottom));
    background: rgba(6, 7, 10, 0.9);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-top: 1px solid var(--line-gold);
    transform: translateY(115%);
    transition: transform 0.3s ease;
  }

  .sticky-cta.is-visible { transform: translateY(0); }

  .sticky-cta .btn {
    flex: 1 1 0;
    justify-content: center;
    text-align: center;
    white-space: nowrap;
  }

  .sticky-cta__cart { flex: 0 1 34%; }

  /* Keep the bar off the end of the page. */
  .site-foot { padding-bottom: 5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .sticky-cta { transition: none; }
}

/* ---------- stock ----------

   The red is --loss, the same down-candle red the prices already use, so a
   scarcity warning reads as part of the trading palette rather than as a
   generic shop alert.

   .stock-spacer is an empty element of the same height. Without it a row that
   mixes a low-stock product against a well-stocked one has its prices and
   buttons sitting at different heights. */

.stock {
  margin: 0.35rem 0 0;
  min-height: 1.15rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.stock-low {
  color: var(--loss);
}

.stock-out {
  color: var(--dim);
}

.stock-spacer {
  min-height: 1.15rem;
}

/* Sold out: dim the photo, but keep the card legible and still clickable for
   the lightbox. Someone who cannot buy it today may well come back. */
.card.is-sold-out .card-media img {
  filter: grayscale(0.85) brightness(0.55);
}

.card.is-sold-out .btn-add,
.btn-add[disabled] {
  background: var(--ink-3);
  background-image: none;
  border-color: var(--line);
  color: var(--dim);
  cursor: not-allowed;
  transform: none;
}

.btn-add[disabled]:hover {
  border-color: var(--line);
  transform: none;
}

/* ---------- zoom affordance on the card ----------

   Covers the whole media box so the entire photo is the click target, with a
   small + badge so it is discoverable rather than a hidden easter egg. The
   badge is always visible on touch, where there is no hover to reveal it. */

.card-zoom {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: transparent;
  cursor: zoom-in;
}

.card-zoom-icon {
  position: absolute;
  right: 0.6rem;
  bottom: 0.6rem;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border: 1px solid var(--line-gold);
  border-radius: 50%;
  background: rgba(6, 7, 10, 0.72);
  color: var(--gold);
  font-size: 1.05rem;
  line-height: 1;
  opacity: 0;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.card-media:hover .card-zoom-icon,
.card-zoom:focus-visible .card-zoom-icon {
  opacity: 1;
}

.card-zoom:focus-visible {
  outline: 2px solid var(--gold);
  outline-offset: -3px;
}

/* No hover on touch, so the badge just stays put. */
@media (hover: none) {
  .card-zoom-icon { opacity: 1; }
}

/* ---------- lightbox ----------

   Fixed full-screen layer. The image is capped by BOTH dimensions so a tall
   4:5 product shot cannot run off the bottom of a short laptop window, which
   is the usual way these break. */

.lightbox {
  cursor: zoom-out;
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 0.5rem;
  padding: 0.75rem;
  background: rgba(4, 5, 8, 0.93);
  backdrop-filter: blur(6px);
}

.lightbox[hidden] { display: none; }

/* Stops the page behind from scrolling under the overlay on touch. */
body.no-scroll { overflow: hidden; }

.lightbox-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.25rem 0.35rem;
}

.lightbox-title {
  margin: 0;
  font-size: 0.95rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gold);
}

.lightbox-close {
  flex: none;
  width: 2.4rem;
  height: 2.4rem;
  border: 1px solid var(--line);
  border-radius: 50%;
  background: var(--ink-2);
  color: var(--text);
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox-close:hover { border-color: var(--gold); color: var(--gold); }

.lightbox-stage {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 0; /* lets the 1fr row actually shrink */
}

.lightbox-img {
  /* Sized off the VIEWPORT, not off the stage.
     Two dead ends before this, both worth not repeating:
       `max-height: 100%`  — a percentage max-height needs a containing block
                             with a definite height, and a 1fr grid row is not
                             definite (its size depends on this content).
       `height: 100%`      — fails for the same reason, so the image fell back
                             to its aspect ratio at max-width and a 1000x1250
                             shot rendered 1125px tall in a 914px window.
     100dvh is definite everywhere and also tracks mobile browser chrome as it
     hides. The subtracted 9.5rem is the title bar, the caption row, the grid
     gaps and the outer padding. */
  max-width: min(100%, 900px);
  max-height: calc(100vh - 9.5rem);
  max-height: calc(100dvh - 9.5rem);
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: var(--radius);
  cursor: zoom-out;
}

.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 2.75rem;
  height: 2.75rem;
  border: 1px solid var(--line-gold);
  border-radius: 50%;
  background: rgba(6, 7, 10, 0.78);
  color: var(--gold);
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
}

.lightbox-nav:hover { background: var(--ink-3); }
.lightbox-nav { cursor: pointer; }
.lightbox-close { cursor: pointer; }
.lightbox-nav[hidden] { display: none; }
.lightbox-prev { left: 0.25rem; }
.lightbox-next { right: 0.25rem; }

.lightbox-foot {
  display: grid;
  gap: 0.5rem;
  justify-items: center;
  padding-bottom: 0.35rem;
}

.lightbox-caption {
  margin: 0;
  font-size: 0.8rem;
  color: var(--muted);
  text-align: center;
}

.lightbox-dots {
  display: flex;
  gap: 0.4rem;
}

.lightbox-dots[hidden] { display: none; }

.lightbox-dots .dot {
  width: 0.45rem;
  height: 0.45rem;
  border-radius: 50%;
  background: var(--line);
}

.lightbox-dots .dot.is-on { background: var(--gold); }

/* ---------- the rally line ---------- */

.rally {
  margin: 0 0 1.25rem;
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--text);
  text-align: center;
}

/* ---------- add-to-cart badge bump ----------

   A green chart glyph used to fly from the button to the badge. It was
   removed on request: green is the price colour, and a green mark travelling
   across the page read as an alert rather than a confirmation. The button's
   own glimmer now carries the confirmation. Do not reintroduce the flyer. */

/* The badge takes a short bump so the sequence resolves on the number that
   actually changed. Ring is gold, not the old green: nothing near the cart
   should read as a price move. */
.cart-count.is-bumped {
  animation: badge-bump 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
}

@keyframes badge-bump {
  0%   { transform: scale(1); }
  35%  { transform: scale(1.5); box-shadow: 0 0 0 4px rgba(232, 193, 79, 0.35); }
  100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(232, 193, 79, 0); }
}

/* ============================================================
   CENTRED LAYOUT, FROM "EVERY LINK, ONE PLACE" DOWN

   Scope: #links, #shop, #faq and the footer. The site header is
   deliberately UNTOUCHED — the wordmark, Links / Shop / FAQ and the cart
   button stay exactly where they are.

   Why flex and not grid: a CSS grid cannot centre a partial last row. The
   links row held five cards in four tracks and the shop held three products
   in four, so the leftovers hugged the left edge and the whole block read as
   misaligned. Flex with a fixed basis and justify-content:center centres each
   row independently, orphans included. Do not "tidy" these back to grid.
   ============================================================ */

#links .section-head,
#shop .section-head,
#faq .section-head {
  justify-content: center;
  text-align: center;
}

/* The shop head holds the zone picker as well as the heading, so one centred
   flex row centres the PAIR and leaves "Dress like the premium paid for it"
   sitting left of centre with the picker parked beside it. Stacking the head
   into a column puts the heading on the page centre line where the rest of
   the site's headings are, and drops the picker underneath, still centred.
   Do not put this back on one row. */
#shop .section-head {
  flex-direction: column;
  align-items: center;
}

#shop .section-head h2 {
  width: 100%;
}

#links .section-sub,
#shop .section-sub,
#faq .section-sub {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

#links .disclosure {
  margin-left: auto;
  margin-right: auto;
  text-align: center;
}

/* The link cards. Basis matches the old 260px minimum so nothing reflows at
   a different breakpoint than before. */
.links-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
}

.links-grid > .link-card {
  flex: 0 1 280px;
  max-width: 100%;
}

/* The product cards, same treatment. `align-items: stretch` keeps a row of
   cards the same height, which is what the grid was doing for free. */
.grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
}

.grid > .card {
  flex: 0 1 280px;
  max-width: 100%;
}

/* The zone picker sits above the products and looked stranded once the
   grid under it centred. */
#shop .zone-picker,
#shop .zone-note {
  justify-content: center;
  text-align: center;
}

/* The note is capped at 64ch, so centring its text is not enough: without
   auto margins the box itself still hugs the left edge and the centred text
   inside it reads as off centre. */
#shop .zone-note {
  margin-left: auto;
  margin-right: auto;
}

#shop .zone-picker {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

/* FAQ items are already a centred column; only the heading needed moving.
   The footer closes the page centred rather than pushed to both edges. */
.site-foot {
  justify-content: center;
  text-align: center;
}

.site-foot nav {
  justify-content: center;
  flex-wrap: wrap;
}

/* One narrow-screen note: at small widths every flex row collapses to a
   single full-width column anyway, so the centring is a no-op there rather
   than something that needs undoing. */
@media (max-width: 640px) {
  .links-grid > .link-card,
  .grid > .card {
    flex-basis: 100%;
  }
}

/* A country we do not ship to. Uses --loss because it is a blocker, not a
   hint, and the buyer needs to see it before building a cart. */
.zone-note-warn {
  color: var(--loss);
  font-weight: 600;
}

/* Test-mode banner. Deliberately loud and ugly: it only appears when a dev
   token is active, and it should be impossible to leave on by accident. */
.dev-flag {
  margin: 0;
  padding: 0.5rem 1rem;
  text-align: center;
  background: var(--loss);
  color: #fff;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* ---------- giveaway code box ---------- */

.code-box {
  margin: 0.9rem 0 0.2rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--line);
}

.code-label {
  display: block;
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  margin-bottom: 0.4rem;
}

.code-row {
  display: flex;
  gap: 0.5rem;
}

.code-input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--line);
  border-radius: 8px;
  background: var(--ink-2);
  color: var(--text);
  font-size: 0.9rem;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.code-input::placeholder {
  color: var(--dim);
  letter-spacing: 0.06em;
}

.code-input:focus {
  outline: none;
  border-color: var(--gold);
}

.code-apply {
  flex: 0 0 auto;
  padding: 0.6rem 1rem;
  font-size: 0.85rem;
}

.code-msg {
  margin: 0.5rem 0 0;
  font-size: 0.78rem;
  line-height: 1.5;
}

.code-msg-ok { color: var(--profit); }
.code-msg-bad { color: var(--loss); }
