// V1 — PASTEL MEADOW
// Vaporwave-meets-meadow: pastel sky gradient, 3D animated grass, glossy chrome serif title,
// floating glass player. Single viewport. Audio-reactive.

const V1 = () => {
  const radio = useEphemeralRadio();
  const { state, track, listeners, toggle, levels, live } = radio;
  const playing = state === "playing";
  const [panel, setPanel] = React.useState(null); // null | "about" | "contact" | "streams"
  const isLive = !!live?.isLive;
  const streamer = live?.streamer || "live operator";

  // Floating particles (pollen / dust motes)
  const motes = React.useMemo(() => Array.from({ length: 24 }, (_, i) => ({
    id: i,
    x: Math.random() * 100,
    y: Math.random() * 100,
    size: 2 + Math.random() * 4,
    dur: 14 + Math.random() * 18,
    delay: -Math.random() * 30,
  })), []);

  // Grass blades — distributed across the bottom
  const blades = React.useMemo(() => Array.from({ length: 70 }, (_, i) => {
    const x = (i / 70) * 100 + (Math.random() - 0.5) * 1.4;
    return {
      id: i,
      x,
      h: 60 + Math.random() * 90,
      sway: 1.4 + Math.random() * 1.6,
      delay: -Math.random() * 4,
      hue: 95 + Math.random() * 30,
      light: 55 + Math.random() * 18,
      z: Math.random(),
    };
  }).sort((a,b) => a.z - b.z), []);

  // Reactive level: 0..1, biased toward bass
  const level = React.useMemo(() => {
    if (!playing) return 0;
    let s = 0; const n = Math.min(16, levels.length);
    for (let i = 0; i < n; i++) s += levels[i];
    return Math.min(1, (s / n) / 200);
  }, [levels, playing]);

  return (
    <div style={v1Styles.root} className="v1-root">
      <style>{v1CSS}</style>

      {/* Sky */}
      <div style={v1Styles.sky} />
      <div style={v1Styles.sun} />
      <div style={v1Styles.sunGlow} />

      {/* Distant clouds */}
      <div style={{ ...v1Styles.cloud, top: "18%", left: "8%", animationDuration: "120s" }} />
      <div style={{ ...v1Styles.cloud, top: "28%", left: "60%", animationDuration: "180s", opacity: 0.55, transform: "scale(0.7)" }} />
      <div style={{ ...v1Styles.cloud, top: "12%", left: "70%", animationDuration: "210s", opacity: 0.7, transform: "scale(1.3)" }} />

      {/* Pollen motes */}
      {motes.map(m => (
        <div key={m.id} className="mote" style={{
          left: `${m.x}%`, top: `${m.y}%`,
          width: m.size, height: m.size,
          animationDuration: `${m.dur}s`,
          animationDelay: `${m.delay}s`,
        }} />
      ))}

      {/* Distant hills */}
      <svg style={v1Styles.hills} viewBox="0 0 1440 240" preserveAspectRatio="none">
        <defs>
          <linearGradient id="h1" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0" stopColor="#b8d8b3"/><stop offset="1" stopColor="#94c4a0"/>
          </linearGradient>
          <linearGradient id="h2" x1="0" x2="0" y1="0" y2="1">
            <stop offset="0" stopColor="#86b594"/><stop offset="1" stopColor="#5d9474"/>
          </linearGradient>
        </defs>
        <path d="M0,140 C220,80 360,180 540,130 C720,80 900,170 1080,120 C1260,80 1340,140 1440,130 L1440,240 L0,240 Z" fill="url(#h1)" opacity="0.85"/>
        <path d="M0,180 C200,140 380,210 580,170 C780,140 980,200 1180,165 C1300,145 1380,170 1440,170 L1440,240 L0,240 Z" fill="url(#h2)"/>
      </svg>

      {/* Header */}
      <header style={v1Styles.header} className="v1-header">
        <div style={v1Styles.brandRow} className="v1-brandRow">
          <span style={v1Styles.brandDot}>◉</span>
          <span style={v1Styles.brandMono}>EPHEMERAL.CLUB // LOSSLESS · ON AIR</span>
          <span style={v1Styles.brandMono}>·</span>
          <span style={v1Styles.brandMono}>{listeners != null ? `${listeners} drifting` : "—"}</span>
        </div>
        <nav style={v1Styles.nav}>
          <a href="#" onClick={(e)=>{e.preventDefault(); setPanel(panel==="about"?null:"about");}} style={v1Styles.navLink}>about</a>
          <a href="#" onClick={(e)=>{e.preventDefault(); setPanel(panel==="streams"?null:"streams");}} style={v1Styles.navLink}>streams</a>
          <a href="#" onClick={(e)=>{e.preventDefault(); setPanel(panel==="contact"?null:"contact");}} style={v1Styles.navLink}>contact</a>
        </nav>
      </header>

      {/* Hero wordmark */}
      <main style={v1Styles.main} className="v1-main">
        <div style={v1Styles.heroBlock} className="v1-heroBlock">
          <div style={v1Styles.eyebrow} className="v1-eyebrow">
            <span className="blink">▸</span>&nbsp;a transmission, briefly
          </div>
          <h1 style={v1Styles.wordmark} className="v1-wordmark">
            <span style={v1Styles.wmLine1} className="v1-wmLine1">ephemeral</span>
            <span style={v1Styles.wmLine2} className="v1-wmLine2">
              <em style={v1Styles.wmEm} className="v1-wmEm">fm</em>
              <span style={v1Styles.wmDot} className="v1-wmDot">·</span>
              <span style={v1Styles.wmYear} className="v1-wmYear">MMXXVI</span>
            </span>
          </h1>
          <p style={v1Styles.manifesto} className="v1-manifesto">
            nothing is permanent. the song you are hearing<br/>
            will never play this way again. <span style={v1Styles.manifestoEm}>touch grass,</span><br/>
            stream sound, then let it pass.
          </p>
        </div>

        {/* Player card */}
        <PlayerCard
          radio={radio}
          level={level}
          playing={playing}
        />
      </main>

      {/* Live DJ banner — only shown when a streamer has taken over from AutoDJ */}
      {isLive && (
        <div style={v1Styles.liveBanner} className="v1-liveBanner">
          <span style={v1Styles.liveDot} className="v1-liveDot"/>
          <span style={v1Styles.liveLabel}>LIVE DJ</span>
          <span style={v1Styles.liveStreamer}>{streamer}</span>
          <span style={v1Styles.liveTail}>on air · no archive · no rewinds</span>
        </div>
      )}

      {/* Footer ticker */}
      <footer style={v1Styles.footer} className="v1-footer">
        <div style={v1Styles.tickerWrap} className="v1-tickerWrap">
          <div className="ticker">
            <span>★ broadcasting from a hill in alberta</span>
            <span>◉ lossless flac · 24-bit · 44.1 khz · ogg</span>
            <span>✿ {track ? `now: ${track.artist} — ${track.title}` : "waiting for signal"}</span>
            <span>◐ live DJ sets · whenever the spirit moves us</span>
            <span>◐ no compression artifacts. only weather.</span>
            <span>◐ source files: as hi-res as we can find them</span>
            <span>◐ ephemeral.club</span>
            <span>★ broadcasting from a hill in alberta</span>
            <span>◉ lossless flac · 24-bit · 44.1 khz · ogg</span>
            <span>✿ {track ? `now: ${track.artist} — ${track.title}` : "waiting for signal"}</span>
            <span>◐ live DJ sets · whenever the spirit moves us</span>
            <span>◐ no compression artifacts. only weather.</span>
            <span>◐ source files: as hi-res as we can find them</span>
            <span>◐ ephemeral.club</span>
          </div>
        </div>
        <div style={v1Styles.socials}>
          <a href="https://instagram.com/ephemeral.khz" target="_blank" rel="noreferrer" style={v1Styles.social}>◐ instagram · @ephemeral.khz</a>
        </div>
      </footer>

      {/* Grass — drawn last, top of stack */}
      <div style={v1Styles.grassLayer}>
        {blades.map(b => (
          <div key={b.id} className="blade" style={{
            left: `${b.x}%`,
            height: `${b.h * (0.85 + level * 0.5)}px`,
            background: `linear-gradient(to top, hsl(${b.hue} 50% ${b.light - 18}%) 0%, hsl(${b.hue} 60% ${b.light}%) 60%, hsl(${b.hue} 70% ${b.light + 12}%) 100%)`,
            animationDuration: `${b.sway}s`,
            animationDelay: `${b.delay}s`,
            zIndex: Math.floor(b.z * 10),
            opacity: 0.55 + b.z * 0.45,
          }} />
        ))}
      </div>

      {/* Lens flare */}
      <div style={v1Styles.flare} />

      {/* Modal panels */}
      {panel && <Panel kind={panel} onClose={()=>setPanel(null)} />}
    </div>
  );
};

