/* ============================================================
   HOST SCREEN — dense control panel
   ============================================================ */
function MapControl({ game }){
  const fileRef = useRef(null);
  const [busy, setBusy] = useState(false);
  const onFiles = async (files)=>{
    if(!files || !files.length) return;
    setBusy(true);
    for(const f of Array.from(files)){
      if(!f.type.startsWith('image/')) continue;
      const src = await compressImage(f, 1200, 0.6);
      if(src) Game.addMap(f.name.replace(/\.[^.]+$/,'').slice(0,40) || 'Map', src);
    }
    setBusy(false);
    if(fileRef.current) fileRef.current.value='';
  };
  return (
    <div className="host-card liquid-glass host-card-wide">
      <h2 className="section-label"><Icon name="book" size={16}/> H · Maps &amp; Handouts</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',letterSpacing:'.01em',margin:'0 0 .7rem'}}>
        Upload map or handout images, then reveal them on the Main screen or hand them privately to specific players.
      </p>
      <div className="ctrl-row" style={{marginTop:0}}>
        <button className="chip-btn" onClick={()=>fileRef.current&&fileRef.current.click()} disabled={busy}>
          {busy ? 'Processing…' : '⤓ Upload image(s)'}
        </button>
        <input ref={fileRef} type="file" accept="image/*" multiple style={{display:'none'}} onChange={e=>onFiles(e.target.files)}/>
        {game.mainMap && <button className="tiny-btn danger-outline" style={{marginLeft:'auto'}} onClick={()=>Game.clearMainMap()}>Hide map from Main</button>}
      </div>
      {game.maps.length===0
        ? <div className="muted" style={{fontSize:'.78rem',marginTop:'.7rem'}}>No maps uploaded yet — drop in a dungeon map, a letter, a clue…</div>
        : <div className="map-list">
            {game.maps.map(m=>{
              const onMain = game.mainMap && game.mainMap.id===m.id;
              const heldBy = game.players.filter(p=>(p.maps||[]).some(x=>x.id===m.id));
              return (
                <div key={m.id} className={`map-card ${onMain?'on-main':''}`}>
                  <div className="map-thumb-wrap"><img className="map-thumb" src={m.src} alt={m.name}/></div>
                  <div className="map-info">
                    <input className="map-name-input" value={m.name} onChange={e=>Game.renameMap(m.id, e.target.value)}/>
                    <div className="map-share">
                      <span className="ctrl-label">Reveal</span>
                      <button className={`tiny-btn ${onMain?'on':''}`} onClick={()=>onMain?Game.clearMainMap():Game.shareMapToMain(m)}>
                        {onMain?'● On Main':'Main screen'}
                      </button>
                      <button className="tiny-btn" onClick={()=>Game.shareMapToPlayer('all', m)} disabled={!game.players.length}>All players</button>
                    </div>
                    {game.players.length>0 && (
                      <div className="map-share">
                        <span className="ctrl-label">Hand to</span>
                        {game.players.map(p=>{
                          const has = (p.maps||[]).some(x=>x.id===m.id);
                          return (
                            <button key={p.id} className={`tiny-btn ${has?'heal-outline':''}`}
                              onClick={()=> has ? Game.revokeMapFromPlayer(p.id, m.id) : Game.shareMapToPlayer(p.id, m)}
                              title={has?'Tap to take it back':'Hand this map to '+p.name.split(' ')[0]}>
                              {has?'✓ ':''}{p.name.split(' ')[0]}
                            </button>
                          );
                        })}
                      </div>
                    )}
                  </div>
                  <button className="sc-x" onClick={()=>Game.removeMap(m.id)} title="Delete map">×</button>
                </div>
              );
            })}
          </div>}
    </div>
  );
}

/* ============================================================
   SETUP PANEL — pre-game prep: upload NPC / Enemy / Boss / Knowledge
   art + lore, save/load the whole setup, and push entries live.
   ============================================================ */
function SetupEntryCard({ kind, entry, game }){
  const meta = KIND_META[kind];
  const soundRef = useRef(null);

  const onSound = (file)=>{
    if(!file) return;
    if(!file.type.startsWith('audio/')){ alert('Please choose an audio file.'); return; }
    if(file.size > 800*1024){ if(!confirm('That clip is large ('+Math.round(file.size/1024)+'KB) and may sync slowly. Use it anyway?')) return; }
    const r = new FileReader();
    r.onload = ()=> setupStore.update('npc', entry.id, { sound:r.result, soundName:file.name.slice(0,40) });
    r.readAsDataURL(file);
    if(soundRef.current) soundRef.current.value='';
  };

  return (
    <div className="setup-entry" style={{'--accent':KIND_ACCENT[kind]}}>
      <div className="se-thumb">
        {entry.src
          ? <img src={entry.src} alt={entry.name}/>
          : <span className="se-noimg"><Icon name={kind==='knowledge'?'book':'skull'} size={20}/></span>}
      </div>
      <div className="se-info">
        <input className="se-name" value={entry.name} placeholder={meta.label+' name'}
          onChange={e=>setupStore.update(kind, entry.id, {name:e.target.value})}/>
        {meta.hasDesc && (
          <textarea className="se-desc" rows="2" placeholder="Description / intel…"
            value={entry.desc} onChange={e=>setupStore.update(kind, entry.id, {desc:e.target.value})}></textarea>
        )}

        {/* NPC sound — a saved property of the entry (plays on Main when this NPC is shown live) */}
        {kind==='npc' && (
          <div className="se-push">
            <button className={`tiny-btn ${entry.sound?'on':''}`} onClick={()=>soundRef.current&&soundRef.current.click()}
              title={entry.sound?('Sound: '+(entry.soundName||'clip')+' — tap to replace'):'Add a sound (plays on Main when shown live)'}>
              ♪ {entry.sound?'Sound':'Add sound'}
            </button>
            <input ref={soundRef} type="file" accept="audio/*" style={{display:'none'}} onChange={e=>onSound(e.target.files[0])}/>
            {entry.sound && <button className="tiny-btn danger-outline" onClick={()=>setupStore.update('npc',entry.id,{sound:'',soundName:''})}>♪×</button>}
          </div>
        )}
      </div>
      <button className="sc-x" onClick={()=>setupStore.remove(kind, entry.id)} title="Delete">×</button>
    </div>
  );
}

/* ============================================================
   LIBRARY REVEAL — the live "where do I show this?" controls for a
   saved entry. Lives in the RUN GAME zone (setup is upload-only).
   ============================================================ */
