/* SLIRPZRIPZ — slirpz.com
   ===========================================================================
   DESIGN NOTES, so the ideas do not get "tidied" away later.

   THE PAGE HAS ONE JOB: get the visitor into an eBay Live show. The live call to
   action is in the sticky bar, in the hero, after the steps, and again at the end,
   because people decide at a different point than a page expects them to.

   THE PAGE IS A STACK OF GRADED SLABS. Each section below the hero is a plastic
   shell with a cream label across the top — descriptor left, grade badge right —
   exactly how a graded card is presented. It is the one visual grammar that
   belongs to this business and to nobody else.

   THE SIGNATURE is the wall: real card art out of the library, two rows drifting
   in opposite directions behind the hero, blurred and dimmed to atmosphere. The
   library holds 38,710 pictures of cards that nobody else has. A page about
   opening packs that shows five of them is wasting its only unrepeatable asset.

   THE HERO CARD still tilts toward the pointer with a holo band sliding across
   it. Catching the light is what makes a card worth chasing, so the page opens by
   doing it — once, and nowhere else, so it lands.

   TWO COLOURS ARE RESERVED AND MEAN ONE THING EACH. Gold is money: if something
   gold is not a price, it is wrong. Red is on-air: the live dot and nothing else.
   The three holo accents separate the three Pokémon lanes.
   =========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

:root {
  --ground: #08060d;
  --shell: #17131f;
  --shell-2: #1f1a2c;
  --label: #f6f3ea;          /* a grading label is printed stock — cream, never pure white */
  --label-ink: #14111c;
  --ink: #f0edf8;
  --dim: #a79fbd;
  --line: #2e2643;
  --gold: #ffc46b;           /* MONEY ONLY */
  --live: #ff4d5e;           /* ON AIR ONLY */
  --holo-1: #b678ff;
  --holo-2: #61e2c4;
  --holo-3: #ffb454;
  --holo: linear-gradient(100deg, #b678ff, #61e2c4 45%, #ffb454 90%);

  --display: Anton, 'Arial Narrow', 'Segoe UI Black', Impact, sans-serif;
  --labelface: 'Archivo Narrow', 'Arial Narrow', sans-serif;
  --body: Manrope, 'Segoe UI', system-ui, sans-serif;

  --max: 1180px;
}

html { scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--body);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3 { font-family: var(--display); font-weight: 400; text-transform: uppercase; margin: 0; }
a { color: inherit; }
:focus-visible { outline: 3px solid var(--holo-2); outline-offset: 3px; border-radius: 4px; }

/* -------------------------------------------------------- bookmark the show */
/* ABOVE the sticky topbar and NOT sticky itself. It is an announcement, not a control: it
   should be the first thing read and then scroll away, rather than eat a second band of a
   phone screen for the whole visit. The topbar keeps the persistent call to action. */
.bookmark {
  background: linear-gradient(100deg, rgba(182,120,255,.20), rgba(97,226,196,.14) 55%, rgba(255,180,84,.18));
  border-bottom: 1px solid var(--line);
}
.bookmark[hidden] { display: none; }
/* While he is actually on air the live call to action owns the page — see the note in
   index.html. Belt and braces: app.js hides it too, so this cannot be got wrong by a
   render order. */
body.is-live .bookmark { display: none; }

.bookmark-link {
  display: flex; align-items: center; gap: 14px;
  padding: 10px max(18px, calc((100vw - var(--max)) / 2));
  text-decoration: none; color: var(--ink);
}
.bookmark-art {
  flex: none; width: 48px; height: 72px; object-fit: cover;
  border-radius: 6px; border: 1px solid var(--line); background: var(--shell);
}
.bookmark-art[hidden] { display: none; }
.bookmark-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bookmark-kicker {
  font-family: var(--labelface); font-weight: 700; font-size: 11px;
  letter-spacing: 1.6px; text-transform: uppercase; color: var(--label);
}
/* ONE LINE, CLIPPED. His show titles run past sixty characters and wrap to three lines on a
   phone, which turns a strip into a paragraph. */