const StreamRow = ({ label, sub, url }) => {
  const [copied, setCopied] = React.useState(false);
  const copy = async () => {
    try {
      await navigator.clipboard.writeText(url);
      setCopied(true);
      setTimeout(()=>setCopied(false), 1600);
    } catch {
      const ta = document.createElement("textarea");
      ta.value = url; document.body.appendChild(ta); ta.select();
      try { document.execCommand("copy"); setCopied(true); setTimeout(()=>setCopied(false),1600); } catch {}
      ta.remove();
    }
  };
  return (
    <div style={v1Styles.streamRow} className="v1-streamRow">
      <div style={v1Styles.streamHead}>
        <span style={v1Styles.streamLabel}>{label}</span>
        <span style={v1Styles.streamSub}>{sub}</span>
      </div>
      <div style={v1Styles.streamUrlRow}>
        <a href={url} target="_blank" rel="noreferrer" style={v1Styles.streamUrl} className="v1-streamUrl">{url}</a>
        <button onClick={copy} style={v1Styles.streamCopy} className="v1-streamCopy" aria-label={`copy ${label} url`}>
          {copied ? "copied ◉" : "copy ▸"}
        </button>
      </div>
    </div>
  );
};

const Panel = ({ kind, onClose }) => {
  const isAbout = kind === "about";
  const isStreams = kind === "streams";
  const eyebrowTxt = isAbout ? "// about.txt" : isStreams ? "// streams.txt" : "// contact.txt";
  return (
    <div style={v1Styles.panelBackdrop} onClick={onClose}>
      <div style={v1Styles.panelCard} onClick={(e)=>e.stopPropagation()}>
        <button onClick={onClose} style={v1Styles.panelClose} aria-label="close">×</button>
        <div style={v1Styles.panelEyebrow}>
          <span className="blink">▸</span>&nbsp;{eyebrowTxt}
        </div>
        {isStreams ? (
          <>
            <h2 style={v1Styles.panelTitle} className="v1-panelTitle">tune in <em>elsewhere</em></h2>
            <div style={v1Styles.panelBody}>
              <p>
                paste either url into any third-party player — VLC, foobar2000,
                <em> Transistor,</em> <em>RadioDroid,</em> <em>Broadcasts,</em> your car stereo if it'll have it.
                the signal is the same. only the wrapper changes.
              </p>
              <div style={v1Styles.streamList}>
                <StreamRow
                  label="flac"
                  sub="lossless · 24-bit · 44.1 khz"
                  url="https://listen.ephemeral.club/listen/ephemeral/radio.flac"
                />
                <StreamRow
                  label="mp3"
                  sub="320 kbps · for stubborn apps"
                  url="https://listen.ephemeral.club/listen/ephemeral/radio.mp3"
                />
              </div>
              <p style={{marginTop:18, fontSize:15, opacity:.85}}>
                flac is the way we intend it. mp3 is provided as a courtesy
                for hardware and apps that won't speak anything else.
              </p>
              <p style={v1Styles.signoff}>
                ◉ live stream · no archive · no rewinds<br/>
                ◉ same signal · different wrappers
              </p>
            </div>
          </>
        ) : isAbout ? (
          <>
            <h2 style={v1Styles.panelTitle}>lossless transmission, <em>fleeting moment</em></h2>
            <div style={v1Styles.panelBody}>
              <p>
                <strong>Ephemeral FM</strong> broadcasts in <strong>lossless FLAC </strong>
                wrapped in Ogg — 24-bit, 44.1 kHz, bit-perfect from the stream out.
                no MP3 fog. no codec haze. no re-encoded ghosts of a song you almost heard.
                source files are as hi-res as we can get them, and we keep upgrading them where we can.
              </p>
              <p>
                the paradox is the point. we use the most permanent format we can find
                to deliver the most impermanent thing we know: a moment. the file is uncompressed;
                the moment is non-renewable. the bits are exact; <em>your experience is unique to when you listen.</em>
              </p>
              <p>
                no rewinds. no on-demand. no recommendation engine pretending to know you.
                the playlist is curated, then forgotten. the schedule is when we feel like it.
                the recordings are not retained. <em>what you missed, you missed.</em>
                what you caught — that song, the way the grass looked while you heard it —
                belongs to you, briefly, and to no one else.
              </p>
              <p>
                a handful of trusted operators hold uplink credentials.
                when one of them keys in, the station yields — the playlist halts,
                the broadcast becomes <em>them,</em> live, into the void.
                no schedule. no announcement. no archive.
                a <strong>live DJ transmission</strong> is exactly as long as it lasts,
                and it lasts exactly once. tune in. find out. or don't.
              </p>
              <p style={v1Styles.signoff}>
                ◉ flac-in-ogg · 24-bit · 44.1 khz · always lossless<br/>
                ◉ touch grass between transmissions
              </p>
            </div>
          </>
        ) : (
          <>
            <h2 style={v1Styles.panelTitle} className="v1-panelTitle">send a <em>signal</em></h2>
            <div style={v1Styles.panelBody}>
              <p>demos, dedications, complaints about the weather:</p>
              <a href="mailto:dj@ephemeral.club" style={v1Styles.bigEmail} className="v1-bigEmail">
                dj@ephemeral.club
              </a>
              <p style={{marginTop:24}}>
                we read everything. we reply when the spirit moves us.
                attach an mp3, a flac, a field recording, a photo of where
                you are listening from. the more ephemeral the better.
              </p>
              <div style={v1Styles.panelMeta}>
                <span>◐ replies are not guaranteed</span>
                <span>◐ replies are sincere when sent</span>
                <span>◐ no PR pitches please</span>
              </div>
            </div>
          </>
        )}
      </div>
    </div>
  );
};

