/* =======================================================================
   SHWING :: shwing.css
   A third room in the Frost Milano network and the loudest one: where the
   Frost page is ice/chrome and the Neck page is shellac/amber, this room is
   pure late-'80s retrowave — a neon sunset over a Tron grid, VHS tracking
   lines, Memphis-Group confetti, and a big cable-access TV you channel-surf.

   The band is real — a group of dudes out of the Austin scene playing their
   own songs — so the site is built like a cable-access TV you flip channels
   on, one real reel at a time. The retrowave look is this ROOM's costume and
   says nothing about how they sound; see the COPY RULE at the top of
   shwing.html before writing any prose for this page.

   All colours live in :root below. Nothing loads from outside the site
   except the four Google fonts in the <head>.
   ======================================================================= */

:root{
  /* THE TAPE — the dark it all sits on: a purple-black VHS night. */
  --vhs-1:   #0b0718;   /* the far dark / deepest black-purple */
  --vhs-2:   #150a2e;   /* the night sky */
  --vhs-3:   #23124f;   /* panel bodies, lit a touch */
  --vhs-4:   #341a6b;   /* raised panel / grid glow */

  /* THE NEON — the three tubes everything is lit by. Hot magenta and
     electric cyan are the pair; violet bridges them. Used loud, on black. */
  --magenta:     #ff2e97;
  --magenta-lit: #ff77c2;
  --magenta-deep:#b3155f;
  --cyan:        #1fe3ff;
  --cyan-lit:    #8af6ff;
  --cyan-deep:   #0f8fa8;
  --violet:      #a24bff;
  --violet-lit:  #c78bff;

  /* THE SUNSET — the classic slatted retrowave sun behind the grid. */
  --sun-1: #ff5e8a;
  --sun-2: #ff9d4d;
  --sun-3: #ffe83d;

  /* Accents that read as "broadcast": a caution yellow and a signal green. */
  --yellow:  #ffe83d;
  --lime:    #5bff8f;
  --alert:   #ff3b3b;

  /* Ink — never pure white; a warm CRT phosphor white that browns toward lilac. */
  --ink:      #f4ecff;
  --ink-soft: #cbb9ff;
  --ink-faint:#8f7ec9;

  --grid-line: rgba(255,46,151,.55);   /* magenta grid on the floor */
  --grid-line2:rgba(31,227,255,.35);   /* a cyan cross-glow */

  /* Type — an all-'80s stack, none of it shared with the other two rooms. */
  --neon:  "Monoton", "Impact", sans-serif;                 /* the tube logo */
  --chunk: "Bungee", "Arial Black", sans-serif;             /* fat headings/buttons */
  --osd:   "Share Tech Mono", "Courier New", monospace;     /* broadcast on-screen text */
  --body:  "Oxanium", "Trebuchet MS", system-ui, sans-serif;/* readable body */
  /* The crate's hand, and the only place on the page anything is written
     rather than set: the song names on the cassette labels. A marker, not a
     pen — this is a label on a tape a band hands you. */
  --hand:  "Permanent Marker", "Comic Sans MS", cursive;
}

*{ box-sizing:border-box; }

html{ scroll-behavior:smooth; }