.bookmark-title {
  font-family: var(--display); font-size: 19px; letter-spacing: .4px; line-height: 1.15;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bookmark-when { font-size: 13px; color: var(--dim); }
/* His own words off the show. Clipped to one line for the same reason the title is: these run
   to a couple of sentences and the strip has to stay a strip. */
.bookmark-blurb {
  font-size: 13px; color: var(--ink); opacity: .85;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bookmark-blurb[hidden] { display: none; }
.bookmark-cta {
  margin-left: auto; flex: none;
  padding: 10px 18px; border-radius: 999px;
  background: var(--label); color: var(--label-ink);
  font-family: var(--labelface); font-weight: 700; font-size: 12px;
  letter-spacing: 1.2px; text-transform: uppercase;
  transition: transform .16s ease;
}
.bookmark-link:hover .bookmark-cta { transform: translateY(-2px); }

/* On a phone the button drops under the text rather than squeezing the title to nothing.
   `flex: 1 1 0` on the text is what keeps the picture BESIDE it: without it the title's own
   width pushes past the row and the art wraps onto a line of its own, which reads as a
   stray thumbnail sitting above an unrelated headline. */
@media (max-width: 620px) {
  .bookmark-link { flex-wrap: wrap; gap: 12px; }
  .bookmark-text { flex: 1 1 0; }
  .bookmark-cta { margin-left: 0; width: 100%; text-align: center; }
  .bookmark-title { font-size: 17px; }
}

/* ------------------------------------------------------------------ topbar */
.topbar {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; gap: 20px;
  padding: 12px max(18px, calc((100vw - var(--max)) / 2));
  background: rgba(8, 6, 13, .82);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; font-family: var(--display); letter-spacing: .5px; font-size: 20px; }
.brand img { border-radius: 50%; }
.brand b { background: var(--holo); -webkit-background-clip: text; background-clip: text; color: transparent; }
.nav { display: flex; gap: 22px; margin-left: auto; font-size: 14px; }
.nav a { color: var(--dim); text-decoration: none; }
.nav a:hover { color: var(--ink); }

/* ------------------------------------------------------------------ buttons */
.btn {
  display: inline-flex; align-items: center; gap: 9px;
  padding: 14px 24px; border-radius: 999px;
  font-family: var(--labelface); font-weight: 700; font-size: 14px;
  letter-spacing: 1.2px; text-transform: uppercase; text-decoration: none;
  border: 1px solid transparent; transition: transform .16s ease, box-shadow .16s ease;
}
.btn:hover { transform: translateY(-2px); }
.btn-primary { background: var(--holo); color: var(--label-ink); box-shadow: 0 10px 30px rgba(182, 120, 255, .28); }
.btn-ghost { border-color: var(--line); color: var(--ink); background: rgba(255,255,255,.04); }
/* Cream, not red: this button is always on the page, so it cannot wear the on-air colour. It
   turns red only while the show actually is on. */
.btn-live { background: var(--label); color: var(--label-ink); padding: 11px 18px; font-size: 13px; }
/* Dark ink, not white. White on --live measures 3.24:1, and at 13px AA asks 4.5:1 — a failure
   on the primary call to action at the one moment it matters. --label-ink on the same red is
   5.75:1, so the on-air colour is untouched and only the ink moves. Measured 8 Sept 2026. The
   white dot below stays white at 3.24:1, clearing the 3.0 a non-text indicator needs. */
body.is-live .btn-live { background: var(--live); color: var(--label-ink); }
/* The state line under the wordmark goes red on air too: the one time the first screen speaks
   about now. app.js writes the words; this is only the colour, and red still means on air only. */
body.is-live .eyebrow { color: var(--live); }
.btn-wide { margin-top: 26px; }
/* A disabled control must read as BLOCKED, not merely dimmer. */
.btn[aria-disabled='true'] { opacity: .45; background: var(--shell-2); color: var(--dim); box-shadow: none; pointer-events: none; }

/* THE ON-AIR DOT IS OFF UNTIL SOMETHING KNOWS THE SHOW IS ON.
 *
 * It shipped pulsing red on five elements permanently, and the nav button was solid red — so a
 * page that is live a few evenings a week told every visitor, at every hour, that it was on air
 * now. That is the same failure the deck refuses on the overlay: never tell the audience
 * something untrue. It also broke this stylesheet's own rule that red means ON AIR and nothing
 * else, by making red the permanent brand colour of the main button.
 *
 * The dot is now dark and still by default. `body.is-live` turns it on, and app.js:677 sets that
 * class from /api/live, which answers only `ok && live`. Corrected 8 Sept 2026: this comment used
 * to say nothing set the class and the eBay reader was not connected. Both were true when written
 * and neither is now — measured live on air the same day.
 */
.dot { width: 9px; height: 9px; border-radius: 50%; background: #5b5470; flex: none; }
body.is-live .dot { background: var(--live); box-shadow: 0 0 0 0 rgba(255,77,94,.55); animation: pulse 2s infinite; }
body.is-live .btn-live .dot { background: #fff; }
@keyframes pulse {
  0%   { box-shadow: 0 0 0 0 rgba(255, 77, 94, .55); }
  70%  { box-shadow: 0 0 0 9px rgba(255, 77, 94, 0); }
  100% { box-shadow: 0 0 0 0 rgba(255, 77, 94, 0); }
}

/* --------------------------------------------------------------------- hero */
.hero { position: relative; overflow: hidden; padding: 74px max(18px, calc((100vw - var(--max)) / 2)) 54px; }

/* THE WALL. Blurred and dimmed hard so it is atmosphere and never competes with the type in
   front of it, and masked to nothing at every edge so it has no visible start or end. Each row
   holds its cards TWICE and travels exactly -50%, which is what makes the loop seamless. */
.wall {
  position: absolute; inset: -8% -12% auto -12%;
  display: grid; gap: 18px; opacity: .3;
  filter: blur(3px) saturate(1.3);
  pointer-events: none;
  /* Faded at BOTH ends. Without the top fade the wall meets the sticky header on a hard line,
     which reads as a banner someone forgot to finish rather than as depth behind the page. */
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 18% 50%, transparent 88%);
  mask-image: linear-gradient(to bottom, transparent, #000 18% 50%, transparent 88%);
}
.wall-row { display: flex; gap: 14px; width: max-content; animation: drift 90s linear infinite; }
.wall-rev { animation-direction: reverse; animation-duration: 125s; }
/* height: auto MATTERS: app.js gives each wall card width and height attributes so the row has
   its height before the image arrives, and the phone rule below narrows the width to 96px. Without
   height: auto the attribute would pin the height at 182px under a 96px width and squash every
   card; with it, the attributes only supply the aspect ratio. Measured 5 Sept 2026. */
.wall-row img { width: 132px; height: auto; border-radius: 8px; display: block; }
@keyframes drift { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.hero-grid { position: relative; display: grid; grid-template-columns: 1.05fr .95fr; gap: 40px; align-items: center; }
.eyebrow { display: inline-flex; align-items: center; gap: 9px; font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 2.4px; font-size: 12px; color: var(--dim); margin: 0 0 18px; }

/* THE HERO LOCKUP. Big enough to be the name of the business, small enough that it never
   competes with the slogan underneath — the eye should land on the mark, read the name, and drop
   straight into the headline. Same holo treatment on RIPZ as the nav, so the two read as one
   wordmark rather than two designs of it. */
.wordmark { display: flex; align-items: center; gap: 12px; margin: 0 0 14px; font-family: var(--display); font-size: clamp(26px, 3.4vw, 38px); letter-spacing: .5px; line-height: 1; }
.wordmark img { border-radius: 50%; box-shadow: 0 6px 18px rgba(0,0,0,.5); }
.wordmark b { background: var(--holo); -webkit-background-clip: text; background-clip: text; color: transparent; }

/* The one place the type is allowed to shout. */
.hero-h1 { font-size: clamp(56px, 9.5vw, 128px); line-height: .84; letter-spacing: .5px; }
.hero-h1 span { display: block; }
.hero-h1 .holo { background: var(--holo); -webkit-background-clip: text; background-clip: text; color: transparent; }

.lede { max-width: 54ch; color: #cdc6dd; margin: 24px 0 28px; }
.cta-row { display: flex; flex-wrap: wrap; gap: 12px; }
/* These were style= attributes (three cta-row margins on index.html, the code wrap on the callback
   page). As classes, style-src in _headers needs no unsafe-inline (5 Sept 2026). */
.cta-row-after { margin-top: 26px; }
.code-wrap { white-space: pre-wrap; word-break: break-all; }
.note { color: var(--dim); font-size: 14px; margin: 14px 0 0; }

.when { margin: 34px 0 0; display: grid; gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: 14px; overflow: hidden; }
.when > div { display: grid; grid-template-columns: 108px 1fr; gap: 14px; background: #15111f; padding: 12px 16px; }
.when dt { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.6px; font-size: 11px; color: var(--dim); }
.when dd { margin: 0; font-size: 15px; }
/* The scheduled show's own title, under the time. Real product names run long — "SLIRPZRIPZ POKEMON
   CRYSTAL GATHERING WHEEL!" — so it gets its own line rather than being wrapped into the sentence. */
.when-title { display: block; margin-top: 3px; color: var(--holo-2); font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.2px; font-size: 12px; }
.when-local { display: block; color: var(--dim); font-size: 13.5px; }

.hero-fan { display: grid; place-items: center; }

/* THE PACK STAGE holds two things in the same box: the card fan, and the canvas the 3D pack is
   drawn on. Only one is ever visible. The fan is what is in the markup and what a visitor sees;
   `has-pack` is added by the module at the foot of index.html and ONLY once the pack has really
   mounted, so a blocked CDN or a browser with no WebGL leaves the fan exactly where it was.
   Nothing here may hide the fan by default — that would make the fallback depend on the thing it
   is a fallback for. */
/* 1:1.08, not 5:6 — at 5:6 the caption underneath fell below the fold on a 1280x820 window, and a
   caption nobody scrolls to is the wrong place for the line that says this is a demonstration. */
.pack-stage { position: relative; width: min(520px, 100%); aspect-ratio: 1 / 1.08; display: grid; place-items: center; }
/* THE CANVAS EDGE MUST NOT BE VISIBLE, and feathering it is the only guarantee.
   Additive smoke, the flash and the card glow all spill past the canvas bounds, and WebGL cuts
   them off dead square — so a soft cloud ends in a hard corner and the hero sits in a faint
   rectangle. It was reported off a phone, where the page either side is nearly black.
   Killing the leftover smoke removed today's cause; this removes the whole class of it, for
   anything drawn near an edge in future. The middle 76% is untouched, so the card is never dimmed
   — measured: at rest it occupies 17-83% vertically and 24-76% horizontally. */
/* ONE GRADIENT PER ELEMENT, and they compose through the DOM.
   Two mask layers on a single element only overlap with `mask-composite: intersect`, which Safari
   spells differently or not at all — and iOS Safari is the browser this was reported from. Without
   it the layers UNION instead, so only the corners feather and all four edges stay hard: a fix
   that appears to work everywhere it was tested and does nothing where it was needed.
   The veil fades left and right, the canvas fades top and bottom, and nesting multiplies them.

   LITERAL PERCENTAGES, NOT `calc(100% - var(--edge))`. The stops are constants, so the calc bought
   nothing but a second way to fail: an engine that will not evaluate calc() inside a gradient
   inside a mask throws the whole declaration out, and the hard edge comes back on exactly the
   class of device this was reported from. 12% and 88% are the same two numbers with nothing
   between them and the parser. */
.pack-veil { position: absolute; inset: 0;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 12%, #000 88%, transparent);
}
.pack-canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; display: block; outline: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
  mask-image: linear-gradient(to bottom, transparent, #000 12%, #000 88%, transparent);
}
.pack-canvas[hidden] { display: none; }
.pack-canvas:focus-visible { outline: 2px solid var(--holo-2); outline-offset: 4px; border-radius: 10px; }
body.has-pack .fan { display: none; }
.pack-say { margin-top: 10px; text-align: center; max-width: 44ch; }
.pack-hint { margin: 0 0 6px; font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 2.2px; font-size: 12px; color: var(--holo-2); }
.pack-note { margin: 0; font-size: 12.5px; line-height: 1.45; color: var(--dim); }

.fan { position: relative; width: min(430px, 100%); aspect-ratio: 733 / 1024; perspective: 1200px; }
/* TOP/LEFT/RIGHT ONLY — never `inset: 0`. Pinning bottom as well as top makes the box the full
   height of the fan, so each card's shadow is cast by a container-sized rectangle rather than by
   the card, and the fan sits on a visible grey slab. */
.fan img { position: absolute; top: 0; left: 0; right: 0; width: 78%; height: auto; margin: 0 auto; border-radius: 10px; box-shadow: 0 18px 40px rgba(0,0,0,.55); transition: transform .5s cubic-bezier(.2,.7,.3,1); }
.fan .b1 { transform: translate(-34%, 4%) rotate(-15deg); }
.fan .b2 { transform: translate(-16%, 1%) rotate(-7deg); }
.fan .b3 { transform: translate(16%, 1%) rotate(7deg); }
.fan .b4 { transform: translate(34%, 4%) rotate(15deg); }
.front-wrap { position: absolute; top: 0; left: 0; right: 0; width: 84%; margin: 0 auto; transform-style: preserve-3d; transition: transform .18s ease-out; --holo-angle: 115deg; }
.front-wrap img { position: static; width: 100%; border-radius: 12px; box-shadow: 0 30px 70px rgba(0,0,0,.7); }
/* --------------------------------------------------------------------- holo */
/* THE EFFECT A CARD BUYER RECOGNISES: the rainbow shine of a holofoil moving with the light, and
   the glare of the sleeve. Pure CSS on the real card art, no library. Approved by the operator
   5 Sept 2026 ("yes to the holo effect"); scoped after the other console's point that eighteen
   compositing layers is how a page that scores 90 in the lab feels bad in the hand.

   TWO LAYERS, AND THEY DO NOT EXIST AT REST. `::before` is the shine, bands of colour at an angle
   the pointer drives; `::after` is the glare, a soft spot under the pointer. Both are opacity 0
   until app.js marks the card `is-on`, so a still card costs a still card. The hero's front card
   is the exception and carries a dimmed shine always, because it is the one card on the first
   screen and a plain card there would be a step backwards from the band it had before.

   --px / --py are 0..1 across the card; --holo-angle turns with --px. app.js sets them on the one
   card under a fine pointer only (a phone cannot hover; the maths would be cost without effect)
   and never under prefers-reduced-motion, where the still shine stays and nothing moves. */
/* holo-card, not holo: the headline's "day." already carries class holo for its gradient text
   (.hero-h1 .holo), and a bare .holo rule with preserve-3d and two inset layers landed on it too.
   Found by the review, 5 Sept. */
.holo-card { position: relative; --px: .5; --py: .5; --holo-angle: 115deg; transform-style: preserve-3d; transition: transform .18s ease-out; }
.holo-card::before, .holo-card::after {
  content: ''; position: absolute; inset: 0; border-radius: 12px; pointer-events: none;
  opacity: 0; transition: opacity .25s ease;
}
/* overlay and soft-light, not color-dodge: dodge over a pale card bleaches it to white with a
   rainbow strip, which is a scanner glitch, not a foil. Looked at on 5 Sept and retuned. */
.holo-card::before {
  background: linear-gradient(var(--holo-angle),
    transparent 22%, rgba(255, 92, 168, .42) 36%, rgba(255, 214, 96, .42) 44%,
    rgba(112, 255, 204, .42) 52%, rgba(120, 168, 255, .42) 60%, transparent 76%);
  mix-blend-mode: overlay;
}
.holo-card::after {
  background: radial-gradient(farthest-corner circle at calc(var(--px) * 100%) calc(var(--py) * 100%),
    rgba(255, 255, 255, .34) 0%, rgba(255, 255, 255, .08) 28%, transparent 60%);
  mix-blend-mode: soft-light;
}
.holo-card.is-on::before, .holo-card.is-on::after { opacity: 1; }
.front-wrap.holo-card::before, .front-wrap.holo-card::after { opacity: .55; }
.front-wrap.holo-card.is-on::before, .front-wrap.holo-card.is-on::after { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .holo-card { transition: none; transform: none !important; }
}

/* ------------------------------------------------------------ pulled on stream */
.pulls-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 20px 18px; perspective: 900px; }
.pull { margin: 0; }
.pull img { display: block; width: 100%; height: auto; border-radius: 12px; box-shadow: 0 12px 30px rgba(0, 0, 0, .5); }
.pull figcaption { margin-top: 9px; font-size: 13px; line-height: 1.35; }
.pull figcaption b { display: block; color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pull figcaption span { display: block; color: var(--dim); font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.2px; font-size: 10.5px; }
/* Gold, because this line is a price and gold is money (DESIGN.md). It exists only when data.js
   carries the date the comparable was read; app.js prints nothing here otherwise. */
.pull figcaption .pull-price { color: var(--gold); text-transform: none; letter-spacing: .2px; font-family: var(--body); font-weight: 600; font-size: 12.5px; margin-top: 2px; }
/* Three across on a phone: two across made eighteen cards a nine-row scroll (looked at 5 Sept). */
@media (max-width: 620px) { .pulls-grid { grid-template-columns: repeat(auto-fill, minmax(100px, 1fr)); gap: 14px 10px; } .pull figcaption { font-size: 12px; } }

.stats { position: relative; list-style: none; display: flex; flex-wrap: wrap; gap: 46px; margin: 52px 0 0; padding: 0; }
.stats b { display: block; font-family: var(--display); font-size: 40px; line-height: 1; }
.stats span { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.8px; font-size: 11px; color: var(--dim); }

/* --------------------------------------------------------------------- slab */
.slab { max-width: var(--max); margin: 30px auto; border: 1px solid var(--line); border-radius: 20px; overflow: hidden; background: linear-gradient(180deg, var(--shell-2), var(--shell)); }
/* THE HEADER IS THE SEAM OF A FOIL PACK, NOT A PRINTED LABEL.
 *
 * It was a cream grading-label strip, and cream on nine stacked sections turned the page into a
 * ladder of white bars — the device stopped reading as a collector object and started reading as
 * a table header. This keeps the same job (descriptor left, badge right, structure carrying
 * meaning) in the dark palette: a crimped seam with a holo hairline catching the light along its
 * top edge, which is what you actually see on the crimp of a sealed pack.
 *
 * The hairline is the ONLY holo on the chrome. Every section wearing a full gradient would be the
 * white-bar problem again in colour.
 *
 * CREAM AGAIN, BY THE OPERATOR'S RULING, 5 SEPT 2026: "cream strips". The seam above was a real
 * fix for a real problem, and DESIGN.md never recorded it, so the panel found the spec and the
 * page disagreeing and put the choice to him. He chose the grading label. The holo hairline stays
 * on its top edge, the crimp dots stay on its bottom edge, and the badge is ink on cream, the way
 * a grade sits on a label. If nine cream bars read as a ladder again, the fix is fewer slabs on
 * the page, not a darker label. */
.label {
  position: relative; display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 11px 18px 10px;
  background: var(--label);
  border-bottom: 1px solid #0c0912;
  color: var(--label-ink);
  font-family: var(--labelface); font-weight: 700; text-transform: uppercase;
  letter-spacing: 2.4px; font-size: 11px;
}
.label::before {
  content: ''; position: absolute; inset: 0 0 auto; height: 2px;
  background: var(--holo); opacity: .85;
}
.label span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--label-ink); }
.label b {
  flex: none; border: 1px solid var(--label-ink); color: var(--label);
  background: var(--label-ink); border-radius: 999px; padding: 3px 11px; font-size: 10px;
}
.window { padding: 34px 30px 40px; }
.window h2 { font-size: clamp(30px, 4.2vw, 46px); line-height: .95; margin-bottom: 14px; }
.sub { color: var(--dim); max-width: 62ch; margin: 0 0 26px; font-size: 15px; }
.sub-tight { margin: 34px 0 16px; }

/* -------------------------------------------------------------------- steps */
.steps { counter-reset: s; list-style: none; margin: 0; padding: 0; display: grid; gap: 14px; max-width: 74ch; }
.steps li { counter-increment: s; position: relative; padding: 14px 16px 14px 58px; border: 1px solid var(--line); border-radius: 14px; background: rgba(255,255,255,.025); font-size: 15px; color: #cdc6dd; }
.steps li b { color: var(--ink); }
.steps li::before { content: counter(s); position: absolute; left: 16px; top: 13px; width: 28px; height: 28px; border-radius: 50%; display: grid; place-items: center; font-family: var(--display); font-size: 15px; color: var(--label-ink); background: var(--holo); }

/* -------------------------------------------------------------------- lanes */
.lanes { display: grid; gap: 16px; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); }
.lane { border: 1px solid var(--line); border-top: 3px solid var(--line); border-radius: 14px; background: rgba(255,255,255,.035); padding: 16px 18px; transition: transform .18s ease; }
.lane:hover { transform: translateY(-3px); }
.lane h3 { font-family: var(--body); font-weight: 800; text-transform: none; font-size: 16px; margin-bottom: 6px; }
.lane p { margin: 0; color: var(--dim); font-size: 14px; }
.lane-1 { border-top-color: var(--holo-1); }
.lane-2 { border-top-color: var(--holo-2); }
.lane-3 { border-top-color: var(--holo-3); }

/* ------------------------------------------------------------------- find us */
.ebay-card { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 4px 20px; text-decoration: none; border: 1px solid var(--line); border-radius: 18px; padding: 26px 28px; background: radial-gradient(120% 160% at 0% 0%, rgba(255,77,94,.16), transparent 60%), rgba(255,255,255,.04); transition: transform .18s ease, border-color .18s ease; }
.ebay-card:hover { transform: translateY(-4px); border-color: var(--live); }
.ebay-k { display: inline-flex; align-items: center; gap: 9px; grid-column: 1; font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 2px; font-size: 11px; color: var(--dim); }
.ebay-v { grid-column: 1; font-family: var(--display); font-size: clamp(34px, 5vw, 52px); line-height: 1.05; }
.ebay-note { grid-column: 1; color: var(--dim); font-size: 14px; }
.ebay-go { grid-column: 2; grid-row: 1 / span 3; font-size: 34px; color: var(--live); }

/* ------------------------------------------------------------------- listings */
/* His live eBay stock. The product photograph does the selling, so the tile is mostly picture and
   the chrome around it is kept to a title and a figure. `contain` not `cover`: these are seller
   photos of boxes and slabs at whatever aspect he shot them, and cropping one to fill a square
   cuts the top off a booster box. */
.lede-sm { color: var(--dim); font-size: 14.5px; max-width: 62ch; margin: 0 0 22px; }

/* ------------------------------------------------------------- what is on the table */
/* A COUNT AND A NAME, nothing else. There are no photographs of these — they are pool names out
   of the deck, not eBay listings with seller images — so this is a list and is built to look like
   a deliberate one rather than a grid with the pictures missing. The number leads because the
   number is the point: sixteen boxes reads differently from one. */
.ontable { display: grid; gap: 10px; }
.ontable-row {
  display: grid; grid-template-columns: 62px 1fr auto; align-items: baseline; gap: 4px 16px;
  border: 1px solid var(--line); border-left: 3px solid var(--holo-2); border-radius: 12px;
  padding: 13px 18px; background: rgba(255,255,255,.035);
}
.ontable-n { font-family: var(--display); font-size: 26px; line-height: 1; color: var(--label); }
.ontable-name { font-weight: 700; font-size: 15.5px; }
.ontable-kind {
  font-family: var(--labelface); font-weight: 700; text-transform: uppercase;
  letter-spacing: 1.6px; font-size: 11px; color: var(--dim);
}
.ontable-slabs { margin-top: 18px; border-top: 1px solid var(--line); padding-top: 18px; }
.ontable-slabs h3 { font-family: var(--body); font-weight: 800; text-transform: none; font-size: 16px; margin-bottom: 8px; }
/* The species run long and are the least important thing here, so they wrap as prose rather than
   forcing twenty more rows the visitor has to scroll past to reach the eBay link. */
.ontable-names { margin: 0; color: var(--dim); font-size: 13.5px; line-height: 1.75; }
@media (max-width: 560px) {
  .ontable-row { grid-template-columns: 48px 1fr; padding: 11px 14px; }
  .ontable-kind { grid-column: 2; }
  .ontable-n { font-size: 22px; }
}

.items { display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
.item {
  display: flex; flex-direction: column; gap: 8px; text-decoration: none; color: var(--ink);
  border: 1px solid var(--line); border-radius: 14px; padding: 12px;
  background: rgba(255,255,255,.035); transition: transform .18s ease, border-color .18s ease;
}
.item:hover { transform: translateY(-3px); border-color: var(--holo-1); }
.item img {
  width: 100%; aspect-ratio: 1; object-fit: contain; border-radius: 9px;
  background: rgba(0,0,0,.35); display: block;
}
.item-title {
  font-size: 13.5px; line-height: 1.35;
  /* Two lines, then an ellipsis. His titles run to eBay's 80-character limit and a tile that grows
     to fit one drags every tile in its row taller with it. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.item-foot { margin-top: auto; display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.item-price { font-family: var(--display); font-size: 19px; letter-spacing: .5px; color: var(--gold); }
.item-tag {
  font-family: var(--labelface); font-weight: 700; text-transform: uppercase;
  letter-spacing: 1.4px; font-size: 10.5px; color: var(--dim);
}

.tiles { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.tile { display: grid; gap: 3px; text-decoration: none; border: 1px solid var(--line); border-radius: 14px; padding: 14px 16px; background: rgba(255,255,255,.03); transition: transform .18s ease, border-color .18s ease; }
.tile:hover { transform: translateY(-3px); border-color: var(--holo-1); }
.tile-k { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.8px; font-size: 10px; color: var(--dim); }
.tile-v { font-family: var(--display); font-size: 22px; }

/* ------------------------------------------------------------------- footer */
.footer { margin-bottom: 60px; }
.foot-grid { display: grid; grid-template-columns: auto 1fr; gap: 22px; align-items: start; }
.foot-mark { border-radius: 50%; }
.foot-lead { margin: 0 0 12px; font-size: 15px; }
.fine { color: var(--dim); font-size: 12.5px; line-height: 1.6; margin: 0 0 10px; max-width: 88ch; }
.store-link { display: inline-block; margin-top: 8px; color: var(--holo-2); text-decoration: none; font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.6px; font-size: 12px; }
.store-link:hover { text-decoration: underline; }

/* --------------------------------------------------------------- responsive */
@media (max-width: 980px) {
  .hero-grid { grid-template-columns: 1fr; }
  /* THE COPY COMES FIRST ON A PHONE, THE PACK SECOND. Until 5 Sept 2026 `.hero-fan { order: -1 }`
     put the pack above the name of the business, so a visitor from a clip link saw an animation
     and had to scroll to learn whose page it was (design lens screenshot, home-phone-top.png).
     The other console's argument settled it: in three seconds a stranger absorbs one fact and one
     button, so the state and the action lead and the pack, which is the argument for staying,
     follows. On show night the state is the event and the same order still holds. */
  .fan { width: min(320px, 74%); }
  .pack-stage { width: min(360px, 84%); }
  .nav { display: none; }
}
@media (max-width: 640px) {
  body { font-size: 16px; }
  .window { padding: 26px 18px 32px; }
  .stats { gap: 28px; }
  .stats b { font-size: 32px; }
  .when > div { grid-template-columns: 1fr; gap: 2px; }
  .wall-row img { width: 96px; }
  /* ONE CLEAN ROW ON A PHONE. Two rows at 3px blur read as a diagonal smear at 9:16 (design lens,
     5 Sept 2026); the cards themselves are the signature, so one row, lightly softened. */
  .wall-rev { display: none; }
  /* inset 0 at the top, not -8%: with one row the box was starting above the hero and ending
     under the sticky header, so no card showed at all (looked at 5 Sept, first-phone-top.png). */
  .wall { inset: 0 -12% auto -12%; filter: blur(1.2px) saturate(1.3); opacity: .45; }
  .btn { padding: 13px 20px; }
  .foot-grid { grid-template-columns: 1fr; }
}

/* Every transition here is decoration; none of it carries meaning, so all of it can go. */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .wall-row, .dot { animation: none; }
  .btn, .tile, .lane, .ebay-card, .fan img, .front-wrap { transition: none; }
  .front-wrap::after { display: none; }
}

/* ---------------------------------------------------------------- the cursor */
/* A POKEBALL THAT OPENS WHEN YOU CLICK.
 *
 * Two SVG cursors, swapped on :active. A CSS cursor cannot animate — it is a static image the OS
 * draws — so "opens" is the second image, shown while the button is held. That is the whole trick
 * and it is why the open state has to read instantly: the halves split, and a spark sits in the
 * gap where the card would come out.
 *
 * THE HOTSPOT IS THE CENTRE (16 16), not the top-left. Left at the default the pointer would sit
 * on the ball's edge and every click would land 16px up and left of where it looked — which feels
 * like the site is misaligned rather than like the cursor is wrong.
 *
 * `, auto` and `, pointer` at the end are not decoration: if the data URI ever fails to parse the
 * browser falls back to a real cursor instead of leaving the visitor with no pointer at all.
 *
 * THE BALL STAYS OVER TEXT TOO. The first version let paragraphs keep the I-beam, on the grounds
 * that a caret tells you text is selectable. He watched it and the flicker between ball and beam
 * as the pointer crossed a heading was worse than losing that hint — on a page nobody comes to in
 * order to select text, consistency wins. `*` carries it everywhere so no element can opt itself
 * back out, which is what the per-element list kept doing.
 */
:root {
  --ball: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='13' fill='%23fff' stroke='%230b0b0b' stroke-width='2'/%3E%3Cpath d='M3 16a13 13 0 0 1 26 0Z' fill='%23ee3b3b' stroke='%230b0b0b' stroke-width='2'/%3E%3Cpath d='M3 16h26' stroke='%230b0b0b' stroke-width='3'/%3E%3Ccircle cx='16' cy='16' r='4.6' fill='%23fff' stroke='%230b0b0b' stroke-width='2'/%3E%3C/svg%3E") 16 16;
  --ball-open: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M3 12a13 13 0 0 1 26 0Z' fill='%23ee3b3b' stroke='%230b0b0b' stroke-width='2'/%3E%3Cpath d='M3 20a13 13 0 0 0 26 0Z' fill='%23fff' stroke='%230b0b0b' stroke-width='2'/%3E%3Cpath d='M16 13.2l1.5 2.8 2.8 1.5-2.8 1.5L16 21.8l-1.5-2.8L11.7 17.5l2.8-1.5Z' fill='%23ffe08a'/%3E%3C/svg%3E") 16 16;
}

body, * { cursor: var(--ball), auto; }
a, button, .btn, .tile, .lane, .ebay-card, summary, [role='button'] { cursor: var(--ball), pointer; }
body:active, body:active * { cursor: var(--ball-open), pointer; }

/* A custom cursor is a pointing device only. Touch has no pointer, and forcing one there just
   costs two image downloads for something nobody will ever see. */
@media (hover: none), (pointer: coarse) {
  body, *, a, button, .btn, .tile, .lane, .ebay-card { cursor: auto; }
}

/* --------------------------------------------------------------- the reveal */
/* Slabs rise as they come into view, staggered. The hidden state is added BY JAVASCRIPT, never in
   the stylesheet — if the script fails to run, nothing is hidden and the page is simply static.
   A reveal that hides content and then never un-hides it is a blank page, which is the one
   failure mode this effect can have and the only one worth engineering against. */
.slab.reveal { opacity: 0; transform: translateY(18px); }
.slab.reveal.in { opacity: 1; transform: none; transition: opacity .6s ease, transform .6s cubic-bezier(.2,.7,.3,1); }

/* ---------------------------------------------------------------- the grain */
/* A very light film grain over everything. Flat dark backgrounds band badly on cheap panels —
   visible steps across the hero gradient — and a little noise breaks the bands up. At 3.5% it is
   below the threshold of "an effect" and above the threshold of doing its job. */
body::after {
  content: ''; position: fixed; inset: 0; z-index: 100; pointer-events: none; opacity: .035;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

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

/* ------------------------------------------------------------ foil stamping */
/* THE HEADLINE IS FOIL-STAMPED, NOT GRADIENT-FILLED.
 *
 * A gradient across letterforms is the house style of every AI-built landing page. Foil stamping
 * is what is actually on the cards this show opens: a metallic layer pressed onto stock, which
 * catches light in narrow bands and misses in patches where the press did not bite.
 *
 * So the fill is a set of tight diagonal stripes over a cream base, clipped to the text. The
 * stripes SHIFT with the pointer while the letters stay put — the material moves, not the type,
 * which is exactly what foil does when you tilt a card under a lamp.
 *
 * `--foil` is written by app.js on pointermove, throttled to one requestAnimationFrame. On a
 * device with no fine pointer it simply never changes and the stamp sits still, which is a
 * perfectly good static headline.
 */
.hero-h1 span {
  --foil: 0px;
  background-image:
    repeating-linear-gradient(102deg,
      rgba(255,255,255,.00) 0px, rgba(255,255,255,.00) 6px,
      rgba(255,255,255,.42) 8px, rgba(255,255,255,.00) 13px,
      rgba(255,255,255,.00) 22px),
    linear-gradient(180deg, #fffdf6, #d9d2ea 62%, #b9b0d4);
  background-position: var(--foil) 0, 0 0;
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  /* The press mark: one hard shadow down-right, the way stamped foil sits proud of the stock. */
  filter: drop-shadow(1px 2px 0 rgba(0,0,0,.55));
}

/* The last line keeps the holo, because that IS the chase-card colour and the line is "day." */
.hero-h1 .holo {
  background-image:
    repeating-linear-gradient(102deg,
      rgba(255,255,255,.00) 0px, rgba(255,255,255,.00) 6px,
      rgba(255,255,255,.55) 8px, rgba(255,255,255,.00) 13px,
      rgba(255,255,255,.00) 22px),
    var(--holo);
  background-position: var(--foil) 0, 0 0;
}

@media (prefers-reduced-motion: reduce) {
  .hero-h1 span { --foil: 0px !important; }
}

/* ------------------------------------------------------------- the tear strip */
/* Under every grading label sits a perforation, the way a booster wrapper tears.
 *
 * The slab and the wrapper are the two objects this business handles all night, and the page
 * already had the slab. This is three pixels of the other one: a dotted rule where the label meets
 * the window, so the label reads as something you peel rather than a coloured bar.
 *
 * Pure CSS — a radial-gradient repeated along the axis. No image, no request, no layout cost, and
 * it scales with the slab because the dots are sized in em.
 */
.label { position: relative; }
.label::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: -3px; height: 3px;
  background-image: radial-gradient(circle at 50% 120%, var(--ground) 46%, transparent 47%);
  background-size: 9px 6px; background-repeat: repeat-x; opacity: .6;
  pointer-events: none;
}


/* ----------------------------------------------------------------- the loop */
.story {
  display: block; width: 100%; height: auto; margin: 0 0 26px;
  border: 1px solid var(--line); border-radius: 14px; background: var(--ground);
  box-shadow: 0 22px 50px rgba(0,0,0,.5);
}
/* A looping video is motion nobody asked for. Reduced motion gets the poster frame instead —
   `display:none` on the video would leave a hole where a picture should be. */
@media (prefers-reduced-motion: reduce) {
  .story { content: url('spot-story-poster.jpg'); }
}


/* ---------------------------------------------------------------- follow me */
.follow { margin-top: 22px; border: 1px solid var(--line); border-radius: 16px; padding: 22px 24px; background: rgba(255,255,255,.03); }
.follow-lead { margin: 0 0 10px; font-size: 16px; }
.follow-steps { margin: 0 0 12px; padding-left: 20px; color: #cdc6dd; font-size: 15px; display: grid; gap: 6px; }
.follow-steps b { color: var(--ink); }
.follow-fine { color: var(--dim); font-size: 13px; margin: 0 0 16px; max-width: 62ch; }


/* ------------------------------------------------------------- easter eggs */
/* Two, both invisible until found, neither in the way of anything.
   A page for people who open packs for fun should reward poking at it. */

/* The holo flash: the whole page catches the light once, like a card tilted under a lamp. */
.holo-flash::before {
  content: ''; position: fixed; inset: -50%; z-index: 95; pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(255,255,255,.5) 48%,
    rgba(150,220,255,.42) 52%, transparent 62%);
  mix-blend-mode: screen; animation: holo-sweep 1.1s ease-out forwards;
}
@keyframes holo-sweep { from { transform: translateX(-60%) rotate(8deg); } to { transform: translateX(60%) rotate(8deg); } }

/* The toast. Bottom centre, out of the way of the live button. */
.egg-toast {
  position: fixed; left: 50%; bottom: 26px; transform: translate(-50%, 130%);
  z-index: 96; display: flex; align-items: center; gap: 10px;
  padding: 12px 18px; border-radius: 999px; border: 1px solid var(--line);
  background: rgba(23,19,31,.96); backdrop-filter: blur(10px);
  font-family: var(--labelface); font-weight: 700; text-transform: uppercase;
  letter-spacing: 1.6px; font-size: 12px; color: var(--ink);
  transition: transform .45s cubic-bezier(.2,.7,.3,1);
}
.egg-toast.up { transform: translate(-50%, 0); }
.egg-toast img { width: 26px; height: 26px; border-radius: 50%; }

@media (prefers-reduced-motion: reduce) {
  .holo-flash::before { animation: none; opacity: 0; }
  .egg-toast { transition: none; }
}

/* What buyers said. A quote card, deliberately quieter than an item card — a testimonial should
   read as someone talking, not as another thing to click. No avatar, no name, no star row:
   the buyer's identity never leaves the server and a decorative five-star graphic would be a
   claim the page is asserting rather than a figure eBay gave it. No caption either: the item
   title is not carried, because his listing titles name a spin. */
.quotes {
  display: grid; gap: 14px; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  /* Cards size to their own text. Without this a grid row is as tall as its tallest card, and
     one long quote leaves three short ones sitting in a third of a box of empty space --
     seen on the live page before this line existed. */
  align-items: start;
}
.quote {
  margin: 0; padding: 20px 22px; border: 1px solid var(--line); border-radius: 16px;
  background: rgba(255,255,255,.03);
}
.quote blockquote {
  margin: 0; font-size: 15.5px; line-height: 1.5; color: var(--ink);
  /* A buyer's typing is not trusted to be short, or to contain spaces. */
  overflow-wrap: anywhere;
}
.quote blockquote::before { content: '\201C'; }
.quote blockquote::after { content: '\201D'; }

/* Text for screen readers only: the description of the how-it-works animation (aria-describedby
   on the video, 5 Sept 2026). The standard clip pattern; display:none would hide it from them too. */
.visually-hidden { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }

/* ------------------------------------------------- what we're posting (#socials)
   Tiles, not embeds. Facebook's page plugin and Instagram's embed would each need the CSP in
   site/_headers opened to Meta and a privacy-policy change, because they set cookies on every
   visitor. These are our own elements linking to their posts, so nothing third-party runs.
   The platform is named in TEXT rather than shown as a channel avatar: those avatars are hosted
   on the publisher's CDN and img-src does not allow it - see functions/api/socials.js. */
.posts { display: grid; gap: 12px; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); margin: 14px 0 0; }
.post { display: grid; gap: 6px; align-content: start; text-decoration: none; color: inherit; border: 1px solid var(--line); border-radius: 14px; padding: 13px 15px; background: rgba(255,255,255,.03); transition: transform .18s ease, border-color .18s ease; }
a.post:hover, a.post:focus-visible { transform: translateY(-3px); border-color: var(--holo-1); }
.post-plat { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.8px; font-size: 10px; color: var(--dim); }
.post-title { font-family: var(--display); font-size: 17px; line-height: 1.25; }
.post-when { font-size: 12px; color: var(--dim); }
/* Queued posts are not links - there is nothing to open yet - so they must not look clickable. */
.post-next { opacity: .72; border-style: dashed; }
.posts-next { margin-top: 8px; }
@media (max-width: 560px) {
  .posts { grid-template-columns: 1fr; }
  .post-title { font-size: 16px; }
}

/* ------------------------------------------------------ shows coming up (#shows)
   Cards for the events eBay is advertising. A featured show (config/events.json) spans the full
   width and gets its artwork larger; everything else sits in the grid. Artwork is eBay's own by
   default, which is what a visitor sees on the show page itself. */
.shows { display: grid; gap: 14px; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); margin: 16px 0 0; }
.show { display: grid; grid-template-columns: 76px 1fr; gap: 14px; align-items: start; text-decoration: none; color: inherit; border: 1px solid var(--line); border-radius: 16px; padding: 14px; background: rgba(255,255,255,.03); transition: transform .18s ease, border-color .18s ease; }
.show:hover, .show:focus-visible { transform: translateY(-3px); border-color: var(--holo-1); }
.show-art { width: 76px; height: 114px; object-fit: cover; border-radius: 10px; display: block; background: rgba(255,255,255,.05); }
.show-body { display: grid; gap: 5px; align-content: start; }
.show-kicker { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.8px; font-size: 10px; color: var(--gold); }
.show-title { font-family: var(--display); font-size: 19px; line-height: 1.2; }
.show-when { font-size: 13px; color: var(--holo-2); }
/* HOW FAR AWAY IT IS, as a chip beside the date. Deliberately NOT gold and NOT --live: gold is
   money only and --live is on-air only, and a countdown is neither. It reads as quiet furniture
   next to eBay's own time, which is the line that matters. */
.show-countdown { align-self: start; font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.4px; font-size: 10px; color: var(--dim); border: 1px solid var(--line); border-radius: 999px; padding: 3px 9px; margin-top: 2px; }
.show-blurb { font-size: 13.5px; color: var(--dim); line-height: 1.5; }
.show-cta { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.6px; font-size: 11px; color: var(--holo-2); margin-top: 3px; }
/* The featured show takes the whole row and a bigger picture - it is the one being singled out. */
.show-featured { grid-column: 1 / -1; grid-template-columns: 116px 1fr; border-color: var(--gold); background: rgba(226,188,110,.06); }
.show-featured .show-art { width: 116px; height: 174px; }
.show-featured .show-title { font-size: 23px; }
@media (max-width: 560px) {
  .shows { grid-template-columns: 1fr; }
  .show, .show-featured { grid-template-columns: 64px 1fr; gap: 12px; }
  .show-art, .show-featured .show-art { width: 64px; height: 96px; }
  .show-featured .show-title { font-size: 20px; }
}

/* ============================================================== THE POKEDEX RUN (blog.html)
   Written for a page that loads NO JavaScript, so everything here has to be right at rest —
   there is no observer to reveal anything and no script to fix a layout afterwards.
   Borrows the .show card idiom deliberately (same border, radius, lift on hover) so the blog
   reads as part of the site rather than a bolted-on second design. */
.ep-h1 { font-family: var(--display); font-size: clamp(30px, 5vw, 52px); line-height: .95; margin: 0 0 14px; }
.eps { display: grid; gap: 14px; }
.ep {
  border: 1px solid var(--line);
  border-radius: 16px;
  padding: 18px 20px;
  background: rgba(255,255,255,.03);
  transition: border-color .18s ease;
}
.ep:hover { border-color: var(--holo-1); }
/* The title and the date share a row and stack on a phone. `gap` does the spacing so nothing
   collapses or doubles, per the layout rule the rest of this file follows. */
.ep-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 14px; margin-bottom: 8px; }
.ep-title { font-family: var(--display); font-size: clamp(21px, 3vw, 27px); line-height: 1.1; margin: 0; }
.ep-when { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.4px; font-size: 11px; color: var(--dim); white-space: nowrap; }
.ep-dex { font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.5px; font-size: 11px; color: var(--holo-2); margin: 0 0 10px; }
.ep-body { color: var(--ink); font-size: 15px; line-height: 1.6; margin: 0 0 8px; max-width: 68ch; }
.ep-count { color: var(--dim); font-size: 13.5px; margin: 0 0 12px; }
.ep-links { font-size: 12.5px; color: var(--dim); margin: 0; display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.ep-link {
  font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.2px;
  font-size: 11px; color: var(--holo-2); text-decoration: none;
  border: 1px solid var(--line); border-radius: 999px; padding: 4px 10px;
}
.ep-link:hover, .ep-link:focus-visible { border-color: var(--holo-2); }
@media (max-width: 560px) {
  .ep { padding: 16px; }
  .ep-head { flex-direction: column; align-items: flex-start; }
}

/* ============================================ REAL FRAMES FROM THE SHOW (20 Sept 2026)
   Every human image on this site was the drawn mascot until now. These are frames out of
   real replays — the operator's own desk, his own hands, his own laugh.

   TWO DIFFERENT JOBS, AND THEY MUST NOT BLUR INTO ONE:
     .who-shot   ONE photograph, at full strength, in #about. That section asks "who runs it"
                 and a face is the answer. Faded, it would answer nothing.
     .slab-shot  TEXTURE, at ~12%, behind three other slabs. These are not pictures to look
                 at; they are evidence that a real bench exists behind the copy. Any higher and
                 they fight the card wall, which DESIGN.md says owns the page's colour. */

/* THE PHOTO SITS BESIDE THE COPY, and drops under it on a phone. auto-fit would let it collapse
   into a column of its own at odd widths; an explicit two-track grid with a stated breakpoint
   is predictable, which matters more than clever here. */
.who-window { display: grid; grid-template-columns: 300px 1fr; gap: 26px; align-items: start; }
.who-window > h2, .who-window > .lede-sm { grid-column: 2; }
.who-shot { grid-row: 1 / span 2; grid-column: 1; margin: 0; }
.who-shot img {
  width: 100%; height: auto; display: block;
  border-radius: 14px; border: 1px solid var(--line);
  /* The frame is 720px of source upscaled once. A hard edge would advertise that; a soft inner
     shadow and the site's own border let it sit as part of the slab rather than a pasted-in JPEG. */
  box-shadow: 0 18px 40px rgba(0,0,0,.5);
}
.who-window > .lanes, .who-window > .fine { grid-column: 1 / -1; }
@media (max-width: 860px) {
  .who-window { grid-template-columns: 1fr; gap: 18px; }
  .who-window > h2, .who-window > .lede-sm { grid-column: 1; }
  .who-shot { grid-row: auto; grid-column: 1; max-width: 340px; }
}

/* PRESENT AT REST, NOT REVEALED. The slabs animate in on an observer; a background that waited
   for one would be missing from the first painted frame and from any screenshot of it. This is a
   plain ::before with a fixed opacity — it is simply there. */
.slab-shot { position: relative; }
.slab-shot > * { position: relative; z-index: 1; }
.slab-shot::before {
  content: ''; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background-size: cover; background-position: center;
  opacity: .12;
  /* Fades out toward the label strip so the cream band stays clean and the text keeps its
     contrast where the copy actually sits. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 38%, #000 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 38%, #000 100%);
}
#how.slab-shot::before    { background-image: url('brand/stream/hands.webp'); }
#before.slab-shot::before { background-image: url('brand/stream/card.webp'); }
#find.slab-shot::before   { background-image: url('brand/stream/table.webp'); }
#about.slab-shot::before  { background-image: url('brand/stream/bench.webp'); opacity: .08; }
/* QUIETER ON A PHONE, because the same opacity is not the same picture.
   A slab is 1,180px wide on a desktop and ~360 on a phone, so `background-size: cover` crops the
   frame far harder and what survives is one bright region filling the whole panel instead of a
   wide, mostly-dark scene. Looked at on the 390px shot: the tiles in #find were sitting on the
   brightest part of the table photo and losing contrast. Mobile is the majority of this site's
   real traffic — ChromeMobileWebview, ChromeMobile, MobileSafari and Instagram together beat
   desktop Chrome on 19 Sept — so the phone is the case to tune for, not the exception. */
@media (max-width: 620px) {
  .slab-shot::before { opacity: .06; }
  #about.slab-shot::before { opacity: .05; }
}

/* ================================================ NEVER MISS A SHOW (20 Sept 2026)
   The follow block moved out of the bottom of #find and became its own slab under the main
   event. Same copy — it is the most carefully hedged text on the site and none of it changed —
   but it now has to read as the ask it is, rather than as a footnote under nine channel tiles.

   IT DOES NOT TAKE .btn-primary. The holo button belongs to the one action the hero is making at
   that moment: "Watch live now" during a show, "Follow on eBay" outside one. A second holo button
   further down the page would be two primaries competing, which is the thing the 11 Sept fold-in
   was done to stop. This takes the cream .btn-live treatment instead — unmistakably a button,
   visibly not the same button. */
#never-miss .follow { margin-top: 0; border: none; background: none; padding: 0; }
#never-miss .follow-lead { font-family: var(--display); font-size: clamp(21px, 2.8vw, 30px); line-height: 1.05; margin: 0 0 14px; text-transform: uppercase; letter-spacing: .5px; }
#never-miss .follow-lead b { font-weight: 400; }

/* THE STEPS ARE THE POINT. eBay publishes no link that performs a follow, so the only thing this
   page can do is name the two controls and where they are. Numbered, spaced, and readable at a
   glance rather than set as a tight list. */
/* THE NUMBER IS POSITIONED, NOT LAID OUT, and the first attempt got this wrong in a way worth
   recording. `li { display: grid; grid-template-columns: 30px 1fr }` turns EVERY child of the li
   into a grid item — including the inline <b>heart</b> and <b>bell</b> — so "Tap the" landed in
   column two and "heart" was flung onto a new row in column one. Reproduced in isolation rather
   than inferred from the screenshot: under grid the <b> computes display:block, the li measures
   52px over two rows and the bold sits at x=20; under the rule below it computes inline, the li
   measures 21px and the bold stays in the sentence. Measured 20 Sept 2026.
   Nothing in the suite inspects layout, so test/site-follow-steps.test.js guards the
   declaration instead.
   An absolutely positioned ::before leaves the li's content as ordinary inline flow, so the bold
   words stay in the sentence where they belong. */
#never-miss .follow-steps { list-style: none; padding: 0; margin: 0 0 16px; display: grid; gap: 12px; counter-reset: step; max-width: 62ch; }
#never-miss .follow-steps li { counter-increment: step; position: relative; padding-left: 40px; font-size: 15.5px; line-height: 1.5; }
#never-miss .follow-steps li::before {
  content: counter(step);
  position: absolute; left: 0; top: 0;
  font-family: var(--labelface); font-weight: 700; font-size: 12px;
  color: var(--holo-2); border: 1px solid var(--line); border-radius: 999px;
  width: 26px; height: 26px; display: grid; place-items: center;
}
#never-miss .follow-fine { font-size: 13.5px; margin: 0 0 18px; }
#never-miss #follow-cta { background: var(--label); color: var(--label-ink); border-color: transparent; }
#never-miss #follow-cta:hover { background: #fffdf6; }

/* ==================================================== THE MAIN EVENT (20 Sept 2026)
   "Super visible and different to the live now one", and it says who is hosting.

   DIFFERENT TO A SLAB ON PURPOSE. Every other section on this page is a graded holder with a
   cream label. This one is not a slab: it is a lit panel with the holo sweep around it, so it
   does not read as "another section" — which is the whole point of the instruction. */
.mainevent {
  max-width: var(--max); margin: 30px auto; border-radius: 22px; padding: 2px;
  /* The border IS the holo. A gradient border needs a padded wrapper; the inner panel paints the
     ground back over the middle, leaving the sweep as a 2px edge. */
  background: var(--holo);
  box-shadow: 0 24px 60px rgba(0,0,0,.55);
}
.me-inner {
  display: grid; grid-template-columns: 150px 1fr; gap: 22px; align-items: center;
  background: linear-gradient(180deg, var(--shell-2), var(--shell));
  border-radius: 20px; padding: 24px 26px;
}
.me-kicker {
  grid-column: 1 / -1; margin: 0 0 2px;
  font-family: var(--labelface); font-weight: 700; text-transform: uppercase;
  letter-spacing: 3px; font-size: 11px; color: var(--dim);
}
.me-art { width: 150px; height: auto; border-radius: 12px; display: block; background: rgba(255,255,255,.05); }
.me-body { display: grid; gap: 6px; }
/* WHO. The largest thing in the block after the title, because the instruction was that it say
   who it is — that has to be readable before anything is read. */
.me-host {
  margin: 0; font-family: var(--display); font-size: clamp(20px, 2.6vw, 30px);
  line-height: 1; text-transform: uppercase; letter-spacing: .5px;
  background: var(--holo); -webkit-background-clip: text; background-clip: text; color: transparent;
}
.me-title { margin: 0; font-family: var(--display); font-size: clamp(22px, 3.2vw, 34px); line-height: 1.05; }
.me-meta { margin: 4px 0 0; display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.me-when { font-size: 15px; color: var(--holo-2); }
.me-count {
  font-family: var(--labelface); font-weight: 700; text-transform: uppercase; letter-spacing: 1.4px;
  font-size: 10px; color: var(--dim); border: 1px solid var(--line); border-radius: 999px; padding: 3px 9px;
}
.me-blurb { margin: 6px 0 0; font-size: 14px; color: var(--dim); line-height: 1.5; max-width: 60ch; }
.me-cta { margin-top: 12px; justify-self: start; }

/* THE GUEST SIGNAL. A takeover flashes its kicker; his own show does not. If everything pulsed,
   nothing would mean anything — the flash exists to mark the exception.
   NOT --live RED AND NOT GOLD: red is on-air only and gold is money only, per DESIGN.md. */
.mainevent.is-guest .me-kicker { color: var(--holo-1); animation: guest-flash 1.9s ease-in-out infinite; }
@keyframes guest-flash { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) {
  .mainevent.is-guest .me-kicker { animation: none; opacity: 1; }
}
@media (max-width: 620px) {
  .me-inner { grid-template-columns: 1fr; gap: 16px; padding: 20px; }
  .me-art { width: 118px; }
}

/* ============================================ COME AND JOIN US (20 Sept 2026)
   The six channels were introduced by a caption and a colon at the bottom of the page. They are
   the room; they now get a heading of their own and tiles that read as destinations. */
.join-h {
  font-family: var(--display); font-size: clamp(22px, 3vw, 32px); line-height: 1;
  margin: 34px 0 8px; text-transform: uppercase; letter-spacing: .5px;
}
.join-sub { margin-top: 0; }
/* Bigger targets, a lit edge, and the holo sweep on hover. Same grammar as .tile, turned up —
   not a second design. */
.tiles-join { grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; }
.tiles-join .tile { padding: 18px 18px; background: rgba(255,255,255,.05); border-color: #3a3057; }
.tiles-join .tile:hover, .tiles-join .tile:focus-visible { border-color: var(--holo-2); background: rgba(255,255,255,.08); }
.tiles-join .tile-v { font-size: 25px; }
.tiles-join .tile-k { color: var(--holo-2); }