// ----- Sleek volume popout: speaker button + reveal-on-click slider -----
const VolumePopout = ({ volume, setVolume }) => {
  const [open, setOpen] = React.useState(false);
  const lastRef = React.useRef(volume > 0 ? volume : 0.8);
  const wrapRef = React.useRef(null);
  React.useEffect(() => { if (volume > 0) lastRef.current = volume; }, [volume]);
  React.useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (!wrapRef.current?.contains(e.target)) setOpen(false); };
    document.addEventListener("pointerdown", onDoc);
    return () => document.removeEventListener("pointerdown", onDoc);
  }, [open]);
  const tier = volume === 0 ? 0 : volume < 0.34 ? 1 : volume < 0.67 ? 2 : 3;
  const toggleMute = (e) => { e.stopPropagation(); if (volume > 0) { lastRef.current = volume; setVolume(0); } else { setVolume(lastRef.current || 0.8); } };
  return (
    <div ref={wrapRef} className={`v1-vol ${open ? "is-open" : ""}`}>
      <input
        type="range" min="0" max="1" step="0.01" value={volume}
        onChange={(e) => setVolume(parseFloat(e.target.value))}
        className="v1-volRange" aria-label="volume" tabIndex={open ? 0 : -1}
        style={{ "--vol": `${Math.round(volume * 100)}%` }}
      />
      <button
        type="button" className="v1-volBtn" aria-label={open ? "close volume" : "volume"}
        aria-expanded={open} onClick={() => setOpen(o => !o)} onContextMenu={toggleMute}
        onDoubleClick={toggleMute}
      >
        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
          <path d="M5 9v6h3l5 4V5L8 9H5z" fill="currentColor" fillOpacity=".22"/>
          <path d="M5 9v6h3l5 4V5L8 9H5z"/>
          {tier === 0 && <path d="M16 9.5l5 5M21 9.5l-5 5"/>}
          {tier >= 1 && <path d="M16 9.5c.8 1 .8 4 0 5"/>}
          {tier >= 2 && <path d="M18.8 7.3c1.6 1.6 1.6 7.8 0 9.4"/>}
          {tier >= 3 && <path d="M21.5 5.2c2.6 2.6 2.6 11 0 13.6"/>}
        </svg>
      </button>
    </div>
  );
};

// Resolve a human store name from a purchase URL so the button can adapt
// its wording. Returns "bandcamp", "itunes", or null (generic "buy").
function buyStore(url) {
  if (!url) return null;
  const u = url.toLowerCase();
  if (u.includes("bandcamp.com")) return "bandcamp";
  if (u.includes("qobuz.com") || u.includes("open.qobuz.com")) return "qobuz";
  if (u.includes("itunes.apple.com") || u.includes("music.apple.com") || u.includes("apple.co")) return "itunes";
  return null;
}

// Buy tab — a slim ribbon that slides up out of the player card's top edge
// ONLY when the current track carries a store URL. Stays mounted so it can
// animate in/out; wording adapts to the destination but stays understated.
const BuyTab = ({ url }) => {
  const store = buyStore(url);
  const label = store || "purchase";
  const nice = store ? store[0].toUpperCase() + store.slice(1) : "this track";
  return (
    <a
      href={url} target="_blank" rel="noreferrer"
      className="v1-buyTab"
      aria-label={`buy ${nice}`} title={`buy · ${nice}`}
    >
      <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round">
        <path d="M20.6 13.4 13.4 20.6a2 2 0 0 1-2.8 0l-7-7A2 2 0 0 1 3 12.2V5a2 2 0 0 1 2-2h7.2a2 2 0 0 1 1.4.6l7 7a2 2 0 0 1 0 2.8Z"/>
        <circle cx="7.5" cy="7.5" r="1.5" fill="currentColor"/>
      </svg>
      <span>{label}</span>
      <span style={{opacity:.5}}>▸</span>
    </a>
  );
};