function LibraryRevealCard({ kind, entry, game }){
  const meta = KIND_META[kind];
  const payloadImg = { id:entry.id, kind, name:entry.name, src:entry.src };
  const hasDesc = !!(entry.desc && entry.desc.trim());

  const npcOnMain = kind==='npc' && game.mainNpc && game.mainNpc.id===entry.id;
  const figureOnMain = (kind==='enemy'||kind==='boss') && (game.mainEnemies||[]).some(e=>e.id===entry.id);
  const codexOnMain = game.mainCodex && game.mainCodex.id===entry.id;
  // is the live codex currently showing WITH the description?
  const codexHasIntel = codexOnMain && game.mainCodex.desc && game.mainCodex.desc.trim();
  const onMain = npcOnMain || figureOnMain || codexOnMain;

  return (
    <div className={`setup-entry reveal ${onMain?'on-main':''}`} style={{'--accent':KIND_ACCENT[kind]}}>
      <div className="se-thumb">
        {entry.src
          ? <img src={entry.src} alt={entry.name}/>
          : <span className="se-noimg"><Icon name={kind==='knowledge'?'book':'skull'} size={20}/></span>}
      </div>
      <div className="se-info">
        <div className="se-name-static">{entry.name || <em className="muted">Unnamed {meta.label}</em>}</div>

        {/* --- show on Main --- */}
        <div className="se-push">
          {kind==='npc' && (
            <button className={`tiny-btn ${npcOnMain?'on':''}`} onClick={()=> npcOnMain ? Game.clearMainNpc() : Game.showNpcOnMain(entry)}>
              {npcOnMain?'● On Main (left)':'Show on Main'}
            </button>
          )}
          {(kind==='enemy'||kind==='boss') && (
            <button className={`tiny-btn ${figureOnMain?'on':''}`} onClick={()=> Game.toggleMainEnemy(entry)}>
              {figureOnMain?'● On Main':'Image → Main'}
            </button>
          )}
          {/* codex reveal — explicit image-only vs. with-intel buttons (works for enemies, bosses & knowledge) */}
          {meta.hasDesc && (
            <>
              <button className={`tiny-btn ${codexOnMain && !codexHasIntel?'on':''}`}
                onClick={()=> (codexOnMain && !codexHasIntel) ? Game.clearMainCodex() : Game.showCodexOnMain({...payloadImg, desc:''})}
                title="Show the reveal card on the Main screen — image & name only.">
                {codexOnMain && !codexHasIntel ? (kind==='knowledge'?'● Revealed':'● Codex') : (kind==='knowledge'?'Reveal on Main':'Codex → Main')}
              </button>
              <button className={`tiny-btn ${codexHasIntel?'on':'heal-outline'}`} disabled={!hasDesc}
                onClick={()=> codexHasIntel ? Game.clearMainCodex() : Game.showCodexOnMain({...payloadImg, desc:entry.desc})}
                title={hasDesc?'Show the reveal card WITH its description/intel.':'Add a description in Setup to use intel.'}>
                {codexHasIntel ? '● + Intel shown' : '+ Intel → Main'}
              </button>
            </>
          )}
        </div>

        {/* --- hand to players --- */}
        {game.players.length>0 && kind==='npc' && (
          <div className="se-push">
            <span className="ctrl-label">Hand to</span>
            <button className="tiny-btn" onClick={()=>Game.pushNpcToPlayer('all', entry)}>All</button>
            {game.players.map(p=>{
              const has = p.npc && p.npc.id===entry.id;
              return (
                <button key={p.id} className={`tiny-btn ${has?'heal-outline':''}`}
                  onClick={()=> has ? Game.clearNpcFromPlayer(p.id) : Game.pushNpcToPlayer(p.id, entry)}
                  title={has?'Tap to take it back':'Hand to '+p.name.split(' ')[0]}>
                  {has?'✓ ':''}{p.name.split(' ')[0]}
                </button>
              );
            })}
          </div>
        )}

        {game.players.length>0 && meta.hasDesc && (
          <>
            <div className="se-push">
              <span className="ctrl-label">Image</span>
              <button className="tiny-btn" onClick={()=>Game.pushKnowledge('all', {...payloadImg, desc:''})}>All</button>
              {game.players.map(p=>{
                const k = (p.knowledge||[]).find(x=>x.id===entry.id);
                const hasImg = k && !(k.desc && k.desc.trim());
                return (
                  <button key={p.id} className={`tiny-btn ${hasImg?'heal-outline':''}`}
                    onClick={()=> hasImg ? Game.revokeKnowledge(p.id, entry.id) : Game.pushKnowledge(p.id, {...payloadImg, desc:''})}
                    title={hasImg?'Tap to take it back':'Image only → '+p.name.split(' ')[0]}>
                    {hasImg?'✓ ':''}{p.name.split(' ')[0]}
                  </button>
                );
              })}
            </div>
            <div className="se-push">
              <span className="ctrl-label">+ Intel</span>
              <button className="tiny-btn heal-outline" disabled={!(entry.desc&&entry.desc.trim())}
                onClick={()=>Game.pushKnowledge('all', {...payloadImg, desc:entry.desc})}>All</button>
              {game.players.map(p=>{
                const k = (p.knowledge||[]).find(x=>x.id===entry.id);
                const hasIntel = k && (k.desc && k.desc.trim());
                return (
                  <button key={p.id} className={`tiny-btn ${hasIntel?'on':''}`} disabled={!(entry.desc&&entry.desc.trim())}
                    onClick={()=> hasIntel ? Game.revokeKnowledge(p.id, entry.id) : Game.pushKnowledge(p.id, {...payloadImg, desc:entry.desc})}
                    title={hasIntel?'Tap to take it back':'With intel → '+p.name.split(' ')[0]}>
                    {hasIntel?'✓ ':''}{p.name.split(' ')[0]}
                  </button>
                );
              })}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

/* run-game card: browse the saved library & reveal/hand out entries live */
function RevealControl({ game }){
  const lib = useSetup();
  const total = KIND_ORDER.reduce((n,k)=> n+((lib[k]||[]).length), 0);
  return (
    <div className="host-card liquid-glass">
      <h2 className="section-label"><Icon name="eye" size={16}/> Reveal &amp; Hand Out</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',letterSpacing:'.01em',margin:'0 0 .7rem'}}>
        Show saved NPCs, enemies, bosses &amp; knowledge on the Main screen or hand them to players (with or without intel). Build the library in the Setup tab.
      </p>
      {total===0 && <div className="muted" style={{fontSize:'.8rem'}}>Nothing saved yet — add art &amp; lore in the Setup tab first.</div>}
      {KIND_ORDER.map(kind=>{
        const list = lib[kind]||[];
        if(!list.length) return null;
        return (
          <div key={kind} className="reveal-section">
            <div className="reveal-section-head" style={{'--accent':KIND_ACCENT[kind]}}>{KIND_META[kind].plural}</div>
            <div className="reveal-list">
              {list.map(entry=> <LibraryRevealCard key={entry.id} kind={kind} entry={entry} game={game}/>)}
            </div>
          </div>
        );
      })}
    </div>
  );
}
window.RevealControl = RevealControl;

function SetupPanel({ game }){
  const lib = useSetup();
  const loadRef = useRef(null);
  const fileRefs = useRef({});
  const [busy, setBusy] = useState('');

  const upload = async (kind, files)=>{
    if(!files || !files.length) return;
    setBusy(kind);
    for(const f of Array.from(files)){
      if(!f.type.startsWith('image/')) continue;
      const src = await compressImage(f);
      if(src) setupStore.add(kind, { name: f.name.replace(/\.[^.]+$/,'').slice(0,40) || KIND_META[kind].label, src, desc:'' });
    }
    setBusy('');
    if(fileRefs.current[kind]) fileRefs.current[kind].value='';
  };
  const onLoadFile = async (file)=>{
    if(!file) return;
    try{ await loadSetupFile(file); }catch(e){ alert('Could not read that setup file.'); }
    if(loadRef.current) loadRef.current.value='';
  };

  return (
    <div className="host-card liquid-glass host-card-wide setup-card">
      <h2 className="section-label"><Icon name="book" size={16}/> S · Game Setup &amp; Library</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',letterSpacing:'.01em',margin:'0 0 .7rem'}}>
        Prep the realm before play: import NPC, enemy and boss portraits plus knowledge handouts, give them names and descriptions, and save the whole setup to a file to load again later. To show any of these on the Main screen or hand them to a hero, use <b>Reveal &amp; Hand Out</b> in the Run Game tab. This library stays on your device only.
      </p>

      {/* save / load row */}
      <div className="ctrl-row" style={{marginTop:0, flexWrap:'wrap'}}>
        <input className="tinput" style={{flex:'1 1 140px'}} placeholder="Setup name (e.g. The Hollow Crown)"
          value={lib.name} onChange={e=>setupStore.setName(e.target.value)}/>
        <button className="tiny-btn" onClick={downloadSetup} disabled={!setupStore.count()}>⤓ Save setup</button>
        <button className="tiny-btn" onClick={()=>loadRef.current&&loadRef.current.click()}>⤒ Load setup</button>
        <input ref={loadRef} type="file" accept="application/json,.json" style={{display:'none'}} onChange={e=>onLoadFile(e.target.files[0])}/>
        <button className="tiny-btn danger-outline" onClick={()=>{ if(confirm('Start a NEW game with this setup? Heroes & progress reset; your library stays.')) Game.resetAll(); }}>New game</button>
        {setupStore.count()>0 && <button className="tiny-btn danger-outline" onClick={()=>{ if(confirm('Clear the entire library?')) setupStore.clear(); }}>Clear library</button>}
      </div>

      {/* four kinds */}
      {KIND_ORDER.map(kind=>(
        <div key={kind} className="setup-section">
          <div className="setup-section-head">
            <div>
              <span className="setup-kind-label" style={{color:KIND_ACCENT[kind]}}>{KIND_META[kind].plural}</span>
              <span className="setup-kind-sub">{KIND_META[kind].sub}</span>
            </div>
            <button className="tiny-btn" onClick={()=>fileRefs.current[kind]&&fileRefs.current[kind].click()} disabled={busy===kind}>
              {busy===kind?'…':'⤓ Upload'}
            </button>
            <input ref={el=>fileRefs.current[kind]=el} type="file" accept="image/*" multiple style={{display:'none'}}
              onChange={e=>upload(kind, e.target.files)}/>
            {kind==='knowledge' && <button className="tiny-btn" onClick={()=>setupStore.add('knowledge',{name:'Note',src:'',desc:''})}>+ Text only</button>}
          </div>
          {lib[kind].length===0
            ? <div className="muted setup-empty">None yet.</div>
            : <div className="setup-list">
                {lib[kind].map(e=> <SetupEntryCard key={e.id} kind={kind} entry={e} game={game}/>)}
              </div>}
        </div>
      ))}

      {/* cinematic story scenes (videos on Cloudflare Stream) */}
      <StorySetupPanel game={game}/>
    </div>
  );
}

function Stepper({ onMinus, onPlus, minusClass='', plusClass='' }){
  return (
    <>
      <div className={`stepper liquid-glass glass-btn ${minusClass}`} onClick={onMinus}>−</div>
      <div className={`stepper liquid-glass glass-btn ${plusClass}`} onClick={onPlus}>+</div>
    </>
  );
}

/* ============================================================
   REST · XP · REWARD — opens when the moderator taps Sleep for a
   hero. Grant XP → if they level up, choose coins, a card upgrade
   and items, then push the celebration to the Main screen & player.
   ============================================================ */
function rewardItemPalette(game){
  // every shop item + the unique finds, de-duped by name
  const seen = new Set(); const out = [];
  const add = (it)=>{ if(!it||!it.name||seen.has(it.name)) return; seen.add(it.name);
    out.push({ name:it.name, type:it.type||'item', desc:it.desc||'', reqs:it.reqs||[] }); };
  Object.values(game.shop.shops||{}).forEach(s=> (s.items||[]).forEach(add));
  (window.RANDOM_ITEMS||[]).forEach(add);
  return out;
}
function RestXpModal({ p, game, onClose }){
  const [step, setStep] = useState('xp');
  const [xpAmt, setXpAmt] = useState(25);
  const [doRest, setDoRest] = useState(true);
  const [gainedTo, setGainedTo] = useState(null);   // {gained, level}
  // reward state
  const [coins, setCoins] = useState(5);
  const [statPts, setStatPts] = useState(2);
  const [card, setCard] = useState('none');
  const [picked, setPicked] = useState([]);         // selected item specs
  const palette = useRef(null);
  if(!palette.current) palette.current = rewardItemPalette(game);
  const xpMax = xpNeeded(p.level||1);

  const grant = ()=>{
    if(doRest) Game.restPlayer(p.id);
    const res = Game.giveXp(p.id, Number(xpAmt)||0);
    if(res.gained>0){ setGainedTo(res); setStep('reward'); }
    else { onClose(); }
  };
  const togglePick = (it)=> setPicked(s=> s.some(x=>x.name===it.name) ? s.filter(x=>x.name!==it.name) : [...s, it]);
  const sendReward = ()=>{
    Game.applyLevelReward(p.id, { coins:Number(coins)||0, card, items:picked });
    if(Number(statPts)>0) Game.grantStatPoints(p.id, Number(statPts));
    onClose();
  };

  return (
    <div className="intel-modal-bg" onClick={onClose}>
      <div className="xpmodal liquid-glass animate-fade-rise" onClick={e=>e.stopPropagation()}>
        <span className="codex-x glass-btn" style={{position:'absolute',top:14,right:14}} onClick={onClose}>✕</span>

        {step==='xp' ? (
          <>
            <div className="xpm-head"><Icon name="moon" size={20} style={{color:'hsl(var(--primary))'}}/>
              <div><div className="xpm-name display">{p.name.split(' ')[0]} rests</div>
              <div className="xpm-sub">Level {p.level||1} · {p.xp||0} / {xpMax} XP</div></div>
            </div>
            <div className="section-sub">Experience to grant</div>
            <div className="radio-row">
              {[10,25,50,100].map(n=>(
                <button key={n} className={`chip-btn ${Number(xpAmt)===n?'on':''}`} onClick={()=>setXpAmt(n)}>+{n}</button>
              ))}
              <input className="tinput" style={{width:64}} type="number" value={xpAmt} onChange={e=>setXpAmt(e.target.value)}/>
            </div>
            <div className="xpm-preview">
              {(p.xp||0)+Number(xpAmt) >= xpMax
                ? <span className="gold">★ This will level {p.name.split(' ')[0]} up!</span>
                : <span className="muted">{xpMax-((p.xp||0)+Number(xpAmt))} XP short of the next level.</span>}
            </div>
            <label className="xpm-check">
              <input type="checkbox" checked={doRest} onChange={e=>setDoRest(e.target.checked)}/>
              Also rest — reshuffle deck &amp; heal +2
            </label>
            <button className="keeper-go" style={{marginTop:'1rem'}} onClick={grant}>Grant XP →</button>
          </>
        ) : (
          <>
            <div className="xpm-head"><Icon name="star" size={20} style={{color:'hsl(var(--primary))'}}/>
              <div><div className="xpm-name display gold">Level {gainedTo.level}!</div>
              <div className="xpm-sub">Choose {p.name.split(' ')[0]}'s reward — give any, all, or none.</div></div>
            </div>

            <div className="section-sub">Coins</div>
            <div className="radio-row">
              {[0,5,10,20,50].map(n=>(
                <button key={n} className={`chip-btn ${Number(coins)===n?'on':''}`} onClick={()=>setCoins(n)}>{n}</button>
              ))}
              <input className="tinput" style={{width:60}} type="number" value={coins} onChange={e=>setCoins(e.target.value)}/>
            </div>

            <div className="section-sub">Stat points to spend</div>
            <div className="radio-row">
              {[0,1,2,3,5].map(n=>(
                <button key={n} className={`chip-btn ${Number(statPts)===n?'on':''}`} onClick={()=>setStatPts(n)}>{n}</button>
              ))}
              <input className="tinput" style={{width:60}} type="number" value={statPts} onChange={e=>setStatPts(e.target.value)}/>
            </div>
            <div className="muted" style={{fontSize:'.7rem',textTransform:'none',marginTop:'-.2rem'}}>The hero picks which stats to raise on their phone.</div>

            <div className="section-sub">Card upgrade</div>
            <div className="radio-row">
              {[['none','None'],['1to2','+1 → +2'],['2to5','+2 → +5'],['1to5','+1 → +5']].map(([v,l])=>(
                <button key={v} className={`chip-btn ${card===v?'on':''}`} onClick={()=>setCard(v)}>{l}</button>
              ))}
            </div>

            <div className="section-sub">Items &amp; weapons {picked.length>0 && <span className="muted" style={{textTransform:'none'}}>· {picked.length} selected</span>}</div>
            <div className="reward-palette no-scrollbar">
              {palette.current.map(it=>{
                const on = picked.some(x=>x.name===it.name);
                return (
                  <button key={it.name} className={`reward-chip ${on?'on':''} type-${it.type}`} onClick={()=>togglePick(it)}>
                    <Icon name={iconFor(it.name)} size={14}/> {it.name}
                  </button>
                );
              })}
            </div>

            <div className="ctrl-row" style={{marginTop:'1rem'}}>
              <button className="tiny-btn" onClick={onClose}>Skip reward</button>
              <button className="keeper-go" style={{marginLeft:'auto',width:'auto',padding:'.6rem 1.1rem'}} onClick={sendReward}>Send reward →</button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

function PlayerControlCard({ p, allPlayers, game }){
  const [amt, setAmt] = useState(2);
  const [free, setFree] = useState('');
  const [grant, setGrant] = useState('Wood');
  const [xpOpen, setXpOpen] = useState(false);
  const a = free!=='' ? Number(free)||0 : amt;
  const pct = (p.hp/p.maxHp)*100;
  const ITEMS = [
    'Iron Sword','Steel Longsword','Greatsword','Hand Axe','Battle Axe','War Hammer','Mace',
    'Spear','Halberd','Dagger','Twin Daggers','Rapier','Longbow','Crossbow',
    'Oak Shield','Tower Shield','Leather Armor','Chainmail','Plate Armor','Iron Helm',
    'Ember Staff','Frost Wand','Storm Orb','Ring of Warmth','Amulet of Warding','Cloak of Shadows','Boots of Speed',
    'Healing Draught','Greater Healing Draught','Mana Vial','Antidote','Elixir of Vigor',
    'Bread','Dried Meat','Cheese Wheel','Trail Rations','Honey Cake','Waterskin',
    'Rope (50ft)','Torch','Lantern','Lockpicks','Grappling Hook','Bedroll','Bandages','Smoke Vial','Tinderbox',
    'Scroll of Light','Scroll of Fire','Scroll of Frost','Scroll of Healing','Scroll of Teleport',
    'Scroll of the Merchant','Scroll of Arcana','Scroll of Provisions',
  ];

  return (
    <div className="pcard liquid-glass">
      <div className="ph">
        <div>
          <div className="pn">{p.name.split(' ')[0]} <span className="muted" style={{fontSize:'.7rem'}}>{p.name.split(' ').slice(1).join(' ')}</span></div>
          <div className="pc">{p.cls}</div>
        </div>
        <div className="ph-right">
          <span className="lvl-badge" title={`${p.xp||0} / ${xpNeeded(p.level||1)} XP`}><Icon name="star" size={11} style={{verticalAlign:'-1px'}}/> Lvl {p.level||1}</span>
          <div className={`statusdot ${p.dead?'dead':'alive'}`}></div>
        </div>
      </div>

      <div className="mini-hp">
        <div className="mini-hp-fill" style={{width:pct+'%', background: pct<30?'hsl(var(--damage))':'linear-gradient(90deg,hsl(var(--heal)),hsl(var(--primary)))'}}></div>
      </div>
      <div style={{display:'flex',justifyContent:'space-between',fontSize:'.78rem'}}>
        <span className="muted">HP</span><span className="display">{p.hp} / {p.maxHp}</span>
      </div>

      {/* amount selector */}
      <div className="ctrl-row">
        <span className="ctrl-label">Amount</span>
        <div className="amt-sel">
          {[1,2,5].map(n=>(
            <button key={n} className={amt===n&&free===''?'on':''} onClick={()=>{setAmt(n);setFree('');}}>{n}</button>
          ))}
        </div>
        <input className="tinput" style={{width:54,padding:'.25rem .4rem',fontSize:'.78rem'}} placeholder="#" value={free} onChange={e=>setFree(e.target.value)}/>
      </div>

      {/* hp steppers */}
      <div className="ctrl-row">
        <span className="ctrl-label">Health</span>
        <Stepper minusClass="danger-outline" plusClass="heal-outline"
          onMinus={()=>Game.triggerDamage(p.id,a)} onPlus={()=>Game.triggerHeal(p.id,a)}/>
        <button className="tiny-btn danger-outline" onClick={()=>Game.triggerDamage(p.id,a)} title="Incoming enemy attack">
          ⚔ Attack {a}
        </button>
      </div>

      {/* kill / revive */}
      <div className="ctrl-row">
        <span className="ctrl-label">State</span>
        {!p.dead
          ? <button className="tiny-btn danger-outline" onClick={()=>Game.triggerDeath(p.id)}><Icon name="skull" size={13} style={{verticalAlign:'-2px'}}/> Kill</button>
          : <button className="tiny-btn heal-outline" onClick={()=>Game.triggerRevive(p.id)}><Icon name="spark" size={13} style={{verticalAlign:'-2px'}}/> Revive</button>}
      </div>

      {/* coins */}
      <div className="ctrl-row">
        <span className="ctrl-label">Coins · {p.coins}</span>
        <Stepper onMinus={()=>Game.setCoins(p.id, p.coins-Math.max(1,a))} onPlus={()=>Game.setCoins(p.id, p.coins+Math.max(1,a))}/>
        <span className="gold display" style={{marginLeft:4}}><Icon name="coin" size={14} style={{verticalAlign:'-2px'}}/> {p.coins}</span>
      </div>

      {/* dice — per-player toggle (shines on their screen) + last result */}
      {(()=>{
        const dice = game.dice || {};
        const allowed = !!(dice.allow||{})[p.id];
        const res = (dice.results||{})[p.id];
        return (
          <div className="play-switch-row">
            <button className={`play-switch dice-switch ${allowed?'on':'off'}`}
              onClick={()=> allowed ? Game.denyDice(p.id) : Game.allowDice(p.id)}
              title="Switch this hero's dice on or off">
              <span className="ps-track"><span className="ps-knob"></span></span>
              <span className="ps-lbl">⚅ Dice {allowed?'ON':'OFF'}</span>
              <span className="ps-hint">{allowed?'their dice shines — they may roll once':'tap to allow one roll'}</span>
            </button>
            <span className="scope-seg" title="Where the dice result shows">
              <button className={`scope-opt ${!dice.public?'on':''}`} onClick={()=>Game.setDicePublic(false)}>Private</button>
              <button className={`scope-opt ${dice.public?'on pub':''}`} onClick={()=>Game.setDicePublic(true)}>Public</button>
            </span>
            {res && <span className="pc-dice-result" title={`rolled ${res.a} + ${res.b}`}>
              <Pip6 n={res.a} small/><Pip6 n={res.b} small/><b>{res.sum}</b>
            </span>}
          </div>
        );
      })()}

      {/* action deck */}
      {(()=>{
        const deck = p.cards.deck||[]; const played = p.cards.played||[];
        const byTier = {common:0,rare:0,premium:0};
        deck.forEach(c=>{ byTier[c.tier] = (byTier[c.tier]||0)+1; });
        const last = played[0];
        return (
          <div className="deck-ctrl">
            <div className="ctrl-row" style={{marginTop:'.55rem',marginBottom:8}}>
              <span className="ctrl-label">Deck · {deck.length} left</span>
              <span className="deck-tiers">
                <span className="dt common" title="+1 cards left">{byTier.common}×+1</span>
                <span className="dt rare" title="+2 cards left">{byTier.rare}×+2</span>
                <span className="dt premium" title="+5 premium left">{byTier.premium}×+5</span>
              </span>
            </div>
            {/* simple ON/OFF — the hero can only play a card while this is ON */}
            <div className="play-switch-row">
              <button className={`play-switch ${p.cards.locked?'off':'on'}`}
                onClick={()=> p.cards.locked ? Game.allowCard(p.id) : Game.lockCard(p.id)}
                title="Switch this hero's card play on or off">
                <span className="ps-track"><span className="ps-knob"></span></span>
                <span className="ps-lbl">Card play {p.cards.locked?'OFF':'ON'}</span>
                <span className="ps-hint">{p.cards.locked?'tap to allow one play':'they may play one card'}</span>
              </button>
              <span className="scope-seg" title="Where a played card shows">
                <button className={`scope-opt ${(game.cardPlay&&game.cardPlay.public===false)?'on':''}`} onClick={()=>Game.setCardPublic(false)}>Private</button>
                <button className={`scope-opt ${!(game.cardPlay&&game.cardPlay.public===false)?'on pub':''}`} onClick={()=>Game.setCardPublic(true)}>Public</button>
              </span>
            </div>
            <div className="ctrl-row" style={{marginTop:8}}>
              <button className="tiny-btn heal-outline" onClick={()=>Game.addCard(p.id)} title="Return a card to the deck">+ Card</button>
              <button className="tiny-btn danger-outline" onClick={()=>Game.removeCard(p.id)} title="Discard the top card">− Card</button>
              <button className="tiny-btn" onClick={()=>Game.resetCards(p.id)}>↻ Reshuffle</button>
              <button className="tiny-btn heal-outline" onClick={()=>setXpOpen(true)}><Icon name="moon" size={12} style={{verticalAlign:'-1px'}}/> Sleep · XP</button>
              {last && <span className={`deck-last tier-${last.tier}`}>last +{last.value}</span>}
            </div>
          </div>
        );
      })()}

      {/* requests inbox */}
      {(p.requests||[]).map(r=> r.kind==='buy' ? (
        <div key={r.id} className="buyreq">
          <div className="buyreq-head">
            <span className="buyreq-title"><Icon name="cart" size={14} style={{color:'hsl(var(--primary))'}}/> Buy request · <span className="gold">{r.total}g</span></span>
            <span className={`buyreq-afford ${p.coins>=r.total?'ok':'no'}`}>{p.coins>=r.total?'can afford':'short on gold'}</span>
          </div>
          <div className="buyreq-items">
            {(r.items||[]).map(it=>(
              <span key={it.uid} className="buyreq-item"><Icon name={iconFor(it.name)} size={12}/> {it.name} <span className="muted">{it.cost}g</span></span>
            ))}
          </div>
          <div className="buyreq-actions">
            <button className="tiny-btn heal-outline" onClick={()=>Game.resolveBuy(p.id,r.id,true)}>Accept &amp; deduct {r.total}g</button>
            <button className="tiny-btn danger-outline" onClick={()=>Game.resolveBuy(p.id,r.id,false)}>Decline</button>
          </div>
        </div>
      ) : r.kind==='give' ? (
        <div key={r.id} className="usereq give">
          <div className="usereq-head">
            <Icon name="bag" size={14} style={{color:'hsl(var(--primary))'}}/>
            <span className="usereq-label">{r.label}</span>
          </div>
          <div className="usereq-actions">
            <button className="tiny-btn heal-outline" onClick={()=>Game.resolveGive(p.id,r.id,true)}>✓ Allow gift</button>
            <button className="tiny-btn danger-outline" onClick={()=>Game.resolveGive(p.id,r.id,false)}>Decline</button>
          </div>
        </div>
      ) : r.kind==='use' ? (
        <div key={r.id} className={`usereq ${r.status==='awaiting-card'?'awaiting':''}`}>
          <div className="usereq-head">
            <Icon name={r.icon||'use'} size={14} style={{color:'hsl(var(--primary))'}}/>
            <span className="usereq-label">{r.label}</span>
          </div>
          {r.status==='awaiting-card'
            ? <div className="usereq-wait"><span className="ur-dot"></span> Waiting for {p.name.split(' ')[0]} to play a card…
                <button className="tiny-btn danger-outline" style={{marginLeft:'auto'}} onClick={()=>Game.resolveUse(p.id,r.id,'decline')}>Cancel</button>
              </div>
            : <div className="usereq-actions">
                <button className="tiny-btn heal-outline" onClick={()=>Game.resolveUse(p.id,r.id,'free')}>✓ Free</button>
                <button className="tiny-btn" onClick={()=>Game.resolveUse(p.id,r.id,'card')}><Icon name="cards" size={12} style={{verticalAlign:'-2px'}}/> Use card</button>
                <button className="tiny-btn danger-outline" onClick={()=>Game.resolveUse(p.id,r.id,'decline')}>Decline</button>
              </div>}
        </div>
      ) : (
        <div key={r.id} className="req-row">
          <Icon name={r.icon||'use'} size={15} style={{color:'hsl(var(--primary))'}}/>
          <span className="rl">{r.label}</span>
          <button className="tiny-btn heal-outline" onClick={()=>Game.resolveRequest(p.id,r.id,true)}>Accept</button>
          <button className="tiny-btn danger-outline" onClick={()=>Game.resolveRequest(p.id,r.id,false)}>Deny</button>
        </div>
      ))}

      {/* grant item */}
      <div className="ctrl-row">
        <span className="ctrl-label">Grant item</span>
        <select className="tinput" value={grant} onChange={e=>setGrant(e.target.value)} style={{flex:1}}>
          {ITEMS.map(it=> <option key={it}>{it}</option>)}
        </select>
        <button className="tiny-btn" onClick={()=>Game.grantItem(p.id,grant)}>Give</button>
      </div>

      {/* held inventory — remove · break · give to another player */}
      {(()=>{
        const inv = [...(p.weapons||[]), ...(p.items||[])];
        if(inv.length===0) return <div className="inv-empty muted">No items held.</div>;
        const others = allPlayers.filter(o=>o.id!==p.id);
        return (
          <div className="inv-list">
            <div className="ctrl-label" style={{marginBottom:4}}>Inventory · {inv.length}</div>
            {inv.map(it=>(
              <div key={it.id} className={`inv-row type-${it.type} ${it.broken?'broken':''}`}>
                <Icon name={iconFor(it.name)} size={14}/>
                <span className="inv-name">{it.name}{it.broken && <span className="inv-broken-tag">broken</span>}</span>
                <div className="inv-acts">
                  {others.length>0 && (
                    <select className="inv-give" defaultValue="" onChange={e=>{ if(e.target.value){ Game.transferInvItem(p.id, e.target.value, it.id); e.target.value=''; } }} title="Give to another player">
                      <option value="">give…</option>
                      {others.map(o=> <option key={o.id} value={o.id}>{o.name.split(' ')[0]}</option>)}
                    </select>
                  )}
                  <button className="inv-btn" onClick={()=>Game.breakInvItem(p.id, it.id)} title={it.broken?'Repair':'Break'}>{it.broken?'⟳':'✱'}</button>
                  <button className="inv-btn danger" onClick={()=>Game.removeInvItem(p.id, it.id)} title="Remove">✕</button>
                </div>
              </div>
            ))}
          </div>
        );
      })()}

      {/* character sheet from creation */}
      {(p.mastery || p.weakness || p.backstory) && (
        <details className="pcard-traits">
          <summary>Character sheet</summary>
          {p.mastery && <div className="pt-row"><b>Mastery:</b> {p.mastery}</div>}
          {p.weakness && <div className="pt-row"><b>Weakness:</b> {p.weakness}</div>}
          {p.backstory && <div className="pt-row"><b>Backstory:</b> {p.backstory}</div>}
        </details>
      )}

      {xpOpen && <RestXpModal p={p} game={game} onClose={()=>setXpOpen(false)}/>}
    </div>
  );
}

const CRAFT_RECIPES = ['Torch (Wood + Rope)','Spear (Wood + Knife)','Bandage (Rope)','Campfire (Wood + Torch)'];
const ENEMY_PRESETS = [
  { name:'Blackroot Warg', hp:18, stats:{AC:14,STR:'+4',Speed:'40ft',Trait:'Pack tactics'} },
  { name:'Goblin Cutter', hp:8, stats:{AC:13,STR:'+2',Speed:'30ft',Trait:'Nimble'} },
  { name:'Skeleton', hp:12, stats:{AC:12,STR:'+2',Trait:'Undead · fears fire'} },
  { name:'Ogre', hp:32, stats:{AC:11,STR:'+6',Trait:'Brutal swing'} },
  { name:'Bandit', hp:14, stats:{AC:13,STR:'+3',Trait:'Sneak attack'} },
  { name:'Cave Troll', hp:42, stats:{AC:15,STR:'+7',Trait:'Regenerates'} },
];

function BattleControl({ game }){
  const b = game.battle;
  const lib = useSetup();
  const [name, setName] = useState('');
  const [hp, setHp] = useState(12);
  const [dmgAmt, setDmgAmt] = useState(3);
  const libFoes = [...lib.enemy, ...lib.boss];

  const push = (preset)=>{
    if(preset){ Game.pushEnemy(preset); }
    else { Game.pushEnemy({ name: name.trim()||'Enemy', hp: Number(hp)||10 }); setName(''); }
  };
  const spawnLib = (e, isBoss)=>{
    Game.pushEnemy({ name:e.name, hp: isBoss?30:12, src:e.src, desc:e.desc, stats:{} });
  };

  return (
    <div className="host-card liquid-glass host-card-wide">
      <h2 className="section-label"><Icon name="sword" size={16}/> G · Action Sequence</h2>

      <div className="ctrl-row" style={{marginTop:0}}>
        {!b.active
          ? <button className="chip-btn" onClick={()=>Game.startBattle()}>▶ Begin the action</button>
          : <>
              <span className="gate-live"><span className="gate-live-dot"></span>Battle live · Round {b.round}</span>
              <button className="chip-btn on" onClick={()=>Game.endBattle()} style={{marginLeft:'auto'}}>End battle</button>
            </>}
      </div>

      {b.active && <>
        {/* turn / round controls */}
        <div className="section-sub">Whose turn {b.lastAction && <span className="last-action">last: {(game.players.find(p=>p.id===b.lastAction.playerId)?.name.split(' ')[0])||'?'} → {b.lastAction.action}</span>}</div>
        <div className="turn-row">
          {game.players.map(p=>{
            const isTurn = b.turn&&b.turn.kind==='player'&&b.turn.id===p.id;
            const acted = isTurn && b.acted===p.id;
            return (
              <button key={p.id} className={`chip-btn ${isTurn?'on':''}`}
                onClick={()=>Game.setTurn({kind:'player',id:p.id})}>
                {acted && <span className="acted-check">✓</span>}{p.name.split(' ')[0]}
              </button>
            );
          })}
          <button className={`chip-btn ${!b.turn?'on':''}`} onClick={()=>Game.setTurn(null)}>Clear</button>
          <button className="tiny-btn" onClick={()=>Game.nextRound()}>Next round →</button>
        </div>

        {/* spawn from the Game Setup library */}
        <div className="section-sub">Spawn from your library</div>
        {libFoes.length===0
          ? <div className="muted" style={{fontSize:'.72rem'}}>No enemies or bosses in your Setup library yet — add portraits in the Game Setup panel, then spawn them here.</div>
          : <div className="spawn-grid">
              {lib.enemy.map(e=>(
                <button key={e.id} className="spawn-tile" onClick={()=>spawnLib(e,false)} title={'Spawn '+e.name}>
                  {e.src ? <img src={e.src} alt={e.name}/> : <span className="spawn-noimg"><Icon name="skull" size={16}/></span>}
                  <span className="spawn-nm">{e.name}</span>
                </button>
              ))}
              {lib.boss.map(e=>(
                <button key={e.id} className="spawn-tile boss" onClick={()=>spawnLib(e,true)} title={'Spawn boss '+e.name}>
                  {e.src ? <img src={e.src} alt={e.name}/> : <span className="spawn-noimg"><Icon name="skull" size={16}/></span>}
                  <span className="spawn-nm">{e.name}</span>
                  <span className="spawn-boss-tag">BOSS</span>
                </button>
              ))}
            </div>}

        {/* custom / preset push */}
        <div className="section-sub">Or push a quick foe</div>
        <div className="enemy-presets">
          {ENEMY_PRESETS.map(p=>(
            <button key={p.name} className="tiny-btn" onClick={()=>push(p)}>+ {p.name}</button>
          ))}
        </div>
        <div className="ctrl-row" style={{marginTop:'.5rem'}}>
          <input className="tinput" style={{flex:1}} placeholder="Custom name" value={name} onChange={e=>setName(e.target.value)}/>
          <input className="tinput" style={{width:56}} type="number" placeholder="HP" value={hp} onChange={e=>setHp(e.target.value)}/>
          <button className="chip-btn" onClick={()=>push(null)}>Push →</button>
        </div>

        {/* enemies on field */}
        {b.enemies.length>0 && <>
          <div className="section-sub" style={{display:'flex',justifyContent:'space-between'}}>
            <span>On the field</span>
            <span className="amt-sel">{[1,3,5].map(n=><button key={n} className={dmgAmt===n?'on':''} onClick={()=>setDmgAmt(n)}>{n}</button>)}</span>
          </div>
          <div className="bf-list">
            {b.enemies.map(e=>{
              const pct=(e.hp/e.maxHp)*100; const dead=e.hp<=0;
              return (
                <div key={e.id} className={`bf-list-item ${dead?'dead':''} ${b.turn&&b.turn.id===e.id?'acting':''}`}>
                  <div className="bli-top">
                    <span className="bli-name">{e.name}</span>
                    <span className="bli-hp display">{e.hp}/{e.maxHp}</span>
                    <button className="sc-x" onClick={()=>Game.removeEnemy(e.id)}>×</button>
                  </div>
                  <div className="mini-hp"><div className="mini-hp-fill" style={{width:pct+'%', background:pct<30?'hsl(var(--damage))':'linear-gradient(90deg,hsl(var(--heal)),hsl(var(--primary)))'}}></div></div>
                  <div className="bli-actions">
                    <button className="tiny-btn danger-outline" onClick={()=>Game.damageBattleEnemy(e.id,dmgAmt)}>−{dmgAmt}</button>
                    <button className="tiny-btn heal-outline" onClick={()=>Game.healBattleEnemy(e.id,dmgAmt)}>+{dmgAmt}</button>
                    <button className={`tiny-btn ${e.intel?'on':''}`} onClick={()=>Game.toggleEnemyIntel(e.id)}>Intel</button>
                    <button className={`tiny-btn ${b.turn&&b.turn.id===e.id?'on':''}`} onClick={()=>Game.setTurn({kind:'enemy',id:e.id})}>Its turn</button>
                  </div>
                  {/* enemy strike → target a player */}
                  <div className="bli-strike">
                    <span className="ctrl-label">Strike</span>
                    {game.players.map(p=>(
                      <button key={p.id} className="tiny-btn danger-outline" disabled={p.dead}
                        onClick={()=>Game.enemyStrike(e.id, p.id, dmgAmt)}>{p.name.split(' ')[0]} −{dmgAmt}</button>
                    ))}
                  </div>
                </div>
              );
            })}
          </div>
        </>}
      </>}
    </div>
  );
}
const ALL_SCENES = ['forest','deep-forest','gate','warcamp','hall','house'];
const ALL_NPCS = [
  {name:'Old Lady', line:'You shouldn\u2019t have come this far north, child. The roots remember.'},
  {name:'Soldier', line:'Hold there. None pass the gate without the old song.'},
  {name:'Orc', line:'Grukk smells iron on you. Grukk likes iron.'},
  {name:'Dwarf', line:'Steel\u2019s honest. Coin\u2019s honest. The rest? Bah.'},
  {name:'Merchant', line:'Ahh, travelers! Coin for comfort, blades for the brave.'},
];

/* ============================================================
   SHOP FORGE — stock and style each shop. Every item carries a
   type, cost, description and up to 3 stat requirements. Drop in
   suggested unique finds, set a backdrop, then open it for the room.
   ============================================================ */
function ReqEditor({ item, type }){
  const reqs = item.reqs || [];
  const setReq = (i, patch)=> Game.updateShopItem(type, item.id, { reqs: reqs.map((r,j)=> j===i ? {...r, ...patch} : r) });
  const addReq = ()=> { if(reqs.length<3) Game.updateShopItem(type, item.id, { reqs:[...reqs, {stat:'STR', n:1}] }); };
  const delReq = (i)=> Game.updateShopItem(type, item.id, { reqs: reqs.filter((_,j)=>j!==i) });
  return (
    <div className="req-editor">
      <span className="ctrl-label">Requires</span>
      {reqs.map((r,i)=>(
        <span key={i} className="req-edit-row">
          <input className="tinput" style={{width:38,padding:'.2rem'}} type="number" min="1" value={r.n}
            onChange={e=>setReq(i,{n:Math.max(1,Number(e.target.value)||1)})}/>
          <select className="tinput" style={{width:62,padding:'.2rem'}} value={r.stat} onChange={e=>setReq(i,{stat:e.target.value})}>
            {STAT_KEYS.map(k=> <option key={k} value={k}>{k}</option>)}
          </select>
          <button className="req-del" onClick={()=>delReq(i)}>×</button>
        </span>
      ))}
      {reqs.length<3 && <button className="tiny-btn" onClick={addReq}>+ stat</button>}
      {reqs.length===0 && <span className="muted" style={{fontSize:'.66rem'}}>none — anyone may use</span>}
    </div>
  );
}

function ShopItemEditor({ item, type }){
  const [open, setOpen] = useState(false);
  const fileRef = useRef(null);
  const onFile = (e)=>{ const f=e.target.files&&e.target.files[0]; if(!f) return;
    const r=new FileReader(); r.onload=()=>Game.setShopItemImg(type,item.id,r.result); r.readAsDataURL(f); e.target.value=''; };
  return (
    <div className="sfi liquid-glass">
      <div className="sfi-top">
        <button className="sfi-ico sfi-art" onClick={()=>fileRef.current&&fileRef.current.click()} title="Upload item PNG">
          {item.img ? <img src={item.img} alt="" style={{width:'100%',height:'100%',objectFit:'contain'}}/> : <Icon name={iconFor(item.name)} size={20}/>}
        </button>
        <input ref={fileRef} type="file" accept="image/*" style={{display:'none'}} onChange={onFile}/>
        <input className="tinput sfi-name" value={item.name} onChange={e=>Game.updateShopItem(type,item.id,{name:e.target.value})}/>
        <select className="tinput sfi-type" value={item.type} onChange={e=>Game.updateShopItem(type,item.id,{type:e.target.value})}>
          {Object.entries(ITEM_TYPES).map(([v,m])=> <option key={v} value={v}>{m.label}</option>)}
        </select>
        <span className="sfi-cost"><Icon name="coin" size={12}/>
          <input className="tinput" style={{width:44,padding:'.25rem'}} type="number" value={item.cost} onChange={e=>Game.updateShopItem(type,item.id,{cost:Number(e.target.value)||0})}/>
        </span>
        <span className="sfi-cost" title="Stock (blank = unlimited)">×
          <input className="tinput" style={{width:40,padding:'.25rem'}} type="number" min="0" placeholder="∞"
            value={item.stock==null?'':item.stock}
            onChange={e=>Game.setStock(type,item.id, e.target.value===''?null:Number(e.target.value))}/>
        </span>
        <button className="sfi-x" onClick={()=>Game.removeShopItem(type,item.id)} title="Remove item">×</button>
      </div>
      <div className="sfi-row2">
        <button className="tiny-btn" onClick={()=>setOpen(o=>!o)}>{open?'▾ Details':'▸ Details'}</button>
        <ReqEditor item={item} type={type}/>
      </div>
      {open && (
        <div className="sfi-details">
          <textarea className="tinput" rows="2" placeholder="Description (shown in inventory & on request)"
            value={item.desc} onChange={e=>Game.updateShopItem(type,item.id,{desc:e.target.value})}></textarea>
          <div className="ctrl-row" style={{marginTop:'.4rem'}}>
            <button className={`tiny-btn ${item.showDesc?'on':''}`} onClick={()=>Game.toggleShopItemDesc(type,item.id)}
              title="Show this item's description on the rack">Rack desc {item.showDesc?'ON':'OFF'}</button>
            <button className="tiny-btn" onClick={()=>Game.spotlightItem(item)}
              title="Push a big cinematic showcase of this item onto the Main screen">✦ Showcase on Main</button>
            <button className="tiny-btn" onClick={()=>Game.clearSpotlight()} title="Dismiss the showcase">Hide</button>
          </div>
        </div>
      )}
    </div>
  );
}

function ShopControl({ game }){
  const [tab, setTab] = useState(game.shop.type || 'general');
  const [showSugg, setShowSugg] = useState(false);
  const bgRef = useRef(null);
  const shop = game.shop.shops[tab] || { name:'', items:[], bg:'' };
  const isOpen = game.shop.open && game.shop.type===tab;

  const onBg = async (file)=>{
    if(!file || !file.type.startsWith('image/')) return;
    const src = await compressImage(file, 1100, 0.62);
    if(src) Game.setShopBg(tab, src);
    if(bgRef.current) bgRef.current.value='';
  };

  return (
    <div className="host-card liquid-glass host-card-wide">
      <h2 className="section-label"><Icon name="buy" size={16}/> B · Shop Forge</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',letterSpacing:'.01em',margin:'0 0 .7rem'}}>
        Stock each shop with unique wares. Every item has a type, cost, description and up to three stat requirements. Opening a shop shows it on the Main screen and on every hero's handset, where they fill a cart and request to buy.
      </p>

      {/* which shop */}
      <div className="radio-row">
        {Object.keys(SHOP_TYPES).map(key=>(
          <button key={key} className={`chip-btn ${tab===key?'on':''}`} onClick={()=>setTab(key)}>
            {game.shop.shops[key].name}{game.shop.open&&game.shop.type===key?' ●':''}
          </button>
        ))}
      </div>

      {/* shop identity + open/close + backdrop */}
      <div className="ctrl-row" style={{marginTop:'.7rem'}}>
        <input className="tinput" style={{flex:1}} value={shop.name} onChange={e=>Game.renameShop(tab, e.target.value)} placeholder="Shop name"/>
        {isOpen
          ? <button className="chip-btn on" onClick={()=>Game.setShopOpen(false)}>Close</button>
          : <button className="chip-btn" onClick={()=>Game.openShop(tab)}>Open →</button>}
      </div>
      <div className="ctrl-row" style={{marginTop:'.5rem'}}>
        <span className="ctrl-label">Backdrop</span>
        <button className="tiny-btn" onClick={()=>bgRef.current&&bgRef.current.click()}>{shop.bg?'Replace image':'⤓ Upload image'}</button>
        <input ref={bgRef} type="file" accept="image/*" style={{display:'none'}} onChange={e=>onBg(e.target.files[0])}/>
        {shop.bg && <>
          <span className="sf-bg-thumb" style={{backgroundImage:`url(${shop.bg})`}}></span>
          <button className="tiny-btn danger-outline" onClick={()=>Game.setShopBg(tab,'')}>Clear</button>
        </>}
      </div>

      {/* items */}
      <div className="section-sub" style={{display:'flex',justifyContent:'space-between',alignItems:'center'}}>
        <span>Wares · {shop.items.length}</span>
        <span style={{display:'flex',gap:6}}>
          <button className="tiny-btn" onClick={()=>setShowSugg(s=>!s)}>{showSugg?'Hide finds':'+ Suggested find'}</button>
          <button className="tiny-btn heal-outline" onClick={()=>Game.addShopItem(tab,{name:'New item',type:'item',cost:5,desc:'',reqs:[]})}>+ Blank item</button>
        </span>
      </div>

      {showSugg && (
        <div className="sf-suggest">
          {RANDOM_ITEMS.map(it=>{
            const have = shop.items.some(x=>x.name===it.name);
            return (
              <button key={it.name} className={`sf-sugg-chip ${have?'have':''}`} disabled={have}
                onClick={()=>Game.addShopItem(tab, it)} title={it.desc}>
                <Icon name={iconFor(it.name)} size={13}/> {it.name} <span className="muted">{it.cost}g</span>
              </button>
            );
          })}
        </div>
      )}

      <div className="sf-items">
        {shop.items.length===0
          ? <div className="muted setup-empty">No wares yet — add a blank item or drop in a suggested find.</div>
          : shop.items.map(it=> <ShopItemEditor key={it.id} item={it} type={tab}/>)}
      </div>
    </div>
  );
}

/* ============================================================
   SHOP BOARD — moderator's live trading dashboard, shown while a
   shop is open. Per hero: coins, pending buy requests (accept/
   decline), what's in their cart right now, and their inventory.
   ============================================================ */
function ShopBoard({ game }){
  const meta = game.shop.shops[game.shop.type] || { name:'Shop', items:[] };
  const items = meta.items || [];
  const spot = game.shopItemSpotlight;
  return (
    <div className="host-card liquid-glass host-card-wide shopboard">
      <h2 className="section-label"><Icon name="buy" size={16}/> Trading · {meta.name}
        <span className="sb-live"><span className="sb-live-dot"></span>open</span>
        <button className="tiny-btn" style={{marginLeft:'auto'}} onClick={()=>Game.setShopOpen(false)}>Close shop</button>
      </h2>

      {/* item browser — Dota-2 scroll list; click to spotlight on the Main screen */}
      <div className="sb-browser">
        <div className="sb-browse-head">
          <span><Icon name="cards" size={13}/> Wares · {items.length}</span>
          <span className="al-sub" style={{textTransform:'none'}}>{spot ? `Spotlit: ${spot.name}` : 'Tap an item to reveal it on the big screen'}</span>
          {spot && <button className="tiny-btn" style={{marginLeft:'auto'}} onClick={()=>Game.clearSpotlight()}>Clear spotlight</button>}
        </div>
        <div className="sb-browse-list no-scrollbar">
          {items.length===0 && <div className="muted" style={{fontSize:'.74rem',padding:'.6rem'}}>No wares — stock this shop in the Shop Forge.</div>}
          {items.map(it=>{
            const live = spot&&spot.id===it.id;
            const oos = it.stock!=null && it.stock<=0;
            return (
            <div key={it.id} className={`sb-ware type-${it.type} ${live?'on':''} ${oos?'oos':''}`}>
              <span className="sb-ware-ico">{it.img ? <img className="sb-ware-img" src={it.img} alt=""/> : <Icon name={iconFor(it.name)} size={24}/>}</span>
              <span className="sb-ware-info">
                <span className="sb-ware-name">{it.name}{oos && <span className="sb-ware-oos">SOLD OUT</span>}</span>
                <span className="sb-ware-meta">
                  <span className="sb-ware-cost"><Icon name="coin" size={11}/> {it.cost}</span>
                  {(it.reqs||[]).map((r,i)=> <span key={i} className="sb-ware-req">{r.n} {r.stat}</span>)}
                </span>
              </span>
              <span className="sb-ware-ctrls">
                <span className="sb-stock" title="Stock (∞ = unlimited)">
                  <button className="sb-step" onClick={()=>Game.setStock(game.shop.type, it.id, it.stock==null?0:it.stock-1)}>−</button>
                  <span className="sb-stock-n">{it.stock==null?'∞':it.stock}</span>
                  <button className="sb-step" onClick={()=>Game.setStock(game.shop.type, it.id, it.stock==null?1:it.stock+1)}>+</button>
                  <button className="sb-step inf" title="Unlimited" onClick={()=>Game.setStock(game.shop.type, it.id, null)}>∞</button>
                </span>
                <span className="sb-describe">
                  <span className="sb-describe-lbl">Describe</span>
                  <button className={`sb-desc-btn all ${live?'on':''}`} onClick={()=>live?Game.clearSpotlight():Game.spotlightItem(it)} title="Reveal a big showcase to ALL players on the Main screen">{live?'● Live':'All'}</button>
                  {game.players.map(p=>(
                    <button key={p.id} className="sb-desc-btn" onClick={()=>Game.pushIntel(p.id, `${it.name} — ${it.desc||'(no description)'}`)} title={`Send this description privately to ${p.name.split(' ')[0]}`}>{p.name.split(' ')[0].slice(0,5)}</button>
                  ))}
                </span>
              </span>
            </div>
            );
          })}
        </div>
      </div>

      <div className="sb-grid">
        {game.players.length===0 && <div className="muted" style={{fontSize:'.78rem'}}>No heroes yet.</div>}
        {game.players.map(p=>{
          const cart = p.cart||[]; const cartTotal = cart.reduce((s,c)=>s+(c.cost||0),0);
          const buys = (p.requests||[]).filter(r=>r.kind==='buy');
          return (
            <div key={p.id} className="sb-card">
              <div className="sb-head">
                <span className="sb-name">{p.name.split(' ')[0]}</span>
                <span className="sb-coins"><Icon name="coin" size={13}/> {p.coins}</span>
              </div>

              {buys.length>0 && buys.map(r=>(
                <div key={r.id} className="sb-req">
                  <div className="sb-req-top">
                    <span className="sb-req-title"><Icon name="cart" size={13}/> Buy request · <span className="gold">{r.total}g</span></span>
                    <span className={`sb-afford ${p.coins>=r.total?'ok':'no'}`}>{p.coins>=r.total?'can afford':'short'}</span>
                  </div>
                  <div className="sb-req-items">
                    {(r.items||[]).map(it=> <span key={it.uid} className="sb-chip"><Icon name={iconFor(it.name)} size={11}/> {it.name} <span className="muted">{it.cost}g</span></span>)}
                  </div>
                  <div className="sb-req-actions">
                    <button className="tiny-btn heal-outline" onClick={()=>Game.resolveBuy(p.id,r.id,true)}>Accept · −{r.total}g</button>
                    <button className="tiny-btn danger-outline" onClick={()=>Game.resolveBuy(p.id,r.id,false)}>Decline</button>
                  </div>
                </div>
              ))}

              <div className="sb-sub">In cart {cart.length>0 && <span className="muted">· {cartTotal}g</span>}</div>
              {cart.length===0
                ? <div className="sb-empty">— nothing yet —</div>
                : <div className="sb-chips">{cart.map(c=> <span key={c.uid} className="sb-chip"><Icon name={iconFor(c.name)} size={11}/> {c.name}</span>)}</div>}

              <div className="sb-sub">Inventory <span className="muted">· {(p.weapons||[]).length + (p.items||[]).length}</span></div>
              {((p.weapons||[]).length + (p.items||[]).length)===0
                ? <div className="sb-empty">— empty —</div>
                : <div className="sb-chips">
                    {(p.weapons||[]).map(w=> <span key={w.id||w.name} className="sb-chip weapon"><Icon name={iconFor(w.name||w)} size={11}/> {w.name||w}</span>)}
                    {(p.items||[]).map(it=> <span key={it.id||it.name} className="sb-chip"><Icon name={iconFor(it.name||it)} size={11}/> {it.name||it}</span>)}
                  </div>}
            </div>
          );
        })}
      </div>
    </div>
  );
}

/* ============================================================
   TARGET MENU — reusable "where to show this?" control for any
   image+lore asset (enemy · knowledge · map). All four routes:
   Main · Main + intel · → a player · → a player + intel.
   ============================================================ */
function TargetMenu({ game, entry, kind, children, triggerClass, stage }){
  const [open, setOpen] = React.useState(false);
  const [pos, setPos] = React.useState({top:0, left:0, drop:'down'});
  const [intel, setIntel] = React.useState(true);   // include the description?
  const btnRef = React.useRef(null);
  const popRef = React.useRef(null);
  const hasDesc = !!(entry.desc && entry.desc.trim());
  const isMap = kind==='map';

  // position the fixed popup against the trigger; flip up if near the bottom
  const place = ()=>{
    const b = btnRef.current && btnRef.current.getBoundingClientRect();
    if(!b) return;
    const W = 240, H = 250;
    let left = Math.min(b.right - W, window.innerWidth - W - 8);
    left = Math.max(8, left);
    const below = window.innerHeight - b.bottom;
    const drop = below < H+12 ? 'up' : 'down';
    const top = drop==='up' ? b.top - 6 : b.bottom + 6;
    setPos({ top, left, drop });
  };
  const toggle = ()=>{ if(!open){ place(); } setOpen(o=>!o); };
  React.useEffect(()=>{
    if(!open) return;
    const onDoc = (e)=>{ if(popRef.current && !popRef.current.contains(e.target) && btnRef.current && !btnRef.current.contains(e.target)) setOpen(false); };
    const onScroll = ()=> setOpen(false);
    document.addEventListener('mousedown', onDoc);
    window.addEventListener('resize', onScroll, true);
    return ()=>{ document.removeEventListener('mousedown', onDoc); window.removeEventListener('resize', onScroll, true); };
  }, [open]);

  const payload = { id:entry.id, name:entry.name, src:entry.src||'', kind:kind, desc: (intel && hasDesc) ? entry.desc : '' };
  const toMain = ()=>{
    if(isMap && !(intel && hasDesc)) Game.showMapOnMain(entry);
    else Game.showCodexOnMain(payload);
  };
  const toPlayer = (pid)=>{
    if(isMap) Game.shareMapToPlayer(pid, (intel&&hasDesc) ? {...entry} : {...entry, desc:''});
    else Game.pushKnowledge(pid, {...payload});
  };
  return (
    <div className="tmenu">
      <button className={`tmenu-trigger ${open?'on':''} ${triggerClass||''}`} ref={btnRef} onClick={toggle}>{children || 'Show ▾'}</button>
      {open && (
        <div ref={popRef} className={`tmenu-pop drop-${pos.drop}`}
             style={{ position:'fixed', top:pos.top, left:pos.left, transform: pos.drop==='up'?'translateY(-100%)':'none' }}>
          {stage && (
            <button className={`tmenu-opt stagefig ${stage.on?'on':''}`} onClick={()=>{stage.onClick(); setOpen(false);}}>
              <Icon name="skull" size={13}/> {stage.on?'● Remove from stage':'Stage figures on Main'}
            </button>
          )}
          <label className={`tmenu-intel ${!hasDesc?'disabled':''}`} title={hasDesc?'Include the description/lore':'No description on this entry'}>
            <input type="checkbox" checked={intel&&hasDesc} disabled={!hasDesc} onChange={e=>setIntel(e.target.checked)}/>
            Include intel{!hasDesc && ' (none)'}
          </label>
          <button className="tmenu-opt" onClick={()=>{toMain(); setOpen(false);}}>
            <Icon name="eye" size={13}/> {kind==='enemy'||kind==='boss'?'Danger card on Main':'Main screen'}{intel&&hasDesc?' + intel':''}
          </button>
          <div className="tmenu-sub">To a player{intel&&hasDesc?' + intel':''}</div>
          <div className="tmenu-players">
            {game.players.length===0 && <span className="muted" style={{fontSize:'.7rem'}}>no heroes yet</span>}
            {game.players.map(p=>(
              <button key={p.id} className="tmenu-pbtn" onClick={()=>{toPlayer(p.id); setOpen(false);}}>{p.name.split(' ')[0]}</button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}
window.TargetMenu = TargetMenu;

/* ============================================================
   FOE SHOW MENU — on/off toggle buttons (green = on, white = off)
   for a SELECTED foe: figures on Main, danger card (+intel), and
   per-player (image / +intel). Lets the moderator turn each OFF.
   ============================================================ */
function FoeShowMenu({ game, entry }){
  const [open, setOpen] = React.useState(false);
  const [pos, setPos] = React.useState({top:0, left:0, drop:'down'});
  const btnRef = React.useRef(null);
  const popRef = React.useRef(null);
  const hasDesc = !!(entry.desc && entry.desc.trim());

  const place = ()=>{
    const b = btnRef.current && btnRef.current.getBoundingClientRect();
    if(!b) return;
    const W = 260, H = 300;
    let left = Math.min(b.right - W, window.innerWidth - W - 8); left = Math.max(8, left);
    const below = window.innerHeight - b.bottom;
    const drop = below < H+12 ? 'up' : 'down';
    const top = drop==='up' ? b.top - 6 : b.bottom + 6;
    setPos({ top, left, drop });
  };
  const toggle = ()=>{ if(!open){ place(); } setOpen(o=>!o); };
  React.useEffect(()=>{
    if(!open) return;
    const onDoc = (e)=>{ if(popRef.current && !popRef.current.contains(e.target) && btnRef.current && !btnRef.current.contains(e.target)) setOpen(false); };
    const onScroll = ()=> setOpen(false);
    document.addEventListener('mousedown', onDoc);
    window.addEventListener('resize', onScroll, true);
    return ()=>{ document.removeEventListener('mousedown', onDoc); window.removeEventListener('resize', onScroll, true); };
  }, [open]);

  const figureOn = game.mainEnemies.some(e=>e.id===entry.id);
  const dangerOn = game.mainCodex && game.mainCodex.id===entry.id;
  const playerImg = (p)=>{ const k=(p.knowledge||[]).find(x=>x.id===entry.id); return k && !(k.desc&&k.desc.trim()); };
  const playerIntel = (p)=>{ const k=(p.knowledge||[]).find(x=>x.id===entry.id); return k && (k.desc&&k.desc.trim()); };

  const Toggle = ({on, label, onClick, disabled})=> (
    <button className={`foe-toggle ${on?'on':'off'} ${disabled?'disabled':''}`} disabled={disabled} onClick={onClick}>
      <span className="foe-toggle-dot"></span>
      <span className="foe-toggle-lbl">{label}</span>
      <span className="foe-toggle-state">{on?'ON':'OFF'}</span>
    </button>
  );

  return (
    <div className="tmenu">
      <button className={`tmenu-trigger ${open?'on':''}`} ref={btnRef} onClick={toggle}>Show ▾</button>
      {open && (
        <div ref={popRef} className={`tmenu-pop foe-show drop-${pos.drop}`}
             style={{ position:'fixed', top:pos.top, left:pos.left, transform: pos.drop==='up'?'translateY(-100%)':'none' }}>
          <div className="tmenu-sub">On the Main screen</div>
          <Toggle on={figureOn} label="Figure on stage" onClick={()=>Game.toggleFoeFigure(entry)}/>
          <Toggle on={dangerOn} label={hasDesc?'Danger card + intel':'Danger card'}
            onClick={()=> dangerOn ? Game.clearMainCodex() : Game.showCodexOnMain({id:entry.id,kind:entry.kind,name:entry.name,src:entry.src||'',desc:hasDesc?entry.desc:''})}/>
          {game.players.length>0 && <>
            <div className="tmenu-sub">To a player — image</div>
            <div className="foe-player-row">
              {game.players.map(p=>{
                const on = playerImg(p);
                return <button key={p.id} className={`foe-pchip ${on?'on':'off'}`}
                  onClick={()=> on ? Game.revokeKnowledge(p.id, entry.id) : Game.pushKnowledge(p.id, {id:entry.id,kind:entry.kind,name:entry.name,src:entry.src||'',desc:''})}>
                  <span className="foe-toggle-dot"></span>{p.name.split(' ')[0]}</button>;
              })}
            </div>
            <div className="tmenu-sub">To a player — + intel</div>
            <div className="foe-player-row">
              {game.players.map(p=>{
                const on = playerIntel(p);
                return <button key={p.id} className={`foe-pchip ${on?'on':'off'} ${!hasDesc&&!on?'disabled':''}`} disabled={!hasDesc&&!on}
                  onClick={()=> on ? Game.revokeKnowledge(p.id, entry.id) : Game.pushKnowledge(p.id, {id:entry.id,kind:entry.kind,name:entry.name,src:entry.src||'',desc:entry.desc})}>
                  <span className="foe-toggle-dot"></span>{p.name.split(' ')[0]}</button>;
              })}
            </div>
          </>}
        </div>
      )}
    </div>
  );
}
window.FoeShowMenu = FoeShowMenu;

/* ============================================================
   ASSET SHOW MENU — on/off toggle menu (green = on, white = off)
   for KNOWLEDGE and MAPS: reveal on Main (+intel) and per-player
   (image / +intel). Lets the moderator turn each route OFF.
   ============================================================ */
function AssetShowMenu({ game, entry, kind }){
  const [open, setOpen] = React.useState(false);
  const [pos, setPos] = React.useState({top:0, left:0, drop:'down'});
  const btnRef = React.useRef(null);
  const popRef = React.useRef(null);
  const hasDesc = !!(entry.desc && entry.desc.trim());
  const isMap = kind==='map';

  const place = ()=>{
    const b = btnRef.current && btnRef.current.getBoundingClientRect();
    if(!b) return;
    const W = 260, H = 300;
    let left = Math.min(b.right - W, window.innerWidth - W - 8); left = Math.max(8, left);
    const below = window.innerHeight - b.bottom;
    const drop = below < H+12 ? 'up' : 'down';
    const top = drop==='up' ? b.top - 6 : b.bottom + 6;
    setPos({ top, left, drop });
  };
  const toggle = ()=>{ if(!open){ place(); } setOpen(o=>!o); };
  React.useEffect(()=>{
    if(!open) return;
    const onDoc = (e)=>{ if(popRef.current && !popRef.current.contains(e.target) && btnRef.current && !btnRef.current.contains(e.target)) setOpen(false); };
    const onScroll = ()=> setOpen(false);
    document.addEventListener('mousedown', onDoc);
    window.addEventListener('resize', onScroll, true);
    return ()=>{ document.removeEventListener('mousedown', onDoc); window.removeEventListener('resize', onScroll, true); };
  }, [open]);

  const codexHere = game.mainCodex && game.mainCodex.id===entry.id;
  const codexIntel = codexHere && game.mainCodex.desc && game.mainCodex.desc.trim();
  const mapHere = game.mainMap && game.mainMap.id===entry.id;
  // main routes
  const mainImgOn = isMap ? mapHere : (codexHere && !codexIntel);
  const mainIntelOn = codexHere && codexIntel;
  const payload = (withDesc)=> ({ id:entry.id, kind:isMap?'knowledge':kind, name:entry.name, src:entry.src||'', desc: withDesc?entry.desc:'' });
  const toggleMainImg = ()=>{
    if(mainImgOn){ isMap ? Game.clearMainMap() : Game.clearMainCodex(); }
    else { isMap ? Game.shareMapToMain(entry) : Game.showCodexOnMain(payload(false)); }
  };
  const toggleMainIntel = ()=>{
    if(mainIntelOn){ Game.clearMainCodex(); }
    else { Game.showCodexOnMain(payload(true)); }
  };
  // player routes
  const pEntry = (p)=> isMap ? (p.maps||[]).find(m=>m.id===entry.id) : (p.knowledge||[]).find(k=>k.id===entry.id);
  const pImgOn = (p)=>{ const e=pEntry(p); return e && !(e.desc&&e.desc.trim()); };
  const pIntelOn = (p)=>{ const e=pEntry(p); return e && (e.desc&&e.desc.trim()); };
  const giveImg = (p)=> isMap ? Game.shareMapToPlayer(p.id, {...entry, desc:''}) : Game.pushKnowledge(p.id, payload(false));
  const giveIntel = (p)=> isMap ? Game.shareMapToPlayer(p.id, {...entry}) : Game.pushKnowledge(p.id, payload(true));
  const takeBack = (p)=> isMap ? Game.revokeMapFromPlayer(p.id, entry.id) : Game.revokeKnowledge(p.id, entry.id);

  const Toggle = ({on, label, onClick, disabled})=> (
    <button className={`foe-toggle ${on?'on':'off'} ${disabled?'disabled':''}`} disabled={disabled} onClick={onClick}>
      <span className="foe-toggle-dot"></span>
      <span className="foe-toggle-lbl">{label}</span>
      <span className="foe-toggle-state">{on?'ON':'OFF'}</span>
    </button>
  );

  return (
    <div className="tmenu">
      <button className={`tmenu-trigger ${open?'on':''}`} ref={btnRef} onClick={toggle}>Show ▾</button>
      {open && (
        <div ref={popRef} className={`tmenu-pop foe-show drop-${pos.drop}`}
             style={{ position:'fixed', top:pos.top, left:pos.left, transform: pos.drop==='up'?'translateY(-100%)':'none' }}>
          <div className="tmenu-sub">On the Main screen</div>
          <Toggle on={mainImgOn} label={isMap?'Show map on Main':'Reveal on Main'} onClick={toggleMainImg}/>
          <Toggle on={mainIntelOn} label={hasDesc?'Main + intel':'Main + intel (none)'} disabled={!hasDesc && !mainIntelOn} onClick={toggleMainIntel}/>
          {game.players.length>0 && <>
            <div className="tmenu-sub">To a player — image</div>
            <div className="foe-player-row">
              {game.players.map(p=>{
                const on = pImgOn(p);
                return <button key={p.id} className={`foe-pchip ${on?'on':'off'}`}
                  onClick={()=> on ? takeBack(p) : giveImg(p)}>
                  <span className="foe-toggle-dot"></span>{p.name.split(' ')[0]}</button>;
              })}
            </div>
            <div className="tmenu-sub">To a player — + intel</div>
            <div className="foe-player-row">
              {game.players.map(p=>{
                const on = pIntelOn(p);
                return <button key={p.id} className={`foe-pchip ${on?'on':'off'} ${!hasDesc&&!on?'disabled':''}`} disabled={!hasDesc&&!on}
                  onClick={()=> on ? takeBack(p) : giveIntel(p)}>
                  <span className="foe-toggle-dot"></span>{p.name.split(' ')[0]}</button>;
              })}
            </div>
          </>}
        </div>
      )}
    </div>
  );
}
window.AssetShowMenu = AssetShowMenu;

/* ============================================================
   MAIN-SWITCH CONFIRM — "are you sure?" when the moderator takes
   over the Main screen while a live puzzle is mid-progress.
   ============================================================ */
function MainSwitchConfirm({ game }){
  const cs = game.confirmSwitch;
  if(!cs) return null;
  return (
    <div className="switch-confirm-bg" onClick={()=>Game.confirmSwitchNo()}>
      <div className="switch-confirm liquid-glass" onClick={e=>e.stopPropagation()}>
        <div className="sc-ico">⚠</div>
        <h3 className="display">Switch the Main screen?</h3>
        <p className="sc-body">
          <b>{cs.fromLabel}</b> is live and players may be in the middle of it.
          Showing <b>{cs.toLabel}</b> will shut it down.
        </p>
        <div className="sc-actions">
          <button className="sc-cancel glass-btn" onClick={()=>Game.confirmSwitchNo()}>Keep {cs.fromLabel}</button>
          <button className="sc-go" onClick={()=>Game.confirmSwitchYes()}>Yes, switch →</button>
        </div>
      </div>
    </div>
  );
}
window.MainSwitchConfirm = MainSwitchConfirm;

/* compact map/handout uploader for the Main Stage maps mode */
function MapUploader({ game }){
  const ref = React.useRef(null);
  const [busy, setBusy] = React.useState(false);
  const onFiles = async (files)=>{
    if(!files || !files.length) return; setBusy(true);
    for(const f of Array.from(files)){
      if(!f.type.startsWith('image/')) continue;
      const src = await compressImage(f, 1200, 0.6);
      if(src) Game.addMap(f.name.replace(/\.[^.]+$/,'').slice(0,40)||'Map', src);
    }
    setBusy(false); if(ref.current) ref.current.value='';
  };
  return (
    <div className="ctrl-row" style={{marginTop:0, marginBottom:'.5rem'}}>
      <button className="tiny-btn" onClick={()=>ref.current&&ref.current.click()} disabled={busy}>{busy?'Processing…':'⤓ Upload map / handout'}</button>
      <input ref={ref} type="file" accept="image/*" multiple style={{display:'none'}} onChange={e=>onFiles(e.target.files)}/>
    </div>
  );
}
window.MapUploader = MapUploader;

/* moderator table controls — global Dice & Card-play modes (off / private / public) */
function DiceControl({ game }){
  const [open, setOpen] = React.useState(false);
  const dmode = (game.dice && game.dice.mode) || 'private';
  const cmode = (game.cardPlay && game.cardPlay.mode) || 'public';
  const reqs = (game.dice && game.dice.requests) || {};
  const pendingCount = Object.keys(reqs).length;
  const Seg = ({val, set, cur})=> (
    <span className="trule-seg">
      {['off','private','public'].map(m=>(
        <button key={m} className={`trule-opt ${cur===m?'on':''} ${m}`} onClick={()=>set(m)}>{m==='public'?'Main':m==='private'?'Private':'Off'}</button>
      ))}
    </span>
  );
  return (
    <span className="dice-ctrl-wrap">
      <button className={`dice-ctrl-btn ${pendingCount>0?'pending':''}`} onClick={()=>setOpen(o=>!o)}>
        ⚅ Table{pendingCount>0 && <span className="dice-ctrl-badge">{pendingCount}</span>}
      </button>
      {open && (
        <div className="dice-ctrl-pop trule-pop" onMouseLeave={()=>setOpen(false)}>
          <div className="trule-row">
            <span className="trule-lbl">⚅ Dice rolls</span>
            <Seg cur={dmode} set={m=>Game.setDiceMode(m)}/>
          </div>
          <div className="trule-row">
            <span className="trule-lbl">🃏 Card play</span>
            <Seg cur={cmode} set={m=>Game.setCardMode(m)}/>
          </div>
          <div className="trule-hint">Off freezes it for everyone · Private = that player's screen only · Main = also shown on the shared screen. Approve individual dice rolls in each hero's party-control card.</div>
        </div>
      )}
    </span>
  );
}
window.DiceControl = DiceControl;

function MainStage({ game }){
  const lib = useSetup();
  const staged = game.stagedFoes || [];
  // what is actually on the shared screen right now
  const cur = game.gate.open ? 'gate'
    : game.circuit.open ? 'circuit'
    : game.defuser.open ? 'defuser'
    : game.constellation.open ? 'constellation'
    : game.auction.open ? 'auction'
    : game.vote.open ? 'vote'
    : game.lightbender.open ? 'lightbender'
    : game.wager.open ? 'wager'
    : game.alchemy.open ? 'alchemy'
    : game.ritual.active ? 'ritual'
    : game.totems.active ? 'totems'
    : game.battle.active ? 'battle'
    : game.shop.open ? 'shop'
    : (staged.length || game.mainEnemies.length) ? 'enemy'
    : game.mainNpc ? 'npc'
    : 'scene';
  const [mode, setMode] = useState(cur);

  const curLabel = game.gate.open ? 'The Sealed Gate'
    : game.circuit.open ? 'The Circuit Lock'
    : game.defuser.open ? 'The Defuser'
    : game.constellation.open ? 'Constellation'
    : game.auction.open ? 'Loot Auction'
    : game.vote.open ? 'A Vote'
    : game.lightbender.open ? 'The Light-Bender'
    : game.wager.open ? 'The Wager'
    : game.alchemy.open ? 'The Cauldron'
    : game.ritual.active ? 'Ritual Interrupt'
    : game.totems.active ? 'Phase Totems'
    : game.battle.active ? 'Action Sequence'
    : game.shop.open ? SHOP_TYPES[game.shop.type].label
    : game.mainEnemies.length ? `${game.mainEnemies.length} foe${game.mainEnemies.length>1?'s':''} on stage`
    : staged.length ? `${staged.length} foe${staged.length>1?'s':''} selected`
    : game.mainNpc ? game.mainNpc.name
    : sceneInfo(game.scene).label;

  const MODES = [
    { id:'scene',  icon:'eye',   label:'Scene' },
    { id:'npc',    icon:'book',  label:'NPC' },
    { id:'shop',   icon:'buy',   label:'Shop' },
    { id:'enemy',  icon:'skull', label:'Enemies' },
    { id:'knowledge', icon:'book', label:'Knowledge' },
    { id:'maps',   icon:'scroll', label:'Maps' },
    { id:'story',  icon:'book',  label:'Story' },
    { id:'battle', icon:'sword', label:'Battle' },
    { id:'gate',   icon:'music', label:'Beat Gate' },
    { id:'circuit',icon:'key',   label:'Circuit' },
    { id:'lightbender', icon:'spark', label:'Light' },
    { id:'defuser',icon:'skull', label:'Defuser' },
    { id:'alchemy',icon:'use',   label:'Brew' },
    { id:'constellation', icon:'eye', label:'Stars' },
    { id:'ritual', icon:'spark', label:'Ritual' },
    { id:'totems', icon:'shield',label:'Totems' },
    { id:'breath', icon:'use',   label:'Breath' },
    { id:'auction',icon:'buy',   label:'Auction' },
    { id:'wager',  icon:'buy',   label:'Wager' },
    { id:'vote',   icon:'book',  label:'Vote' },
    { id:'bonds',  icon:'ring',  label:'Bonds' },
    { id:'spectral', icon:'eye', label:'Spectral' },
    { id:'secrets',icon:'eye',   label:'Secrets' },
  ];

  const POS3 = ['Left','Centre','Right'];
  const posLabel = (i, n)=> n===1 ? 'Centre' : n===2 ? ['Left','Right'][i] : n===3 ? POS3[i] : `#${i+1}`;

  return (
    <>
    <div className="host-card liquid-glass stage-card">
      <h2 className="section-label"><Icon name="eye" size={16}/> Main Stage · what the room sees</h2>
      <div className="stage-now">
        <span className="stage-now-lbl">Now showing</span>
        <span className="stage-now-pill"><span className="live-dot"></span>{curLabel}</span>
      </div>

      <div className="stage-grid">
        {MODES.map(m=>{
          const active = cur===m.id;
          return (
            <button key={m.id} className={`stage-btn ${active?'on':''} ${mode===m.id?'sel':''}`}
              onClick={()=>{
                setMode(m.id);
                if(m.id==='scene') Game.showStage('scene', game.scene);
                else if(m.id==='shop') Game.showStage('shop', game.shop.type);
                else if(m.id==='gate') Game.showStage('gate');
                else if(m.id==='circuit'){ if(!game.circuit.open) Game.pushCircuit(game.circuit.difficulty||'normal'); }
                else if(m.id==='lightbender'){ if(!game.lightbender.open) Game.pushLightBender({}); }
                else if(m.id==='alchemy'){ if(!game.alchemy.open) Game.startBrew({}); }
                else if(m.id==='ritual'){ if(!game.ritual.active) Game.startRitual('The Unmaking', 30000, 100, {}); }
                else if(m.id==='totems'){ if(!game.totems.active) Game.spawnTotems({}); }
                else if(m.id==='wager'){ /* panel composes */ }
                else if(m.id==='battle') Game.startBattle();
                // npc & enemy: just open the picker below (selection does the showing)
              }}>
              <span className="sb-top"><Icon name={m.icon} size={15}/> {m.label}</span>
            </button>
          );
        })}
      </div>

      {/* contextual reveal for the selected mode */}
      <div className="stage-reveal">
        {mode==='scene' && (
          <div className="stage-chip-row">
            {ALL_SCENES.map(s=>(
              <button key={s} className={`chip-btn ${cur==='scene'&&game.scene===s?'on':''}`}
                onClick={()=>Game.showStage('scene', s)}>{sceneInfo(s).label}</button>
            ))}
          </div>
        )}

        {mode==='shop' && (
          <>
            <div className="stage-chip-row">
              {Object.keys(SHOP_TYPES).map((key)=>(
                <button key={key} className={`chip-btn ${game.shop.open&&game.shop.type===key?'on':''}`}
                  onClick={()=>Game.openShop(key)}>{game.shop.shops[key].name}</button>
              ))}
              <button className="tiny-btn" onClick={()=>Game.setShopOpen(false)} disabled={!game.shop.open} style={{marginLeft:'auto'}}>Close shop</button>
            </div>
            {game.shop.open && (()=>{
              const total = (game.shop.shops[game.shop.type].items||[]).length;
              const pages = Math.max(1, Math.ceil(total/12));
              const page = Math.min(game.shop.page||0, pages-1);
              if(pages<=1) return null;
              return (
                <div className="stage-chip-row" style={{marginTop:'.5rem'}}>
                  <span className="al-sub">Page on Main screen:</span>
                  {Array.from({length:pages}).map((_,i)=>(
                    <button key={i} className={`chip-btn ${i===page?'on':''}`} onClick={()=>Game.setShopPage(i)}>{i+1}</button>
                  ))}
                </div>
              );
            })()}
            <p className="muted stage-hint" style={{marginTop:'.5rem'}}>Opens on the Main screen and every hero's handset. {(game.shop.shops[game.shop.type].items||[]).length>12 && 'More than 12 items — pick which page shows on the Main screen.'} Stock &amp; style shops in the Shop Forge panel below.</p>
          </>
        )}

        {mode==='npc' && (
          <div className="stage-pick">
            <div className="section-sub">Show an NPC on the left {game.mainNpc && <button className="tiny-btn" style={{float:'right'}} onClick={()=>Game.clearMainNpc()}>Clear</button>}</div>
            {lib.npc.length===0
              ? <div className="muted stage-pick-empty">No NPCs yet — add them in the Setup panel.</div>
              : <div className="stage-pick-grid">
                  {lib.npc.map(n=>{
                    const on = game.mainNpc && game.mainNpc.id===n.id;
                    return (
                      <button key={n.id} className={`pick-tile ${on?'on':''}`} onClick={()=> on ? Game.clearMainNpc() : Game.showNpcOnMain(n)}>
                        {n.src ? <img src={n.src} alt={n.name}/> : <span className="pick-noimg"><Icon name="book" size={18}/></span>}
                        <span className="pick-name">{n.name}{n.sound?' ♪':''}</span>
                      </button>
                    );
                  })}
                </div>}
          </div>
        )}

        {mode==='enemy' && (
          <div className="stage-pick">
            <div className="section-sub">
              Tap a foe to select it (it won’t show yet) — then use <b>Show</b> to choose where it appears
              {staged.length>0 && <button className="tiny-btn" style={{float:'right'}} onClick={()=>Game.clearStagedFoes()}>Clear all</button>}
            </div>
            {(lib.enemy.length+lib.boss.length)===0
              ? <div className="muted stage-pick-empty">No enemies or bosses yet — add them in the Setup panel.</div>
              : <div className="stage-pick-grid">
                  {[...lib.enemy, ...lib.boss].map(e=>{
                    const isOn = staged.some(x=>x.id===e.id);
                    const idx = staged.findIndex(x=>x.id===e.id);
                    return (
                      <button key={e.id} className={`pick-tile ${isOn?'on':''}`} onClick={()=>Game.toggleStageFoe(e)}
                        title={isOn?'Selected — tap to deselect':'Tap to select'}>
                        {e.src ? <img src={e.src} alt={e.name}/> : <span className="pick-noimg"><Icon name="skull" size={18}/></span>}
                        <span className="pick-name">{e.name}</span>
                        {isOn && <span className="pick-pos">{posLabel(idx, staged.length)}</span>}
                        {isOn && <span className="pick-check">✓</span>}
                      </button>
                    );
                  })}
                </div>}
            {staged.length>0 && (
              <div className="stage-counts">
                {staged.map((e,i)=>(
                  <div key={e.id} className="stage-count-row">
                    <span className="scr-pos">{posLabel(i, staged.length)}</span>
                    <span className="scr-name">{e.name}</span>
                    <span className="amt-step">
                      <button onClick={()=>Game.setStagedFoeCount(e.id, e.count-1)}>−</button>
                      <span className="scr-n">×{e.count}</span>
                      <button onClick={()=>Game.setStagedFoeCount(e.id, e.count+1)}>+</button>
                    </span>
                    <FoeShowMenu game={game} entry={e}/>
                  </div>
                ))}
              </div>
            )}
          </div>
        )}

        {mode==='gate' && (
          <div className="stage-chip-row">
            <span className="muted stage-hint">⛬ The Sealed Gate is on the Main screen. Configure the beat &amp; arm it in the panel below ↓</span>
          </div>
        )}

        {mode==='knowledge' && (
          <div className="stage-pick">
            <div className="section-sub">Knowledge &amp; lore — choose where each shows {game.mainCodex && <button className="tiny-btn" style={{float:'right'}} onClick={()=>Game.clearMainCodex()}>Clear Main</button>}</div>
            {lib.knowledge.length===0
              ? <div className="muted stage-pick-empty">No knowledge entries yet — add them in the Setup panel.</div>
              : <div className="tasset-list">
                  {lib.knowledge.map(k=>(
                    <div key={k.id} className="tasset-row">
                      <div className="tasset-thumb">{k.src ? <img src={k.src} alt={k.name}/> : <Icon name="book" size={18}/>}</div>
                      <div className="tasset-info">
                        <div className="tasset-name">{k.name}</div>
                        {k.desc && <div className="tasset-desc">{k.desc}</div>}
                      </div>
                      <AssetShowMenu game={game} entry={k} kind="knowledge"/>
                    </div>
                  ))}
                </div>}
          </div>
        )}

        {mode==='maps' && (
          <div className="stage-pick">
            <div className="section-sub">Maps &amp; handouts — show on Main or hand to a player {game.mainMap && <button className="tiny-btn" style={{float:'right'}} onClick={()=>Game.clearMainMap()}>Clear Main</button>}</div>
            <MapUploader game={game}/>
            {(game.maps||[]).length===0
              ? <div className="muted stage-pick-empty">No maps yet — upload one above.</div>
              : <div className="tasset-list">
                  {(game.maps||[]).map(m=>(
                    <div key={m.id} className="tasset-row">
                      <div className="tasset-thumb">{m.src ? <img src={m.src} alt={m.name}/> : <Icon name="scroll" size={18}/>}</div>
                      <div className="tasset-info">
                        <div className="tasset-name">{m.name}</div>
                        {m.desc && <div className="tasset-desc">{m.desc}</div>}
                      </div>
                      <AssetShowMenu game={game} entry={m} kind="map"/>
                    </div>
                  ))}
                </div>}
          </div>
        )}

        {mode==='circuit' && (
          <div className="stage-chip-row">
            <span className="muted stage-hint">⛬ The Circuit Lock is on the Main screen. Set difficulty, assign rings &amp; run it in the panel below ↓</span>
          </div>
        )}

        {mode==='defuser' && (
          <div className="stage-chip-row">
            <span className="muted stage-hint">☠ The Defuser splits rules from controls. Pick modules, set the timer &amp; assign Operators/Readers below ↓</span>
          </div>
        )}
        {mode==='constellation' && (
          <div className="stage-chip-row">
            <span className="muted stage-hint">✦ Constellation — choose the figure, set tolerance &amp; assign layers below ↓</span>
          </div>
        )}
        {mode==='auction' && (
          <div className="stage-chip-row">
            <span className="muted stage-hint">⚖ Loot Auction — drop an item, set the timer &amp; run the sealed bids below ↓</span>
          </div>
        )}
        {mode==='vote' && (
          <div className="stage-chip-row">
            <span className="muted stage-hint">🗳 Branching Vote — compose the dilemma, choose secret/open &amp; tally below ↓</span>
          </div>
        )}
        {mode==='secrets' && (
          <div className="stage-chip-row">
            <span className="muted stage-hint">🎭 Secret Roles stay private — the Main screen never shows them. Assign &amp; reveal below ↓</span>
          </div>
        )}

        {mode==='battle' && (
          <div className="stage-chip-row">
            {!game.battle.active
              ? <button className="chip-btn" onClick={()=>Game.startBattle()}>▶ Begin the action</button>
              : <button className="chip-btn on" onClick={()=>Game.endBattle()}>End battle</button>}
            <span className="muted stage-hint">Manage turns, foes &amp; strikes in the Action Sequence panel.</span>
          </div>
        )}
      </div>
    </div>
    {mode==='gate' && (
      <div className="gate-popdown">
        <GatePanel game={game}/>
      </div>
    )}
    {mode==='circuit' && (
      <div className="gate-popdown">
        <CircuitHostPanel game={game}/>
      </div>
    )}
    {mode==='defuser' && (<div className="gate-popdown"><DefuserHostPanel game={game}/></div>)}
    {mode==='constellation' && (<div className="gate-popdown"><ConstellationHostPanel game={game}/></div>)}
    {mode==='auction' && (<div className="gate-popdown"><AuctionHostPanel game={game}/></div>)}
    {mode==='vote' && (<div className="gate-popdown"><VoteHostPanel game={game}/></div>)}
    {mode==='secrets' && (<div className="gate-popdown"><SecretsHostPanel game={game}/></div>)}
    {mode==='lightbender' && (<div className="gate-popdown"><LightBenderHostPanel game={game}/></div>)}
    {mode==='alchemy' && (<div className="gate-popdown"><AlchemyHostPanel game={game}/></div>)}
    {mode==='ritual' && (<div className="gate-popdown"><RitualHostPanel game={game}/></div>)}
    {mode==='totems' && (<div className="gate-popdown"><TotemsHostPanel game={game}/></div>)}
    {mode==='breath' && (<div className="gate-popdown"><BreathHostPanel game={game}/></div>)}
    {mode==='wager' && (<div className="gate-popdown"><WagerHostPanel game={game}/></div>)}
    {mode==='bonds' && (<div className="gate-popdown"><BondsHostPanel game={game}/></div>)}
    {mode==='spectral' && (<div className="gate-popdown"><SpectralHostPanel game={game}/></div>)}
    {mode==='story' && (<div className="gate-popdown"><StoryHostPanel game={game}/></div>)}
    </>
  );
}

/* ============================================================
   BEAT GATE — one panel that pops down under the Main Stage when the
   moderator selects Gate. Plan the beat (length · countdown · order ·
   destination) AND arm / operate it live, all in the Run Game realm.
   ============================================================ */
function GatePanel({ game }){
  const seqLen = (game.gate.seq && game.gate.seq.length) || 0;
  const [gateLen, setGateLen] = useState([4,8,10].includes(seqLen) ? seqLen : 8);
  const [gateSecs, setGateSecs] = useState(Math.round((game.gate.timeLimit||20000)/1000) || 20);
  const solved = game.gate.solved;
  const open = game.gate.open;
  // the puzzle is actually *running* (vs. the gate merely idling on Main)
  const armed = game.gate.demo || !!game.gate.deadline || (game.gate.progress&&game.gate.progress.length>0) || solved;
  const nextLabel = game.gate.nextScene ? sceneInfo(game.gate.nextScene).label : null;

  return (
    <div className="host-card liquid-glass gate-panel">
      <h2 className="section-label"><Icon name="music" size={16}/> Beat Gate · Sound Puzzle</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',letterSpacing:'.01em',margin:'0 0 .7rem'}}>
        Set the beat, the countdown and where the party lands once it opens — then arm &amp; play it live on the Main screen. Players echo the order from their handsets.
      </p>

      <div className="gate-panel-grid">
        {/* ---- PLAN ---- */}
        <div className="gate-col">
          <div className="section-sub">Beat length</div>
          <div className="radio-row">
            {[4,8,10].map(n=>(
              <button key={n} className={`chip-btn ${gateLen===n?'on':''}`} disabled={armed}
                onClick={()=>{ setGateLen(n); Game.generateGate(n, gateSecs); }}>{n} notes</button>
            ))}
          </div>

          <div className="section-sub">Countdown · {gateSecs}s</div>
          <div className="radio-row">
            {[10,20,30,45,60].map(s=>(
              <button key={s} className={`chip-btn ${gateSecs===s?'on':''}`} disabled={armed}
                onClick={()=>{ setGateSecs(s); Game.setGate({timeLimit:s*1000}); }}>{s}s</button>
            ))}
          </div>

          <div className="section-sub">On open → enter scene</div>
          <div className="stage-chip-row">
            <button className={`chip-btn ${!game.gate.nextScene?'on':''}`} onClick={()=>Game.setGateNextScene('')}>Stay</button>
            {ALL_SCENES.map(s=>(
              <button key={s} className={`chip-btn ${game.gate.nextScene===s?'on':''}`} onClick={()=>Game.setGateNextScene(s)}>{sceneInfo(s).label}</button>
            ))}
          </div>
        </div>

        {/* ---- OPERATE ---- */}
        <div className="gate-col">
          <div className="section-sub">Beat order · {seqLen} notes{game.gate.timeLimit?` · ${Math.round(game.gate.timeLimit/1000)}s`:''}</div>
          <div className="gate-order-row">
            {(seqLen?game.gate.seq:[]).map((n,i)=>(
              <span key={i} className="order-chip" style={{'--nc':NOTE_META[n].color, background:NOTE_META[n].color}}>
                <span className="oc-i">{i+1}</span>
              </span>
            ))}
            {!seqLen && <span className="muted" style={{fontSize:'.72rem'}}>Pick a length to conjure a beat.</span>}
          </div>

          <div className="section-sub">Who holds the beats</div>
          <div className="assign-row">
            {game.players.length===0
              ? <span className="muted" style={{fontSize:'.72rem'}}>No players yet.</span>
              : noteAssignments(game.players.length).map((notes,k)=> game.players[k] ? (
                  <span key={k} className="assign-chip">
                    {game.players[k].name.split(' ')[0]}:
                    {notes.map(n=> <span key={n} className="assign-dot" style={{background:NOTE_META[n].color}}></span>)}
                  </span>
                ) : null)}
          </div>

          <div className="ctrl-row" style={{marginTop:'.7rem', flexWrap:'wrap'}}>
            <button className="chip-btn" onClick={()=>{ if(!seqLen) Game.generateGate(gateLen, gateSecs); Game.armGate(); }} disabled={game.gate.demo}>
              {armed ? '↻ Re-arm & play →' : 'Arm & play on Main →'}
            </button>
            <button className="tiny-btn" onClick={()=>Game.generateGate(gateLen, gateSecs)} disabled={armed && !solved}>↻ New beat</button>
            {open && !solved && <button className="tiny-btn" onClick={()=>Game.playDemo()} disabled={game.gate.demo}>▶ Replay</button>}
            {open && !solved && armed && <button className="tiny-btn" onClick={()=>Game.resetGate()}>Reset</button>}
            {open && <button className="chip-btn on" onClick={()=>Game.closeGate()}>Hide gate</button>}
          </div>

          {open && (
            <div className="gate-live" style={{marginTop:'.6rem'}}>
              <span className="gate-live-dot"></span>
              {solved
                ? <span className="gold">Gate is OPEN</span>
                : game.gate.demo
                  ? <span>Playing the beat…</span>
                  : <span>Live · {game.gate.progress.length}/{seqLen} echoed · {game.gate.replays} chances left</span>}
            </div>
          )}

          {solved && (
            <div className="gate-pass" style={{marginTop:'.7rem'}}>
              <button className="chip-btn on" style={{width:'100%'}} onClick={()=>Game.enterAfterGate()}>
                {nextLabel ? `The party passes through → ${nextLabel}` : 'The party passes through'}
              </button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   CIRCUIT LOCK — moderator panel (sits beside GatePanel). Push the
   lock, set difficulty, assign rings to players, run surge / hints,
   and pass the party through once the current completes.
   ============================================================ */
function CircuitHostPanel({ game }){
  const c = game.circuit;
  const open = c.open, complete = c.complete;
  const live = circuitLiveness(c);
  const litCount = live.liveRing.filter(Boolean).length;
  const nextLabel = c.nextScene ? sceneInfo(c.nextScene).label : null;
  const diff = c.difficulty || 'normal';
  const gap = (!complete && live.firstGap) ? live.firstGap : null;

  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="key" size={16}/> Circuit Lock · Path Puzzle</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',letterSpacing:'.01em',margin:'0 0 .7rem'}}>
        Four concentric rings. Players rotate their ring from their handset to chain the current from the source to the core. The whole solution only shows here on the Main screen — they must look up and talk.
      </p>

      <div className="gate-panel-grid">
        {/* ---- PLAN ---- */}
        <div className="gate-col">
          <div className="section-sub">Difficulty</div>
          <div className="cl-diff-row">
            {[['easy','Easy'],['normal','Normal'],['hard','Hard · surge']].map(([v,l])=>(
              <button key={v} className={`chip-btn ${diff===v?'on':''}`}
                onClick={()=>Game.pushCircuit(v)}>{l}</button>
            ))}
          </div>
          <p className="muted" style={{fontSize:'.68rem',textTransform:'none',margin:'.4rem 0 0'}}>
            Picking a difficulty arms a fresh lock. Hard runs a power surge that jolts a random ring every {Math.round((c.surgeInterval||30000)/1000)}s.
          </p>

          <div className="section-sub" style={{marginTop:'.8rem'}}>On complete → enter scene</div>
          <div className="stage-chip-row">
            <button className={`chip-btn ${!c.nextScene?'on':''}`} onClick={()=>Game.setCircuitNextScene('')}>Stay</button>
            {ALL_SCENES.map(s=>(
              <button key={s} className={`chip-btn ${c.nextScene===s?'on':''}`} onClick={()=>Game.setCircuitNextScene(s)}>{sceneInfo(s).label}</button>
            ))}
          </div>

          <div className="section-sub" style={{marginTop:'.8rem'}}>Assign rings to players</div>
          {game.players.length===0
            ? <span className="muted" style={{fontSize:'.72rem'}}>No players yet — rings auto-assign on push.</span>
            : <div className="cl-assign-grid">
                {CIRCUIT_META.map((m,i)=>{
                  const r = c.rings[i];
                  return (
                    <div key={i} className="cl-assign" style={{'--rc':m.color}}>
                      <span className="cl-assign-dot"></span>
                      <span className="cl-assign-name">{m.label}</span>
                      <select className="tinput" value={(r&&r.owner)||''} disabled={!open}
                        onChange={e=>Game.assignRing(i, e.target.value)}>
                        {game.players.map(p=>(
                          <option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>
                        ))}
                      </select>
                    </div>
                  );
                })}
              </div>}
        </div>

        {/* ---- OPERATE ---- */}
        <div className="gate-col">
          {open && c.rings.length>0 && (
            <div className="cl-mini-row">
              {c.rings.map((r,i)=>(
                <div key={i} className="cl-mini">
                  <CircuitRingPreview ringIndex={i} circuit={c} live={live} size={62}/>
                  <span className="cl-mini-lbl">{CIRCUIT_META[i].label}</span>
                </div>
              ))}
            </div>
          )}

          <div className="ctrl-row" style={{flexWrap:'wrap'}}>
            {!open
              ? <button className="chip-btn" onClick={()=>Game.pushCircuit(diff)}>Push lock to Main →</button>
              : <>
                  <button className="tiny-btn" onClick={()=>Game.resetLock()} disabled={complete}>↻ Re-scramble</button>
                  <button className="tiny-btn" onClick={()=>Game.triggerSurge()} disabled={complete}>⚡ Surge now</button>
                  <button className={`tiny-btn ${c.surge?'heal-outline':''}`} onClick={()=>Game.setCircuitSurge(!c.surge)} disabled={complete}>Surge {c.surge?'ON':'OFF'}</button>
                </>}
          </div>

          {open && !complete && (
            <>
              <div className="section-sub" style={{marginTop:'.7rem'}}>Flash a hint</div>
              <div className="cl-hint-btns">
                {CIRCUIT_META.map((m,i)=>(
                  <button key={i} className="tiny-btn" style={{'--rc':m.color, boxShadow:`inset 0 0 0 1px ${m.color}`}}
                    onClick={()=>Game.grantHint(i)}>{m.label}</button>
                ))}
              </div>
            </>
          )}

          {open && (
            <div className="ctrl-row" style={{marginTop:'.7rem', flexWrap:'wrap'}}>
              {!complete && <button className="tiny-btn danger-outline" onClick={()=>Game.forceOpen()}>Force open</button>}
              <button className="chip-btn on" onClick={()=>Game.closeCircuit()}>Hide lock</button>
            </div>
          )}

          {open && (
            <div className="cl-status-line">
              <span className="gate-live-dot"></span>
              {complete
                ? <span className="gold">Circuit COMPLETE</span>
                : <span>Live · {litCount}/{c.rings.length} rings lit{gap ? ` · stuck at the ${CIRCUIT_META[gap.ring].label} ring` : ''}</span>}
            </div>
          )}

          {complete && (
            <div className="gate-pass" style={{marginTop:'.7rem'}}>
              <button className="chip-btn on" style={{width:'100%'}} onClick={()=>Game.enterAfterCircuit()}>
                {nextLabel ? `The party passes through → ${nextLabel}` : 'The party passes through'}
              </button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   THE DEFUSER — moderator panel
   ============================================================ */
function DefuserHostPanel({ game }){
  const d = game.defuser;
  const open = d.open;
  const [kinds, setKinds] = useState(['wires','glyphs','sequence']);
  const [secs, setSecs] = useState(240);
  const toggleKind = (k)=> setKinds(ks=> ks.includes(k) ? ks.filter(x=>x!==k) : [...ks, k]);
  const sceneL = d.nextScene ? sceneInfo(d.nextScene).label : null;

  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="skull" size={16}/> The Defuser · Comms Trap</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>
        Operators get a module's controls but no rules; Readers get the rules but no controls — and never for the same module. The table must describe &amp; direct out loud.
      </p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Modules</div>
          <div className="cl-diff-row">
            {Object.keys(DEFUSER_KINDS).map(k=>(
              <button key={k} className={`chip-btn ${kinds.includes(k)?'on':''}`} onClick={()=>toggleKind(k)}>{DEFUSER_KINDS[k].label}</button>
            ))}
          </div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Timer</div>
          <div className="cl-diff-row">
            {[[120,'2 min'],[240,'4 min'],[360,'6 min']].map(([v,l])=>(
              <button key={v} className={`chip-btn ${secs===v?'on':''}`} onClick={()=>setSecs(v)}>{l}</button>
            ))}
          </div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>On disarm → enter scene</div>
          <div className="stage-chip-row">
            <button className={`chip-btn ${!d.nextScene?'on':''}`} onClick={()=>Game.setDefuserNextScene('')}>Stay</button>
            {ALL_SCENES.map(s=>(<button key={s} className={`chip-btn ${d.nextScene===s?'on':''}`} onClick={()=>Game.setDefuserNextScene(s)}>{sceneInfo(s).label}</button>))}
          </div>
          <div className="ctrl-row" style={{marginTop:'.7rem'}}>
            <button className="chip-btn" disabled={kinds.length===0} onClick={()=>Game.pushDefuser({kinds, seconds:secs})}>{open?'Re-arm trap':'Arm the trap →'}</button>
          </div>
        </div>

        <div className="gate-col">
          {open ? (
            <>
              <div className="cl-status-line" style={{marginTop:0}}>
                <span className="gate-live-dot"></span>
                {d.status==='disarmed' ? <span className="gold">DISARMED</span>
                 : d.status==='boom' ? <span style={{color:'hsl(var(--damage))'}}>DETONATED</span>
                 : <span>Live · {d.modules.filter(m=>m.locked).length}/{d.modules.length} locked · {d.strikes}/{d.maxStrikes} strikes</span>}
              </div>
              <div className="section-sub" style={{marginTop:'.6rem'}}>Assign roles (Operator ≠ Reader)</div>
              <div className="df-assign-list">
                {d.modules.map(m=>{
                  const rule = d.rules.find(r=>r.moduleId===m.id);
                  return (
                    <div key={m.id} className="df-assign-row">
                      <span className="df-assign-mod">{m.label}</span>
                      <label className="df-assign-cell">
                        <span>Op</span>
                        <select className="tinput" value={m.operator||''} onChange={e=>Game.assignRole(e.target.value,'operator',m.id)}>
                          <option value="">—</option>
                          {game.players.map(p=><option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}
                        </select>
                      </label>
                      <label className="df-assign-cell">
                        <span>Read</span>
                        <select className="tinput" value={(rule&&rule.reader)||''} onChange={e=>Game.assignRole(e.target.value,'reader',m.id)}>
                          <option value="">—</option>
                          {game.players.map(p=><option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}
                        </select>
                      </label>
                      <button className="tiny-btn" title="Reveal this rule on the Main screen" onClick={()=>rule&&Game.grantDefuserHint(rule.id)}>Hint</button>
                    </div>
                  );
                })}
              </div>
              <div className="ctrl-row" style={{marginTop:'.6rem', flexWrap:'wrap'}}>
                {d.status==='live' && <button className="tiny-btn heal-outline" onClick={()=>Game.forceDisarm()}>Force disarm</button>}
                {d.status==='live' && <button className="tiny-btn danger-outline" onClick={()=>Game.onBoom()}>Trigger boom</button>}
                <button className="chip-btn on" onClick={()=>Game.closeDefuser()}>Hide</button>
              </div>
              {d.status==='disarmed' && (
                <div className="gate-pass" style={{marginTop:'.6rem'}}>
                  <button className="chip-btn on" style={{width:'100%'}} onClick={()=>Game.enterAfterDefuser()}>{sceneL?`Proceed → ${sceneL}`:'The party proceeds'}</button>
                </div>
              )}
            </>
          ) : <span className="muted" style={{fontSize:'.74rem'}}>Pick your modules and arm the trap. Roles auto-distribute, then fine-tune here.</span>}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   CONSTELLATION — moderator panel
   ============================================================ */
function ConstellationHostPanel({ game }){
  const c = game.constellation;
  const open = c.open;
  const [target, setTarget] = useState('wolf');
  const [tol, setTol] = useState(14);
  const sceneL = c.nextScene ? sceneInfo(c.nextScene).label : null;
  const CONS = window.CONSTELLATIONS || {};
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="eye" size={16}/> Constellation · Star Alignment</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>
        Each player rotates one transparent layer of stars. Only the Main sky shows them stacked — they must call their angles aloud. Calm, no timer.
      </p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Figure</div>
          <div className="cl-diff-row">
            {Object.keys(CONS).map(k=>(<button key={k} className={`chip-btn ${target===k?'on':''}`} onClick={()=>setTarget(k)}>{CONS[k].name}</button>))}
          </div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Tolerance</div>
          <div className="cl-diff-row">
            {[[22,'Forgiving'],[14,'Normal'],[8,'Exacting']].map(([v,l])=>(<button key={v} className={`chip-btn ${tol===v?'on':''}`} onClick={()=>setTol(v)}>{l}</button>))}
          </div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>On complete → enter scene</div>
          <div className="stage-chip-row">
            <button className={`chip-btn ${!c.nextScene?'on':''}`} onClick={()=>Game.setConstellationNextScene('')}>Stay</button>
            {ALL_SCENES.map(s=>(<button key={s} className={`chip-btn ${c.nextScene===s?'on':''}`} onClick={()=>Game.setConstellationNextScene(s)}>{sceneInfo(s).label}</button>))}
          </div>
          <div className="ctrl-row" style={{marginTop:'.7rem'}}>
            <button className="chip-btn" onClick={()=>Game.pushConstellation({target, tolerance:tol})}>{open?'Re-scatter sky':'Reveal the sky →'}</button>
          </div>
        </div>
        <div className="gate-col">
          {open ? (
            <>
              <div className="cl-status-line" style={{marginTop:0}}>
                <span className="gate-live-dot"></span>
                {c.complete ? <span className="gold">ALIGNED</span> : <span>Live · {c.layers.filter(l=>Math.abs(angNorm(l.rot))<=c.tolerance).length}/{c.layers.length} layers aligned</span>}
              </div>
              <div className="section-sub" style={{marginTop:'.6rem'}}>Assign layers</div>
              <div className="cl-assign-grid">
                {c.layers.map((l,i)=>(
                  <div key={i} className="cl-assign" style={{'--rc':l.color}}>
                    <span className="cl-assign-dot"></span>
                    <span className="cl-assign-name">L{i+1}</span>
                    <select className="tinput" value={l.owner||''} onChange={e=>Game.assignLayer(i, e.target.value)}>
                      {game.players.map(p=><option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}
                    </select>
                  </div>
                ))}
              </div>
              <div className="section-sub" style={{marginTop:'.6rem'}}>Hint — snap a layer true</div>
              <div className="cl-hint-btns">
                {c.layers.map((l,i)=>(<button key={i} className="tiny-btn" style={{boxShadow:`inset 0 0 0 1px ${l.color}`}} onClick={()=>Game.grantConstellationHint(i)}>L{i+1}</button>))}
              </div>
              <div className="ctrl-row" style={{marginTop:'.6rem', flexWrap:'wrap'}}>
                {!c.complete && <button className="tiny-btn heal-outline" onClick={()=>Game.onConstellationComplete()}>Force align</button>}
                <button className="chip-btn on" onClick={()=>Game.closeConstellation()}>Hide</button>
              </div>
              {c.complete && (
                <div className="gate-pass" style={{marginTop:'.6rem'}}>
                  <button className="chip-btn on" style={{width:'100%'}} onClick={()=>Game.enterAfterConstellation()}>{sceneL?`Proceed → ${sceneL}`:'The party proceeds'}</button>
                </div>
              )}
            </>
          ) : <span className="muted" style={{fontSize:'.74rem'}}>Choose a figure and reveal the sky. Layers auto-distribute to the players present.</span>}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   LOOT AUCTION — moderator panel
   ============================================================ */
const AUCTION_PRESETS = [
  { name:'Sunshard Blade', type:'weapon', desc:'A longsword that drinks the light.' },
  { name:'Cloak of Embers', type:'magic', desc:'Smoulders but never burns its wearer.' },
  { name:'Vial of Starlight', type:'item', desc:'One sip mends what moonlight cannot.' },
  { name:'Crown of the Drowned King', type:'magic', desc:'Whispers the tongues of the deep.' },
];
function AuctionHostPanel({ game }){
  const a = game.auction;
  const open = a.open;
  const [name, setName] = useState('');
  const [type, setType] = useState('item');
  const [secs, setSecs] = useState(30);
  const [sealed, setSealed] = useState(true);
  const [reserve, setReserve] = useState(0);
  const drop = (item)=> Game.openAuction(item, { seconds:secs, sealed, reserve });
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="buy" size={16}/> Loot Auction · Sealed Bids</h2>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Drop a relic</div>
          <div className="stage-chip-row">
            {AUCTION_PRESETS.map(it=>(<button key={it.name} className="chip-btn" onClick={()=>drop(it)}>{it.name}</button>))}
          </div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>…or author one</div>
          <div className="ctrl-row" style={{flexWrap:'wrap'}}>
            <input className="tinput" style={{flex:'1 1 130px'}} placeholder="Item name" value={name} onChange={e=>setName(e.target.value)}/>
            <select className="tinput" value={type} onChange={e=>setType(e.target.value)}>
              <option value="item">Item</option><option value="weapon">Weapon</option><option value="magic">Magic</option>
            </select>
            <button className="tiny-btn" disabled={!name.trim()} onClick={()=>drop({name:name.trim(), type, desc:''})}>Drop →</button>
          </div>
        </div>
        <div className="gate-col">
          <div className="section-sub">Timer</div>
          <div className="cl-diff-row">
            {[15,30,60].map(v=>(<button key={v} className={`chip-btn ${secs===v?'on':''}`} onClick={()=>setSecs(v)}>{v}s</button>))}
          </div>
          <div className="ctrl-row" style={{marginTop:'.6rem'}}>
            <button className={`tiny-btn ${sealed?'on':''}`} onClick={()=>setSealed(s=>!s)}>{sealed?'● Sealed':'Open bids'}</button>
            <span className="ctrl-label" style={{marginLeft:'auto'}}>Reserve</span>
            <input className="tinput" style={{width:60}} type="number" value={reserve} onChange={e=>setReserve(Math.max(0,parseInt(e.target.value)||0))}/>
          </div>
          {open && (
            <>
              <div className="cl-status-line">
                <span className="gate-live-dot"></span>
                {a.status==='closed'
                  ? (a.winner ? <span className="gold">SOLD → {(game.players.find(p=>p.id===a.winner)||{name:'?'}).name.split(' ')[0]}</span>
                     : a.tie.length ? <span>TIE — break it below</span> : <span>No sale</span>)
                  : <span>Live · {Object.values(a.bids).filter(b=>!b.passed).length} bids · {Object.values(a.bids).filter(b=>b.passed).length} passed</span>}
              </div>
              {a.status==='live' && <div className="ctrl-row" style={{marginTop:'.5rem'}}><button className="chip-btn" onClick={()=>Game.closeAuction()}>Close &amp; reveal</button></div>}
              {a.status==='closed' && a.tie.length>0 && (
                <div className="stage-chip-row" style={{marginTop:'.5rem'}}>
                  <span className="al-sub">Break tie:</span>
                  {a.tie.map(id=>{ const p=game.players.find(x=>x.id===id); return <button key={id} className="tiny-btn heal-outline" onClick={()=>Game.breakTie(id)}>{p?p.name.split(' ')[0]:'?'} ({a.bids[id].amount}c)</button>; })}
                </div>
              )}
              <div className="ctrl-row" style={{marginTop:'.5rem'}}><button className="chip-btn on" onClick={()=>Game.closeAuctionStage()}>Hide auction</button></div>
            </>
          )}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   BRANCHING VOTES — moderator panel
   ============================================================ */
const VOTE_PRESETS = [
  { prompt:'The bridge or the river?', options:['Cross the bridge','Ford the river'] },
  { prompt:'Spare the captured raider?', options:['Spare them','Put them to the sword','Set them free for a price'] },
];
function VoteHostPanel({ game }){
  const v = game.vote;
  const open = v.open;
  const [prompt, setPrompt] = useState('');
  const [opts, setOpts] = useState(['','']);
  const [secret, setSecret] = useState(true);
  const setOpt = (i,val)=> setOpts(o=>o.map((x,k)=>k===i?val:x));
  const push = ()=> Game.pushVote(prompt, opts.map(o=>({label:o})), { secret });
  const tally = window.voteTally ? null : null;
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="book" size={16}/> Branching Vote</h2>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Quick dilemmas</div>
          <div className="stage-chip-row">
            {VOTE_PRESETS.map((p,i)=>(<button key={i} className="chip-btn" onClick={()=>{ setPrompt(p.prompt); setOpts([...p.options, '', ''].slice(0,4)); }}>{p.prompt}</button>))}
          </div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>The dilemma</div>
          <input className="tinput" style={{width:'100%'}} placeholder="What must the party decide?" value={prompt} onChange={e=>setPrompt(e.target.value)}/>
          <div className="section-sub" style={{marginTop:'.6rem'}}>Options</div>
          {opts.map((o,i)=>(
            <div key={i} className="ctrl-row" style={{marginTop:'.3rem'}}>
              <input className="tinput" style={{flex:1}} placeholder={`Option ${i+1}`} value={o} onChange={e=>setOpt(i,e.target.value)}/>
              {opts.length>2 && <button className="tiny-btn danger-outline" onClick={()=>setOpts(os=>os.filter((_,k)=>k!==i))}>✕</button>}
            </div>
          ))}
          {opts.length<4 && <button className="tiny-btn" style={{marginTop:'.3rem'}} onClick={()=>setOpts(o=>[...o,''])}>+ Option</button>}
          <div className="ctrl-row" style={{marginTop:'.6rem'}}>
            <button className={`tiny-btn ${secret?'on':''}`} onClick={()=>setSecret(s=>!s)}>{secret?'● Secret':'Open'}</button>
            <button className="chip-btn" style={{marginLeft:'auto'}} disabled={opts.filter(o=>o.trim()).length<2} onClick={push}>{open?'Re-open vote':'Call the vote →'}</button>
          </div>
        </div>
        <div className="gate-col">
          {open ? (
            <>
              <div className="cl-status-line" style={{marginTop:0}}>
                <span className="gate-live-dot"></span>
                {v.status==='closed' ? (v.result ? <span className="gold">Decided</span> : <span>Tied — resolve below</span>) : <span>Live · {Object.keys(v.ballots).length}/{game.players.length} voted</span>}
              </div>
              <div className="section-sub" style={{marginTop:'.6rem'}}>Vote weight (×2 = counts double)</div>
              <div className="df-assign-list">
                {game.players.map(p=>{
                  const w=(v.weights&&v.weights[p.id])||1;
                  return (
                    <div key={p.id} className="df-assign-row">
                      <span className="df-assign-mod">{p.name.split(' ')[0]}</span>
                      <button className={`tiny-btn ${w===1?'on':''}`} onClick={()=>Game.setVoteWeight(p.id,1)}>×1</button>
                      <button className={`tiny-btn ${w===2?'on':''}`} onClick={()=>Game.setVoteWeight(p.id,2)}>×2</button>
                    </div>
                  );
                })}
              </div>
              {v.status==='live' && <div className="ctrl-row" style={{marginTop:'.5rem'}}><button className="chip-btn" onClick={()=>Game.closeVote()}>Close &amp; tally</button></div>}
              {v.status==='closed' && (
                <div className="stage-chip-row" style={{marginTop:'.5rem'}}>
                  <span className="al-sub">{v.result?'Override:':'Break tie:'}</span>
                  {v.options.map(o=>(<button key={o.id} className={`tiny-btn ${v.result===o.id?'heal-outline':''}`} onClick={()=>Game.resolveVote(o.id)}>{o.label}</button>))}
                </div>
              )}
              <div className="ctrl-row" style={{marginTop:'.5rem'}}><button className="chip-btn on" onClick={()=>Game.closeVoteStage()}>Hide vote</button></div>
            </>
          ) : <span className="muted" style={{fontSize:'.74rem'}}>Compose a dilemma and call the vote. Players tap to choose; you tally and push the branch.</span>}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   SECRET ROLES — moderator panel (the host may see all)
   ============================================================ */
const SECRET_PRESETS = {
  loyal: [
    { role:'Sworn Blade', faction:'party', objectives:['Make sure the party survives the next gate.'] },
    { role:'Keeper of Coin', faction:'party', objectives:['Ensure the party ends the night richer than it began.'] },
  ],
  traitor: [
    { role:'The Cursed', faction:'traitor', objectives:['Make sure the party loses 10+ coins.','Be the one to open the final gate.'] },
    { role:'Hollow Heart', faction:'traitor', objectives:['Make sure a chosen ally ends below 5 HP.'] },
  ],
};
function SecretsHostPanel({ game }){
  const s = game.secrets;
  const [draftFor, setDraftFor] = useState('');
  const [role, setRole] = useState('');
  const [faction, setFaction] = useState('party');
  const [objs, setObjs] = useState(['']);
  const assign = (pid)=>{
    Game.assignSecretRole(pid, { role: role.trim()||'Cultist', faction, objectives: objs.map(o=>o.trim()).filter(Boolean) });
    setRole(''); setObjs(['']); setDraftFor('');
  };
  const applyPreset = (pr)=>{ setRole(pr.role); setFaction(pr.faction); setObjs(pr.objectives); };
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="eye" size={16}/> Secret Roles</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>
        Secrets render only on the owning player's device. The Main screen shows nothing identifying until you reveal. You, the Keeper, may see all.
      </p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Compose a secret</div>
          <div className="cl-diff-row">
            <button className={`chip-btn ${faction==='party'?'on':''}`} onClick={()=>setFaction('party')}>✦ Loyal</button>
            <button className={`chip-btn ${faction==='traitor'?'on':''}`} onClick={()=>setFaction('traitor')}>⚔ Traitor</button>
          </div>
          <div className="stage-chip-row" style={{marginTop:'.4rem'}}>
            {(SECRET_PRESETS[faction]||[]).map((pr,i)=>(<button key={i} className="chip-btn" onClick={()=>applyPreset(pr)}>{pr.role}</button>))}
          </div>
          <input className="tinput" style={{width:'100%',marginTop:'.5rem'}} placeholder="Role name" value={role} onChange={e=>setRole(e.target.value)}/>
          <div className="section-sub" style={{marginTop:'.5rem'}}>Objectives</div>
          {objs.map((o,i)=>(
            <div key={i} className="ctrl-row" style={{marginTop:'.3rem'}}>
              <input className="tinput" style={{flex:1}} placeholder={`Objective ${i+1}`} value={o} onChange={e=>setObjs(os=>os.map((x,k)=>k===i?e.target.value:x))}/>
              {objs.length>1 && <button className="tiny-btn danger-outline" onClick={()=>setObjs(os=>os.filter((_,k)=>k!==i))}>✕</button>}
            </div>
          ))}
          <button className="tiny-btn" style={{marginTop:'.3rem'}} onClick={()=>setObjs(o=>[...o,''])}>+ Objective</button>
          <div className="section-sub" style={{marginTop:'.6rem'}}>Hand to</div>
          <div className="stage-chip-row">
            {game.players.map(p=>(<button key={p.id} className={`chip-btn ${s.roles[p.id]?'on':''}`} onClick={()=>assign(p.id)}>{p.name.split(' ')[0]}{s.roles[p.id]?' ✓':''}</button>))}
            {game.players.length===0 && <span className="muted" style={{fontSize:'.72rem'}}>No players yet.</span>}
          </div>
          <input className="tinput" style={{width:'100%',marginTop:'.5rem'}} placeholder="Ambient cue on Main (non-identifying)" value={s.cue} onChange={e=>Game.setSecretCue(e.target.value)}/>
        </div>

        <div className="gate-col">
          <div className="cl-status-line" style={{marginTop:0}}>
            <span className="gate-live-dot"></span>
            {s.revealed ? <span className="gold">REVEALED to all</span> : <span>{Object.keys(s.roles).length} secret{Object.keys(s.roles).length===1?'':'s'} in play · hidden</span>}
          </div>
          <div className="section-sub" style={{marginTop:'.6rem'}}>Assignments (Keeper's eyes)</div>
          <div className="secret-host-list">
            {Object.keys(s.roles).length===0 && <span className="muted" style={{fontSize:'.72rem'}}>None assigned yet.</span>}
            {game.players.filter(p=>s.roles[p.id]).map(p=>{
              const r = s.roles[p.id];
              return (
                <div key={p.id} className={`secret-host-row ${r.faction}`}>
                  <div className="shr-top">
                    <span className="shr-name">{p.name.split(' ')[0]}</span>
                    <span className={`shr-fac ${r.faction}`}>{r.faction==='traitor'?'⚔ Traitor':'✦ Loyal'} · {r.role}</span>
                    <button className="tiny-btn danger-outline" onClick={()=>Game.clearSecretRole(p.id)}>✕</button>
                  </div>
                  {r.objectives.map(o=>(
                    <div key={o.id} className="shr-obj">
                      <span className="shr-obj-text">{o.text}</span>
                      <button className={`tiny-btn ${o.met===true?'heal-outline':''}`} onClick={()=>Game.resolveObjective(p.id,o.id,true)}>✓</button>
                      <button className={`tiny-btn ${o.met===false?'danger-outline':''}`} onClick={()=>Game.resolveObjective(p.id,o.id,false)}>✗</button>
                    </div>
                  ))}
                </div>
              );
            })}
          </div>
          <div className="ctrl-row" style={{marginTop:'.6rem', flexWrap:'wrap'}}>
            {!s.revealed
              ? <button className="chip-btn" disabled={Object.keys(s.roles).length===0} onClick={()=>Game.revealRoles()}>🎭 Reveal all</button>
              : <button className="tiny-btn" onClick={()=>Game.hideRoles()}>Conceal again</button>}
            <button className="chip-btn on" onClick={()=>Game.clearSecrets()}>Clear secrets</button>
          </div>
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   PACK #2 — moderator panels
   ============================================================ */
function StageEnterScene({ value, onPick }){
  return (
    <div className="stage-chip-row">
      <button className={`chip-btn ${!value?'on':''}`} onClick={()=>onPick('')}>Stay</button>
      {ALL_SCENES.map(s=>(<button key={s} className={`chip-btn ${value===s?'on':''}`} onClick={()=>onPick(s)}>{sceneInfo(s).label}</button>))}
    </div>
  );
}

function LightBenderHostPanel({ game }){
  const lb = game.lightbender; const open = lb.open;
  const [tol, setTol] = useState(7); const [mirrors, setMirrors] = useState(0);
  const sceneL = lb.nextScene ? sceneInfo(lb.nextScene).label : null;
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="spark" size={16}/> Light-Bender · Beam Puzzle</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>Each player rotates one mirror to thread the beam through all of them to the lens. They see only their mirror — the whole path is on the Main screen.</p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Mirrors</div>
          <div className="cl-diff-row">{[2,3,4,5].map(v=>(<button key={v} className={`chip-btn ${mirrors===v?'on':''}`} onClick={()=>setMirrors(v)}>{v}</button>))}<button className={`chip-btn ${mirrors===0?'on':''}`} onClick={()=>setMirrors(0)}>Auto</button></div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Hit tolerance</div>
          <div className="cl-diff-row">{[[10,'Wide'],[7,'Normal'],[5,'Tight']].map(([v,l])=>(<button key={v} className={`chip-btn ${tol===v?'on':''}`} onClick={()=>setTol(v)}>{l}</button>))}</div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>On complete → scene</div>
          <StageEnterScene value={lb.nextScene} onPick={Game.setLbNextScene}/>
          <div className="ctrl-row" style={{marginTop:'.7rem'}}><button className="chip-btn" onClick={()=>Game.pushLightBender({tolerance:tol, mirrors:mirrors||undefined})}>{open?'Re-scatter mirrors':'Reveal the chamber →'}</button></div>
        </div>
        <div className="gate-col">
          {open ? (<>
            <div className="cl-status-line" style={{marginTop:0}}><span className="gate-live-dot"></span>{lb.complete?<span className="gold">BEAM TRUE</span>:<span>Live · {lb.mirrors.length} mirrors</span>}</div>
            <div className="section-sub" style={{marginTop:'.6rem'}}>Assign mirrors</div>
            <div className="cl-assign-grid">
              {lb.mirrors.map((m,i)=>(
                <div key={i} className="cl-assign" style={{'--rc':m.color}}>
                  <span className="cl-assign-dot"></span><span className="cl-assign-name">M{i+1}</span>
                  <select className="tinput" value={m.owner||''} onChange={e=>Game.assignMirror(i,e.target.value)}>{game.players.map(p=><option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}</select>
                </div>
              ))}
            </div>
            <div className="section-sub" style={{marginTop:'.6rem'}}>Flash a hint</div>
            <div className="cl-hint-btns">{lb.mirrors.map((m,i)=>(<button key={i} className="tiny-btn" style={{boxShadow:`inset 0 0 0 1px ${m.color}`}} onClick={()=>Game.grantLbHint(i)}>M{i+1}</button>))}</div>
            <div className="ctrl-row" style={{marginTop:'.6rem',flexWrap:'wrap'}}>{!lb.complete && <button className="tiny-btn heal-outline" onClick={()=>Game.lbComplete()}>Force solve</button>}<button className="chip-btn on" onClick={()=>Game.closeLightBender()}>Hide</button></div>
            {lb.complete && <div className="gate-pass" style={{marginTop:'.6rem'}}><button className="chip-btn on" style={{width:'100%'}} onClick={()=>Game.enterAfterLb()}>{sceneL?`Advance → ${sceneL}`:'The party advances'}</button></div>}
          </>) : <span className="muted" style={{fontSize:'.74rem'}}>Reveal the chamber — mirrors auto-distribute.</span>}
        </div>
      </div>
    </div>
  );
}

function AlchemyHostPanel({ game }){
  const al = game.alchemy; const open = al.open;
  const [len, setLen] = useState(0); const [item, setItem] = useState('Elixir of the Deep');
  const sceneL = al.nextScene ? sceneInfo(al.nextScene).label : null;
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="use" size={16}/> Alchemy Sequence · Split-Clue Brew</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>Each player holds reagents + one clue fragment. They must talk to reconstruct the order — a wrong add backfires and singes someone.</p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Recipe length</div>
          <div className="cl-diff-row">{[3,4,5].map(v=>(<button key={v} className={`chip-btn ${len===v?'on':''}`} onClick={()=>setLen(v)}>{v}</button>))}<button className={`chip-btn ${len===0?'on':''}`} onClick={()=>setLen(0)}>Auto</button></div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Result item</div>
          <input className="tinput" style={{width:'100%'}} value={item} onChange={e=>setItem(e.target.value)}/>
          <div className="section-sub" style={{marginTop:'.7rem'}}>On complete → scene</div>
          <StageEnterScene value={al.nextScene} onPick={Game.setBrewNextScene}/>
          <div className="ctrl-row" style={{marginTop:'.7rem'}}><button className="chip-btn" onClick={()=>Game.startBrew({length:len||undefined, resultItem:item})}>{open?'New brew':'Light the cauldron →'}</button></div>
        </div>
        <div className="gate-col">
          {open ? (<>
            <div className="cl-status-line" style={{marginTop:0}}><span className="gate-live-dot"></span>{al.status==='done'?<span className="gold">PERFECTED</span>:<span>Live · {al.added.length}/{al.recipe.length} added</span>}</div>
            <div className="section-sub" style={{marginTop:'.6rem'}}>The order (Keeper's eyes)</div>
            <div className="stage-chip-row">{al.recipe.map((rid,i)=>{ const r=reagentById(rid); return <span key={i} className="al-recipe-chip" style={{'--rc':r.color}}>{i+1}. {r.name}</span>; })}</div>
            <div className="ctrl-row" style={{marginTop:'.6rem',flexWrap:'wrap'}}>
              {al.status!=='done' && <button className="tiny-btn heal-outline" onClick={()=>Game.onBrewComplete()}>Force complete</button>}
              {al.status==='done' && <button className="tiny-btn" onClick={()=>Game.grantBrewToAll()}>Grant to all →</button>}
              <button className="chip-btn on" onClick={()=>Game.closeBrew()}>Hide</button>
            </div>
            {al.status==='done' && <div className="gate-pass" style={{marginTop:'.6rem'}}><button className="chip-btn on" style={{width:'100%'}} onClick={()=>Game.enterAfterBrew()}>{sceneL?`Move on → ${sceneL}`:'The party moves on'}</button></div>}
          </>) : <span className="muted" style={{fontSize:'.74rem'}}>Light the cauldron — reagents &amp; clues auto-split among players.</span>}
        </div>
      </div>
    </div>
  );
}

function RitualHostPanel({ game }){
  const r = game.ritual; const active = r.active;
  const [spell, setSpell] = useState('The Unmaking'); const [cast, setCast] = useState(30); const [dmg, setDmg] = useState(5);
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="spark" size={16}/> Ritual Interrupt · Race the Cast</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>A red cast bar races a gold interrupt bar. Every player gets a quick task to fill the interrupt before the cast lands.</p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Spell name</div>
          <input className="tinput" style={{width:'100%'}} value={spell} onChange={e=>setSpell(e.target.value)}/>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Cast time</div>
          <div className="cl-diff-row">{[[20,'20s · hard'],[30,'30s'],[45,'45s · easy']].map(([v,l])=>(<button key={v} className={`chip-btn ${cast===v?'on':''}`} onClick={()=>setCast(v)}>{l}</button>))}</div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Catastrophe damage (all)</div>
          <div className="cl-diff-row">{[3,5,8].map(v=>(<button key={v} className={`chip-btn ${dmg===v?'on':''}`} onClick={()=>setDmg(v)}>{v}</button>))}</div>
          <div className="ctrl-row" style={{marginTop:'.7rem'}}><button className="chip-btn" onClick={()=>Game.startRitual(spell, cast*1000, 100, {catastrophe:'all-'+dmg})}>{active?'Recast':'Begin the cast →'}</button></div>
        </div>
        <div className="gate-col">
          {active ? (<>
            <div className="cl-status-line" style={{marginTop:0}}><span className="gate-live-dot"></span>{r.status==='interrupted'?<span className="gold">SHATTERED</span>:r.status==='complete'?<span style={{color:'hsl(var(--damage))'}}>UNLEASHED</span>:<span>Live · interrupt {Math.round((r.interrupt/r.threshold)*100)}%</span>}</div>
            <div className="ctrl-row" style={{marginTop:'.6rem',flexWrap:'wrap'}}>
              {r.status==='live' && <button className="tiny-btn heal-outline" onClick={()=>Game.onInterrupt()}>Force interrupt</button>}
              {r.status==='live' && <button className="tiny-btn danger-outline" onClick={()=>Game.onRitualComplete()}>Force cast</button>}
              <button className="chip-btn on" onClick={()=>Game.closeRitual()}>Hide</button>
            </div>
          </>) : <span className="muted" style={{fontSize:'.74rem'}}>Begin the cast — every present hero gets an interrupt task.</span>}
        </div>
      </div>
    </div>
  );
}

function TotemsHostPanel({ game }){
  const t = game.totems; const active = t.active;
  const [hp, setHp] = useState(4); const [win, setWin] = useState(2500);
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="shield" size={16}/> Phase Totems · Synchronized Break</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>The boss is invulnerable until all four totems break within the window. One totem per player; out of sync, they surge back.</p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Totem health</div>
          <div className="cl-diff-row">{[3,4,6].map(v=>(<button key={v} className={`chip-btn ${hp===v?'on':''}`} onClick={()=>setHp(v)}>{v}</button>))}</div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Synchrony window</div>
          <div className="cl-diff-row">{[[4000,'4s · easy'],[2500,'2.5s'],[1500,'1.5s · hard']].map(([v,l])=>(<button key={v} className={`chip-btn ${win===v?'on':''}`} onClick={()=>setWin(v)}>{l}</button>))}</div>
          <div className="ctrl-row" style={{marginTop:'.7rem'}}><button className="chip-btn" onClick={()=>Game.spawnTotems({hp, window:win})}>{active?'Re-raise totems':'Raise the totems →'}</button></div>
        </div>
        <div className="gate-col">
          {active ? (<>
            <div className="cl-status-line" style={{marginTop:0}}><span className="gate-live-dot"></span>{t.shieldDown?<span className="gold">SHIELD DOWN</span>:<span>Live · {t.items.filter(x=>x.broken).length}/4 broken</span>}</div>
            <div className="section-sub" style={{marginTop:'.6rem'}}>Assign totems</div>
            <div className="cl-assign-grid">
              {t.items.map((it,i)=>{ const colors=['#f0a836','#5a8fd6','#52b07a','#d9534f']; return (
                <div key={it.id} className="cl-assign" style={{'--rc':colors[i%4]}}>
                  <span className="cl-assign-dot"></span><span className="cl-assign-name">T{i+1}</span>
                  <select className="tinput" value={it.owner||''} onChange={e=>Game.assignTotem ? Game.assignTotem(it.id,e.target.value) : store.set(s=>({totems:{...s.totems, items:s.totems.items.map(x=>x.id===it.id?{...x,owner:e.target.value}:x)}}))}>{game.players.map(p=><option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}</select>
                </div>
              ); })}
            </div>
            <div className="ctrl-row" style={{marginTop:'.6rem',flexWrap:'wrap'}}>{!t.shieldDown && <button className="tiny-btn heal-outline" onClick={()=>Game.dropBossShield()}>Force shield down</button>}<button className="chip-btn on" onClick={()=>Game.closeTotems()}>Hide</button></div>
          </>) : <span className="muted" style={{fontSize:'.74rem'}}>Raise the totems — one auto-assigned per player.</span>}
        </div>
      </div>
    </div>
  );
}

function BreathHostPanel({ game }){
  const b = game.breath; const active = b.active;
  const [drain, setDrain] = useState(5); const [refill, setRefill] = useState(22); const [obj, setObj] = useState('Reach the far grate');
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="use" size={16}/> Breath Meter · One Air Pocket</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>Everyone's breath drains; only one player can breathe at a time. They negotiate who breathes and who pushes the objective. Drowning deals damage.</p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Objective</div>
          <input className="tinput" style={{width:'100%'}} value={obj} onChange={e=>setObj(e.target.value)}/>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Drain rate</div>
          <div className="cl-diff-row">{[[3,'Gentle'],[5,'Normal'],[8,'Harsh']].map(([v,l])=>(<button key={v} className={`chip-btn ${drain===v?'on':''}`} onClick={()=>setDrain(v)}>{l}</button>))}</div>
          <div className="section-sub" style={{marginTop:'.7rem'}}>Refill rate</div>
          <div className="cl-diff-row">{[[16,'Slow'],[22,'Normal'],[30,'Fast']].map(([v,l])=>(<button key={v} className={`chip-btn ${refill===v?'on':''}`} onClick={()=>setRefill(v)}>{l}</button>))}</div>
          <div className="ctrl-row" style={{marginTop:'.7rem'}}><button className="chip-btn" onClick={()=>Game.startBreathPhase({drainRate:drain, refillRate:refill, objective:obj})}>{active?'Restart phase':'Start the dive →'}</button></div>
        </div>
        <div className="gate-col">
          {active ? (<>
            <div className="cl-status-line" style={{marginTop:0}}><span className="gate-live-dot"></span><span>Live · {(game.players.find(p=>p.id===b.pocket)||{}).name?`${game.players.find(p=>p.id===b.pocket).name.split(' ')[0]} breathing`:'pocket free'}</span></div>
            <div className="df-assign-list" style={{marginTop:'.5rem'}}>
              {game.players.map(p=>{ const v=b.bars[p.id]==null?100:b.bars[p.id]; return (
                <div key={p.id} className="df-assign-row"><span className="df-assign-mod">{p.name.split(' ')[0]} · {Math.round(v)}%</span>
                  <button className="tiny-btn" onClick={()=>Game.setBreath(p.id, Math.min(100,v+20))}>+20</button>
                  <button className="tiny-btn" onClick={()=>Game.claimAirPocket(p.id)}>Give air</button>
                </div>
              ); })}
            </div>
            <div className="ctrl-row" style={{marginTop:'.6rem',flexWrap:'wrap'}}><button className="tiny-btn heal-outline" onClick={()=>Game.onAllSafe()}>All safe</button><button className="chip-btn on" onClick={()=>Game.closeBreath()}>End phase</button></div>
          </>) : <span className="muted" style={{fontSize:'.74rem'}}>Start the dive — tune drain/refill so it's winnable with sharing.</span>}
        </div>
      </div>
    </div>
  );
}

const WAGER_PRESETS = [
  { prompt:'We clear this boss in 3 rounds', outcomes:['Yes — 3 or fewer','No — it drags on'] },
  { prompt:'Nobody drops below 5 HP this fight', outcomes:['We hold','Someone drops'] },
];
function WagerHostPanel({ game }){
  const w = game.wager; const open = w.open;
  const [prompt, setPrompt] = useState(''); const [opts, setOpts] = useState(['','']); const [mode, setMode] = useState('house');
  const setOpt=(i,v)=>setOpts(o=>o.map((x,k)=>k===i?v:x));
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="buy" size={16}/> The Wager · Side-Bets</h2>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Quick wagers</div>
          <div className="stage-chip-row">{WAGER_PRESETS.map((p,i)=>(<button key={i} className="chip-btn" onClick={()=>{ setPrompt(p.prompt); setOpts([...p.outcomes,'',''].slice(0,4)); }}>{p.prompt}</button>))}</div>
          <div className="section-sub" style={{marginTop:'.6rem'}}>The bet</div>
          <input className="tinput" style={{width:'100%'}} placeholder="What's the wager?" value={prompt} onChange={e=>setPrompt(e.target.value)}/>
          <div className="section-sub" style={{marginTop:'.5rem'}}>Outcomes</div>
          {opts.map((o,i)=>(<div key={i} className="ctrl-row" style={{marginTop:'.3rem'}}><input className="tinput" style={{flex:1}} placeholder={`Outcome ${i+1}`} value={o} onChange={e=>setOpt(i,e.target.value)}/>{opts.length>2 && <button className="tiny-btn danger-outline" onClick={()=>setOpts(os=>os.filter((_,k)=>k!==i))}>✕</button>}</div>))}
          {opts.length<4 && <button className="tiny-btn" style={{marginTop:'.3rem'}} onClick={()=>setOpts(o=>[...o,''])}>+ Outcome</button>}
          <div className="ctrl-row" style={{marginTop:'.5rem'}}>
            <button className={`tiny-btn ${mode==='house'?'on':''}`} onClick={()=>setMode('house')}>House odds</button>
            <button className={`tiny-btn ${mode==='pot'?'on':''}`} onClick={()=>setMode('pot')}>Shared pot</button>
            <button className="chip-btn" style={{marginLeft:'auto'}} disabled={opts.filter(o=>o.trim()).length<2} onClick={()=>Game.openWager(prompt, opts.map(o=>({label:o})), {mode})}>{open?'Re-open':'Open wager →'}</button>
          </div>
        </div>
        <div className="gate-col">
          {open ? (<>
            <div className="cl-status-line" style={{marginTop:0}}><span className="gate-live-dot"></span>{w.status==='resolved'?<span className="gold">Resolved</span>:<span>Live · {Object.keys(w.bets).length}/{game.players.length} wagered</span>}</div>
            {w.status==='live' && <div className="stage-chip-row" style={{marginTop:'.5rem'}}><span className="al-sub">Resolve as:</span>{w.outcomes.map(o=>(<button key={o.id} className="tiny-btn heal-outline" onClick={()=>Game.resolveWager(o.id)}>{o.label}</button>))}</div>}
            <div className="ctrl-row" style={{marginTop:'.5rem'}}><button className="chip-btn on" onClick={()=>Game.closeWager()}>Hide wager</button></div>
          </>) : <span className="muted" style={{fontSize:'.74rem'}}>Compose a bet and open it. Stakes stay hidden until you resolve.</span>}
        </div>
      </div>
    </div>
  );
}

function BondsHostPanel({ game }){
  const bonds = game.bonds.list;
  const [a, setA] = useState(''); const [b, setB] = useState(''); const [cond, setCond] = useState('co-target'); const [buff, setBuff] = useState(1);
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="ring" size={16}/> Bonds · Paired Buffs</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>Pair two heroes. When the bond condition holds, toggle it active for a small buff that nudges them to cover each other.</p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="section-sub">Forge a bond</div>
          <div className="ctrl-row" style={{flexWrap:'wrap'}}>
            <select className="tinput" value={a} onChange={e=>setA(e.target.value)}><option value="">Hero A</option>{game.players.map(p=><option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}</select>
            <span className="al-sub">&amp;</span>
            <select className="tinput" value={b} onChange={e=>setB(e.target.value)}><option value="">Hero B</option>{game.players.map(p=><option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}</select>
          </div>
          <div className="section-sub" style={{marginTop:'.6rem'}}>Condition</div>
          <div className="cl-diff-row"><button className={`chip-btn ${cond==='co-target'?'on':''}`} onClick={()=>setCond('co-target')}>Same target</button><button className={`chip-btn ${cond==='co-location'?'on':''}`} onClick={()=>setCond('co-location')}>Same place</button></div>
          <div className="section-sub" style={{marginTop:'.6rem'}}>Buff size</div>
          <div className="cl-diff-row">{[1,2].map(v=>(<button key={v} className={`chip-btn ${buff===v?'on':''}`} onClick={()=>setBuff(v)}>+{v}</button>))}</div>
          <div className="ctrl-row" style={{marginTop:'.6rem'}}><button className="chip-btn" disabled={!a||!b||a===b} onClick={()=>{ Game.formBond(a,b,{condition:cond,buff}); setA(''); setB(''); }}>Forge bond →</button></div>
        </div>
        <div className="gate-col">
          <div className="section-sub" style={{marginTop:0}}>Active bonds</div>
          <div className="secret-host-list">
            {bonds.length===0 && <span className="muted" style={{fontSize:'.72rem'}}>No bonds yet.</span>}
            {bonds.map(bd=>{ const pa=game.players.find(p=>p.id===bd.a), pb=game.players.find(p=>p.id===bd.b); return (
              <div key={bd.id} className="secret-host-row">
                <div className="shr-top">
                  <span className="shr-name">{pa?pa.name.split(' ')[0]:'?'} 🜲 {pb?pb.name.split(' ')[0]:'?'}</span>
                  <span className="shr-fac">{bd.condition==='co-target'?'same target':'same place'}</span>
                  <button className="tiny-btn danger-outline" onClick={()=>Game.breakBond(bd.id)}>✕</button>
                </div>
                <div className="ctrl-row">
                  <button className={`tiny-btn ${bd.active?'heal-outline':''}`} onClick={()=>Game.setBondActive(bd.id, !bd.active)}>{bd.active?'● Active':'Inactive'}</button>
                  <button className="tiny-btn" disabled={!bd.active} onClick={()=>Game.applyBondBuff(bd.id)}>Apply +{bd.buff}</button>
                </div>
              </div>
            ); })}
          </div>
        </div>
      </div>
    </div>
  );
}

function SpectralHostPanel({ game }){
  const sp = game.spectral;
  const dead = game.players.filter(p=>p.dead);
  return (
    <div className="host-card liquid-glass gate-panel circuit-panel">
      <h2 className="section-label"><Icon name="eye" size={16}/> Spectral Guide · The Dead Whisper</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',margin:'0 0 .7rem'}}>When a hero dies they enter Spectral mode and may whisper the living one word on a cooldown — easy to miss, never the whole answer.</p>
      <div className="gate-panel-grid">
        <div className="gate-col">
          <div className="ctrl-row"><button className={`chip-btn ${sp.enabled?'on':''}`} onClick={()=>Game.setSpectral(!sp.enabled)}>{sp.enabled?'● Whispers ON':'Whispers OFF'}</button></div>
          <div className="section-sub" style={{marginTop:'.6rem'}}>Cooldown</div>
          <div className="cl-diff-row">{[[12000,'12s'],[20000,'20s'],[30000,'30s']].map(([v,l])=>(<button key={v} className={`chip-btn ${sp.cooldownMs===v?'on':''}`} onClick={()=>Game.setSpectralOpts({cooldownMs:v})}>{l}</button>))}</div>
          <div className="section-sub" style={{marginTop:'.6rem'}}>Word source</div>
          <div className="cl-diff-row"><button className={`chip-btn ${!sp.useBank?'on':''}`} onClick={()=>Game.setSpectralOpts({useBank:false})}>Free word</button><button className={`chip-btn ${sp.useBank?'on':''}`} onClick={()=>Game.setSpectralOpts({useBank:true})}>Word bank</button></div>
        </div>
        <div className="gate-col">
          <div className="section-sub" style={{marginTop:0}}>Ghosts</div>
          {dead.length===0 ? <span className="muted" style={{fontSize:'.72rem'}}>No fallen heroes yet.</span>
            : <div className="df-assign-list">{dead.map(p=>(<div key={p.id} className="df-assign-row"><span className="df-assign-mod">☽ {p.name.split(' ')[0]}</span><button className={`tiny-btn ${sp.muted[p.id]?'danger-outline':''}`} onClick={()=>Game.muteGhost(p.id)}>{sp.muted[p.id]?'Muted':'Mute'}</button></div>))}</div>}
          {sp.whispers.length>0 && <div className="section-sub" style={{marginTop:'.6rem'}}>Recent: {sp.whispers.map(w=>w.word).join(' · ')}</div>}
        </div>
      </div>
    </div>
  );
}

function EntrySetupCard({ game }){
  return (
    <div className="host-card liquid-glass">
      <h2 className="section-label"><Icon name="spark" size={16}/> Entry Artifact</h2>
      <p className="muted" style={{fontSize:'.74rem',textTransform:'none',letterSpacing:'.01em',margin:'0 0 .6rem'}}>
        What players tap to enter the realm on the start screen.
      </p>
      <div className="stage-chip-row">
        {[['amulet','Torch Amulet'],['brazier','Brazier Ember'],['tome','Open Tome'],['rune','Rune Sigil']].map(([v,l])=>(
          <button key={v} className={`chip-btn ${(game.entry&&game.entry.artifact)===v?'on':''}`}
            onClick={()=>Game.setEntryArtifact(v)}>{l}</button>
        ))}
      </div>
    </div>
  );
}

function HostScreen(){
  const game = useGame();
  useGateDemo(game.gate);   // moderator hears the beat demo too (state-driven, plays once)
  const [zone, setZone] = useState('run');   // 'run' = moderate live · 'setup' = plan the game
  const [intelText, setIntelText] = useState('');
  const [intelTarget, setIntelTarget] = useState('main');
  const [lastIntel, setLastIntel] = useState('');
  const [craft, setCraft] = useState(CRAFT_RECIPES[0]);
  const [craftTarget, setCraftTarget] = useState('');

  // keep craft target valid as players join/leave
  useEffect(()=>{
    if(game.players.length && !game.players.some(p=>p.id===craftTarget)){
      setCraftTarget(game.players[0].id);
    }
  }, [game.players, craftTarget]);

  const pendingReqs = game.players.reduce((n,p)=> n + (p.requests||[]).length, 0);

  return (
    <div className="host-screen no-scrollbar">
      {window.MainSwitchConfirm && <window.MainSwitchConfirm game={game}/>}
      <div className="host-wrap">
        <div className="host-head">
          <div>
            <h1 className="display">Moderator</h1>
            <div className="sub">{zone==='setup' ? 'Prep the realm before play' : 'Run the realm, live'}</div>
          </div>
          <div className="zone-switch">
            <button className={`zone-btn ${zone==='setup'?'on':''}`} onClick={()=>setZone('setup')}>
              <Icon name="book" size={15}/> Game Setup
            </button>
            <button className={`zone-btn ${zone==='run'?'on':''}`} onClick={()=>setZone('run')}>
              <Icon name="eye" size={15}/> Run Game
              {pendingReqs>0 && <span className="zone-badge">{pendingReqs}</span>}
            </button>
          </div>
          <button className="tiny-btn danger-outline" onClick={()=>{ if(confirm('Reset the whole session?')) Game.resetAll(); }}>Reset session</button>
        </div>

        {zone==='setup' ? (
          <div className="zone-pane">
            <div className="zone-intro liquid-glass">
              <Icon name="book" size={20} style={{color:'hsl(var(--primary))'}}/>
              <div>
                <div className="zone-intro-t display">Plan your game</div>
                <div className="zone-intro-s">Import portraits &amp; lore, stock the shops, and set the entry artifact. Nothing here is shown to the room until you push it from the Run Game tab — including the beat gate, which now lives on the Main Stage there.</div>
              </div>
            </div>
            <div className="host-grid">
              <SetupPanel game={game}/>
              <ShopControl game={game}/>
              <MapControl game={game}/>
              <EntrySetupCard game={game}/>
            </div>
          </div>
        ) : (
          <div className="zone-pane">
            {/* MAIN STAGE — authoritative control of the shared screen */}
            <MainStage game={game}/>

            {/* SHOP BOARD — live trading dashboard while a shop is open */}
            {game.shop.open && <ShopBoard game={game}/>}

            {/* SECTION A — party */}
            <div className="section-label" style={{margin:'4px 0 12px'}}>Party Control{pendingReqs>0 && <span className="lbl-badge">{pendingReqs} request{pendingReqs>1?'s':''}</span>}</div>
            <div className="player-cards-wrap" style={{marginBottom:20}}>
              {game.players.length===0 && (
                <div className="party-empty liquid-glass">
                  <span className="display">No heroes yet</span>
                  Players forge their own characters on their devices. As each one steps into the realm, they’ll appear here for you to command.
                </div>
              )}
              {game.players.map(p=> <PlayerControlCard key={p.id} p={p} allPlayers={game.players} game={game}/>)}
            </div>

            <div className="host-grid">
              {/* room manager — connection dots + reassignment */}
              {window.RoomManager && <window.RoomManager/>}

              {/* action sequence */}
              <BattleControl game={game}/>

              {/* private GM inbox */}
              <GMInbox game={game}/>

              {/* reveal & hand out saved library entries (moved out of Setup) */}
              {window.RevealControl && <window.RevealControl game={game}/>}

              {/* intel */}
              <div className="host-card liquid-glass">
                <h2 className="section-label"><Icon name="eye" size={16}/> Intel</h2>
                <textarea className="tinput" rows="3" placeholder="Lore, history, enemy weakness…" value={intelText} onChange={e=>setIntelText(e.target.value)}></textarea>
                <div className="section-sub">Send to</div>
                <div className="radio-row">
                  <button className={`chip-btn ${intelTarget==='main'?'on':''}`} onClick={()=>setIntelTarget('main')}>Main screen</button>
                  <button className={`chip-btn ${intelTarget==='all'?'on':''}`} onClick={()=>setIntelTarget('all')}>All players</button>
                  {game.players.map(p=>(
                    <button key={p.id} className={`chip-btn ${intelTarget===p.id?'on':''}`} onClick={()=>setIntelTarget(p.id)}>{p.name.split(' ')[0]}</button>
                  ))}
                </div>
                <button className="chip-btn on" style={{marginTop:'.8rem'}} onClick={()=>{ const t=intelText||lastIntel; if(!t&&!intelText) return; Game.pushIntel(intelTarget, intelText||lastIntel||undefined); if(intelText) setLastIntel(intelText); setIntelText(''); }}>
                  Send Intel →
                </button>
                {lastIntel && (
                  <button className="tiny-btn" style={{marginTop:'.5rem'}} onClick={()=>Game.pushIntel(intelTarget, lastIntel)} title="Send the last message again">
                    ↻ Resend last{intelTarget==='main'?' to Main':''}
                  </button>
                )}
              </div>

              {/* session / clock */}
              <div className="host-card liquid-glass">
                <h2 className="section-label"><Icon name="moon" size={16}/> Session</h2>
                <div className="section-sub">Moon · Watch {game.moon} / 5</div>
                <div className="moon-track">
                  {[1,2,3,4,5].map(n=>(
                    <span key={n} className={`moon-pip ${n<=game.moon?(game.moon>=4?'warn':'on'):''}`}></span>
                  ))}
                </div>
                <div className="ctrl-row" style={{marginTop:0}}>
                  <Stepper onMinus={()=>Game.setMoon(game.moon-1)} onPlus={()=>Game.setMoon(game.moon+1)}/>
                  {game.moon>=4 && <span className="dmg-text" style={{fontSize:'.74rem',letterSpacing:'.1em'}}>Full moon approaches…</span>}
                </div>
                <div className="section-sub">Craft → grant</div>
                <div className="ctrl-row" style={{marginTop:'.3rem'}}>
                  <select className="tinput" value={craft} onChange={e=>setCraft(e.target.value)} style={{flex:1}}>
                    {CRAFT_RECIPES.map(r=> <option key={r}>{r}</option>)}
                  </select>
                  <select className="tinput" value={craftTarget} onChange={e=>setCraftTarget(e.target.value)} style={{width:90}}>
                    {game.players.map(p=> <option key={p.id} value={p.id}>{p.name.split(' ')[0]}</option>)}
                  </select>
                  <button className="tiny-btn" onClick={()=>Game.grantItem(craftTarget, craft.split(' (')[0])}>Craft</button>
                </div>
              </div>

              {/* quick log */}
              <div className="host-card liquid-glass">
                <h2 className="section-label"><Icon name="book" size={16}/> Quick Log</h2>
                <div className="log-feed no-scrollbar">
                  {game.log.length===0 && <div className="muted" style={{fontSize:'.78rem'}}>Actions will appear here…</div>}
                  {game.log.map((l,i)=>(
                    <div key={i} className="log-line">
                      <span className="lt">{new Date(l.t).toLocaleTimeString([], {hour:'2-digit',minute:'2-digit',second:'2-digit'})}</span>
                      <span>{l.text}</span>
                    </div>
                  ))}
                </div>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

window.HostScreen = HostScreen;
