// Host screens — adapted from the design's host path, extended with playlist
// + device picker + live game controls.

(() => {
const R = window.R;
const { Tag, Btn, Overprint, RisoStamp, RisoConfetti, Screen, RisoBars } = window;

function HostStart({ onCreate, onResume, savedRoom, configError, hostPlays, setHostPlays, hostName, setHostName, mode, setMode, fullAccess }) {
  const isSpotifyMode = mode === 'spotify';
  const canCreate = (!isSpotifyMode || !configError) && (!hostPlays || (hostName || '').trim().length >= 2);
  // fullAccess === false → server confirmed user is not on the allowlist. Hide
  // Spotify mode entirely. null/undefined = unknown (no token yet); we still
  // show the option since the user might be allowlisted but hasn't connected.
  const showSpotifyMode = fullAccess !== false;
  return (
    <Screen>
      <RisoStamp size={300} color={R.pink} top={-80} left={-100} duration={28} />
      <RisoStamp size={240} color={R.blue} top={420} left={140} duration={22} delay={1} />

      <div style={{ padding: '8px 22px', display: 'flex', justifyContent: 'space-between' }}>
        <Tag color={R.ink}>Hitfothit · Host</Tag>
        <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.14em' }}>EDITION 04</span>
      </div>

      <div style={{ padding: '24px 22px 0', position: 'relative', zIndex: 2 }}>
        <Overprint sizes={56}>HOST<br/>THE<br/>NIGHT.</Overprint>
        <div style={{ marginTop: 12, fontSize: 12, lineHeight: 1.45, maxWidth: 320, fontWeight: 500 }}>
          You bring the playlist. Your friends guess release years. Closest wins.
        </div>
      </div>

      {isSpotifyMode && configError && (
        <div style={{ margin: '14px 22px', padding: 12, background: R.pink, color: R.paper, fontSize: 11, fontWeight: 700, lineHeight: 1.45, border: `2px solid ${R.ink}` }}>
          {configError}
        </div>
      )}

      <div style={{ padding: '14px 22px 0', position: 'relative', zIndex: 2 }}>
        <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.2em', color: R.muted, marginBottom: 6 }}>MUSIC MODE</div>
        <div style={{ display: 'flex', gap: 8 }}>
          <ModeBox
            picked={mode === 'preview'}
            onClick={() => setMode('preview')}
            title="FREE · 30s"
            desc="Spotify preview clips. No login, no Premium, anyone can host."
            color={R.green}
          />
          {showSpotifyMode && (
            <ModeBox
              picked={mode === 'spotify'}
              onClick={() => setMode('spotify')}
              title="SPOTIFY · FULL"
              desc="Full songs via Connect. Need Premium + dev-app access."
              color={R.blue}
            />
          )}
        </div>
      </div>

      <div style={{ padding: '14px 22px 0', position: 'relative', zIndex: 2 }}>
        <label style={{
          display: 'flex', alignItems: 'center', gap: 12, padding: '12px 14px',
          background: hostPlays ? R.yellow : R.paper2,
          border: `2px solid ${R.ink}`, boxShadow: `3px 3px 0 ${R.ink}`,
          cursor: 'pointer',
        }}>
          <span style={{
            width: 22, height: 22, border: `2px solid ${R.ink}`, flexShrink: 0,
            background: hostPlays ? R.ink : 'transparent',
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            color: R.paper, fontWeight: 900, fontSize: 14,
          }}>{hostPlays ? '✓' : ''}</span>
          <input type="checkbox" checked={hostPlays} onChange={(e) => setHostPlays(e.target.checked)}
            style={{ position: 'absolute', opacity: 0, pointerEvents: 'none' }} />
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 14, fontWeight: 800, letterSpacing: '.02em' }}>I'M PLAYING TOO</div>
            <div style={{ fontSize: 11, fontWeight: 600, color: R.muted, marginTop: 2 }}>
              You'll be hidden the song info and guess like everyone else.
            </div>
          </div>
        </label>

        {hostPlays && (
          <div style={{ marginTop: 10 }}>
            <input
              value={hostName || ''}
              onChange={(e) => setHostName(e.target.value.slice(0, 12))}
              placeholder="Your name"
              autoCorrect="off"
              autoCapitalize="words"
              style={{
                width: '100%', padding: '14px',
                fontSize: 18, fontWeight: 800, color: R.ink,
                background: R.paper2,
                border: `2px solid ${R.ink}`,
                boxShadow: `3px 3px 0 ${R.ink}`,
                outline: 'none', letterSpacing: '-.01em',
              }}
            />
          </div>
        )}
      </div>

      <div style={{ flex: 1 }} />

      <div style={{ padding: '0 22px', position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column', gap: 10 }}>
        <Btn bg={R.pink} onClick={onCreate} disabled={!canCreate}>CREATE A ROOM →</Btn>
        {savedRoom && (
          <Btn bg={R.paper2} onClick={onResume}>RESUME · {savedRoom}</Btn>
        )}
        <a href="/" style={{ textDecoration: 'none' }}>
          <Btn bg={R.paper2}>I'M JOINING INSTEAD</Btn>
        </a>
      </div>
    </Screen>
  );
}

function ModeBox({ picked, onClick, title, desc, color }) {
  return (
    <div onClick={onClick} style={{
      flex: 1, padding: 12, cursor: 'pointer',
      background: picked ? color : R.paper2,
      border: `2px solid ${R.ink}`,
      boxShadow: picked ? `4px 4px 0 ${R.ink}` : `2px 2px 0 ${R.ink}`,
      transform: picked ? 'translate(-1px,-1px)' : 'translate(0,0)',
      transition: 'all .12s',
    }}>
      <div style={{ fontSize: 13, fontWeight: 900, letterSpacing: '.04em' }}>{title}</div>
      <div style={{ fontSize: 11, fontWeight: 600, marginTop: 4, lineHeight: 1.35 }}>{desc}</div>
    </div>
  );
}

function HostSpotify({ onConnect, busy, error }) {
  return (
    <Screen>
      <div style={{ padding: '8px 22px', display: 'flex', justifyContent: 'space-between' }}>
        <Tag color={R.blue}>Host · 01 of 03</Tag>
      </div>
      <div style={{ padding: '20px 22px', flex: 1, display: 'flex', flexDirection: 'column', gap: 18 }}>
        <Overprint sizes={56}>HOOK UP<br/>SPOTIFY.</Overprint>
        <div style={{ fontSize: 14, color: R.muted, fontWeight: 600, lineHeight: 1.5 }}>
          You'll get bounced to Spotify to log in. We need a Premium account to control playback.
        </div>

        {error && (
          <div style={{ padding: 12, background: R.pink, color: R.paper, fontSize: 12, fontWeight: 700, border: `2px solid ${R.ink}` }}>
            {error}
          </div>
        )}

        <div style={{ flex: 1 }} />

        <Btn bg={busy ? R.paper3 : R.green} onClick={onConnect} disabled={busy}>
          {busy ? 'CONNECTING…' : 'CONNECT SPOTIFY →'}
        </Btn>
      </div>
    </Screen>
  );
}

function HostPlaylist({ onPick, onPickCurated, curatedPresets, busy, error, onReauth }) {
  const [val, setVal] = React.useState('');
  const pastedId = window.Spotify.parsePlaylistId(val);

  return (
    <Screen>
      <div style={{ padding: '8px 22px', display: 'flex', justifyContent: 'space-between' }}>
        <Tag color={R.blue}>Host · 02 of 03</Tag>
        {onReauth && (
          <span style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.14em', cursor: 'pointer', color: R.muted }} onClick={onReauth}>
            ↻ RECONNECT
          </span>
        )}
      </div>
      <div style={{ padding: '20px 22px', flex: 1, display: 'flex', flexDirection: 'column', gap: 14, overflow: 'auto' }}>
        <Overprint sizes={48}>PICK A<br/>PLAYLIST.</Overprint>

        {error && (
          <div style={{ padding: 12, background: R.pink, color: R.paper, fontSize: 12, fontWeight: 700, lineHeight: 1.5, border: `2px solid ${R.ink}` }}>{error}</div>
        )}

        {(curatedPresets || []).length > 0 && (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.2em', color: R.muted }}>CURATED · YEARS VERIFIED</div>
            {curatedPresets.map((p) => (
              <div key={p.slug} onClick={() => !busy && onPickCurated(p.slug)} style={{
                padding: 12, cursor: busy ? 'wait' : 'pointer',
                background: R.yellow,
                border: `2px solid ${R.ink}`,
                boxShadow: `3px 3px 0 ${R.ink}`,
                fontSize: 14, fontWeight: 800,
                display: 'flex', alignItems: 'center', gap: 8,
              }}>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{p.name}</div>
                  <div style={{ fontSize: 10, fontWeight: 700, color: R.muted, letterSpacing: '.1em', marginTop: 2 }}>{p.trackCount} TRACKS</div>
                </div>
                <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.14em', color: R.muted }}>TAP TO LOAD →</span>
              </div>
            ))}
          </div>
        )}

        <div>
          <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.2em', color: R.muted, marginBottom: 6 }}>OR PASTE ANY SPOTIFY PLAYLIST</div>
          <input
            value={val}
            onChange={(e) => setVal(e.target.value)}
            placeholder="https://open.spotify.com/playlist/..."
            autoCorrect="off"
            autoCapitalize="off"
            style={{
              width: '100%', padding: '12px',
              fontSize: 13, fontWeight: 600, color: R.ink,
              background: R.paper2,
              border: `2px solid ${R.ink}`,
              boxShadow: `3px 3px 0 ${R.ink}`,
              outline: 'none', letterSpacing: '0',
            }}
          />
          <div style={{ marginTop: 6, fontSize: 10, color: R.muted, fontWeight: 700, letterSpacing: '.1em' }}>
            {pastedId ? `ID: ${pastedId}` : 'Any public Spotify playlist works.'}
          </div>
        </div>

        <div style={{ padding: 10, background: R.yellow, border: `2px solid ${R.ink}`, fontSize: 11, fontWeight: 700, lineHeight: 1.4 }}>
          ⚠ Years come from Spotify album metadata. Accurate for studio albums, off for compilations / remasters / "Greatest Hits". For game-grade years, pick a curated playlist above.
        </div>

        <div style={{ flex: 1, minHeight: 8 }} />

        <Btn bg={pastedId ? R.pink : R.paper3} fg={R.paper} disabled={!pastedId || busy} onClick={() => pastedId && onPick([pastedId])}>
          {busy ? 'LOADING TRACKS…' : 'LOAD FROM LINK →'}
        </Btn>
      </div>
    </Screen>
  );
}

function HostDevice({ devices, picked, onPick, onRefresh, refreshing, onContinue }) {
  // Auto-poll devices while no target is chosen. Spotify only exposes a
  // device after the user opens Spotify *and* plays something briefly.
  // 6s cadence — gentle on the API, fast enough to feel automatic.
  React.useEffect(() => {
    if (picked) return;
    const id = setInterval(() => { onRefresh(); }, 6000);
    return () => clearInterval(id);
  }, [picked, onRefresh]);

  const hasPhone = devices.some(d => /smartphone|tablet/i.test(d.type || ''));

  return (
    <Screen>
      <div style={{ padding: '8px 22px', display: 'flex', justifyContent: 'space-between' }}>
        <Tag color={R.blue}>Host · 03 of 03</Tag>
      </div>
      <div style={{ padding: '20px 22px', flex: 1, display: 'flex', flexDirection: 'column', gap: 14 }}>
        <Overprint sizes={48}>WHERE<br/>SHOULD IT<br/>PLAY?</Overprint>

        <div style={{ fontSize: 13, color: R.muted, fontWeight: 600, lineHeight: 1.5 }}>
          Pick a Spotify device — that's where the music plays. List refreshes itself.
        </div>

        {!picked && !hasPhone && (
          <div style={{ padding: 14, background: R.yellow, border: `2px solid ${R.ink}`, fontSize: 13, fontWeight: 700, lineHeight: 1.5 }}>
            <div style={{ fontWeight: 900, letterSpacing: '.04em', marginBottom: 6 }}>
              📱 To play on your phone
            </div>
            <div style={{ fontWeight: 700 }}>
              Open <b>Spotify</b> on the phone → hit ▶ on any song for a sec → it shows up here. Same trick for speakers / TVs.
            </div>
          </div>
        )}

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          {devices.map((d) => {
            const active = picked === d.id;
            return (
              <div key={d.id} onClick={() => onPick(d.id)} style={{
                padding: 12, cursor: 'pointer',
                background: active ? R.green : (d.is_active ? R.paper2 : R.paper),
                border: `2px solid ${R.ink}`,
                boxShadow: active ? `4px 4px 0 ${R.ink}` : `2px 2px 0 ${R.ink}`,
                transform: active ? 'translate(-1px,-1px)' : 'translate(0,0)',
                display: 'flex', alignItems: 'center', gap: 10,
              }}>
                <div style={{
                  width: 14, height: 14, border: `2px solid ${R.ink}`,
                  background: active ? R.ink : 'transparent',
                }} />
                <div style={{ flex: 1 }}>
                  <div style={{ fontSize: 14, fontWeight: 800 }}>{d.name}</div>
                  <div style={{ fontSize: 10, fontWeight: 700, color: R.muted, letterSpacing: '.1em', marginTop: 2 }}>
                    {d.type?.toUpperCase()}{d.is_active ? ' · ACTIVE' : ''}
                  </div>
                </div>
              </div>
            );
          })}
        </div>

        <div style={{ flex: 1 }} />

        <Btn big={false} bg={R.paper2} onClick={onRefresh} disabled={refreshing}>
          {refreshing ? 'CHECKING…' : '↻ REFRESH DEVICES'}
        </Btn>
        <Btn bg={picked ? R.pink : R.paper3} fg={R.paper} disabled={!picked} onClick={onContinue}>
          OPEN ROOM →
        </Btn>
      </div>
    </Screen>
  );
}

// Round-timer presets surfaced in the lobby. null = no timer (manual reveal only).
// Spotify-Free preview mode plays a 30-second clip, so anything above 30s
// just runs out mid-round — those options get filtered out below when
// mode==='preview'. Spotify-Premium (Connect) mode plays full songs so it
// gets the longer options.
const TIMER_OPTIONS = [
  { sec: null, label: 'OFF' },
  { sec: 15, label: '15s' },
  { sec: 20, label: '20s' },
  { sec: 30, label: '30s' },
  { sec: 45, label: '45s', requiresFull: true },
  { sec: 60, label: '60s', requiresFull: true },
  { sec: 90, label: '90s', requiresFull: true },
];

// Scoring schemes — small groups feel rewarded with CLOSEST, bigger groups
// stay engaged with PODIUM (top 3) or BANDED (everyone within 5 years scores).
const SCORING_OPTIONS = [
  { rule: 'closest', label: 'CLOSEST',  blurb: 'Exact = 2 · Closest = 1.  Best 2–4 players.' },
  { rule: 'podium',  label: '3·2·1',    blurb: 'Top 3 distances earn 3, 2, 1.  Ties share rank.' },
  { rule: 'banded',  label: 'BANDED',   blurb: 'Exact 5 · ≤1y 3 · ≤3y 2 · ≤5y 1.  Big groups.' },
];

function HostRoom({
  code, players, joinUrl, onStart, onEnd, trackPoolCount, usedCount,
  previewablePoolCount, previewableUsedCount,
  mode, pickedDevice, devices, onChangeDevice,
  playlistName, onChangePlaylist,
  roundTimerSec, onChangeRoundTimer,
  scoringRule, onChangeScoringRule,
}) {
  const isPreview = mode === 'preview';
  // In preview mode "tracks remaining" must mean tracks that actually have a
  // 30-sec clip — Spotify Free hosts hit "Out of tracks with previews" when
  // we counted everything indiscriminately.
  const totalForUI     = isPreview ? (previewablePoolCount ?? trackPoolCount ?? 0) : (trackPoolCount ?? 0);
  const usedForUI      = isPreview ? (previewableUsedCount ?? usedCount ?? 0)      : (usedCount ?? 0);
  const remaining      = Math.max(0, totalForUI - usedForUI);
  const totalAllTracks = trackPoolCount ?? 0;
  const deviceName = devices?.find(d => d.id === pickedDevice)?.name;
  return (
    <Screen>
      <div style={{ padding: '8px 22px', display: 'flex', justifyContent: 'space-between' }}>
        <Tag color={R.blue}>Lobby</Tag>
        <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.14em' }}>{players.length} JOINED</span>
      </div>

      <div style={{ padding: '12px 22px', flex: 1, display: 'flex', flexDirection: 'column', gap: 12, overflow: 'auto' }}>
        <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.2em' }}>CODE</div>
        <div style={{ position: 'relative', height: 130 }}>
          <div style={{
            position: 'absolute', left: -3, top: 4, fontSize: 130, fontWeight: 900,
            color: R.pink, lineHeight: .9, letterSpacing: '-.05em',
            mixBlendMode: 'multiply', opacity: .9, fontFamily: 'ui-monospace, monospace',
          }}>{code}</div>
          <div style={{
            fontSize: 130, fontWeight: 900, color: R.blue,
            lineHeight: .9, letterSpacing: '-.05em',
            mixBlendMode: 'multiply', opacity: .9, fontFamily: 'ui-monospace, monospace',
          }}>{code}</div>
        </div>

        <QRBlock url={joinUrl} />

        <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginTop: 4 }}>
          <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.2em' }}>PLAYERS</div>
          {players.length === 0 && (
            <div style={{ padding: 12, fontSize: 13, fontWeight: 700, color: R.muted }}>Nobody in yet — share the code.</div>
          )}
          <div style={{ display: 'flex', flexDirection: 'column' }}>
            {players.map((p, i) => (
              <div key={p.id} style={{
                display: 'flex', alignItems: 'center', gap: 10,
                padding: '10px 12px',
                background: R.paper2,
                border: `2px solid ${R.ink}`,
                marginTop: i === 0 ? 0 : -2,
                animation: 'rpopin .35s',
              }}>
                <div style={{ width: 22, height: 22, background: p.color, border: `2px solid ${R.ink}` }} />
                <span style={{ flex: 1, fontSize: 14, fontWeight: 700 }}>{p.name}</span>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div style={{ padding: '0 22px', display: 'flex', flexDirection: 'column', gap: 8 }}>
        {/* Active playlist — tap to swap before starting. Hidden if nothing loaded yet. */}
        {playlistName && onChangePlaylist && (
          <div onClick={onChangePlaylist} style={{
            padding: '8px 10px', background: R.paper2,
            border: `2px solid ${R.ink}`, cursor: 'pointer',
            display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8,
          }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ letterSpacing: '.18em', color: R.muted, fontSize: 9, fontWeight: 800 }}>PLAYLIST</div>
              <div style={{
                fontSize: 13, fontWeight: 800, marginTop: 1,
                whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',
              }}>{playlistName}</div>
            </div>
            <span style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.14em', color: R.muted }}>CHANGE →</span>
          </div>
        )}

        {/* Scoring scheme picker. Persists across rounds for this room. */}
        {onChangeScoringRule && (() => {
          const active = SCORING_OPTIONS.find((o) => o.rule === scoringRule) || SCORING_OPTIONS[0];
          return (
            <div style={{
              padding: '8px 10px', background: R.paper2,
              border: `2px solid ${R.ink}`,
            }}>
              <div style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                fontSize: 9, fontWeight: 800, letterSpacing: '.18em', color: R.muted,
              }}>
                <span>SCORING</span>
                <span style={{ color: R.ink }}>{active.label}</span>
              </div>
              <div style={{ display: 'flex', gap: 4, marginTop: 6 }}>
                {SCORING_OPTIONS.map((opt) => {
                  const on = active.rule === opt.rule;
                  return (
                    <span key={opt.rule} onClick={() => onChangeScoringRule(opt.rule)} style={{
                      flex: 1, padding: '6px 0', textAlign: 'center', cursor: 'pointer',
                      background: on ? R.ink : R.paper, color: on ? R.paper : R.ink,
                      border: `2px solid ${R.ink}`,
                      fontSize: 11, fontWeight: 900, letterSpacing: '.06em',
                    }}>{opt.label}</span>
                  );
                })}
              </div>
              <div style={{ marginTop: 6, fontSize: 10, fontWeight: 700, color: R.muted, lineHeight: 1.35 }}>
                {active.blurb}
              </div>
            </div>
          );
        })()}

        {/* Round timer presets. null = no timer = manual reveal. Persists across rounds. */}
        {onChangeRoundTimer && (() => {
          // Spotify Free preview clips are 30s — hide longer options in
          // preview mode so hosts can't pick a timer the audio can't reach.
          const opts = TIMER_OPTIONS.filter((o) => !(isPreview && o.requiresFull));
          const overCap = isPreview && roundTimerSec && roundTimerSec > 30;
          return (
            <div style={{
              padding: '8px 10px', background: R.paper2,
              border: `2px solid ${R.ink}`,
            }}>
              <div style={{
                display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                fontSize: 9, fontWeight: 800, letterSpacing: '.18em', color: R.muted,
              }}>
                <span>ROUND TIMER{isPreview ? ' · MAX 30s (PREVIEW)' : ''}</span>
                <span style={{ color: R.ink }}>{roundTimerSec ? `${roundTimerSec}s` : 'OFF'}</span>
              </div>
              <div style={{ display: 'flex', gap: 4, marginTop: 6 }}>
                {opts.map((opt) => {
                  const active = (roundTimerSec || null) === opt.sec;
                  return (
                    <span key={opt.label} onClick={() => onChangeRoundTimer(opt.sec)} style={{
                      flex: 1, padding: '6px 0', textAlign: 'center', cursor: 'pointer',
                      background: active ? R.ink : R.paper, color: active ? R.paper : R.ink,
                      border: `2px solid ${R.ink}`,
                      fontSize: 11, fontWeight: 900, letterSpacing: '.1em',
                    }}>{opt.label}</span>
                  );
                })}
              </div>
              {overCap && (
                <div style={{ marginTop: 6, fontSize: 10, fontWeight: 700, color: R.pink }}>
                  ⚠ {roundTimerSec}s is longer than the 30s preview clip — pick 30s or shorter.
                </div>
              )}
            </div>
          );
        })()}

        {isPreview ? (
          <div style={{
            padding: '8px 10px', background: R.paper2,
            border: `2px solid ${R.ink}`,
            fontSize: 11, fontWeight: 700, lineHeight: 1.3,
          }}>
            <div style={{ letterSpacing: '.18em', color: R.muted, fontSize: 9, fontWeight: 800 }}>PLAYING ON</div>
            <div style={{ fontSize: 13, fontWeight: 800, marginTop: 1 }}>This phone · 30s previews</div>
          </div>
        ) : (
          <div style={{
            padding: '8px 10px', background: deviceName ? R.paper2 : R.yellow,
            border: `2px solid ${R.ink}`,
            fontSize: 11, fontWeight: 700, lineHeight: 1.3,
            display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8,
          }}>
            <div>
              <div style={{ letterSpacing: '.18em', color: R.muted, fontSize: 9, fontWeight: 800 }}>PLAYING ON</div>
              <div style={{ fontSize: 13, fontWeight: 800, marginTop: 1 }}>{deviceName || '⚠ No device'}</div>
            </div>
            <span onClick={onChangeDevice} style={{ cursor: 'pointer', fontSize: 10, fontWeight: 800, letterSpacing: '.14em' }}>CHANGE →</span>
          </div>
        )}
        <div style={{ fontSize: 11, fontWeight: 700, color: R.muted, letterSpacing: '.06em' }}>
          {remaining}/{totalForUI} tracks remaining
          {isPreview && totalForUI < totalAllTracks && (
            <span style={{ color: R.pink, marginLeft: 6 }}>
              · only {totalForUI} of {totalAllTracks} have a 30s preview
            </span>
          )}
        </div>
        {isPreview && totalAllTracks > 0 && totalForUI / totalAllTracks < 0.3 && (
          <div style={{
            padding: '8px 10px', background: R.yellow, border: `2px solid ${R.ink}`,
            fontSize: 11, fontWeight: 700, lineHeight: 1.35,
          }}>
            ⚠ This playlist has thin preview coverage. Spotify dropped preview clips on a lot of tracks. Try a <b>curated preset</b> (CHANGE → above) for full coverage on Free, or switch to <b>Spotify Premium</b> mode.
          </div>
        )}
        <Btn bg={players.length > 0 ? R.pink : R.paper3} fg={R.paper} disabled={players.length === 0} onClick={onStart}>
          START ROUND 01 →
        </Btn>
      </div>
    </Screen>
  );
}