// ----- Player card (used by V1 only here, but designed to be reused) -----
const PlayerCard = ({ radio, level, playing }) => {
  const { track, toggle, state, listeners, volume, setVolume, live } = radio;
  const elapsed = useElapsed(track, playing);
  const isLive = !!live?.isLive;

  return (
    <div style={v1Styles.player} className={`v1-player ${isLive ? "is-live" : ""}`}>
      {/* Buy tab — slides out of the card's top edge only when the track is for sale */}
      {track?.buyUrl && <BuyTab url={track.buyUrl}/>}
      <div style={v1Styles.playerInner} className="v1-playerInner">
        {/* Volume popout — anchored to player card top-right */}
        <VolumePopout volume={volume} setVolume={setVolume}/>
        {/* Album art */}
        <button onClick={toggle} style={v1Styles.artBtn} className="v1-artBtn" aria-label="play / pause">
          <div className="v1-art" style={{
            ...v1Styles.art,
            backgroundImage: track?.art ? `url(${track.art})` : "none",
            transform: `scale(${1 + level * 0.04})`,
          }}>
            {!track?.art && <ArtPlaceholder/>}
            <div style={v1Styles.artGloss}/>
            <div style={{
              ...v1Styles.playOverlay,
              opacity: state === "loading" ? 1 : (playing ? 0 : 1),
            }}>
              {state === "loading"
                ? <div className="spinner"/>
                : (playing
                  ? <svg width="34" height="34" viewBox="0 0 24 24"><rect x="6" y="5" width="4" height="14" rx="1" fill="white"/><rect x="14" y="5" width="4" height="14" rx="1" fill="white"/></svg>
                  : <svg width="34" height="34" viewBox="0 0 24 24"><path d="M7 5 L19 12 L7 19 Z" fill="white"/></svg>
                )}
            </div>
          </div>
        </button>

        {/* Meta */}
        <div style={v1Styles.meta} className="v1-meta">
          <div style={v1Styles.metaLabel}>
            {isLive
              ? <><span className="v1-liveDotInline"/>LIVE DJ ▸ <span style={{color:"#d6275a", letterSpacing:".25em"}}>{live?.streamer || "live operator"}</span></>
              : <>NOW DRIFTING ▸ <span style={{color:"#7a4a8c", letterSpacing:".25em"}}>FLAC · 24-BIT · 44.1 KHZ</span></>
            }
          </div>
          <Marquee className="v1-title" style={v1Styles.title} text={track?.title || "—"}/>
          <div style={v1Styles.artist}>{track?.artist || "—"}</div>
          {track?.album && <div style={v1Styles.album}>from <em>{track.album}</em></div>}

          {/* Visualizer */}
          <Visualizer levels={radio.levels} playing={playing}/>

          {/* Progress */}
          <div style={v1Styles.progress}>
            <div style={{ ...v1Styles.progressFill, width: `${Math.min(100, (elapsed / Math.max(1, track?.duration || 1)) * 100)}%` }}/>
          </div>
          <div style={v1Styles.progRow}>
            <span>{fmtTime(elapsed)}</span>
            <span style={{opacity:.55}}>{listeners != null ? `${listeners} listening · live` : "live"}</span>
            <span>{track?.duration ? fmtTime(track.duration) : "∞"}</span>
          </div>

        </div>
      </div>
    </div>
  );
};

// Marquee — slides text back and forth ONLY when it overflows its container.
const Marquee = ({ text, className, style }) => {
  const wrap = React.useRef(null);
  const inner = React.useRef(null);
  const [shift, setShift] = React.useState(0);
  React.useLayoutEffect(() => {
    const w = wrap.current, t = inner.current;
    if (!w || !t) return;
    const measure = () => {
      const overflow = t.scrollWidth - w.clientWidth;
      if (overflow > 4) setShift(-overflow);
      else setShift(0);
    };
    measure();
    const ro = new ResizeObserver(measure);
    ro.observe(w); ro.observe(t);
    return () => ro.disconnect();
  }, [text]);
  const isOverflow = shift < 0;
  return (
    <span
      ref={wrap}
      className={`v1-marquee ${isOverflow ? "is-overflow" : ""} ${className || ""}`}
      style={{ ...(style || {}), "--marquee-shift": `${shift}px` }}
    >
      <span ref={inner} className="v1-marquee-track">{text}</span>
    </span>
  );
};

const ArtPlaceholder = () => (
  <svg width="100%" height="100%" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid slice">
    <defs>
      <linearGradient id="ph" x1="0" x2="1" y1="0" y2="1">
        <stop offset="0" stopColor="#ffd6e7"/>
        <stop offset=".5" stopColor="#c8e0ff"/>
        <stop offset="1" stopColor="#c8eecf"/>
      </linearGradient>
    </defs>
    <rect width="100" height="100" fill="url(#ph)"/>
    <circle cx="50" cy="50" r="22" fill="white" opacity=".55"/>
    <circle cx="50" cy="50" r="6" fill="#1a1a2e"/>
  </svg>
);

const Visualizer = ({ levels, playing }) => {
  const bars = 28;
  const arr = React.useMemo(() => {
    const out = new Array(bars).fill(0);
    if (!playing || !levels?.length) return out;
    const step = Math.floor(levels.length / bars) || 1;
    for (let i = 0; i < bars; i++) {
      let s = 0; let c = 0;
      for (let k = i * step; k < (i + 1) * step && k < levels.length; k++) { s += levels[k]; c++; }
      out[i] = c ? s / c : 0;
    }
    return out;
  }, [levels, playing]);
  return (
    <div style={v1Styles.viz}>
      {arr.map((v, i) => (
        <div key={i} style={{
          ...v1Styles.vizBar,
          height: `${playing ? Math.max(3, (v / 255) * 100) : 3}%`,
        }}/>
      ))}
    </div>
  );
};

function useElapsed(track, playing) {
  const [t, setT] = React.useState(0);
  React.useEffect(() => {
    if (!track) return;
    const base = track.elapsed || 0;
    const start = Date.now();
    setT(base);
    if (!playing) return;
    const id = setInterval(() => {
      setT(base + (Date.now() - start) / 1000);
    }, 500);
    return () => clearInterval(id);
  }, [track, playing]);
  return t;
}
function fmtTime(s) {
  if (!isFinite(s)) return "0:00";
  s = Math.max(0, Math.floor(s));
  const m = Math.floor(s / 60); const r = s % 60;
  return `${m}:${r.toString().padStart(2, "0")}`;
}