body{
  margin:0;
  font-family:var(--body);
  color:var(--ink);
  line-height:1.65;
  background-color:var(--vhs-1);
  /* The retrowave sky: deep violet-black at the very top, warming down to a
     magenta/orange horizon glow around 64%, then the dark floor the grid
     sits on. Built entirely from gradients — no image file. The receding
     neon grid itself is body::before (below), drawn in perspective. */
  background-image:
    radial-gradient(120% 70% at 50% 64%, rgba(255,94,138,.28) 0%, rgba(255,94,138,0) 46%),
    radial-gradient(60% 42% at 50% 66%, rgba(255,157,77,.22) 0%, rgba(255,157,77,0) 60%),
    linear-gradient(180deg,
      var(--vhs-1) 0%, var(--vhs-2) 34%, #3a1560 58%,
      #6a1e6e 64%, #2a0f45 70%, var(--vhs-1) 100%);
  background-attachment:fixed;
  overflow-x:hidden;
}

/* THE GRID FLOOR — the retrowave staple: a neon grid receding to the horizon.
   A fixed layer pinned to the lower half, tilted back in perspective, with the
   grid lines drawn as repeating gradients and scrolled toward the viewer. It's
   purely decorative and sits behind the whole page column (z-index below .deck).
   Animation is stilled under reduced motion at the very bottom of this file. */
body::before{
  content:""; position:fixed; z-index:0; pointer-events:none;
  left:-50%; right:-50%; bottom:-10%; height:70vh;
  background-image:
    repeating-linear-gradient(90deg,
      transparent 0, transparent 78px, var(--grid-line) 78px, var(--grid-line) 82px),
    repeating-linear-gradient(0deg,
      transparent 0, transparent 78px, var(--grid-line2) 78px, var(--grid-line2) 82px);
  background-size:82px 82px, 82px 82px;
  transform:perspective(340px) rotateX(74deg);
  transform-origin:50% 100%;
  animation:grid-run 2.6s linear infinite;
  mask-image:linear-gradient(180deg, transparent 0%, #000 42%, #000 100%);
  -webkit-mask-image:linear-gradient(180deg, transparent 0%, #000 42%, #000 100%);
  opacity:.9;
}
@keyframes grid-run{ from{ background-position:0 0, 0 0; } to{ background-position:0 82px, 0 82px; } }

/* THE SUN — the slatted disc sitting on the horizon behind everything. Fixed,
   behind the grid. The horizontal slats are a repeating-linear-gradient mask,
   the whole retrowave-sunset trick in one element. */
body::after{
  content:""; position:fixed; z-index:0; pointer-events:none;
  left:50%; bottom:26vh; width:min(560px,86vw); height:min(560px,86vw);
  transform:translateX(-50%);
  border-radius:50%;
  background:linear-gradient(180deg, var(--sun-3) 0%, var(--sun-2) 40%, var(--sun-1) 72%, var(--magenta) 100%);
  -webkit-mask-image:
    linear-gradient(180deg, #000 0 54%, transparent 54%),
    repeating-linear-gradient(180deg, #000 0 8px, transparent 8px 15px);
  -webkit-mask-composite:source-in;
  mask-image:
    linear-gradient(180deg, #000 0 54%, transparent 54%),
    repeating-linear-gradient(180deg, #000 0 8px, transparent 8px 15px);
  mask-composite:intersect;
  filter:drop-shadow(0 0 60px rgba(255,94,138,.55));
  opacity:.85;
}

/* ================= CONFETTI (drifting Memphis-Group shapes) =============
   The room's decorative motion layer — the counterpart to Frost's snow and
   Neck's rising embers. Neon triangles, zigzags, dots and squiggles drift
   across, populated by js/shwing.js. Fixed, behind the page. JS skips it
   entirely under reduced motion. */
#confetti{
  position:fixed; inset:0; z-index:1; pointer-events:none; overflow:hidden;
}
.bit{
  position:absolute; top:-40px; will-change:transform;
  animation:bit-fall linear forwards;
  opacity:.85;
}
@keyframes bit-fall{
  0%{ transform:translateY(-10vh) translateX(0) rotate(0deg); }
  100%{ transform:translateY(115vh) translateX(var(--drift,40px)) rotate(var(--spin,360deg)); }
}
/* the shapes, all drawn in CSS so there's no image file */
.bit--tri{ width:0; height:0; border-left:9px solid transparent; border-right:9px solid transparent; border-bottom:16px solid var(--magenta); filter:drop-shadow(0 0 6px var(--magenta)); }
.bit--dot{ width:11px; height:11px; border-radius:50%; background:var(--cyan); box-shadow:0 0 8px var(--cyan); }
.bit--sq{ width:12px; height:12px; background:var(--yellow); box-shadow:0 0 8px var(--yellow); transform:rotate(45deg); }
.bit--zig{ width:16px; height:16px; border:3px solid var(--violet); border-top:none; border-right:none; filter:drop-shadow(0 0 6px var(--violet)); }
.bit--plus{ width:14px; height:14px; background:
    linear-gradient(var(--lime),var(--lime)) center/100% 4px no-repeat,
    linear-gradient(var(--lime),var(--lime)) center/4px 100% no-repeat;
    filter:drop-shadow(0 0 6px var(--lime)); }

/* ================= THE BROADCAST CRAWL (top ticker) ================= */
.crawl{
  position:relative; z-index:5;
  background:linear-gradient(90deg, var(--magenta-deep), var(--violet), var(--cyan-deep));
  border-bottom:3px solid var(--cyan);
  box-shadow:0 0 22px rgba(31,227,255,.5);
  overflow:hidden; white-space:nowrap;
}
.crawl__track{
  display:inline-block; padding:8px 0;
  font-family:var(--osd); font-size:.98rem; letter-spacing:2px;
  color:#fff; text-shadow:0 0 8px rgba(255,255,255,.55);
  animation:crawl 26s linear infinite;
}
@keyframes crawl{ from{ transform:translateX(0); } to{ transform:translateX(-50%); } }

/* ================= PAGE SHELL ================= */
.deck{
  position:relative; z-index:4;
  max-width:1040px; margin:0 auto; padding:0 16px 60px;
}

/* ---------- HERO ---------- */
.marquee-hero{
  position:relative; text-align:center;
  margin:26px 0 20px; padding:34px 20px 30px;
  border:3px solid var(--magenta);
  border-radius:16px;
  background:
    radial-gradient(120% 90% at 50% -10%, rgba(162,75,255,.35), transparent 60%),
    linear-gradient(180deg, rgba(21,10,46,.86), rgba(11,7,24,.92));
  box-shadow:
    0 0 0 3px rgba(31,227,255,.25),
    0 0 34px rgba(255,46,151,.45),
    inset 0 0 50px rgba(162,75,255,.22);
}

/* the ON AIR tally light */
.onair{
  display:inline-flex; align-items:center; gap:9px;
  font-family:var(--osd); font-size:.92rem; letter-spacing:3px;
  color:var(--ink); padding:5px 14px; margin-bottom:14px;
  border:2px solid var(--alert); border-radius:6px;
  background:rgba(255,59,59,.12);
  text-shadow:0 0 8px rgba(255,59,59,.6);
}
.onair__dot{
  width:11px; height:11px; border-radius:50%;
  background:var(--alert); box-shadow:0 0 12px var(--alert);
  animation:blink 1.3s steps(2,start) infinite;
}
@keyframes blink{ 50%{ opacity:.15; } }

.logo{
  margin:0; line-height:.9;
  font-family:var(--neon); font-weight:400;
  font-size:clamp(3.4rem, 15vw, 8rem);
  letter-spacing:2px;
  color:var(--cyan-lit);
  /* stacked neon-tube glow */
  text-shadow:
    0 0 6px #fff,
    0 0 16px var(--cyan),
    0 0 34px var(--cyan),
    0 0 60px var(--magenta),
    0 0 90px var(--magenta);
  animation:neon-hum 3.6s ease-in-out infinite;
}
@keyframes neon-hum{
  0%,100%{ opacity:1; }
  47%{ opacity:1; }
  48%{ opacity:.72; }        /* the tube flickers once in a while */
  49%{ opacity:1; }
  92%{ opacity:1; }
  93%{ opacity:.6; }
  94%{ opacity:1; }
}
.logo__sub{
  margin:12px 0 0;
  font-family:var(--chunk); font-size:clamp(.85rem,3.4vw,1.25rem);
  letter-spacing:2px; color:var(--yellow);
  text-shadow:0 0 10px rgba(255,232,61,.55);
}
.hero__blurb{
  max-width:640px; margin:16px auto 0;
  color:var(--ink-soft); font-size:1.02rem;
}
.hero__blurb b{ color:var(--magenta-lit); }

.hero__btns{
  display:flex; flex-wrap:wrap; justify-content:center; gap:12px;
  margin-top:22px;
}

/* the chunky arcade button, in three tube colours */
.btn{
  display:inline-block; cursor:pointer; text-decoration:none;
  font-family:var(--chunk); font-size:1rem; letter-spacing:1px;
  color:#0b0718; padding:12px 20px; border-radius:10px;
  background:var(--cyan);
  border:none;
  box-shadow:0 5px 0 var(--cyan-deep), 0 8px 20px rgba(31,227,255,.4);
  transition:transform .08s ease, box-shadow .08s ease, filter .12s ease;
}
.btn:hover{ filter:brightness(1.08); }
.btn:active{ transform:translateY(4px); box-shadow:0 1px 0 var(--cyan-deep), 0 3px 10px rgba(31,227,255,.4); }
.btn:focus-visible{ outline:3px solid var(--yellow); outline-offset:3px; }
.btn--pink{ background:var(--magenta); box-shadow:0 5px 0 var(--magenta-deep), 0 8px 20px rgba(255,46,151,.4); }
.btn--yellow{ background:var(--yellow); box-shadow:0 5px 0 #b39a00, 0 8px 20px rgba(255,232,61,.4); }
.btn--sm{ font-size:.82rem; padding:8px 13px; box-shadow:0 4px 0 var(--cyan-deep); }
.btn--pink.btn--sm{ box-shadow:0 4px 0 var(--magenta-deep); }
.btn--ghost{
  background:transparent; color:var(--cyan-lit);
  border:2px solid var(--cyan); box-shadow:0 0 14px rgba(31,227,255,.35);
}
.btn--ghost:hover{ background:rgba(31,227,255,.12); }

.hero__rules{
  margin:22px auto 0; max-width:720px;
  font-family:var(--osd); font-size:.82rem; letter-spacing:1.5px;
  color:var(--ink-faint); line-height:1.9;
}

/* ---------- NAV (channel buttons) ---------- */
.channels{
  position:sticky; top:0; z-index:6;
  display:flex; flex-wrap:wrap; justify-content:center; gap:7px;
  padding:11px; margin:0 0 26px;
  background:rgba(11,7,24,.82);
  backdrop-filter:blur(6px);
  border:2px solid var(--violet);
  border-radius:12px;
  box-shadow:0 0 22px rgba(162,75,255,.35);
}
.channels__btn{
  font-family:var(--osd); font-size:.82rem; letter-spacing:1px;
  text-decoration:none; color:var(--ink);
  padding:6px 11px; border-radius:7px;
  background:rgba(162,75,255,.16);
  border:1px solid rgba(199,139,255,.5);
  transition:background .12s ease, color .12s ease, box-shadow .12s ease;
}
.channels__btn:hover{ background:var(--magenta); color:#0b0718; box-shadow:0 0 14px var(--magenta); }
.channels__btn:focus-visible{ outline:2px solid var(--yellow); outline-offset:2px; }

/* ================= SECTIONS ================= */
.bay{
  /* the channel nav is sticky, so an anchor jump has to stop clear of it or
     the section's own heading lands underneath the buttons */
  scroll-margin-top:120px;
  margin:0 0 30px; padding:26px 22px;
  background:linear-gradient(180deg, rgba(35,18,79,.7), rgba(21,10,46,.78));
  border:2px solid var(--cyan);
  border-radius:16px;
  box-shadow:0 0 26px rgba(31,227,255,.22), inset 0 0 40px rgba(162,75,255,.14);
}
.bay--pink{ border-color:var(--magenta); box-shadow:0 0 26px rgba(255,46,151,.24), inset 0 0 40px rgba(255,46,151,.12); }
.bay--dark{
  border-color:var(--violet);
  background:linear-gradient(180deg, rgba(11,7,24,.92), rgba(6,3,15,.95));
  box-shadow:0 0 26px rgba(162,75,255,.3), inset 0 0 60px rgba(0,0,0,.5);
}

.head{
  margin:0 0 4px; text-align:center;
  font-family:var(--chunk); font-size:clamp(1.5rem,5vw,2.3rem);
  letter-spacing:1px; color:var(--ink);
  text-shadow:0 0 10px var(--magenta), 0 0 24px rgba(255,46,151,.5);
}
.head span::before{ content:"◄ "; color:var(--cyan); }
.head span::after{ content:" ►"; color:var(--cyan); }
.head--cyan{ text-shadow:0 0 10px var(--cyan), 0 0 24px rgba(31,227,255,.5); }
.dek{
  margin:0 auto 22px; max-width:680px; text-align:center;
  color:var(--ink-soft); font-size:1rem;
}
/* a link in a dek is rare but real (the lounge doorway in CALL-IN LINE) —
   without this it falls back to the browser's unreadable default blue */
.dek a{ color:var(--cyan-lit); text-decoration-color:rgba(31,227,255,.5); }
.dek a:hover{ color:#fff; }
.dek b{ color:var(--yellow); }

/* ================= SHWING-TV :: the broadcast console ==================
   The room's headline feature and its answer to the Frost Video Vault.
   Frost hangs a WALL of small CRTs; SHWING is one big cable-access console
   you channel-surf. Only the tuned-in channel plays; the dial below swaps
   the picture and kicks a tracking glitch. Each channel is one self-hosted
   .mp4 with the same NO SIGNAL fallback the Frost vault uses. */
.broadcast{
  display:grid; grid-template-columns:1fr; gap:18px;
  align-items:start;
}
@media (min-width:760px){
  .broadcast{ grid-template-columns:1fr 190px; }
}

.console{
  background:
    linear-gradient(180deg, #2b2438 0%, #1a1526 60%, #0e0b16 100%);
  border:3px solid #4a4460;
  border-radius:18px;
  padding:16px 16px 12px;
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.14),
    inset 0 -6px 14px rgba(0,0,0,.6),
    0 8px 0 #08060f,
    0 16px 34px rgba(0,0,0,.6);
}
.console__screen{
  position:relative; overflow:hidden;
  aspect-ratio:4/3;
  background:#02040a;
  border:5px inset #575070;
  border-radius:14px / 20px;   /* the CRT bulge */
  box-shadow:inset 0 0 60px rgba(0,0,0,.95), inset 0 0 120px rgba(162,75,255,.12);
}
.console__video{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; display:none;
}
.console__video.is-live{ display:block; }

/* A YouTube channel plays IN the glass. The player sits on the picture layer,
   just above the still it replaces and below every overlay — and each of those
   overlays is pointer-events:none, so the player's own controls stay
   clickable through them. 16:9 in a 4:3 tube letterboxes itself, which is
   exactly what a widescreen tape did on one of these. */
.console__tube{
  position:absolute; inset:0; z-index:1;
  width:100%; height:100%; border:0; background:#000;
}

/* analog static — self-contained SVG noise, same trick as the Frost vault */
.static{
  position:absolute; inset:-40%; z-index:2;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
  opacity:.5; pointer-events:none;
  animation:tvstatic .4s steps(4) infinite;
}
@keyframes tvstatic{
  0%{ transform:translate(0,0); } 25%{ transform:translate(-6%,3%); }
  50%{ transform:translate(4%,-5%); } 75%{ transform:translate(-3%,-2%); }
  100%{ transform:translate(5%,4%); }
}
.console.is-live .static{ opacity:.07; }   /* a little grain stays over the picture */

/* WITH A VIDEO ACTUALLY RUNNING, the CRT dressing steps back. All of it is
   character on a still frame and all of it is in the way of a music video:
   the snow goes, the scanlines thin out to a suggestion, and the OSD and the
   channel bug fade off the picture instead of sitting on it. Bring these back
   up and you can't watch the thing. */
.osd, .bug{ transition:opacity .3s ease; }
.console.is-playing .static{ opacity:0; }
.console.is-playing .scan{ opacity:.35; }
.console.is-playing .osd,
.console.is-playing .bug{ opacity:.25; }
.console.is-playing .nosignal{ display:none; }

.nosignal{
  position:absolute; inset:0; z-index:3;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px;
  text-align:center; padding:16px;
  font-family:var(--osd); color:var(--cyan-lit);
  text-shadow:0 0 12px rgba(31,227,255,.7);
}
.nosignal b{
  font-size:1.4rem; letter-spacing:3px; color:#fff;
  animation:blink 1.4s steps(2,start) infinite;
}
.nosignal span{ font-size:.95rem; color:var(--ink-soft); line-height:1.5; }
.nosignal code{ color:var(--yellow); font-family:var(--osd); }
.console.is-live .nosignal{ display:none; }

/* scanlines + a soft screen glare, always on top of the picture */
.scan{
  position:absolute; inset:0; z-index:4; pointer-events:none;
  background-image:
    repeating-linear-gradient(180deg,
      rgba(0,0,0,.30) 0px, rgba(0,0,0,.30) 1px, transparent 1px, transparent 3px),
    linear-gradient(115deg, rgba(255,255,255,.12) 0%, transparent 40%);
}

/* the VCR on-screen display: PLAY ►, timecode, channel — top-left, in that
   unmistakable chunky broadcast font. */
.osd{
  position:absolute; top:10px; left:12px; z-index:5;
  display:flex; flex-direction:column; gap:3px;
  font-family:var(--osd); letter-spacing:2px;
  color:#fff; text-shadow:0 0 6px rgba(0,0,0,.95), 0 0 10px rgba(31,227,255,.5);
  pointer-events:none;
}
.osd__play{ font-size:1.1rem; color:var(--lime); text-shadow:0 0 8px rgba(91,255,143,.8); }
.osd__tc{ font-size:.92rem; }
.osd__ch{ font-size:.86rem; color:var(--cyan-lit); }

/* the channel badge, top-right, like a broadcast bug */
.bug{
  position:absolute; top:10px; right:12px; z-index:5;
  font-family:var(--chunk); font-size:1.1rem; letter-spacing:1px;
  color:var(--magenta-lit); text-shadow:0 0 10px var(--magenta);
  pointer-events:none;
}

/* the tracking-distortion band that rolls up the screen on a channel change */
.tracking{
  position:absolute; left:0; right:0; height:26px; z-index:6;
  background:linear-gradient(180deg, transparent, rgba(255,255,255,.6), transparent);
  mix-blend-mode:overlay;
  opacity:0; pointer-events:none;
}
.console.is-tracking .tracking{ animation:track-roll .5s linear 1; }
@keyframes track-roll{
  0%{ opacity:.9; top:100%; }
  100%{ opacity:0; top:-30px; }
}
/* a quick full-screen jolt on the same change */
.console.is-tracking .console__screen{ animation:jolt .18s steps(2) 1; }
@keyframes jolt{ 0%{ transform:translateY(0); } 50%{ transform:translateY(-4px); } 100%{ transform:translateY(0); } }

/* ---- THE START KEY -----------------------------------------------------
   What you press to start a YouTube channel. It isn't a web play button: it's
   a VCR transport key, the chunky plastic kind with a hard shadow under it
   that sinks when you push it. The whole glass is the hit area — you're
   pressing the TV, not a control — with a vignette that dims the paused still
   so the key pops off it, and a phosphor ring pulsing out to say "this one
   plays". It goes the moment the video is running; from there the player's own
   controls take over. */
.tv-start{
  position:absolute; inset:0; z-index:7;
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:14px;
  border:0; cursor:pointer;
  background:radial-gradient(ellipse at center, rgba(11,7,24,.25) 0%, rgba(11,7,24,.72) 100%);
  transition:background .2s ease;
}
.tv-start[hidden]{ display:none; }   /* the class would otherwise beat [hidden] */
.tv-start:hover{ background:radial-gradient(ellipse at center, rgba(11,7,24,.12) 0%, rgba(11,7,24,.6) 100%); }
.tv-start:focus-visible{ outline:2px solid var(--yellow); outline-offset:-6px; }

.tv-start__key{
  position:relative;
  display:grid; place-items:center;
  width:102px; height:72px;
  border:3px solid #6a5b96; border-radius:14px;
  background:linear-gradient(180deg,#3d2c6d 0%, #2a1a52 55%, #1c1138 100%);
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.2),
    inset 0 -4px 10px rgba(0,0,0,.55),
    0 7px 0 #0c0720,
    0 13px 24px rgba(0,0,0,.65);
  transition:transform .09s ease, box-shadow .09s ease, border-color .15s ease;
}
/* the ▶ itself, in signal green, cheated right so it looks centred */
.tv-start__key::before{
  content:""; width:0; height:0; margin-left:7px;
  border-left:27px solid var(--lime);
  border-top:18px solid transparent;
  border-bottom:18px solid transparent;
  filter:drop-shadow(0 0 11px rgba(91,255,143,.9));
}
/* the phosphor ring that keeps pulsing off the key until it's pressed */
.tv-start__key::after{
  content:""; position:absolute; inset:-11px; border-radius:22px;
  border:2px solid var(--lime); opacity:0;
  animation:start-pulse 2.1s ease-out infinite;
}
@keyframes start-pulse{
  0%{ transform:scale(.94); opacity:.7; }
  100%{ transform:scale(1.3); opacity:0; }
}
.tv-start:hover .tv-start__key{ border-color:var(--cyan); transform:translateY(-2px); }
.tv-start:hover .tv-start__key::before{ border-left-color:#8fffb6; }
/* pressed: the key sinks onto its own shadow, like the real thing */
.tv-start:active .tv-start__key{
  transform:translateY(6px);
  box-shadow:
    inset 0 2px 0 rgba(255,255,255,.14),
    inset 0 -2px 8px rgba(0,0,0,.6),
    0 1px 0 #0c0720,
    0 4px 10px rgba(0,0,0,.6);
}

.tv-start__label{
  font-family:var(--osd); font-size:.9rem; letter-spacing:4px;
  color:#fff; text-shadow:0 0 8px rgba(0,0,0,.95), 0 0 14px rgba(91,255,143,.6);
}

/* The bottom-left corner of the glass is empty now. It held TAP FOR SOUND when
   the channels were self-hosted clips, then WATCH ON INSTAGRAM when they were
   reel poster frames; both are gone, and the start key is the only thing that
   sits on the picture. */

/* the fascia below the glass: brand plate + prev/next rockers */
.console__panel{
  display:flex; align-items:center; gap:12px;
  padding:12px 6px 4px;
}
.console__brand{
  font-family:var(--neon); font-size:1.15rem; letter-spacing:1px;
  color:var(--magenta-lit); text-shadow:0 0 10px var(--magenta);
  flex:0 0 auto;
}
.console__brand small{ display:block; font-family:var(--osd); font-size:.6rem; letter-spacing:2px; color:var(--ink-faint); }
.console__now{
  flex:1 1 auto; min-width:0;
  font-family:var(--osd); font-size:.9rem; color:var(--cyan-lit);
  text-align:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.console__rock{
  flex:0 0 auto; display:flex; gap:6px;
}
.rocker{
  cursor:pointer; font-family:var(--chunk); font-size:1rem; line-height:1;
  color:#0b0718; width:38px; height:34px; border:none; border-radius:8px;
  background:var(--violet-lit); box-shadow:0 3px 0 #5a2a8a;
  transition:transform .07s ease;
}
.rocker:active{ transform:translateY(3px); box-shadow:0 0 0 #5a2a8a; }
.rocker:focus-visible{ outline:2px solid var(--yellow); outline-offset:2px; }

/* THE DIAL — the channel pad down the side (or below on phones). Each button
   tunes a channel; the tuned one lights up. Built by js/shwing.js from the
   channels in the markup so adding a <article class="chan"> adds a button. */
.dial{
  display:grid; gap:8px;
  grid-template-columns:repeat(auto-fill, minmax(84px,1fr));
  align-content:start;
}
@media (min-width:760px){ .dial{ grid-template-columns:1fr; } }
.dial__head{
  grid-column:1/-1;
  font-family:var(--osd); font-size:.78rem; letter-spacing:2px;
  color:var(--ink-faint); text-align:center;
  border-bottom:1px dashed rgba(199,139,255,.4); padding-bottom:6px; margin-bottom:2px;
}
.dial__btn{
  cursor:pointer; text-align:left;
  font-family:var(--osd); letter-spacing:1px;
  color:var(--ink); padding:8px 10px; border-radius:9px;
  background:rgba(35,18,79,.85);
  border:2px solid rgba(199,139,255,.4);
  transition:transform .08s ease, border-color .12s ease, box-shadow .12s ease;
}
.dial__btn:hover{ border-color:var(--cyan); }
.dial__btn:focus-visible{ outline:2px solid var(--yellow); outline-offset:2px; }
.dial__btn.is-on{
  border-color:var(--magenta);
  background:rgba(255,46,151,.18);
  box-shadow:0 0 16px rgba(255,46,151,.4);
}
.dial__num{ display:block; font-family:var(--chunk); font-size:1.05rem; color:var(--cyan-lit); }
.dial__name{ display:block; font-size:.82rem; color:var(--ink-soft); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.dial__genre{ display:block; font-size:.72rem; color:var(--yellow); letter-spacing:.5px; }
/* No ▶ pip on the dial: it existed to tell playable channels from Instagram
   poster frames, and every channel plays now, so it marked nothing. */

.tv__post{
  margin-top:14px; text-align:center;
}
.tv__post .btn--sm[href="#REPLACE"]{ display:none; }   /* hide until a real post link is set */

/* ================= THE BAND :: cast cards ================= */
.band{
  display:grid; gap:16px;
  grid-template-columns:repeat(auto-fit, minmax(180px,1fr));
}
.member{
  position:relative;
  background:linear-gradient(180deg, rgba(52,26,107,.9), rgba(21,10,46,.95));
  border:2px solid var(--cyan);
  border-radius:14px; padding:16px 15px 15px;
  box-shadow:0 0 18px rgba(31,227,255,.22);
  transition:transform .12s ease, box-shadow .18s ease;
}
.member:nth-child(2n){ border-color:var(--magenta); box-shadow:0 0 18px rgba(255,46,151,.22); }
.member:nth-child(3n){ border-color:var(--yellow); box-shadow:0 0 18px rgba(255,232,61,.2); }
.member:hover{ transform:translateY(-4px); box-shadow:0 0 26px rgba(199,139,255,.5); }
.member__badge{
  display:inline-block; font-family:var(--osd); font-size:.72rem; letter-spacing:2px;
  color:#0b0718; background:var(--cyan); padding:3px 9px; border-radius:5px;
  margin-bottom:10px;
}
.member:nth-child(2n) .member__badge{ background:var(--magenta); }
.member:nth-child(3n) .member__badge{ background:var(--yellow); }
/* the avatar: a big instrument glyph on a neon disc */
.member__face{
  width:74px; height:74px; margin:0 0 10px;
  display:flex; align-items:center; justify-content:center;
  font-size:2.4rem; border-radius:50%;
  background:radial-gradient(circle at 35% 30%, rgba(255,255,255,.28), rgba(162,75,255,.25) 55%, rgba(11,7,24,.6));
  border:2px solid rgba(199,139,255,.6);
  box-shadow:inset 0 0 18px rgba(0,0,0,.5);
}
.member__name{
  margin:0; font-family:var(--chunk); font-size:1.15rem; letter-spacing:.5px;
  color:var(--ink); text-shadow:0 0 10px rgba(31,227,255,.5);
}
.member__role{
  margin:2px 0 8px; font-family:var(--osd); font-size:.82rem; letter-spacing:1px;
  color:var(--yellow);
}
.member__bio{ margin:0 0 10px; font-size:.9rem; color:var(--ink-soft); }
.member__quote{
  margin:0; font-family:var(--osd); font-size:.86rem; font-style:italic;
  color:var(--magenta-lit); border-top:1px dashed rgba(199,139,255,.35); padding-top:8px;
}

/* ================= WE PLAY EVERYTHING :: genres + set list ============= */
.genres{
  display:flex; flex-wrap:wrap; gap:10px; justify-content:center; margin-bottom:22px;
}
.genre{
  font-family:var(--chunk); font-size:.9rem; letter-spacing:.5px;
  padding:8px 14px; border-radius:30px;
  color:#0b0718; background:var(--cyan);
  box-shadow:0 0 12px rgba(31,227,255,.4);
}
.genre:nth-child(4n+2){ background:var(--magenta); box-shadow:0 0 12px rgba(255,46,151,.4); }
.genre:nth-child(4n+3){ background:var(--yellow); box-shadow:0 0 12px rgba(255,232,61,.4); }
.genre:nth-child(4n+4){ background:var(--violet-lit); box-shadow:0 0 12px rgba(162,75,255,.4); }

.setlist{
  max-width:560px; margin:0 auto;
  border:2px dashed rgba(199,139,255,.45); border-radius:12px;
  padding:16px 18px; background:rgba(11,7,24,.5);
}
.setlist__head{
  font-family:var(--osd); letter-spacing:2px; color:var(--cyan-lit);
  text-align:center; margin:0 0 10px; font-size:.9rem;
}
.setlist ol{ margin:0; padding:0 0 0 1.4em; }
.setlist li{ margin:5px 0; color:var(--ink); }
.setlist li small{ color:var(--ink-faint); font-family:var(--osd); }
.setlist__note{
  margin:12px 0 0; text-align:center; font-family:var(--osd);
  font-size:.82rem; color:var(--yellow);
}

/* ================= AIR GUITAR :: the synth gag ================= */
.airg{
  text-align:center; max-width:640px; margin:0 auto;
}
.airg__body{ color:var(--ink-soft); margin:0 0 20px; }
.strike{
  cursor:pointer; font-family:var(--chunk); font-size:1.3rem; letter-spacing:1px;
  color:#0b0718; padding:18px 30px; border-radius:14px;
  background:linear-gradient(180deg, var(--magenta-lit), var(--magenta));
  border:none;
  box-shadow:0 6px 0 var(--magenta-deep), 0 0 34px rgba(255,46,151,.5);
  transition:transform .07s ease, box-shadow .07s ease, filter .1s ease;
}
.strike:hover{ filter:brightness(1.08); }
.strike:active{ transform:translateY(5px); box-shadow:0 1px 0 var(--magenta-deep), 0 0 20px rgba(255,46,151,.5); }
.strike:focus-visible{ outline:3px solid var(--yellow); outline-offset:4px; }
.strike[aria-pressed="true"]{
  background:linear-gradient(180deg, var(--cyan-lit), var(--cyan));
  box-shadow:0 6px 0 var(--cyan-deep), 0 0 40px rgba(31,227,255,.7);
}
.airg__note{
  margin:16px auto 0; max-width:520px;
  font-family:var(--osd); font-size:.86rem; color:var(--ink-faint); line-height:1.7;
}
.airg__lights{
  display:flex; justify-content:center; gap:10px; margin:22px 0 0;
}
.airg__led{
  width:16px; height:16px; border-radius:50%;
  background:rgba(199,139,255,.2);
  border:1px solid rgba(199,139,255,.5);
  transition:background .08s ease, box-shadow .08s ease;
}
.airg__led.lit{ background:var(--lime); box-shadow:0 0 14px var(--lime); border-color:var(--lime); }

/* ================= THE GIGS :: dates table ================= */
.gigs-wrap{ overflow-x:auto; }
.gigs{ width:100%; border-collapse:collapse; }
.gigs th{
  font-family:var(--osd); letter-spacing:2px; font-size:.8rem;
  color:var(--cyan-lit); text-align:left; padding:8px 12px;
  border-bottom:2px solid var(--cyan);
}
.gigs td{
  padding:12px; border-bottom:1px solid rgba(199,139,255,.2);
  color:var(--ink); vertical-align:top;
}
.gigs__with{ color:var(--yellow); font-family:var(--osd); font-size:.82rem; }
.gigs__empty td{ text-align:center; color:var(--ink-faint); font-family:var(--osd); letter-spacing:1px; }

/* ================= BOOKING ================= */
.booking{
  display:grid; gap:26px;
  grid-template-columns:1fr;
}
@media (min-width:680px){ .booking{ grid-template-columns:1fr 1fr; } }
.minihead{
  font-family:var(--chunk); font-size:1.05rem; letter-spacing:.5px;
  color:var(--yellow); margin:0 0 8px; text-shadow:0 0 10px rgba(255,232,61,.4);
}
.booking p{ margin:0 0 12px; color:var(--ink-soft); }
.maillink{
  font-family:var(--osd); color:var(--cyan-lit); word-break:break-all;
  text-shadow:0 0 8px rgba(31,227,255,.4);
}
.maillink:hover{ color:#fff; }

.links{ display:flex; flex-wrap:wrap; gap:9px; margin:4px 0 8px; }
.chip{
  font-family:var(--osd); font-size:.82rem; letter-spacing:1px; text-decoration:none;
  color:var(--ink); padding:7px 12px; border-radius:8px;
  background:rgba(162,75,255,.18); border:1px solid rgba(199,139,255,.5);
  transition:background .12s ease, color .12s ease;
}
.chip:hover{ background:var(--cyan); color:#0b0718; }
.chip--dead{ display:none; }   /* a chip still pointing at #REPLACE removes itself */

/* the mailing-list form */
.subscribe{ display:flex; flex-wrap:wrap; gap:9px; align-items:center; }
.subscribe input[type="email"]{
  flex:1 1 200px; min-width:0;
  font-family:var(--osd); font-size:.95rem; color:var(--ink);
  padding:11px 13px; border-radius:9px;
  background:rgba(11,7,24,.7); border:2px solid var(--violet);
}
.subscribe input[type="email"]::placeholder{ color:var(--ink-faint); }
.subscribe input[type="email"]:focus{ outline:none; border-color:var(--cyan); box-shadow:0 0 14px rgba(31,227,255,.4); }
.form-msg{
  flex:1 1 100%; margin:6px 0 0; min-height:1.2em;
  font-family:var(--osd); font-size:.86rem; color:var(--lime);
}
/* the honeypot: off-screen, invisible to people, catnip to bots */
.hp{ position:absolute; left:-9999px; width:1px; height:1px; opacity:0; }
.sr-only{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }

/* ================= CALL-IN LINE (the guestbook) =================
   Shared markup from js/guestbook.js (.gb-you / .gb-entry), skinned as the
   caller captions that used to sit in the lower third of a public-access
   broadcast: mono type, a magenta call-out bar, a scanline wash. The avatar is
   the visitor's lounge guest — pixel art, so it gets a hard CRT edge and a
   cyan glow instead of the paper mount the juke joint gives it.
   ================================================================= */
.callin{ margin-top:6px; }
.callin__form{ display:flex; flex-wrap:wrap; gap:9px; align-items:flex-start; }
.callin__form input[type="text"], .callin__form textarea{
  min-width:0; font-family:var(--osd); font-size:.95rem; color:var(--ink);
  padding:11px 13px; border-radius:9px;
  background:rgba(11,7,24,.7); border:2px solid var(--violet);
}
.callin__form input[type="text"]{ flex:1 1 190px; letter-spacing:1px; }
.callin__form textarea{ flex:1 1 100%; min-height:76px; resize:vertical; line-height:1.45; }
.callin__form input::placeholder, .callin__form textarea::placeholder{ color:var(--ink-faint); }
.callin__form input:focus, .callin__form textarea:focus{
  outline:none; border-color:var(--cyan); box-shadow:0 0 14px rgba(31,227,255,.4);
}

/* "this is you" — the guest that will call in, above the form */
.gb-you{
  display:flex; align-items:center; gap:12px; margin:0 0 14px; padding:10px 12px;
  border-radius:10px; background:rgba(35,18,79,.55);
  border:1px solid rgba(199,139,255,.45); border-left:4px solid var(--magenta);
}
.gb-you__av{
  flex:none; width:40px; height:auto; image-rendering:auto;
  background:rgba(11,7,24,.85); border:1px solid var(--cyan); border-radius:6px; padding:3px;
  box-shadow:0 0 12px rgba(31,227,255,.35);
}
.gb-you__txt{ font-family:var(--osd); font-size:.85rem; line-height:1.45; color:var(--ink-soft); }
.gb-you__txt b{ color:var(--yellow); }
.gb-you__txt a{ color:var(--cyan-lit); }
.gb-you__txt a:hover{ color:#fff; }

/* the board of calls */
.callin__list{ margin-top:16px; max-height:340px; overflow-y:auto; padding-right:6px; }
.gb-empty{
  font-family:var(--osd); font-size:.86rem; color:var(--ink-faint);
  letter-spacing:1px; text-align:center; padding:18px 8px; margin:0;
}
.gb-entry{
  position:relative; display:flex; gap:12px; align-items:flex-start;
  margin:0 0 11px; padding:12px 14px; border-radius:10px; overflow:hidden;
  background:linear-gradient(180deg, rgba(35,18,79,.72) 0%, rgba(21,10,46,.72) 100%);
  border:1px solid rgba(199,139,255,.4); border-left:4px solid var(--cyan);
}
/* the scanline wash over each caption, like the rest of the broadcast */
.gb-entry::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  background:repeating-linear-gradient(180deg, rgba(0,0,0,.18) 0 1px, transparent 1px 3px);
  opacity:.5;
}
.gb-entry:nth-child(even){ border-left-color:var(--magenta); }
.gb-entry__avatar{
  flex:none; width:36px; height:auto; image-rendering:auto;
  background:rgba(11,7,24,.85); border:1px solid var(--violet-lit); border-radius:5px; padding:3px;
}
.gb-entry__avatar--anon{ opacity:.5; }
.gb-entry__body{ flex:1 1 auto; min-width:0; }
.gb-entry__date{
  float:right; font-family:var(--osd); font-size:.72rem; color:var(--ink-faint); letter-spacing:1px;
}
.gb-entry__head{
  font-family:var(--chunk); font-size:.82rem; letter-spacing:.5px; text-transform:uppercase;
  color:var(--yellow); text-shadow:0 0 10px rgba(255,232,61,.35);
}
.gb-entry__msg{
  margin:3px 0 0; font-family:var(--osd); font-size:.92rem; line-height:1.5;
  color:var(--ink); word-break:break-word;
}

/* ================= FOOTER ================= */
.foot{
  text-align:center; margin-top:34px; padding-top:22px;
  border-top:2px solid var(--violet);
}
.marks{
  display:flex; flex-wrap:wrap; justify-content:center; gap:12px; margin-bottom:18px;
}
.mark{
  font-family:var(--osd); font-size:.72rem; letter-spacing:1px; line-height:1.4;
  color:var(--ink-faint); text-align:center;
  border:1px solid rgba(199,139,255,.35); border-radius:7px; padding:7px 11px;
  background:rgba(11,7,24,.5);
}
.mark b{ color:var(--cyan-lit); }
.foot__line{ margin:6px 0; color:var(--ink-soft); }
.foot__small{ font-size:.86rem; color:var(--ink-faint); }
.foot__small a{ color:var(--magenta-lit); text-decoration:none; }
.foot__small a:hover{ color:#fff; text-decoration:underline; }
.counter{
  font-family:var(--osd); letter-spacing:2px; color:var(--cyan-lit);
  margin-bottom:12px; font-size:.95rem;
}
.counter__digits{
  display:inline-block; font-family:var(--chunk); font-size:1.3rem;
  color:var(--yellow); letter-spacing:3px; padding:2px 8px; margin-left:6px;
  background:rgba(11,7,24,.8); border:1px solid var(--yellow); border-radius:6px;
  text-shadow:0 0 10px rgba(255,232,61,.5);
}

/* ================= THE HITS :: the Spotify feature ==================
   The audio half of the room and its answer to Neck's THE SIDES: SHWING's real
   Spotify embed framed like a boombox, plus cassette cards standing in for the
   catalogue until it's streaming. */
/* The crate leads and the Spotify boombox sits beside it — "here now" on the
   left, "coming" on the right, which is the order the dek reads in. */
.wax{ display:grid; gap:20px; grid-template-columns:1fr; }
@media (min-width:900px){ .wax{ grid-template-columns:1.5fr 1fr; align-items:start; } }

.deck-player{
  border:3px solid var(--cyan); border-radius:16px; padding:12px;
  background:linear-gradient(180deg, #2b2438, #150a2e);
  box-shadow:0 0 24px rgba(31,227,255,.25), inset 0 0 40px rgba(162,75,255,.16);
}
.deck-player__bar{
  display:flex; align-items:center; gap:8px; margin:2px 2px 10px;
  font-family:var(--osd); font-size:.85rem; letter-spacing:1.5px; color:var(--cyan-lit);
}
.deck-player__dot{
  width:10px; height:10px; border-radius:50%;
  background:var(--alert); box-shadow:0 0 10px var(--alert);
  animation:blink 1.3s steps(2,start) infinite;
}
.deck-player iframe{ display:block; border-radius:12px; }
.deck-player__links{ display:flex; flex-wrap:wrap; gap:9px; margin-top:10px; }
.wax__link{
  font-family:var(--osd); font-size:.82rem; letter-spacing:1px; text-decoration:none;
  color:#0b0718; background:var(--cyan); padding:7px 13px; border-radius:8px;
  box-shadow:0 0 12px rgba(31,227,255,.4);
}
.wax__link:hover{ filter:brightness(1.08); }
.wax__link:focus-visible{ outline:2px solid var(--yellow); outline-offset:2px; }

/* ---------- THE CASSETTE ---------- */
/* The shell is the whole object: a label, a window with two hubs in it, and
   the catalogue line across the bottom. This is what it looks like BEFORE the
   crate below picks it up — i.e. what a visitor with no JS gets, one shell per
   song in a plain column. */
.tapes{ display:grid; gap:12px; grid-template-columns:1fr; }
.tape{
  border:2px solid var(--magenta); border-radius:12px; padding:12px;
  background:linear-gradient(180deg, rgba(52,26,107,.85), rgba(21,10,46,.92));
  box-shadow:0 0 16px rgba(255,46,151,.2);
}
.tape--a{ border-color:var(--yellow); box-shadow:0 0 18px rgba(255,232,61,.28); }
.tape__shell{
  position:relative;
  background:linear-gradient(180deg,#1a1526,#0e0b16);
  border:2px solid #4a4460; border-radius:8px; padding:12px 12px 11px;
}
.tape__label{
  display:block; background:linear-gradient(180deg,#f4ecff,#d3c4ff);
  color:#150a2e; border-radius:4px; padding:6px 9px; margin-bottom:10px;
}
/* Written on, not typeset. Marker ink rather than the label's flat dark, and a
   touch loose in the tracking so it doesn't sit like type. */
.tape__title{
  display:block; font-family:var(--hand); font-size:1.05rem;
  letter-spacing:.4px; color:#191036;
}
.tape__sub{ display:block; font-family:var(--osd); font-size:.7rem; letter-spacing:1px; color:#4a3f6e; }
.tape__window{
  display:flex; gap:24px; justify-content:center; align-items:center;
  height:34px; width:74%; margin:0 auto 9px;
  background:rgba(11,7,24,.75); border-radius:20px; border:1px solid #4a4460;
}
.tape__window i{
  width:19px; height:19px; border-radius:50%;
  border:3px solid var(--cyan-deep);
  background:radial-gradient(circle, #2b2438 38%, #0e0b16 40%);
}
.tape--a .tape__window i{ border-color:var(--yellow); }
/* The reels turn only on the tape that's actually running — they used to spin
   on SIDE A forever, back when no tape had a file behind it. */
.tape.is-playing .tape__window i{ animation:reel 3.2s linear infinite; }
@keyframes reel{ to{ transform:rotate(360deg); } }
.tape__cat{ display:block; text-align:center; font-family:var(--osd); font-size:.7rem; letter-spacing:1px; color:var(--ink-faint); }
.tape__pending{ display:block; margin-top:9px; text-align:center; font-family:var(--osd); font-size:.8rem; letter-spacing:1px; color:var(--yellow); }

/* ---------- THE CRATE :: and you flip through it ----------
   Every tape used to be its own card in a column, which made THE HITS the
   tallest section on the page and grew it by another card each time the band
   sent a recording over. They sit in a CRATE now: one stack, the tape on the
   head facing you and the rest fanned back into the box behind it — the same
   height on the page at six tapes as at twenty.

   THE FLIP IS CSS. shwing.js writes three numbers onto each tape and nothing
   else: --i (its slot, signed, 0 = on the head), --d (how far off the head it
   is) and --s (which side it fell on). The transform below reads all three,
   which keeps the geometry here, where a media query can retune the whole
   crate by changing five numbers.

   .is-crate is added by the script once the flip is wired, so a visitor with
   no JS keeps the plain column above rather than a heap of stacked shells. */
.crate{ position:relative; }

.tapes.is-crate{
  /* every measurement of the crate, in one place */
  --tw:234px;     /* the face of a cassette (a real one is about 1.56 : 1) */
  --th:156px;
  --step:124px;   /* how far the FIRST tape behind steps out from the head… */
  --tight:74px;   /* …and how much less each one behind that manages, which is
                     what makes the far end of the crate pack together instead
                     of marching off the page at an even pace */
  --depth:96px;   /* how much further back each tape stands */
  --tilt:44deg;   /* …and how far it turns to face the middle */

  display:block; gap:0;
  position:relative; height:calc(var(--th) + 92px);
  perspective:900px; perspective-origin:50% 44%;
  overflow:hidden;
  touch-action:pan-y;   /* sideways is the crate's, up and down is the page's */
}

/* Six neon shells on rotation, so a crate of tapes looks like a crate of tapes
   and not a stack of the same grey brick. The tint is the label's top band,
   the hubs, the catalogue line — and, on the tape at the head, the light the
   whole crate is lit by. It cycles, so a seventh tape is red again and no new
   song ever needs a colour picked for it. */
.tapes.is-crate .tape:nth-child(6n+1){ --tint:var(--magenta);   --glow:rgba(255,46,151,.5); }
.tapes.is-crate .tape:nth-child(6n+2){ --tint:var(--cyan);      --glow:rgba(31,227,255,.5); }
.tapes.is-crate .tape:nth-child(6n+3){ --tint:var(--yellow);    --glow:rgba(255,232,61,.45); }
.tapes.is-crate .tape:nth-child(6n+4){ --tint:var(--violet-lit);--glow:rgba(199,139,255,.5); }
.tapes.is-crate .tape:nth-child(6n+5){ --tint:var(--sun-2);     --glow:rgba(255,157,77,.45); }
.tapes.is-crate .tape:nth-child(6n+6){ --tint:var(--lime);      --glow:rgba(91,255,143,.42); }
/* the floor the stack stands on — a neon pool, not a wooden box: this room
   doesn't own a single piece of wood */
.tapes.is-crate::before{
  content:""; position:absolute; z-index:0; left:8%; right:8%;
  bottom:14px; height:46px; border-radius:50%;
  background:radial-gradient(50% 50% at 50% 50%, rgba(255,46,151,.62), rgba(255,46,151,0) 72%);
}

.tapes.is-crate .tape{
  position:absolute; left:50%; top:50%; width:var(--tw);
  margin:calc(var(--th) / -2 - 10px) 0 0 calc(var(--tw) / -2);
  padding:0; border:0; border-radius:0; background:none; box-shadow:none;
  cursor:pointer;
  /* --s is 0 on the tape at the head, so the whole sideways term collapses to
     nothing there and the (--d - 1) in it never has to be guarded. */
  transform:
    translateX(calc(var(--s) * (var(--step) + (var(--d) - 1) * var(--tight))))
    translateY(calc(var(--d) * 9px))          /* the head tape stands proudest */
    translateZ(calc(var(--d) * var(--depth) * -1))
    rotateY(calc(var(--s) * var(--tilt)))
    scale(calc(1 - var(--d) * .10));
  opacity:calc(1 - var(--d) * .24);
  filter:brightness(calc(1 - var(--d) * .22));
  transition:transform .44s cubic-bezier(.22,.9,.28,1), opacity .44s ease, filter .44s ease;
}
/* Deeper than the crate is worth drawing: the far tapes go, rather than
   piling up invisibly at the edge and eating the fade. */
.tapes.is-crate .tape.is-gone{ opacity:0; visibility:hidden; }

.tapes.is-crate .tape__shell{
  height:var(--th); display:flex; flex-direction:column;
  padding:11px 12px 9px;
  border-width:3px; border-color:#5a5378; border-radius:10px;
  background:linear-gradient(158deg,#282034 0%,#171222 48%,#0c0913 100%);
  box-shadow:0 14px 26px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.09);
}
/* the tape at the head lights the crate in its own colour */
.tapes.is-crate .tape.is-front .tape__shell{
  border-color:var(--tint);
  box-shadow:0 18px 32px rgba(0,0,0,.6), 0 0 30px var(--glow),
             inset 0 1px 0 rgba(255,255,255,.12);
}
/* The tape actually running, wherever in the crate it has ended up — flipping
   doesn't stop the music, so this is a state of its own and not "the front
   one". Green, because that's the light a deck lights when it's rolling. */
.tapes.is-crate .tape.is-playing .tape__shell{
  border-color:var(--lime);
  box-shadow:0 18px 32px rgba(0,0,0,.6), 0 0 30px rgba(91,255,143,.55),
             inset 0 1px 0 rgba(255,255,255,.12);
}

/* the four case screws. Two pseudo-elements, four dots: each one throws its
   pair across the shell as a box-shadow. */
.tapes.is-crate .tape__screws{
  position:absolute; left:7px; right:7px; top:7px; bottom:7px; pointer-events:none;
}
.tapes.is-crate .tape__screws::before,
.tapes.is-crate .tape__screws::after{
  content:""; position:absolute; left:0; width:4px; height:4px; border-radius:50%;
  background:#6a6288; box-shadow:calc(var(--tw) - 24px) 0 0 #6a6288;
}
.tapes.is-crate .tape__screws::before{ top:0; }
.tapes.is-crate .tape__screws::after{ bottom:0; }

/* The paper label is a fixed height so every window in the crate lines up,
   however long a title runs; a title too long for two lines is clipped rather
   than allowed to shove the reels down its own tape. */
.tapes.is-crate .tape__label{
  height:70px; margin:0 0 6px; padding:5px 8px;
  display:flex; flex-direction:column; justify-content:center;
  background:linear-gradient(180deg,#fffdf9,#f0e9f8 66%,#dfd6ef);
  border-top:5px solid var(--tint);      /* the colour band across the top */
  border-radius:3px; box-shadow:0 1px 0 rgba(0,0,0,.4);
}
/* line-height has to clear the hand's ascenders: the -webkit-box clip cuts at
   the line box, not at the glyph, so a tight one beheads every capital. */
.tapes.is-crate .tape__title{
  font-size:1rem; line-height:1.16; letter-spacing:.2px;
  display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; overflow:hidden;
}
/* Nobody writes on six labels at the same angle. Three lines on rotation, off
   the left margin the way a hand starts a line, so the crate reads as six
   labels somebody wrote rather than one label printed six times. */
.tapes.is-crate .tape:nth-child(3n+1) .tape__title{ transform:rotate(-1.5deg); }
.tapes.is-crate .tape:nth-child(3n+2) .tape__title{ transform:rotate(1deg); }
.tapes.is-crate .tape:nth-child(3n+3) .tape__title{ transform:rotate(-.6deg); }
.tapes.is-crate .tape__title{ transform-origin:left center; }
/* one line, always: a wrapping sub is what shoved the title out of its label */
.tapes.is-crate .tape__sub{
  font-size:.57rem; letter-spacing:.4px; margin-top:2px;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.tapes.is-crate .tape__window{
  position:relative; width:86%; height:36px; gap:26px; margin:0 auto 6px;
  border-color:#585070;
  box-shadow:inset 0 2px 6px rgba(0,0,0,.6);
}
/* the tape itself, wound between the hubs and passing behind them */
.tapes.is-crate .tape__window::before{
  content:""; position:absolute; z-index:0; left:24%; right:24%; top:50%;
  height:13px; transform:translateY(-50%); border-radius:2px;
  background:linear-gradient(180deg,#2e1d12,#120b06);
}
/* the hubs, with teeth you can see turn */
.tapes.is-crate .tape__window i{
  position:relative; z-index:1; width:22px; height:22px;
  border-color:var(--tint);
  background:
    radial-gradient(circle, #241d33 34%, transparent 35%),
    repeating-conic-gradient(from 0deg, #3b3352 0 22deg, #14101f 22deg 45deg);
}
.tapes.is-crate .tape__cat{ margin-top:auto; font-size:.63rem; color:var(--tint); opacity:.85; }
/* the no-JS line; the crate has one transport, on the deck below */
.tapes.is-crate .tape__pending{ display:none; }

/* ---------- the crate's OSD strip ----------
   What's ACTUALLY running, in one fixed place. Flipping the stack doesn't stop
   the music, so the title on the deck (the tape you'd press play on) and the
   title here (the tape rolling) can differ — and this is the one that answers
   "what am I listening to". */
.crate__osd{
  display:flex; align-items:center; gap:9px; min-height:32px;
  margin:0 0 9px; padding:5px 11px;
  border:1px solid rgba(31,227,255,.35); border-radius:8px;
  background:rgba(11,7,24,.6);
  font-family:var(--osd); font-size:.76rem; letter-spacing:1.4px;
  color:var(--ink-faint);
  white-space:nowrap; overflow:hidden;
}
.crate__osd b{ font-weight:400; color:var(--cyan-lit); }
.crate__osd span{ overflow:hidden; text-overflow:ellipsis; }
.crate__dot{
  flex:0 0 auto; width:9px; height:9px; border-radius:50%; background:#3d3358;
}
/* rolling: the strip goes green with the tape's own light, and the ON AIR pip
   blinks red the way the one on the hero does */
.crate.is-rolling .crate__osd{ border-color:var(--lime); color:var(--ink); }
.crate.is-rolling .crate__dot{
  background:var(--alert); box-shadow:0 0 9px var(--alert);
  animation:blink 1.3s steps(2,start) infinite;
}

/* ---------- the deck under the crate ---------- */
.crate__deck{
  display:grid; grid-template-columns:auto 1fr auto; gap:10px; align-items:center;
  margin-top:12px; padding:10px 11px;
  border:2px solid var(--cyan-deep); border-radius:12px;
  background:linear-gradient(180deg,#2b2438,#150a2e);
  box-shadow:inset 0 0 26px rgba(162,75,255,.18);
}
/* The glyphs are ◄ ► rather than ‹ ›: Bungee has no guillemets, and the two
   VCR arrows are what the console's own rockers wear a floor above. */
.crate__rock{
  width:46px; height:56px; cursor:pointer;
  font-family:var(--osd); font-size:1.15rem; line-height:1;
  color:var(--cyan-lit); background:linear-gradient(180deg,#3a2a6b,#1b1136);
  border:2px solid var(--violet); border-radius:9px;
  text-shadow:0 0 8px rgba(31,227,255,.6);
}
.crate__rock:hover{ filter:brightness(1.25); }
.crate__rock:active{ transform:translateY(1px); }
.crate__rock:focus-visible{ outline:2px solid var(--yellow); outline-offset:2px; }
.crate__mid{ min-width:0; text-align:center; }
.crate__now{
  margin:0; font-family:var(--chunk); font-size:.95rem; letter-spacing:.4px;
  color:var(--ink); line-height:1.25;
  white-space:nowrap; overflow:hidden; text-overflow:ellipsis;
}
.crate__meta{
  margin:2px 0 8px; font-family:var(--osd); font-size:.7rem; letter-spacing:1.3px;
  color:var(--ink-faint); font-variant-numeric:tabular-nums;
}
.crate__meta span + span::before{ content:" · "; }
.crate__play{
  display:block; width:100%; cursor:pointer;
  font-family:var(--osd); font-size:.86rem; letter-spacing:2px; text-transform:uppercase;
  color:var(--ink); background:linear-gradient(180deg,#2b1e52,#1b1136);
  border:2px solid var(--cyan-deep); border-radius:8px; padding:8px 10px;
}
.crate__play:hover{ filter:brightness(1.15); }
.crate__play:focus-visible{ outline:2px solid var(--yellow); outline-offset:2px; }
.crate__play.is-stop{
  color:#150a2e; border-color:var(--yellow);
  background:linear-gradient(180deg,var(--yellow),#e0c81f);
}

/* ---------- the spine rail ----------
   The whole crate at a glance, and the only way to jump straight at a tape.
   It scrolls sideways as the band sends more, rather than growing the page. */
.crate__spines{
  display:flex; gap:6px; margin-top:10px; padding:3px 2px 7px;
  overflow-x:auto; scrollbar-width:thin;
}
.spine{
  flex:0 0 auto; cursor:pointer; min-width:40px; padding:6px 9px 7px;
  font-family:var(--osd); font-size:.72rem; letter-spacing:1px;
  color:var(--ink-faint);
  background:linear-gradient(180deg,#241d33,#12101c);
  border:1px solid #4a4460; border-top:3px solid #4a4460; border-radius:4px;
}
/* the rail wears the crate's colours, in the same order the tapes do */
.crate__spines .spine:nth-child(6n+1){ border-top-color:var(--magenta); }
.crate__spines .spine:nth-child(6n+2){ border-top-color:var(--cyan); }
.crate__spines .spine:nth-child(6n+3){ border-top-color:var(--yellow); }
.crate__spines .spine:nth-child(6n+4){ border-top-color:var(--violet-lit); }
.crate__spines .spine:nth-child(6n+5){ border-top-color:var(--sun-2); }
.crate__spines .spine:nth-child(6n+6){ border-top-color:var(--lime); }
/* These two out-rank the tints above on purpose: which tape is on the head and
   which one is rolling have to beat "which colour is it". */
.crate__spines .spine:hover{ color:var(--ink); }
.crate__spines .spine.is-playing{ color:var(--lime); border-color:var(--lime); }
.crate__spines .spine.is-on{
  color:#0b0718; border-color:var(--cyan-lit);
  background:linear-gradient(180deg,var(--cyan-lit),var(--cyan));
}
.crate__spines .spine.is-on.is-playing{ color:#0b0718; }
.spine:focus-visible{ outline:2px solid var(--yellow); outline-offset:2px; }

@media (max-width:620px){
  .tapes.is-crate{ --tw:190px; --th:130px; --step:96px; --tight:58px; --depth:80px; --tilt:46deg; }
  .tapes.is-crate .tape__label{ height:54px; }
  .tapes.is-crate .tape__title{ font-size:.84rem; -webkit-line-clamp:2; }
  .tapes.is-crate .tape__sub{ display:none; }   /* no room, and it repeats */
  .tapes.is-crate .tape__window{ height:30px; }
  .tapes.is-crate .tape__window i{ width:19px; height:19px; }
  .crate__rock{ width:40px; height:52px; }
  .crate__osd{ font-size:.68rem; letter-spacing:1px; }
}

@media (prefers-reduced-motion: reduce){
  /* is-playing stays as the "this one's running" highlight; the reels hold
     still, the stack cuts between tapes instead of sliding, the light holds. */
  .tape.is-playing .tape__window i{ animation:none; }
  .tapes.is-crate .tape{ transition:none; }
  .crate.is-rolling .crate__dot{ animation:none; }
}

/* ================= THE BAND :: promo, lineup, live shots ============= */
.promo{ margin:0 0 16px; }
.promo__frame{
  position:relative; max-width:580px; margin:0 auto;
  border:3px solid var(--magenta); border-radius:14px; overflow:hidden;
  background:rgba(11,7,24,.6);
  box-shadow:0 0 26px rgba(255,46,151,.35), inset 0 0 40px rgba(162,75,255,.16);
}
.promo__img{ display:block; width:100%; height:auto; }
.promo__pending{
  position:absolute; inset:0; display:none;   /* the script reveals it on a load error */
  flex-direction:column; align-items:center; justify-content:center; gap:8px;
  text-align:center; padding:16px; background:rgba(11,7,24,.92);
  font-family:var(--osd); color:var(--cyan-lit);
}
.promo__pending b{ font-size:1.2rem; letter-spacing:2px; color:#fff; }
.promo__pending code{ color:var(--yellow); }
.promo__cap{
  margin:12px 0 0; text-align:center;
  font-family:var(--osd); font-size:.85rem; letter-spacing:1.5px; color:var(--ink-faint);
}

.lineup{ display:flex; flex-wrap:wrap; gap:10px; justify-content:center; margin:0 0 24px; }
.lineup__chip{
  font-family:var(--chunk); font-size:.9rem; letter-spacing:.5px;
  padding:8px 14px; border-radius:30px; color:#0b0718; background:var(--violet-lit);
  box-shadow:0 0 12px rgba(162,75,255,.4);
}
.lineup__chip:nth-child(2n){ background:var(--cyan); box-shadow:0 0 12px rgba(31,227,255,.4); }
.lineup__chip:nth-child(3n){ background:var(--yellow); box-shadow:0 0 12px rgba(255,232,61,.4); }

.floor{ display:grid; gap:12px; grid-template-columns:repeat(auto-fit, minmax(150px,1fr)); }
.floor__shot{
  margin:0; overflow:hidden; border-radius:12px; background:rgba(11,7,24,.6);
  border:2px solid var(--cyan); box-shadow:0 0 16px rgba(31,227,255,.22);
}
.floor__shot:nth-child(2n){ border-color:var(--magenta); box-shadow:0 0 16px rgba(255,46,151,.22); }
.floor__shot:nth-child(3n){ border-color:var(--yellow); box-shadow:0 0 16px rgba(255,232,61,.2); }
.floor__shot img{ display:block; width:100%; height:230px; object-fit:cover; }
.floor__cap{
  margin:12px 0 0; text-align:center;
  font-family:var(--osd); font-size:.82rem; letter-spacing:1.5px; color:var(--ink-faint);
}

/* the flyer wall in THE GIGS */
.flyers{ display:grid; gap:14px; grid-template-columns:repeat(auto-fit, minmax(220px,1fr)); margin:0 0 20px; }
.flyer{
  margin:0; overflow:hidden; border-radius:12px; background:rgba(11,7,24,.6);
  border:2px solid var(--yellow); box-shadow:0 0 16px rgba(255,232,61,.25);
}
.flyer img{ display:block; width:100%; height:auto; }

/* the real station logo in the footer */
.foot__logo{
  display:block; width:min(240px,62%); margin:0 auto 16px; border-radius:8px;
  border:1px solid rgba(199,139,255,.4); box-shadow:0 0 18px rgba(0,0,0,.5);
}

/* ================= PLACEHOLDER COPY ==================
   Every "--lorem" element is a slot still waiting on real words from the band
   (see the COPY RULE at the top of shwing.html). They're dimmed and set in the
   OSD mono so nobody — the band reading their own page least of all — mistakes
   filler for finished copy. Deleting the modifier class is how a block
   graduates: swap in the real sentence, drop the "--lorem", and it inherits
   the section's normal styling with no other change.

   This is a build-time tell, not a decoration. When the last one is gone,
   delete this whole block. */
.dek--lorem,
.hero__blurb--lorem{
  font-family:var(--osd);
  color:var(--ink-faint);
  opacity:.62;
}
/* The chips keep their neon shape so the layout still reads at a glance, but
   go grey and dashed — clearly a hole, not a genre or a member. */
.genre--lorem,
.lineup__chip--lorem{
  background:transparent !important;
  box-shadow:none !important;
  color:var(--ink-faint);
  border:1px dashed rgba(199,139,255,.45);
}

/* ================= REDUCED MOTION ================= *
   Kill everything that moves for visitors who ask for it: the grid scroll,
   the neon flicker, the crawl, the static, the tally blink. JS also skips
   the confetti entirely, so nothing here needs to hide #confetti children. */
@media (prefers-reduced-motion: reduce){
  *{ animation:none !important; scroll-behavior:auto !important; }
  body::before{ animation:none; }
  .crawl__track{ transform:translateX(0); }
  .static{ opacity:.12; }
  /* the start key's ring can't pulse, so it just sits there as a ring */
  .tv-start__key::after{ opacity:.45; transform:scale(1); }
}