function NowPlayingPanel({ liveTrack, onRefresh, secondary = false }) {
  const has = liveTrack && liveTrack.year;
  return (
    <div style={{
      padding: 12,
      background: has ? R.paper2 : R.yellow,
      border: `2px solid ${R.ink}`,
      boxShadow: `3px 3px 0 ${R.ink}`,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
        <span style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.2em', color: R.muted }}>
          {has ? 'PLAYING IN SPOTIFY' : 'NOTHING PLAYING'}
        </span>
        <span onClick={onRefresh} style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.14em', cursor: 'pointer' }}>↻ CHECK</span>
      </div>
      {has ? (
        <>
          <div style={{ fontSize: 14, fontWeight: 800, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
            {liveTrack.title}
          </div>
          <div style={{ fontSize: 11, fontWeight: 700, color: R.muted, marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
            {liveTrack.artist} · {liveTrack.year}
          </div>
        </>
      ) : (
        <div style={{ fontSize: 12, fontWeight: 700, lineHeight: 1.5 }}>
          Open Spotify, pick a playlist, hit shuffle play. Then tap ↻ Check.
        </div>
      )}
    </div>
  );
}

// Countdown chip — re-renders every 250ms while the deadline is in the future.
// Pulls down to the parent's flex row so it can sit next to LOCKED counter.
function Countdown({ deadline, warnAt = 10 }) {
  const [now, setNow] = React.useState(Date.now());
  React.useEffect(() => {
    if (!deadline) return;
    const id = setInterval(() => setNow(Date.now()), 250);
    return () => clearInterval(id);
  }, [deadline]);
  if (!deadline) return null;
  const remaining = Math.max(0, Math.ceil((deadline - now) / 1000));
  const warn = remaining <= warnAt;
  return (
    <span style={{
      padding: '3px 10px',
      background: warn ? R.pink : R.ink,
      color: R.paper,
      fontSize: 12, fontWeight: 900, letterSpacing: '.14em',
      fontFamily: 'ui-monospace, monospace',
      border: `2px solid ${R.ink}`,
      animation: warn && remaining > 0 ? 'rshake .35s' : 'none',
    }}>⏱ {remaining}s</span>
  );
}

function HostPlaying({ round, song, players, onReveal, onReplay, roundDeadline }) {
  const lockedCount = players.filter(p => p.locked).length;
  return (
    <Screen>
      <div style={{ padding: '8px 22px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
        <Tag color={R.pink}>Round {String(round).padStart(2, '0')}</Tag>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <Countdown deadline={roundDeadline} />
          <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.14em' }}>{lockedCount}/{players.length} LOCKED</span>
        </div>
      </div>

      <div style={{ padding: '12px 22px', flex: 1, display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{
          padding: 14, background: R.paper2,
          border: `2px solid ${R.ink}`, boxShadow: `3px 3px 0 ${R.ink}`,
          display: 'flex', alignItems: 'center', gap: 12,
        }}>
          <div style={{
            width: 56, height: 56, background: R.ink, position: 'relative', overflow: 'hidden',
            backgroundImage: `repeating-linear-gradient(45deg, ${R.pink} 0 4px, ${R.ink} 4px 9px)`,
            animation: 'rspin 12s linear infinite',
          }} />
          <div style={{ flex: 1, minWidth: 0 }}>
            <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.18em', color: R.pink }}>NOW PLAYING (HOST ONLY)</div>
            <div style={{ fontSize: 14, fontWeight: 800, marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{song?.title || '—'}</div>
            <div style={{ fontSize: 12, fontWeight: 700, color: R.muted, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{song?.artist || ''} · {song?.year}</div>
          </div>
          <RisoBars />
        </div>

        <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 6, overflow: 'auto' }}>
          {players.map((p) => (
            <div key={p.id} style={{
              padding: '10px 12px', background: p.locked ? R.green : R.paper2,
              border: `2px solid ${R.ink}`, display: 'flex', alignItems: 'center', gap: 10,
            }}>
              <div style={{ width: 22, height: 22, background: p.color, border: `2px solid ${R.ink}` }} />
              <span style={{ flex: 1, fontSize: 14, fontWeight: 700 }}>{p.name}</span>
              <span style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.16em' }}>{p.locked ? '● LOCKED' : '○ THINKING'}</span>
            </div>
          ))}
        </div>

        <div style={{ display: 'flex', gap: 8 }}>
          {onReplay && (
            <Btn big={false} bg={R.paper2} onClick={onReplay} style={{ flex: '0 0 auto', width: 110 }}>
              ↻ REPLAY
            </Btn>
          )}
          <Btn bg={R.pink} fg={R.paper} onClick={onReveal} style={{ flex: 1 }}>REVEAL · {song?.year} →</Btn>
        </div>
      </div>
    </Screen>
  );
}

// Hybrid screen: host is playing too. Hides song info, shows year drum + lock,
// keeps the Reveal control because the host still controls the round.
function HostPlayingAsPlayer({ round, year, setYear, locked, onLock, onReveal, onReplay, players, roundDeadline }) {
  const lockedCount = players.filter(p => p.locked).length;
  const total = players.length;
  return (
    <Screen>
      <div style={{ padding: '8px 22px', display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
        <Tag color={R.pink}>Round {String(round).padStart(2, '0')}</Tag>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <Countdown deadline={roundDeadline} />
          <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.14em' }}>{lockedCount}/{total} LOCKED</span>
        </div>
      </div>

      <div style={{ padding: '12px 22px', flex: 1, display: 'flex', flexDirection: 'column', gap: 14 }}>
        <div style={{
          padding: 14, background: R.paper2,
          border: `2px solid ${R.ink}`, boxShadow: `3px 3px 0 ${R.ink}`,
          display: 'flex', alignItems: 'center', gap: 12,
        }}>
          <div style={{
            width: 56, height: 56, background: R.ink, position: 'relative', overflow: 'hidden',
            backgroundImage: `repeating-linear-gradient(45deg, ${R.pink} 0 4px, ${R.ink} 4px 9px)`,
            animation: 'rspin 12s linear infinite',
          }} />
          <div style={{ flex: 1 }}>
            <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.18em', color: R.pink }}>NOW PLAYING</div>
            <div style={{ fontSize: 14, fontWeight: 800, marginTop: 2 }}>Mystery track</div>
          </div>
          <RisoBars />
        </div>

        {!locked ? (
          <window.YearDrum value={year} onChange={setYear} accent={R.yellow} />
        ) : (
          <div style={{
            padding: '24px 16px', background: R.green,
            border: `2px solid ${R.ink}`, boxShadow: `3px 3px 0 ${R.ink}`,
            textAlign: 'center',
          }}>
            <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.3em', color: R.muted }}>YOU LOCKED</div>
            <div style={{ fontSize: 80, fontWeight: 900, lineHeight: .9, marginTop: 8, fontVariantNumeric: 'tabular-nums' }}>{year}</div>
          </div>
        )}

        <div style={{ flex: 1 }} />

        {!locked ? (
          <Btn bg={R.pink} fg={R.paper} onClick={() => onLock(year)}>LOCK IN · {year}</Btn>
        ) : null}
        <div style={{ display: 'flex', gap: 8 }}>
          {onReplay && (
            <Btn big={false} bg={R.paper2} onClick={onReplay} style={{ flex: '0 0 auto', width: 110 }}>↻ REPLAY</Btn>
          )}
          <Btn big={false} bg={R.blue} fg={R.paper} onClick={onReveal} style={{ flex: 1 }}>
            REVEAL →
          </Btn>
        </div>
      </div>
    </Screen>
  );
}

function HostRevealed({ song, guesses, players = [], round, totalRounds, myPid, onNext, onEnd, onAgain }) {
  const exact = guesses.filter(g => g.points === 2);
  const sortedGuesses = [...guesses].sort((a, b) => b.points - a.points || a.dist - b.dist);
  const sortedPlayers = [...players].sort((a, b) => b.score - a.score);
  // Compact mode kicks in once 7+ players are in the room — keeps everything
  // on screen for a single host display without scrolling.
  const dense = sortedPlayers.length >= 7;
  const guessRowPad = dense ? '6px 10px' : '8px 12px';
  const guessFs = dense ? 13 : 14;
  const totalRowPad = dense ? '4px 8px' : '6px 10px';
  const totalFs = dense ? 12 : 13;
  return (
    <Screen>
      {exact.length > 0 && <RisoConfetti count={50} />}
      <div style={{ padding: '8px 22px', display: 'flex', justifyContent: 'space-between' }}>
        <Tag color={R.pink}>Reveal · {String(round).padStart(2, '0')}</Tag>
      </div>

      <div style={{ padding: '12px 22px', flex: 1, display: 'flex', flexDirection: 'column', gap: 12, overflow: 'auto' }}>
        <div style={{ position: 'relative', textAlign: 'center', marginTop: 4, height: dense ? 100 : 130 }}>
          <div style={{
            position: 'absolute', left: '50%', top: 6, transform: 'translateX(calc(-50% - 4px))',
            fontSize: dense ? 100 : 130, fontWeight: 900, color: R.pink, lineHeight: .85,
            letterSpacing: '-.05em', mixBlendMode: 'multiply', opacity: .9,
            fontVariantNumeric: 'tabular-nums',
          }}>{song?.year}</div>
          <div style={{
            fontSize: dense ? 100 : 130, fontWeight: 900, color: R.blue, lineHeight: .85,
            letterSpacing: '-.05em', mixBlendMode: 'multiply', opacity: .9,
            fontVariantNumeric: 'tabular-nums',
            animation: 'rstamp-in .55s cubic-bezier(.2,1.4,.4,1)',
          }}>{song?.year}</div>
        </div>

        <div style={{
          padding: 12, background: R.paper2,
          border: `2px solid ${R.ink}`, boxShadow: `3px 3px 0 ${R.ink}`,
        }}>
          <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.18em', color: R.pink }}>THE TRACK</div>
          <div style={{ fontSize: 18, fontWeight: 900, marginTop: 4 }}>{song?.title}</div>
          <div style={{ fontSize: 13, fontWeight: 700, color: R.muted }}>{song?.artist}</div>
        </div>

        {/* Round results — per-player guesses scored this round. */}
        <div>
          <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.18em', color: R.muted, marginBottom: 6 }}>ROUND RESULTS</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: dense ? 4 : 6 }}>
            {sortedGuesses.map((g) => {
              const self = myPid && g.id === myPid;
              return (
                <div key={g.id} style={{
                  padding: guessRowPad,
                  background: g.points === 2 ? R.yellow : g.points === 1 ? R.green : R.paper2,
                  border: `${self ? 3 : 2}px solid ${R.ink}`,
                  boxShadow: self ? `3px 3px 0 ${R.ink}` : 'none',
                  display: 'flex', alignItems: 'center', gap: 10,
                }}>
                  <div style={{ width: 18, height: 18, background: g.color, border: `2px solid ${R.ink}`, flexShrink: 0 }} />
                  <span style={{ flex: 1, fontSize: guessFs, fontWeight: 800, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                    {g.name}
                    {self && <span style={{ marginLeft: 6, fontSize: 9, background: R.ink, color: R.paper, padding: '2px 5px', letterSpacing: '.16em', fontWeight: 800 }}>YOU</span>}
                  </span>
                  <span style={{ fontSize: 12, fontWeight: 700, fontFamily: 'ui-monospace, monospace' }}>{g.guess ?? '—'}</span>
                  <span style={{
                    fontSize: 12, fontWeight: 900, fontFamily: 'ui-monospace, monospace',
                    padding: '2px 8px', minWidth: 32, textAlign: 'center',
                    background: g.points === 2 ? R.pink : g.points === 1 ? R.yellow : 'transparent',
                    color: g.points === 2 ? R.paper : R.ink,
                    border: `2px solid ${R.ink}`,
                  }}>+{g.points}</span>
                </div>
              );
            })}
          </div>
        </div>

        {/* Cumulative totals — clearly separated from round results so the eye
            doesn't conflate "+2 this round" with "12 total". */}
        {sortedPlayers.length > 0 && (
          <div style={{ marginTop: 14 }}>
            <div style={{
              height: 4, marginBottom: 10,
              backgroundImage: `repeating-linear-gradient(90deg, ${R.ink} 0 10px, transparent 10px 16px)`,
            }} />
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
              <span style={{
                padding: '4px 10px', background: R.ink, color: R.paper,
                fontSize: 11, fontWeight: 900, letterSpacing: '.18em',
                border: `2px solid ${R.ink}`, boxShadow: `2px 2px 0 ${R.pink}`,
              }}>
                TOTAL · AFTER {String(round).padStart(2, '0')}
              </span>
              <span style={{ fontSize: 10, fontWeight: 700, color: R.muted, letterSpacing: '.12em' }}>
                {sortedPlayers.length} {sortedPlayers.length === 1 ? 'PLAYER' : 'PLAYERS'}
              </span>
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: dense ? 2 : 4 }}>
              {sortedPlayers.map((p, i) => {
                const self = myPid && p.id === myPid;
                return (
                  <div key={p.id} style={{
                    padding: totalRowPad,
                    background: i === 0 ? R.yellow : R.paper,
                    border: `${self ? 3 : 2}px solid ${R.ink}`,
                    display: 'flex', alignItems: 'center', gap: 10,
                  }}>
                    <span style={{ fontSize: dense ? 13 : 14, fontWeight: 900, width: 18, color: i === 0 ? R.pink : R.ink }}>{i + 1}</span>
                    <div style={{ width: 12, height: 12, background: p.color, border: `2px solid ${R.ink}`, flexShrink: 0 }} />
                    <span style={{ flex: 1, fontSize: totalFs, fontWeight: 800, minWidth: 0, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                      {p.name}{self ? ' (YOU)' : ''}
                    </span>
                    <span style={{ fontSize: dense ? 14 : 16, fontWeight: 900, fontFamily: 'ui-monospace, monospace' }}>{p.score}</span>
                  </div>
                );
              })}
            </div>
          </div>
        )}

        <div style={{ display: 'flex', gap: 8, marginTop: 8 }}>
          <Btn big={false} bg={R.paper2} onClick={onEnd}>END GAME</Btn>
          <Btn bg={R.pink} fg={R.paper} onClick={onNext}>NEXT ROUND →</Btn>
        </div>
      </div>
    </Screen>
  );
}

function HostFinal({ players, onAgain, onSeeStats }) {
  const sorted = [...players].sort((a, b) => b.score - a.score);
  const winner = sorted[0];
  return (
    <Screen>
      <RisoConfetti count={90} />
      <RisoStamp size={300} color={R.yellow} top={-100} left={140} duration={20} />

      <div style={{ padding: '8px 22px', display: 'flex', justifyContent: 'space-between' }}>
        <Tag color={R.ink}>Game Over</Tag>
      </div>

      <div style={{ padding: '20px 22px 0', flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', gap: 14, position: 'relative', zIndex: 5 }}>
        <div style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.3em', color: R.muted }}>WINNER</div>
        <Overprint sizes={64} color1={R.pink} color2={R.blue}>{(winner?.name || '—').toUpperCase()}</Overprint>
        <div style={{
          fontSize: 80, fontWeight: 900, color: R.ink, lineHeight: 1, letterSpacing: '-.04em',
          fontVariantNumeric: 'tabular-nums',
          animation: 'rstamp-in .6s .2s backwards',
        }}>{winner?.score || 0}</div>
        <div style={{ fontSize: 12, fontWeight: 800, letterSpacing: '.18em', color: R.muted }}>POINTS</div>

        <div style={{ display: 'flex', flexDirection: 'column', gap: 6, marginTop: 18, width: '100%' }}>
          {sorted.map((p, i) => (
            <div key={p.id} style={{
              padding: '8px 12px',
              background: i === 0 ? R.yellow : R.paper2,
              border: `2px solid ${R.ink}`,
              display: 'flex', alignItems: 'center', gap: 10,
            }}>
              <span style={{ fontSize: 14, fontWeight: 900, width: 22 }}>{i + 1}</span>
              <div style={{ width: 16, height: 16, background: p.color, border: `2px solid ${R.ink}` }} />
              <span style={{ flex: 1, fontSize: 14, fontWeight: 800 }}>{p.name}</span>
              <span style={{ fontSize: 14, fontWeight: 900, fontFamily: 'ui-monospace, monospace' }}>{p.score}</span>
            </div>
          ))}
        </div>
      </div>

      <div style={{ padding: '0 22px', position: 'relative', zIndex: 5, display: 'flex', flexDirection: 'column', gap: 10 }}>
        {onSeeStats && (
          <Btn bg={R.yellow} onClick={onSeeStats}>📊 SEE THE BREAKDOWN →</Btn>
        )}
        <Btn bg={R.pink} onClick={onAgain}>PLAY AGAIN →</Btn>
      </div>
    </Screen>
  );
}

// QR via the qrcode-generator lib's createDataURL — no innerHTML.
function QRBlock({ url }) {
  const [src, setSrc] = React.useState(null);
  React.useEffect(() => {
    if (!url || !window.qrcode) return;
    try {
      const qr = window.qrcode(0, 'M');
      qr.addData(url);
      qr.make();
      setSrc(qr.createDataURL(4, 2));
    } catch (e) {
      console.warn('QR render failed', e);
    }
  }, [url]);

  return (
    <div style={{
      display: 'flex', alignItems: 'center', gap: 14,
      padding: 14, background: R.paper, border: `2px solid ${R.ink}`,
      boxShadow: `3px 3px 0 ${R.ink}`,
    }}>
      <div style={{
        width: 100, height: 100, background: R.paper, flexShrink: 0,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
      }}>
        {src ? <img src={src} alt="QR" style={{ width: '100%', height: '100%', imageRendering: 'pixelated' }} /> : null}
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.2em', color: R.muted }}>SCAN</div>
        <div style={{
          fontSize: 12, fontWeight: 700, marginTop: 4, wordBreak: 'break-all',
          fontFamily: 'ui-monospace, monospace', lineHeight: 1.3,
        }}>{url}</div>
      </div>
    </div>
  );
}

function SettingsSheet({
  onClose, devices, pickedDevice, onPickDevice, onRefreshDevices, refreshingDevices,
  onChangePlaylist, onReauth, onEndGame, onNewGame, onJoinInstead, onResetScores, onShowState, onResync,
  isPreviewMode,
}) {
  return (
    <div style={{
      position: 'fixed', inset: 0, zIndex: 1100,
      background: 'rgba(26,26,26,.55)',
      display: 'flex', alignItems: 'flex-end',
    }} onClick={onClose}>
      <div onClick={(e) => e.stopPropagation()} style={{
        width: '100%', maxWidth: 460, margin: '0 auto',
        background: R.paper,
        border: `2px solid ${R.ink}`,
        borderBottom: 'none',
        padding: '18px 18px calc(env(safe-area-inset-bottom, 0px) + 24px)',
        maxHeight: '85vh', overflowY: 'auto',
        display: 'flex', flexDirection: 'column', gap: 14,
      }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
          <span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '.2em' }}>SETTINGS</span>
          <span onClick={onClose} style={{ cursor: 'pointer', fontSize: 16, fontWeight: 900, padding: '4px 10px' }}>×</span>
        </div>

        {!isPreviewMode && (
        <div>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 6 }}>
            <span style={{ fontSize: 10, fontWeight: 800, letterSpacing: '.2em', color: R.muted }}>SPOTIFY DEVICE</span>
            <span onClick={onRefreshDevices} style={{ cursor: 'pointer', fontSize: 10, fontWeight: 800, letterSpacing: '.14em' }}>
              {refreshingDevices ? 'CHECKING…' : '↻ REFRESH'}
            </span>
          </div>
          {devices.length === 0 ? (
            <div style={{ padding: 10, background: R.yellow, border: `2px solid ${R.ink}`, fontSize: 12, fontWeight: 700, lineHeight: 1.4 }}>
              No active Spotify device. Open Spotify on a phone or speaker, hit play once, then refresh.
            </div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {devices.map((d) => {
                const active = pickedDevice === d.id;
                return (
                  <div key={d.id} onClick={() => onPickDevice(d.id)} style={{
                    padding: '10px 12px', cursor: 'pointer',
                    background: active ? R.green : R.paper2,
                    border: `2px solid ${R.ink}`,
                    boxShadow: active ? `3px 3px 0 ${R.ink}` : `2px 2px 0 ${R.ink}`,
                    display: 'flex', alignItems: 'center', gap: 10,
                  }}>
                    <div style={{
                      width: 14, height: 14, border: `2px solid ${R.ink}`,
                      background: active ? R.ink : 'transparent',
                    }} />
                    <div style={{ flex: 1, minWidth: 0 }}>
                      <div style={{ fontSize: 13, fontWeight: 800, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{d.name}</div>
                      <div style={{ fontSize: 10, fontWeight: 700, color: R.muted, letterSpacing: '.1em' }}>
                        {d.type?.toUpperCase()}{d.is_active ? ' · ACTIVE' : ''}
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </div>
        )}

        <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
          <Btn big={false} bg={R.paper2} onClick={onChangePlaylist}>SWITCH PLAYLIST</Btn>
          <Btn big={false} bg={R.paper2} onClick={onResetScores}>RESET SCORES</Btn>
          {!isPreviewMode && <Btn big={false} bg={R.paper2} onClick={onReauth}>RECONNECT SPOTIFY</Btn>}
          {onResync && <Btn big={false} bg={R.green} onClick={onResync}>↻ RE-SYNC</Btn>}
          {onShowState && <Btn big={false} bg={R.paper2} onClick={onShowState}>STATE INSPECTOR</Btn>}
          {onNewGame && <Btn big={false} bg={R.paper2} onClick={onNewGame}>← NEW GAME (BACK TO START)</Btn>}
          {onJoinInstead && <Btn big={false} bg={R.paper2} onClick={onJoinInstead}>I'M JOINING INSTEAD →</Btn>}
          <Btn big={false} bg={R.pink} fg={R.paper} onClick={onEndGame}>END GAME (CLOSE ROOM)</Btn>
        </div>
      </div>
    </div>
  );
}

window.HostScreens = {
  HostStart, HostSpotify, HostPlaylist, HostDevice,
  HostRoom, HostPlaying, HostPlayingAsPlayer, HostRevealed, HostFinal,
  SettingsSheet,
};
})();