const v1CSS = `
@keyframes mote { 0%{transform:translateY(0) translateX(0); opacity:0} 10%{opacity:.9} 90%{opacity:.9} 100%{transform:translateY(-220px) translateX(40px); opacity:0} }
.mote { position:absolute; border-radius:50%; background:radial-gradient(circle, #fff 0%, rgba(255,255,255,.6) 40%, transparent 70%); box-shadow:0 0 8px rgba(255,255,255,.6); animation:mote linear infinite; pointer-events:none; }
@keyframes blink { 0%,49%{opacity:1} 50%,100%{opacity:0} }
.blink { animation: blink 1.2s steps(1) infinite; }
@keyframes sway { 0%,100%{transform:rotate(-3deg)} 50%{transform:rotate(4deg)} }
.blade { position:absolute; bottom:0; width:3px; transform-origin:bottom center; clip-path:polygon(50% 0%, 100% 100%, 0% 100%); animation: sway ease-in-out infinite; filter:drop-shadow(0 -1px 0 rgba(255,255,255,.2)); transition:height .25s ease; }
@keyframes drift { 0%{transform:translateX(-100px)} 100%{transform:translateX(calc(100vw + 100px))} }
.cloudish { animation: drift linear infinite; }
@keyframes ticker { 0%{transform:translateX(0)} 100%{transform:translateX(-50%)} }
.ticker { display:flex; gap:48px; white-space:nowrap; animation: ticker 60s linear infinite; }
.ticker > span { font-family:'JetBrains Mono', monospace; font-size:11px; letter-spacing:.18em; text-transform:uppercase; color:#3a3556; }
.spinner { width:22px; height:22px; border:2.5px solid rgba(255,255,255,.3); border-top-color:white; border-radius:50%; animation: spin .9s linear infinite; }
@keyframes spin { to { transform:rotate(360deg) } }
@keyframes fadein { from { opacity:0 } to { opacity:1 } }

/* ---------- live DJ ---------- */
@keyframes livePulse {
  0%, 100% { transform:scale(1); box-shadow:0 0 12px 2px rgba(255,255,255,.85), 0 0 0 3px rgba(255,255,255,.2) }
  50%      { transform:scale(1.25); box-shadow:0 0 18px 4px rgba(255,255,255,.95), 0 0 0 6px rgba(255,255,255,.0) }
}
.v1-liveDotInline {
  display:inline-block; width:8px; height:8px; border-radius:50%;
  background:#d6275a; margin-right:8px; vertical-align:1px;
  box-shadow:0 0 8px 1px rgba(214,39,90,.7);
  animation: livePulse 1.2s ease-in-out infinite;
}
.v1-player.is-live { box-shadow: 0 30px 80px -20px rgba(214,39,90,.45), 0 8px 24px -8px rgba(214,39,90,.35), inset 0 1px 0 rgba(255,255,255,.9), 0 0 0 1.5px rgba(214,39,90,.35); }

/* ---------- sleek volume popout (desktop only) ---------- */
.v1-vol {
  position:absolute; bottom:14px; left:14px; z-index:5;
  display:flex; flex-direction:row-reverse; align-items:center; gap:0;
  height:24px;
}
.v1-volBtn {
  width:24px; height:24px; border-radius:50%; flex-shrink:0;
  border:none; background:transparent;
  color:rgba(58,53,86,.5); cursor:pointer;
  display:inline-flex; align-items:center; justify-content:center;
  transition: color .18s ease, background .18s ease;
  padding:0;
}
.v1-volBtn:hover { color:#7a4a8c; background:rgba(58,53,86,.06); }
.v1-vol.is-open .v1-volBtn { color:#7a4a8c; background:rgba(58,53,86,.08); }
.v1-volRange {
  -webkit-appearance:none; appearance:none;
  width:0; height:2px; border-radius:99px; outline:none; border:none;
  background:linear-gradient(90deg, #7a4a8c 0%, #b88dc8 var(--vol), rgba(58,53,86,.18) var(--vol), rgba(58,53,86,.18) 100%);
  opacity:0; pointer-events:none;
  transition: width .3s cubic-bezier(.2,.85,.25,1), opacity .2s ease, margin-right .3s cubic-bezier(.2,.85,.25,1);
  margin-right:0;
}
.v1-vol.is-open .v1-volRange { width:136px; opacity:1; pointer-events:auto; margin-left:8px; margin-right:0; }
.v1-volRange::-webkit-slider-thumb {
  -webkit-appearance:none; appearance:none;
  width:10px; height:10px; border-radius:50%;
  background:#7a4a8c; border:none;
  box-shadow:0 1px 3px rgba(80,40,120,.3);
  cursor:grab;
}
.v1-volRange::-webkit-slider-thumb:active { cursor:grabbing; }
.v1-volRange::-moz-range-thumb {
  width:10px; height:10px; border-radius:50%;
  background:#7a4a8c; border:none;
  box-shadow:0 1px 3px rgba(80,40,120,.3);
  cursor:grab;
}

/* ---------- mobile ---------- */
@media (max-width: 760px) {
  .v1-root { height:auto !important; min-height:100dvh; overflow-x:hidden !important; overflow-y:visible !important; padding-bottom:0 !important; width:100% !important; max-width:100vw !important; background:linear-gradient(180deg, #f9e9d9 0%, #f4d0c8 22%, #e8b8c8 45%, #d8c0e0 68%, #b8d4c0 88%, #88b894 100%) !important; }
  .v1-header { flex-direction:column; gap:14px; padding:18px 18px !important; align-items:flex-start !important; width:100%; box-sizing:border-box; }
  .v1-brandRow { flex-wrap:wrap; gap:10px !important; max-width:100%; }
  .v1-main { display:flex !important; flex-direction:column; gap:28px; padding:20px 18px 40px !important; height:auto !important; width:100% !important; max-width:100vw !important; box-sizing:border-box; align-items:stretch !important; }
  .v1-heroBlock { width:100%; max-width:100%; min-width:0; }
  .v1-wordmark { line-height:.92 !important; max-width:100%; }
  .v1-wmLine1 { font-size:18vw !important; line-height:.9 !important; display:block; max-width:100%; }
  .v1-wmLine2 { gap:10px !important; flex-wrap:wrap; max-width:100%; }
  .v1-wmEm { font-size:15vw !important; line-height:1 !important; }
  .v1-wmDot { font-size:9vw !important; }
  .v1-wmYear { font-size:10px !important; margin-left:0 !important; }
  .v1-manifesto { font-size:15px !important; max-width:100% !important; margin-top:22px !important; }
  .v1-eyebrow { font-size:10px !important; margin-bottom:12px !important; }
  .v1-player { width:100% !important; max-width:100% !important; box-sizing:border-box; }
  .v1-playerInner { grid-template-columns:1fr !important; padding:18px !important; gap:18px !important; box-sizing:border-box; width:100%; }
  .v1-artBtn { width:100% !important; height:auto !important; aspect-ratio:1/1; max-width:280px; margin:0 auto !important; display:block; }
  .v1-art { aspect-ratio:1/1; height:auto !important; width:100% !important; }
  .v1-meta { min-width:0; max-width:100%; overflow:hidden; }
  .v1-title { font-size:24px !important; }
  .v1-vol { display:none !important; }
  .v1-liveBanner { position:relative !important; top:auto !important; left:auto !important; transform:none !important; margin:0 auto 12px; max-width:fit-content; flex-wrap:wrap; justify-content:center; padding:8px 14px !important; gap:10px !important; }
  .v1-liveStreamer { font-size:16px !important; }
  .v1-liveTail { display:none; }
  .v1-footer { position:relative !important; margin-top:32px; padding-top:18px; background:linear-gradient(180deg, transparent 0%, rgba(170,206,168,.35) 60%, rgba(140,190,150,.65) 100%); }
  .v1-tickerWrap { background:rgba(255,255,255,.42) !important; }
  .v1-grassLayer { display:block !important; position:relative !important; height:96px !important; width:100% !important; background:linear-gradient(180deg, rgba(140,190,150,.65) 0%, rgba(110,170,124,.92) 55%, rgba(82,142,100,1) 100%); margin:0 !important; bottom:auto !important; overflow:hidden; }
  .v1-grassLayer .blade { opacity:.55 !important; }
  .v1-hills { display:none !important; }
  .v1-sun { width:70px !important; height:70px !important; top:6% !important; right:8% !important; }
  .v1-sunGlow { width:70px !important; height:70px !important; top:6% !important; right:8% !important; }
  .v1-flare { display:none; }
  .v1-panelCard { width:92% !important; max-height:88dvh !important; }
  .v1-panelTitle { font-size:30px !important; }
  .v1-bigEmail { font-size:22px !important; word-break:break-all; }
}

/* ---------- buy tab (pops out of the player card top edge) ---------- */
@keyframes v1PopTab { from { transform:translateY(0); opacity:0; } to { transform:translateY(-100%); opacity:1; } }
.v1-buyTab {
  position:absolute; top:0; right:30px; z-index:1;
  display:inline-flex; align-items:center; gap:7px;
  padding:7px 14px 9px;
  border-radius:13px 13px 0 0;
  font-family:'JetBrains Mono', monospace; font-size:9.5px; letter-spacing:.22em;
  text-transform:uppercase; text-decoration:none; color:#d6275a;
  background:linear-gradient(180deg, rgba(255,255,255,.94), rgba(255,236,244,.82));
  border:1px solid rgba(214,39,90,.22); border-bottom:none;
  box-shadow:0 -7px 18px -12px rgba(214,39,90,.55), inset 0 1px 0 rgba(255,255,255,.92);
  transform:translateY(-100%);
  animation:v1PopTab .55s cubic-bezier(.2,.9,.25,1) both;
  transition:transform .25s ease, box-shadow .25s ease, color .25s ease, border-color .25s ease;
}
.v1-buyTab:hover { transform:translateY(calc(-100% - 3px)); color:#b81f49; border-color:rgba(214,39,90,.4); box-shadow:0 -10px 22px -12px rgba(214,39,90,.6), inset 0 1px 0 rgba(255,255,255,.95); }
@media (prefers-reduced-motion: reduce) { .v1-buyTab { animation:none; } }

/* ---------- marquee for long titles ---------- */
.v1-marquee { display:block; overflow:hidden; white-space:nowrap; max-width:100%; }
.v1-marquee.is-overflow { mask-image:linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%); -webkit-mask-image:linear-gradient(90deg, transparent 0, #000 8%, #000 92%, transparent 100%); }
.v1-marquee-track { display:inline-block; padding-left:0; }
.v1-marquee.is-overflow .v1-marquee-track { animation: v1MarqueeSlide 14s ease-in-out infinite alternate; }
@keyframes v1MarqueeSlide {
  0%   { transform:translateX(0); }
  20%  { transform:translateX(0); }
  80%  { transform:translateX(var(--marquee-shift, 0)); }
  100% { transform:translateX(var(--marquee-shift, 0)); }
}
`;

const v1Styles = {
  root: {
    position:"relative", width:"100%", height:"100%", overflow:"hidden",
    fontFamily:"'Inter', system-ui, sans-serif", color:"#2a2548",
    background:"#fce6d4",
  },
  sky: {
    position:"absolute", inset:0,
    background: `
      radial-gradient(ellipse 90% 60% at 78% 18%, rgba(255,236,210,1) 0%, rgba(255,236,210,0) 60%),
      radial-gradient(ellipse 70% 50% at 22% 30%, rgba(212,196,255,.8) 0%, rgba(212,196,255,0) 65%),
      linear-gradient(180deg,
        #f8c8e0 0%,
        #f4d0c4 18%,
        #f7dac8 32%,
        #e6d2f0 50%,
        #c8d8f0 68%,
        #b8e0d8 82%,
        #c4e8c4 100%)`,
  },
  sun: {
    position:"absolute", top:"14%", right:"18%",
    width:120, height:120, borderRadius:"50%",
    background:"radial-gradient(circle at 35% 30%, #fffaf0 0%, #ffe8c4 35%, #ffc89c 70%, #ff9c8a 100%)",
    boxShadow:"0 0 80px 20px rgba(255,200,156,.45)",
  },
  sunGlow: {
    position:"absolute", top:"14%", right:"18%",
    width:120, height:120, borderRadius:"50%",
    boxShadow:"0 0 200px 80px rgba(255,210,180,.4)",
    pointerEvents:"none",
  },
  cloud: {
    position:"absolute", width:200, height:60,
    background:"radial-gradient(ellipse, rgba(255,255,255,.85) 0%, rgba(255,255,255,0) 65%)",
    filter:"blur(8px)",
    animation: "drift linear infinite",
    willChange:"transform",
  },
  hills: { position:"absolute", bottom:120, left:0, width:"100%", height:240, zIndex:2 },
  header: {
    position:"relative", zIndex:5,
    padding:"24px 40px",
    display:"flex", justifyContent:"space-between", alignItems:"center",
  },
  brandRow: { display:"flex", gap:14, alignItems:"center" },
  brandDot: { color:"#d65a8a", fontSize:14, filter:"drop-shadow(0 0 6px rgba(214,90,138,.6))" },
  brandMono: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:11, letterSpacing:".22em",
    textTransform:"uppercase", color:"#3a3556",
  },
  nav: { display:"flex", gap:24 },
  navLink: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:11, letterSpacing:".22em",
    textTransform:"uppercase", color:"#3a3556", textDecoration:"none",
    padding:"6px 12px",
    borderRadius:99, border:"1px solid rgba(58,53,86,.2)",
    background:"rgba(255,255,255,.4)", backdropFilter:"blur(8px)",
  },
  main: {
    position:"relative", zIndex:5,
    display:"grid", gridTemplateColumns:"1.2fr 1fr",
    alignItems:"center", gap:60,
    padding:"40px 60px 200px",
    height:"calc(100% - 200px)",
  },
  heroBlock: { },
  eyebrow: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:11,
    letterSpacing:".3em", textTransform:"uppercase",
    color:"#7a4a8c", marginBottom:18,
  },
  wordmark: {
    margin:0, lineHeight:.86,
    fontFamily:"'Cormorant Garamond', 'EB Garamond', Georgia, serif",
    fontWeight:300, color:"#2a2548",
    letterSpacing:"-.04em",
  },
  wmLine1: {
    display:"block", fontSize:"clamp(72px, 10vw, 148px)",
    fontStyle:"italic",
    background:"linear-gradient(180deg, #2a2548 0%, #5a4a8a 50%, #d68aa8 100%)",
    WebkitBackgroundClip:"text", WebkitTextFillColor:"transparent",
    textShadow:"0 1px 0 rgba(255,255,255,.4)",
  },
  wmLine2: { display:"flex", alignItems:"baseline", gap:18, marginTop:6 },
  wmEm: {
    fontSize:"clamp(60px, 8vw, 120px)", fontWeight:600, fontStyle:"italic",
    color:"#d65a8a",
    textShadow:"0 0 30px rgba(214,90,138,.4)",
  },
  wmDot: { fontSize:"clamp(40px, 6vw, 80px)", color:"#7a4a8c", opacity:.5 },
  wmYear: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:14, letterSpacing:".3em",
    color:"#7a4a8c", alignSelf:"center", marginLeft:6,
  },
  manifesto: {
    marginTop:32, fontSize:17, lineHeight:1.65, maxWidth:480,
    color:"#3a3556", fontFamily:"'Cormorant Garamond', Georgia, serif",
    fontStyle:"italic", fontWeight:400,
  },
  manifestoEm: { color:"#d65a8a", fontWeight:600, fontStyle:"normal", fontFamily:"'Inter', sans-serif", fontSize:14, letterSpacing:".05em", textTransform:"uppercase" },
  player: {
    position:"relative", padding:2, borderRadius:24,
    background:"linear-gradient(145deg, rgba(255,255,255,.9), rgba(255,255,255,.2))",
    boxShadow:"0 30px 80px -20px rgba(80,40,120,.35), 0 8px 24px -8px rgba(80,40,120,.3), inset 0 1px 0 rgba(255,255,255,.9)",
    backdropFilter:"blur(20px)",
  },
  playerInner: {
    background:"linear-gradient(160deg, rgba(255,255,255,.65), rgba(248,232,250,.55))",
    border:"1px solid rgba(255,255,255,.5)",
    borderRadius:22, padding:22, position:"relative",
    display:"grid", gridTemplateColumns:"160px 1fr", gap:22,
  },
  artBtn: {
    border:"none", padding:0, background:"transparent", cursor:"pointer",
    width:160, height:160, borderRadius:14, position:"relative",
  },
  art: {
    width:"100%", height:"100%", borderRadius:14, overflow:"hidden",
    backgroundSize:"cover", backgroundPosition:"center",
    boxShadow:"0 14px 30px -8px rgba(80,40,120,.4), inset 0 0 0 1px rgba(255,255,255,.4)",
    position:"relative", transition:"transform .3s ease",
  },
  artGloss: {
    position:"absolute", inset:0, borderRadius:14, pointerEvents:"none",
    background:"linear-gradient(160deg, rgba(255,255,255,.5) 0%, rgba(255,255,255,0) 35%, rgba(255,255,255,0) 75%, rgba(255,255,255,.15) 100%)",
  },
  playOverlay: {
    position:"absolute", inset:0, display:"flex", alignItems:"center", justifyContent:"center",
    background:"radial-gradient(circle at center, rgba(0,0,0,.35), rgba(0,0,0,.55))",
    borderRadius:14, transition:"opacity .25s ease",
  },
  meta: { display:"flex", flexDirection:"column", justifyContent:"center", minWidth:0, position:"relative" },
  metaLabel: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:10, letterSpacing:".3em",
    color:"#d65a8a", textTransform:"uppercase", marginBottom:6,
  },
  title: {
    fontFamily:"'Cormorant Garamond', Georgia, serif",
    fontSize:28, lineHeight:1.1, fontStyle:"italic",
    color:"#2a2548", marginBottom:4,
    width:"100%",
  },
  artist: { fontSize:14, color:"#3a3556", letterSpacing:".02em", marginBottom:2 },
  album: { fontSize:11, color:"#7a4a8c", marginBottom:10, opacity:.85, fontFamily:"'Cormorant Garamond', serif" },
  viz: { display:"flex", gap:2, alignItems:"flex-end", height:42, marginTop:8, marginBottom:10 },
  vizBar: {
    flex:1, minHeight:3,
    background:"linear-gradient(to top, #d65a8a, #c690d8 50%, #8aaadd 100%)",
    borderRadius:1, transition:"height .08s linear",
  },
  progress: {
    height:4, borderRadius:99,
    background:"rgba(58,53,86,.15)", overflow:"hidden",
  },
  progressFill: {
    height:"100%",
    background:"linear-gradient(90deg, #d65a8a, #c690d8 50%, #8aaadd)",
    transition:"width .5s linear",
  },
  progRow: {
    display:"flex", justifyContent:"space-between", marginTop:6,
    fontFamily:"'JetBrains Mono', monospace", fontSize:10, letterSpacing:".15em",
    color:"#3a3556", textTransform:"uppercase",
  },
  footer: {
    position:"absolute", bottom:0, left:0, right:0, zIndex:6,
    paddingBottom:6,
  },
  tickerWrap: {
    overflow:"hidden", padding:"6px 0",
    background:"rgba(255,255,255,.35)", backdropFilter:"blur(10px)",
    borderTop:"1px solid rgba(58,53,86,.15)",
    borderBottom:"1px solid rgba(58,53,86,.15)",
  },
  socials: {
    display:"flex", gap:18, justifyContent:"center", padding:"8px 0",
  },
  social: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:10, letterSpacing:".25em",
    color:"#3a3556", textDecoration:"none", textTransform:"uppercase",
  },
  grassLayer: {
    position:"absolute", bottom:0, left:0, right:0, height:140,
    pointerEvents:"none", zIndex:7,
  },
  flare: {
    position:"absolute", top:"14%", right:"18%",
    width:300, height:300, borderRadius:"50%",
    background:"radial-gradient(circle, rgba(255,240,200,.4) 0%, rgba(255,200,170,.15) 35%, transparent 65%)",
    transform:"translate(50%, -50%)", pointerEvents:"none", zIndex:3,
    mixBlendMode:"screen",
  },
  panelBackdrop: {
    position:"absolute", inset:0, zIndex:20,
    background:"radial-gradient(ellipse at center, rgba(80,40,120,.25), rgba(40,20,60,.55))",
    backdropFilter:"blur(8px)",
    display:"flex", alignItems:"center", justifyContent:"center",
    animation:"fadein .25s ease",
  },
  panelCard: {
    position:"relative", width:"min(620px, 86%)", maxHeight:"82%",
    overflow:"auto",
    padding:2, borderRadius:24,
    background:"linear-gradient(145deg, rgba(255,255,255,.95), rgba(255,255,255,.4))",
    boxShadow:"0 30px 80px -20px rgba(40,20,60,.55), 0 12px 24px -8px rgba(40,20,60,.4), inset 0 1px 0 rgba(255,255,255,.95)",
  },
  panelClose: {
    position:"absolute", top:14, right:18, zIndex:2,
    width:32, height:32, borderRadius:"50%",
    border:"1px solid rgba(58,53,86,.25)",
    background:"linear-gradient(180deg, rgba(255,255,255,.9), rgba(255,255,255,.5))",
    color:"#3a3556", fontSize:20, lineHeight:"28px", cursor:"pointer",
    fontFamily:"'Inter', sans-serif",
  },
  panelEyebrow: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:11,
    letterSpacing:".3em", textTransform:"uppercase",
    color:"#7a4a8c", padding:"22px 30px 4px",
  },
  panelTitle: {
    margin:"0 30px 18px",
    fontFamily:"'Cormorant Garamond', Georgia, serif",
    fontSize:42, lineHeight:1.05, fontWeight:300, color:"#2a2548",
    letterSpacing:"-.02em",
  },
  panelBody: {
    padding:"0 30px 26px",
    fontFamily:"'Cormorant Garamond', Georgia, serif",
    fontSize:17, lineHeight:1.55, color:"#2a2548",
  },
  bigEmail: {
    display:"block", fontFamily:"'Cormorant Garamond', serif",
    fontSize:38, fontStyle:"italic", color:"#d65a8a",
    textDecoration:"none", margin:"14px 0",
    letterSpacing:"-.01em",
    textShadow:"0 0 30px rgba(214,90,138,.25)",
  },
  signoff: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:11,
    letterSpacing:".18em", color:"#7a4a8c", marginTop:24,
    textTransform:"uppercase", lineHeight:1.9,
  },
  panelMeta: {
    display:"flex", flexDirection:"column", gap:4, marginTop:18,
    fontFamily:"'JetBrains Mono', monospace", fontSize:10,
    letterSpacing:".22em", color:"#7a4a8c", textTransform:"uppercase",
  },

  // ----- streams panel rows -----
  streamList: {
    display:"flex", flexDirection:"column", gap:12,
    marginTop:20,
  },
  streamRow: {
    padding:"14px 16px", borderRadius:14,
    background:"linear-gradient(160deg, rgba(255,255,255,.7), rgba(248,232,250,.5))",
    border:"1px solid rgba(255,255,255,.6)",
    boxShadow:"0 6px 18px -10px rgba(80,40,120,.35), inset 0 1px 0 rgba(255,255,255,.8)",
  },
  streamHead: {
    display:"flex", alignItems:"baseline", justifyContent:"space-between",
    gap:12, marginBottom:8,
  },
  streamLabel: {
    fontFamily:"'Cormorant Garamond', Georgia, serif",
    fontStyle:"italic", fontSize:24, color:"#d65a8a",
    letterSpacing:"-.01em", lineHeight:1,
  },
  streamSub: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:9,
    letterSpacing:".22em", color:"#7a4a8c", textTransform:"uppercase",
    textAlign:"right",
  },
  streamUrlRow: {
    display:"flex", alignItems:"center", gap:10, minWidth:0,
  },
  streamUrl: {
    flex:1, minWidth:0,
    fontFamily:"'JetBrains Mono', monospace", fontSize:11,
    letterSpacing:".04em", color:"#3a3556",
    textDecoration:"none",
    padding:"8px 10px", borderRadius:8,
    background:"rgba(58,53,86,.06)",
    border:"1px dashed rgba(58,53,86,.18)",
    overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap",
  },
  streamCopy: {
    flexShrink:0,
    fontFamily:"'JetBrains Mono', monospace", fontSize:10,
    letterSpacing:".22em", textTransform:"uppercase",
    color:"#3a3556", cursor:"pointer",
    padding:"8px 12px", borderRadius:99,
    border:"1px solid rgba(58,53,86,.25)",
    background:"linear-gradient(180deg, rgba(255,255,255,.9), rgba(255,255,255,.5))",
    transition:"all .18s ease",
  },

  // ----- Live DJ banner (rendered above footer when a streamer is on) -----
  liveBanner: {
    position:"absolute", top:78, left:"50%", transform:"translateX(-50%)",
    zIndex:8,
    display:"flex", alignItems:"center", gap:14,
    padding:"8px 18px", borderRadius:99,
    background:"linear-gradient(135deg, rgba(214,39,90,.95), rgba(255,90,138,.85))",
    boxShadow:"0 10px 30px -10px rgba(214,39,90,.6), 0 4px 10px -4px rgba(214,39,90,.5), inset 0 1px 0 rgba(255,255,255,.45)",
    color:"#fff8fa",
    fontFamily:"'JetBrains Mono', monospace", fontSize:11,
    letterSpacing:".22em", textTransform:"uppercase",
    border:"1px solid rgba(255,255,255,.35)",
    backdropFilter:"blur(8px)",
  },
  liveDot: {
    width:9, height:9, borderRadius:"50%", background:"#fff",
    boxShadow:"0 0 12px 2px rgba(255,255,255,.85), 0 0 0 3px rgba(255,255,255,.2)",
    animation:"livePulse 1.2s ease-in-out infinite",
    flexShrink:0,
  },
  liveLabel: { fontWeight:700 },
  liveStreamer: { color:"#fff", textTransform:"none", fontFamily:"'Cormorant Garamond', Georgia, serif", fontStyle:"italic", fontSize:18, letterSpacing:0, padding:"0 4px" },
  liveTail: { opacity:.8, fontSize:10 },

  // ----- Volume slider -----
  volumeRow: {
    display:"flex", alignItems:"center", gap:10, marginTop:14,
    paddingTop:12, borderTop:"1px dashed rgba(58,53,86,.18)",
  },
  volumeIcon: { display:"flex", alignItems:"center", justifyContent:"center", width:18, height:18, flexShrink:0 },
  volumePct: {
    fontFamily:"'JetBrains Mono', monospace", fontSize:10,
    letterSpacing:".15em", color:"#7a4a8c", minWidth:24, textAlign:"right",
  },
};

window.V1 = V1;
