function SectionNav({show,currentSlide,dispatch,redFlags,extrasBypassed,bonusSections}){
  const sectionIdx=slideToSection(show,currentSlide,redFlags,extrasBypassed,bonusSections);
  const rfo=redFlagSectionOffset(show,redFlags,extrasBypassed);
  // Count sections hidden by BYPASS EXTRAS so the operator can see at-a-glance how much is suppressed.
  var hiddenCount=0;
  if(extrasBypassed){for(var hi=0;hi<show.sections.length;hi++){if(!sectionSurvivesBypass(show.sections[hi]))hiddenCount++}}
  return (
    <div className="section-nav">
      {show.name&&<div style={{padding:'var(--space-03)',color:'var(--signal)',fontFamily:'var(--display)',fontSize:13,letterSpacing:'.06em',textTransform:'uppercase',overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>{show.name}</div>}
      {extrasBypassed&&<div style={{margin:'0 var(--space-03) var(--space-02)',padding:'4px 8px',background:'rgba(255,215,0,.1)',border:'1px solid var(--gold)',color:'var(--gold)',fontFamily:"'Space Mono',monospace",fontSize:10,letterSpacing:'.1em',textTransform:'uppercase',textAlign:'center'}}>EXTRAS HIDDEN{hiddenCount?' · '+hiddenCount+' SECTIONS':''}</div>}
      <div style={{padding:'var(--space-03)',color:'var(--signal)',fontFamily:"'Space Mono',monospace",fontSize:11,letterSpacing:'.15em',borderBottom:'1px solid var(--border-subtle)',marginBottom:'var(--space-02)'}}>SECTIONS</div>
      {show.sections.map((sec,i)=>{
        // When BYPASS EXTRAS is on, hide sections that are filtered out of the flat array entirely.
        if(extrasBypassed&&!sectionSurvivesBypass(sec))return null;
        var count=sec.slides.length;
        if(extrasBypassed)count=(sec.slides||[]).filter(function(sl){return!isExtraType(sl.type)}).length;
        else if(i===rfo.sectionIdx)count+=rfo.offset;
        return (
          <div key={sec.id} className={'section-nav-item'+(i===sectionIdx?' active':'')} onClick={()=>dispatch({type:'JUMP_SECTION',payload:i})}>
            <span style={{overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap',flex:1,marginRight:8}}>{sec.name}</span>
            <span className="section-nav-count">{count}</span>
          </div>
        );
      })}
    </div>
  );
}

function DoaVoteMini({topic}){
  const[votes,setVotes]=useState({dead:0,alive:0});
  useEffect(function(){
    if(!fbdb||!topic)return;
    var key=topic.replace(/[^a-zA-Z0-9]/g,'_');
    var ref=fbdb.ref('votes/'+key);
    var handler=function(snap){var v=snap.val()||{};setVotes({dead:v.dead||0,alive:v.alive||0})};
    ref.on('value',handler);
    return function(){ref.off('value',handler)};
  },[topic]);
  var total=votes.dead+votes.alive;
  var dp=total>0?Math.round(votes.dead/total*100):0;
  var ap=total>0?100-dp:0;
  return (
    <div style={{marginTop:'var(--space-03)',padding:'0 var(--space-03)'}}>
      <div style={{display:'flex',justifyContent:'space-between',fontFamily:"'Space Mono',monospace",fontSize:11,letterSpacing:'.08em',marginBottom:4}}>
        <span style={{color:'var(--signal)'}}>{votes.dead} DEAD</span>
        <span style={{color:'var(--cyan)'}}>{votes.alive} ALIVE</span>
      </div>
      <div style={{width:'100%',height:12,background:'rgba(255,255,255,.05)',border:'1px solid var(--border-subtle)',position:'relative',overflow:'hidden'}}>
        {total>0&&<div style={{position:'absolute',left:0,top:0,bottom:0,width:dp+'%',background:'var(--signal)',transition:'width .4s ease'}}></div>}
        {total>0&&<div style={{position:'absolute',right:0,top:0,bottom:0,width:ap+'%',background:'var(--cyan)',transition:'width .4s ease'}}></div>}
      </div>
      <div style={{display:'flex',justifyContent:'space-between',fontFamily:"'Space Mono',monospace",fontSize:13,marginTop:4}}>
        <span style={{color:'var(--signal)'}}>{dp}%</span>
        <span style={{color:'var(--cyan)'}}>{ap}%</span>
      </div>
      <div style={{textAlign:'center',fontFamily:"'Space Mono',monospace",fontSize:10,color:'var(--text-disabled)',marginTop:2}}>{total} vote{total!==1?'s':''}</div>
    </div>
  );
}

function CurrentSlideCard({slide,slideIndex,totalSlides,revealState,dispatch}){
  const revealed=(revealState[slide.id]||'hidden')==='revealed';
  return (
    <div className="slide-card">
      <div className="slide-card-header">
        <span className="slide-card-type">{slide.type}</span>
        <div style={{display:'flex',alignItems:'center',gap:'var(--space-03)'}}>
          <span className={'reveal-badge '+(revealed?'revealed':'hidden')} onClick={()=>dispatch({type:'TOGGLE_REVEAL'})}>{revealed?'REVEALED':'HIDDEN'}</span>
          <span className="slide-card-counter" data-testid="slide-counter">{slideIndex+1}/{totalSlides}</span>
        </div>
      </div>
      <div className="slide-card-content">
        <div className="slide-card-primary">{slide.content.primary||''}</div>
        {slide.content.answer&&<div className="slide-card-answer">{slide.content.answer}</div>}
        {slide.content.source&&<div className="slide-card-source">{slide.content.source}</div>}
        {slide.content.secondary&&<div style={{fontFamily:"'Space Mono',monospace",fontSize:'var(--type-meta)',color:'var(--text-disabled)',marginTop:'var(--space-02)',textAlign:'center'}}>{slide.content.secondary}</div>}
      </div>
      {slide.type==='doa_vote'&&<DoaVoteMini topic={slide.content.primary||''}/>}
      {slide.host_notes&&<div className="slide-card-notes">// {slide.host_notes}</div>}
    </div>
  );
}

function NextSlideCard({slide}){
  if(!slide)return <div className="slide-card-next"><div className="slide-card-next-label">NEXT</div><div style={{color:'var(--text-disabled)',fontFamily:"'Space Mono',monospace",fontSize:'var(--type-meta)'}}>END OF SHOW</div></div>;
  return (
    <div className="slide-card-next" style={{padding:'var(--space-03)'}}>
      <div className="slide-card-next-label">NEXT</div>
      <div style={{fontFamily:"'Space Mono',monospace",fontSize:'var(--type-meta)',color:'var(--text-secondary)',textTransform:'uppercase'}}>{slide.type}</div>
      <div style={{fontFamily:"'Neue Haas Grotesk Display Pro',sans-serif",fontSize:'var(--type-label)',color:'var(--text-secondary)',marginTop:'var(--space-01)',textTransform:'uppercase'}}>{slide.content.primary||''}</div>
    </div>
  );
}

function ScoreDisplay({scores,dispatch,scoreboardPosition,showScoreboard}){
  function editScore(team){
    var idx=team==='cyan'?0:1;
    var current=scores[idx]||0;
    var input=typeof prompt==='function'?prompt(team.toUpperCase()+' score',String(current)):null;
    if(input===null||input===undefined||input==='')return;
    var n=parseInt(input,10);
    if(isNaN(n))return;
    n=Math.max(0,n);
    var next=scores.slice();next[idx]=n;
    dispatch({type:'SET_SCORES',payload:next});
  }
  function resetScores(){
    if(typeof confirm==='function'&&!confirm('Reset both scores to 0?'))return;
    dispatch({type:'SET_SCORES',payload:[0,0]});
  }
  return (
    <div>
      <div className="op-section-label" style={{display:'flex',justifyContent:'space-between',alignItems:'center'}}>SCOREBOARD<div style={{display:'flex',gap:'4px'}}><button onClick={resetScores} title="Reset both scores to 0" style={{background:'none',border:'1px solid var(--border-subtle)',color:'var(--text-secondary)',fontFamily:"'Space Mono',monospace",fontSize:'9px',padding:'2px 6px',cursor:'pointer',letterSpacing:'.06em',textTransform:'uppercase'}}>RESET</button><button onClick={()=>dispatch({type:'TOGGLE_SCOREBOARD'})} style={{background:'none',border:'1px solid '+(showScoreboard?'var(--signal)':'var(--border-subtle)'),color:showScoreboard?'var(--signal)':'var(--text-secondary)',fontFamily:"'Space Mono',monospace",fontSize:'9px',padding:'2px 6px',cursor:'pointer',letterSpacing:'.06em',textTransform:'uppercase'}}>{showScoreboard?'VISIBLE':'HIDDEN'}</button><button onClick={()=>dispatch({type:'TOGGLE_SCOREBOARD_POS'})} style={{background:'none',border:'1px solid var(--border-subtle)',color:'var(--text-secondary)',fontFamily:"'Space Mono',monospace",fontSize:'9px',padding:'2px 6px',cursor:'pointer',letterSpacing:'.06em',textTransform:'uppercase'}}>{scoreboardPosition==='top'?'▲ TOP':'▼ BTM'}</button></div></div>
      <div className="score-panel">
        <div className="score-team">
          <div className="score-label">CYAN</div>
          <div className="score-value cyan" data-testid="cyan-score" onClick={function(){editScore('cyan')}} title="Click to set exact score" style={{cursor:'pointer'}}>{scores[0]}</div>
          <div className="score-buttons">
            <button className="score-btn" onClick={()=>dispatch({type:'SCORE_CYAN',payload:-1})}>-</button>
            <button className="score-btn" onClick={()=>dispatch({type:'SCORE_CYAN',payload:1})}>+</button>
          </div>
        </div>
        <div className="score-team">
          <div className="score-label">PINK</div>
          <div className="score-value pink" data-testid="pink-score" onClick={function(){editScore('pink')}} title="Click to set exact score" style={{cursor:'pointer'}}>{scores[1]}</div>
          <div className="score-buttons">
            <button className="score-btn" onClick={()=>dispatch({type:'SCORE_PINK',payload:-1})}>-</button>
            <button className="score-btn" onClick={()=>dispatch({type:'SCORE_PINK',payload:1})}>+</button>
          </div>
        </div>
      </div>
    </div>
  );
}

function GameControls({scores,dispatch,scoreboardPosition,showScoreboard,show,flatSlides,slideIndex}){
  var activeSide=getActiveSide(show,flatSlides,slideIndex);
  return (
    <div>
      <ScoreDisplay scores={scores} dispatch={dispatch} scoreboardPosition={scoreboardPosition} showScoreboard={showScoreboard}/>
      {activeSide&&<div style={{marginTop:'var(--space-02)'}}>
        <div className="op-section-label">ACTIVE SIDE</div>
        <div className={'game-side-indicator'+(activeSide==='cyan'?' cyan':activeSide==='pink'?' pink':' open')}>
          {activeSide==='open'?'OPEN':activeSide.toUpperCase()}
        </div>
        {(activeSide==='cyan'||activeSide==='pink')&&<button className="game-violation-btn" onClick={()=>dispatch({type:activeSide==='cyan'?'SCORE_CYAN':'SCORE_PINK',payload:1})}>SILENCE VIOLATION (+1 {activeSide.toUpperCase()})</button>}
        {activeSide==='open'&&<div style={{display:'flex',gap:'var(--space-02)',marginTop:'var(--space-02)'}}>
          <button className="game-claim-btn cyan" onClick={()=>dispatch({type:'SCORE_CYAN',payload:1})}>CLAIM: CYAN</button>
          <button className="game-claim-btn pink" onClick={()=>dispatch({type:'SCORE_PINK',payload:1})}>CLAIM: PINK</button>
        </div>}
      </div>}
    </div>
  );
}

function TimerDisplay({onTimerAction,autoAdvanceRef,autoTimerDuration,dispatch}){
  const[time,setTime]=useState(30);
  const[running,setRunning]=useState(false);
  const[pct,setPct]=useState(100);
  const[paused,setPaused]=useState(false);
  useEffect(()=>{
    if(!running)return;
    const interval=setInterval(()=>{
      const r=timerState.remaining;const t=timerState.total;
      if(t>0)setPct((r/t)*100);
      setTime(Math.ceil(r/1000));
      setPaused(timerState.paused);
      if(!timerState.running){setRunning(false);setPct(0);setPaused(false)}
    },100);
    return()=>clearInterval(interval);
  },[running]);
  const danger=pct<33;const critical=pct<17;
  function startDuration(sec){
    if(autoTimerDuration)autoTimerDuration.current=sec;
    const onEnd=function(){if(autoAdvanceRef&&autoAdvanceRef.current)dispatch({type:'NEXT_SLIDE'})};
    startTimerSystem(sec,null,onEnd);setRunning(true);setPct(100);if(onTimerAction)onTimerAction('start',sec);
  }
  return (
    <div className="timer-display">
      <div className="timer-label">TIMER</div>
      <div className="timer-bar-track"><div className={'timer-bar-fill'+(danger?' danger':'')+(critical?' critical':'')} style={{width:pct+'%'}}></div></div>
      <div className="timer-time">{running?String(Math.floor(time/60)).padStart(2,'0')+':'+String(time%60).padStart(2,'0'):'--:--'}</div>
      <div className="timer-buttons">
        <button className="timer-btn" onClick={()=>startDuration(10)}>10s</button>
        <button className="timer-btn" onClick={()=>startDuration(15)}>15s</button>
        <button className="timer-btn" onClick={()=>startDuration(30)}>30s</button>
        <button className="timer-btn" onClick={()=>startDuration(60)}>1m</button>
      </div>
      <div className="timer-buttons" style={{marginTop:'var(--space-01)'}}>
        <button className="timer-btn" onClick={()=>{if(timerState.paused){resumeTimerSystem();setPaused(false);if(onTimerAction)onTimerAction('resume')}else{pauseTimerSystem();setPaused(true);if(onTimerAction)onTimerAction('pause')}}}>{paused?'RESUME':'PAUSE'}</button>
        <button className="timer-btn" onClick={()=>{stopTimerSystem();setRunning(false);setPct(100);if(onTimerAction)onTimerAction('stop')}}>STOP</button>
      </div>
    </div>
  );
}

function RemoteTimerDisplay({showId,onTimerAction}){
  const[timerData,setTimerData]=useState(null);
  const[time,setTime]=useState(0);
  const[pct,setPct]=useState(0);
  const[running,setRunning]=useState(false);
  const[paused,setPaused]=useState(false);
  const timerRef=useRef(null);

  useEffect(function(){
    if(!fbdb||!showId)return;
    var ref=fbdb.ref('live/'+showId+'/timer');
    ref.on('value',function(snap){
      var d=snap.val();
      if(!d){setTimerData(null);setRunning(false);return}
      setTimerData(d);
      if(d.action==='start'){setRunning(true);setPaused(false)}
      else if(d.action==='stop'){setRunning(false);setPaused(false);setPct(0);setTime(0)}
      else if(d.action==='pause'){setPaused(true)}
      else if(d.action==='resume'){setPaused(false)}
    });
    return function(){ref.off()};
  },[showId]);

  useEffect(function(){
    if(!running||!timerData)return;
    function tick(){
      var d=timerData;
      if(!d||d.action==='stop'){setRunning(false);return}
      var dur=(d.duration||0)*1000;
      var elapsed=Date.now()-d.timestamp;
      if(paused){
        // When paused, show remaining at time of pause
        var remaining=Math.max(0,dur-elapsed);
        setTime(Math.ceil(remaining/1000));
        setPct(dur>0?(remaining/dur)*100:0);
        return;
      }
      var rem=Math.max(0,dur-elapsed);
      setTime(Math.ceil(rem/1000));
      setPct(dur>0?(rem/dur)*100:0);
      if(rem<=0){setRunning(false);setPct(0)}
    }
    tick();
    var iv=setInterval(tick,100);
    return function(){clearInterval(iv)};
  },[running,timerData,paused]);

  var danger=pct<33;var critical=pct<17;
  return (
    <div className="timer-display">
      <div className="timer-label">TIMER</div>
      <div className="timer-bar-track"><div className={'timer-bar-fill'+(danger?' danger':'')+(critical?' critical':'')} style={{width:pct+'%'}}></div></div>
      <div className="timer-time">{running||paused?String(Math.floor(time/60)).padStart(2,'0')+':'+String(time%60).padStart(2,'0'):'--:--'}</div>
      <div className="timer-buttons">
        <button className="timer-btn" onClick={function(){onTimerAction('start',10)}}>10s</button>
        <button className="timer-btn" onClick={function(){onTimerAction('start',15)}}>15s</button>
        <button className="timer-btn" onClick={function(){onTimerAction('start',30)}}>30s</button>
        <button className="timer-btn" onClick={function(){onTimerAction('start',60)}}>1m</button>
      </div>
      <div className="timer-buttons" style={{marginTop:'var(--space-01)'}}>
        <button className="timer-btn" onClick={function(){if(paused)onTimerAction('resume');else onTimerAction('pause')}}>{paused?'RESUME':'PAUSE'}</button>
        <button className="timer-btn" onClick={function(){onTimerAction('stop')}}>STOP</button>
      </div>
    </div>
  );
}

function QuickAccessButtons(){
  const base=window.location.origin;
  const[copied,setCopied]=useState(null);
  function openTab(mode){window.open(base+'/'+mode,'_blank')}
  function copyLink(mode){
    navigator.clipboard.writeText(base+'/'+mode).then(()=>{setCopied(mode);setTimeout(()=>setCopied(null),1500)}).catch(()=>{});
  }
  return(
    <div style={{display:'flex',flexDirection:'column',gap:'var(--space-01)'}}>
      <div className="op-section-label">QUICK ACCESS</div>
      <div className="quick-btn-row">
        <button className="quick-btn" onClick={()=>openTab('answers')}>ANSWERS ↗</button>
        <button className="quick-btn" onClick={()=>copyLink('answers')}>{copied==='answers'?'COPIED!':'COPY LINK'}</button>
      </div>
      <div className="quick-btn-row">
        <button className="quick-btn" onClick={()=>openTab('vote')}>VOTE ↗</button>
        <button className="quick-btn" onClick={()=>copyLink('vote')}>{copied==='vote'?'COPIED!':'COPY LINK'}</button>
      </div>
      <div className="quick-btn-row">
        <button className="quick-btn" onClick={()=>openTab('audience')}>AUDIENCE ↗</button>
      </div>
      <div className="quick-btn-row">
        <button className="quick-btn" onClick={()=>openTab('play')}>COMPANION ↗</button>
        <button className="quick-btn" onClick={()=>copyLink('play')}>{copied==='play'?'COPIED!':'COPY LINK'}</button>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════
// CROWD TEMPERATURE (Operator Panel)
// ═══════════════════════════════════════
function CrowdTemperature({showId,curSlide}){
  const[count,setCount]=useState(0);
  const[responses,setResponses]=useState(null);

  // Listen to companion count
  useEffect(function(){
    if(!fbdb||!showId)return;
    var ref=fbdb.ref('live/'+showId+'/companions');
    var handler=function(snap){var d=snap.val();setCount(d?Object.keys(d).length:0)};
    ref.on('value',handler);
    return function(){ref.off('value',handler)};
  },[showId]);

  // Listen to responses for current slide
  var isQ=curSlide&&['source_q','acronym_q','hotseat_prompt'].indexOf(curSlide.type)!==-1;
  var slideId=curSlide&&curSlide.id;
  useEffect(function(){
    if(!fbdb||!showId||!isQ||!slideId){setResponses(null);return}
    var ref=fbdb.ref('live/'+showId+'/responses/'+slideId);
    var handler=function(snap){var d=snap.val();setResponses(d||{knewIt:0,noClue:0})};
    ref.on('value',handler);
    return function(){ref.off('value',handler)};
  },[showId,slideId,isQ]);

  var total=responses?(responses.knewIt||0)+(responses.noClue||0):0;
  var pct=total>0?Math.round(((responses.knewIt||0)/total)*100):0;

  var[flash,setFlash]=useState(null);
  function opVote(field){
    if(!fbdb||!showId||!slideId)return;
    fbdb.ref('live/'+showId+'/responses/'+slideId+'/'+field).transaction(function(cur){return(cur||0)+10});
    setFlash(field);
    setTimeout(function(){setFlash(null)},300);
  }

  return(
    <div className="crowd-temp">
      <div className="op-section-label">CROWD</div>
      <div className="crowd-row">
        <span className={'crowd-count'+(count===0?' zero':'')}>{count} CONNECTED</span>
      </div>
      {isQ&&responses?<div style={{display:'flex',flexDirection:'column',gap:4}}>
        <div className="crowd-bar">
          <div className="crowd-bar-cy" style={{width:pct+'%'}}></div>
          <div className="crowd-bar-pk" style={{width:(100-pct)+'%'}}></div>
        </div>
        <div className="crowd-row">
          <span className="crowd-pct crowd-pct-cy">{pct}% KNEW IT</span>
          <span className="crowd-pct crowd-pct-pk">{100-pct}% NO CLUE</span>
        </div>
        <div style={{display:'flex',gap:6,marginTop:4}}>
          <button className="crowd-op-btn" style={{flex:1,border:'1px solid var(--cyan)',color:flash==='knewIt'?'#000':'var(--cyan)',background:flash==='knewIt'?'var(--cyan)':'transparent',fontFamily:"'Space Mono',monospace",fontSize:10,padding:'6px 0',cursor:'pointer',letterSpacing:'.06em',transition:'all .15s'}} onClick={function(){opVote('knewIt')}}>LANDED</button>
          <button className="crowd-op-btn" style={{flex:1,border:'1px solid var(--signal)',color:flash==='noClue'?'#000':'var(--signal)',background:flash==='noClue'?'var(--signal)':'transparent',fontFamily:"'Space Mono',monospace",fontSize:10,padding:'6px 0',cursor:'pointer',letterSpacing:'.06em',transition:'all .15s'}} onClick={function(){opVote('noClue')}}>DUD</button>
        </div>
      </div>:null}
    </div>
  );
}

function ConnectionSignal(){
  const[connected,setConnected]=useState(false);
  useEffect(()=>{
    if(!fbdb)return;
    const ref=fbdb.ref('.info/connected');
    const handler=snap=>{setConnected(!!snap.val())};
    ref.on('value',handler);
    return()=>ref.off('value',handler);
  },[]);
  return(
    <div className="conn-signal">
      <span className={'conn-dot '+(connected?'on':'off')}></span>
      <span style={{color:connected?'var(--cyan)':'var(--signal)'}}>{connected?'CONNECTED':'OFFLINE'}</span>
    </div>
  );
}

function ShortcutOverlay({onClose}){
  const groups=[
    ['NAVIGATE',[['SPACE / \u2192','ADVANCE'],['\u2190','PREVIOUS'],['1 \u2013 9','SECTION'],['R','REVEAL'],['ESC','CLOSE']]],
    ['SCORES',[['Q / W','CYAN +/-'],['P / O','PINK +/-'],['S','SCOREBOARD'],['⇧S','SCORE TOP/BTM'],['G','REWARD TIER']]],
    ['TIMING',[['T','START TIMER'],['A','AUTO-TIMER'],['D','AUTO-ADVANCE']]],
    ['SOUNDBOARD',[['Z','AIRHORN'],['X','RIMSHOT'],['B','CRICKETS'],['N','SCREAM'],[',','RAIN']]],
    ['TOOLS',[['C','ADD GUEST'],['L','SETLIST'],['M','MUTE / UNMUTE'],['F','FULLSCREEN'],['?','SHORTCUTS']]],
  ];
  return (
    <div className="shortcut-overlay" onClick={onClose}>
      <div className="shortcut-columns">
        {groups.map(([title,items])=><div className="shortcut-group" key={title}><div className="shortcut-group-title">{title}</div>{items.map(([key,action])=><div className="shortcut-row" key={key}><div className="shortcut-key">{key}</div><div className="shortcut-action">{action}</div></div>)}</div>)}
      </div>
    </div>
  );
}

function RewardTierOverlay({tier}){
  if(!tier)return null;
  const tiers={cancel:{label:'INSTANT CANCEL',cls:'hp',border:'nb-pk'},grind:{label:'THE GRIND',cls:'hg',border:'nb-gd'},flop:{label:'THE FLOP',cls:'hs',border:'nb-wh'}};
  const t=tiers[tier];if(!t)return null;
  return(
    <div className="reward-overlay">
      <div className={'nb '+t.border}></div>
      <div className={'D '+t.cls} style={{fontSize:'clamp(60px,14vw,200px)'}}>{t.label}</div>
    </div>
  );
}

function AutoToggles({autoTimer,autoAdvance,setAutoTimer,setAutoAdvance,muted,onToggleMute,extrasBypassed,dispatch}){
  function toggleExtras(){
    var next=!extrasBypassed;
    dispatch({type:'SET_EXTRAS_BYPASSED',payload:next});
    try{localStorage.setItem('lineconic_extras_bypassed',next?'1':'0')}catch(e){}
  }
  return(
    <div style={{display:'flex',flexDirection:'column',gap:'var(--space-01)'}}>
      <div className="op-section-label">CONTROLS</div>
      <button className={'auto-toggle'+(!muted?' on':'')} onClick={onToggleMute}>
        <span className="auto-toggle-dot"></span>
        <span>{muted?'TONES OFF':'TONES ON'}</span>
        <span style={{marginLeft:'auto',opacity:.5}}>[M]</span>
      </button>
      <button className={'auto-toggle'+(autoTimer?' on':'')} onClick={()=>setAutoTimer(function(v){return!v})}>
        <span className="auto-toggle-dot"></span>
        <span>AUTO-TIMER {autoTimer?'ON':'OFF'}</span>
        <span style={{marginLeft:'auto',opacity:.5}}>[A]</span>
      </button>
      <button className={'auto-toggle'+(autoAdvance?' on':'')} onClick={()=>setAutoAdvance(function(v){return!v})}>
        <span className="auto-toggle-dot"></span>
        <span>AUTO-ADVANCE {autoAdvance?'ON':'OFF'}</span>
        <span style={{marginLeft:'auto',opacity:.5}}>[D]</span>
      </button>
      <button className={'auto-toggle'+(extrasBypassed?' on':'')} onClick={toggleExtras}>
        <span className="auto-toggle-dot"></span>
        <span>BYPASS EXTRAS {extrasBypassed?'ON':'OFF'}</span>
      </button>
    </div>
  );
}

function RedFlagForm({redFlags,dispatch}){
  const[expanded,setExpanded]=useState(false);
  const[text,setText]=useState(redFlags.join('\n'));
  // Sync textarea when redFlags change externally (e.g. loaded from localStorage)
  useEffect(function(){setText(redFlags.join('\n'))},[redFlags.length]);
  function handleSave(){
    var flags=text.split('\n').map(function(l){return l.trim()}).filter(Boolean).slice(0,20);
    dispatch({type:'SET_RED_FLAGS',payload:flags});
    try{localStorage.setItem('lineconic_redflags',JSON.stringify(flags))}catch(e){}
  }
  function handleClear(){
    setText('');
    dispatch({type:'SET_RED_FLAGS',payload:[]});
    try{localStorage.removeItem('lineconic_redflags')}catch(e){}
  }
  return(
    <div className="rf-panel">
      <div className="rf-header" onClick={function(){setExpanded(!expanded)}}>
        <span className="rf-title">RED FLAGS</span>
        <span className="rf-count">{redFlags.length}/20 {expanded?'▾':'▸'}</span>
      </div>
      {expanded&&<div className="rf-body">
        <textarea className="rf-textarea" value={text} onChange={function(e){setText(e.target.value)}} placeholder="One red flag per line...&#10;e.g. Still uses Internet Explorer&#10;Claps when the plane lands" maxLength={2000}/>
        <div className="rf-btns">
          <button className="rf-btn save" onClick={handleSave}>SAVE</button>
          <button className="rf-btn" onClick={handleClear}>CLEAR</button>
        </div>
      </div>}
    </div>
  );
}

function RedFlagSubmissions({showId,dispatch,redFlags}){
  var [submissions,setSubmissions]=useState([]);
  var [filter,setFilter]=useState('all');

  useEffect(function(){
    if(!fbdb||!showId)return;
    var ref=fbdb.ref('live/'+showId+'/redflags');
    var handler=function(snap){
      var val=snap.val();
      if(!val){setSubmissions([]);return}
      var list=Object.entries(val).map(function(entry){
        return{id:entry[0],...entry[1]};
      }).sort(function(a,b){return(a.submittedAt||0)-(b.submittedAt||0)});
      setSubmissions(list);
    };
    ref.on('value',handler);
    return function(){ref.off('value',handler)};
  },[showId]);

  var filtered=filter==='all'?submissions:submissions.filter(function(s){return s.source===filter});
  var preCount=submissions.filter(function(s){return s.source==='pre-show'}).length;
  var doorCount=submissions.filter(function(s){return s.source==='door'}).length;

  function addToShow(sub){
    if(redFlags.indexOf(sub.text)!==-1)return;
    var newFlags=redFlags.concat([sub.text]).slice(0,20);
    dispatch({type:'SET_RED_FLAGS',payload:newFlags});
    try{localStorage.setItem('lineconic_redflags',JSON.stringify(newFlags))}catch(e){}
  }

  function addAll(){
    var texts=filtered.map(function(s){return s.text}).filter(function(t){return redFlags.indexOf(t)===-1});
    var newFlags=redFlags.concat(texts).slice(0,20);
    dispatch({type:'SET_RED_FLAGS',payload:newFlags});
    try{localStorage.setItem('lineconic_redflags',JSON.stringify(newFlags))}catch(e){}
  }

  return React.createElement('div',{className:'rf-panel',style:{marginTop:8}},
    React.createElement('div',{className:'rf-header'},
      React.createElement('span',{className:'rf-title'},'SUBMISSIONS'),
      React.createElement('span',{className:'rf-count'},preCount+' pre-show / '+doorCount+' door')
    ),
    React.createElement('div',{style:{display:'flex',gap:6,marginBottom:8}},
      ['all','pre-show','door'].map(function(f){
        return React.createElement('button',{
          key:f,className:'rf-btn'+(filter===f?' save':''),
          onClick:function(){setFilter(f)},
          style:{fontSize:10,padding:'4px 10px'}
        },f.toUpperCase());
      })
    ),
    React.createElement('div',{style:{maxHeight:200,overflow:'auto'}},
      filtered.length===0
        ?React.createElement('div',{style:{fontSize:11,opacity:0.4,padding:8}},'no submissions yet')
        :filtered.map(function(sub){
            var inShow=redFlags.indexOf(sub.text)!==-1;
            return React.createElement('div',{
              key:sub.id,
              style:{display:'flex',justifyContent:'space-between',alignItems:'center',padding:'4px 0',borderBottom:'1px solid rgba(255,255,255,0.1)',fontSize:12}
            },
              React.createElement('span',{style:{flex:1,opacity:inShow?0.4:1}},sub.text),
              React.createElement('button',{
                className:'rf-btn',disabled:inShow,
                onClick:function(){addToShow(sub)},
                style:{fontSize:9,padding:'1px 6px',marginLeft:4}
              },inShow?'ADDED':'+')
            );
          })
    ),
    filtered.length>0&&React.createElement('div',{style:{marginTop:8}},
      React.createElement('button',{className:'rf-btn save',onClick:addAll,style:{fontSize:10,width:'100%'}},'ADD ALL TO SHOW')
    )
  );
}

function HostRemoteLayout({state,dispatch,flat,slide,showId,onDisconnect}){
  const sectionIdx=slideToSection(state.show,state.currentSlide,state.redFlags,state.extrasBypassed,state.bonusSections);
  const revealed=(state.revealState[slide.id]||'hidden')==='revealed';
  const chipsRef=useRef(null);

  useEffect(function(){
    if(!chipsRef.current)return;
    var active=chipsRef.current.querySelector('.host-chip.active');
    if(active)active.scrollIntoView({behavior:'smooth',inline:'center',block:'nearest'});
  },[sectionIdx]);

  function remoteTimerAction(action,duration){
    if(!fbdb||!showId)return;
    var deviceId=localStorage.getItem('lc_remote_device')||'rem_unknown';
    fbdb.ref('live/'+showId+'/remote_cmd').set({type:'TIMER:'+action+(duration?':'+duration:''),ts:Date.now(),deviceId:deviceId});
  }

  return(
    <div className="host-shell">
      <div className="host-top">
        <div className="host-top-section">{state.show.sections[sectionIdx]?state.show.sections[sectionIdx].name:''}</div>
        <div className="host-top-counter">{state.currentSlide+1}/{flat.length}</div>
      </div>

      <div className="host-scroll">
        <div className="host-chips" ref={chipsRef}>
          {state.show.sections.map(function(sec,i){
            return <button key={sec.id} className={'host-chip'+(i===sectionIdx?' active':'')} onClick={function(){dispatch({type:'JUMP_SECTION',payload:i})}}>{sec.name}</button>;
          })}
        </div>

        <div className="host-card">
          <div className="host-card-type">{slide.type}</div>
          <div className="host-card-primary">{slide.content.primary||''}</div>
          {slide.content.answer&&<div><div className="host-card-answer-label">ANSWER</div><div className="host-card-answer">{slide.content.answer}</div></div>}
          {slide.content.source&&<div style={{fontSize:11,color:'var(--text-disabled)',textAlign:'center',marginTop:6}}>{slide.content.source}</div>}
          {slide.content.secondary&&<div style={{fontSize:11,color:'var(--text-disabled)',textAlign:'center',marginTop:6}}>{slide.content.secondary}</div>}
          <div style={{textAlign:'center'}}>
            <span className={'host-reveal '+(revealed?'revealed':'hidden')} onClick={function(){dispatch({type:'TOGGLE_REVEAL'})}}>{revealed?'REVEALED':'HIDDEN'}</span>
          </div>
          {slide.host_notes&&<div className="host-card-notes">{slide.host_notes}</div>}
        </div>

        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:'8px'}}>
          <div style={{fontFamily:"'Space Mono',monospace",fontSize:'11px',color:'var(--text-secondary)',textTransform:'uppercase',letterSpacing:'.08em'}}>SCOREBOARD</div>
          <div style={{display:'flex',gap:'4px'}}>
            <button onClick={function(){dispatch({type:'TOGGLE_SCOREBOARD'})}} style={{background:'none',border:'1px solid '+(state.showScoreboard?'var(--signal)':'var(--border-subtle)'),color:state.showScoreboard?'var(--signal)':'var(--text-secondary)',fontFamily:"'Space Mono',monospace",fontSize:'10px',padding:'3px 8px',cursor:'pointer',letterSpacing:'.06em',textTransform:'uppercase'}}>{state.showScoreboard?'VISIBLE':'HIDDEN'}</button>
            <button onClick={function(){dispatch({type:'TOGGLE_SCOREBOARD_POS'})}} style={{background:'none',border:'1px solid var(--border-subtle)',color:'var(--text-secondary)',fontFamily:"'Space Mono',monospace",fontSize:'10px',padding:'3px 8px',cursor:'pointer',letterSpacing:'.06em',textTransform:'uppercase'}}>{state.scoreboardPosition==='top'?'▲ TOP':'▼ BTM'}</button>
          </div>
        </div>
        <div className="host-scores">
          <div className="host-score-team">
            <div className="host-score-label" style={{color:'var(--text-secondary)'}}>CYAN</div>
            <div className="host-score-val" style={{color:'var(--cyan)',textShadow:'var(--glow-cyan)'}}>{state.scores[0]}</div>
            <div className="host-score-btns">
              <button className="host-score-btn" onClick={function(){dispatch({type:'SCORE_CYAN',payload:-1})}}>-</button>
              <button className="host-score-btn" onClick={function(){dispatch({type:'SCORE_CYAN',payload:1})}}>+</button>
            </div>
          </div>
          <div className="host-score-team">
            <div className="host-score-label" style={{color:'var(--text-secondary)'}}>PINK</div>
            <div className="host-score-val" style={{color:'var(--signal)',textShadow:'var(--glow-signal)'}}>{state.scores[1]}</div>
            <div className="host-score-btns">
              <button className="host-score-btn" onClick={function(){dispatch({type:'SCORE_PINK',payload:-1})}}>-</button>
              <button className="host-score-btn" onClick={function(){dispatch({type:'SCORE_PINK',payload:1})}}>+</button>
            </div>
          </div>
        </div>

        {(function(){var as=getActiveSide(state.show,flat,state.currentSlide);if(!as)return null;return(
          <div style={{marginBottom:'var(--space-04)'}}>
            <div className={'game-side-indicator'+(as==='cyan'?' cyan':as==='pink'?' pink':' open')}>
              {as==='open'?'OPEN':as.toUpperCase()}
            </div>
            {(as==='cyan'||as==='pink')&&<button className="game-violation-btn" onClick={function(){dispatch({type:as==='cyan'?'SCORE_CYAN':'SCORE_PINK',payload:1})}}>SILENCE VIOLATION (+1 {as.toUpperCase()})</button>}
            {as==='open'&&<div style={{display:'flex',gap:'var(--space-02)',marginTop:'var(--space-02)'}}>
              <button className="game-claim-btn cyan" onClick={function(){dispatch({type:'SCORE_CYAN',payload:1})}}>CLAIM: CYAN</button>
              <button className="game-claim-btn pink" onClick={function(){dispatch({type:'SCORE_PINK',payload:1})}}>CLAIM: PINK</button>
            </div>}
          </div>
        )})()}

        <RemoteTimerDisplay showId={showId} onTimerAction={remoteTimerAction}/>

        <div className="host-footer">
          <ConnectionSignal/>
          <div style={{display:'flex',gap:8}}>
            <button className="host-footer-btn danger" onClick={onDisconnect}>DISCONNECT</button>
            <button className="host-footer-btn" onClick={function(){if(fbauth)fbauth.signOut()}}>SIGN OUT</button>
          </div>
        </div>
      </div>

      <div className="host-bottom">
        <button className="host-nav-btn" onClick={function(){dispatch({type:'PREV_SLIDE'})}}>&larr; PREV</button>
        <div className="host-nav-divider"></div>
        <button className="host-nav-btn primary" onClick={function(){dispatch({type:'NEXT_SLIDE'})}}>NEXT &rarr;</button>
      </div>
    </div>
  );
}

function BonusButtons({show,redFlags,extrasBypassed,bonusSections,currentSlide,dispatch}){
  const[added,setAdded]=useState(null);
  var queueCount=(bonusSections||[]).length;
  function handleBonus(mt){
    var label=mt==='movie'?'MOVIES':mt==='music'?'MUSIC':mt==='hotseat'?'HOT SEAT':'MIXED';
    setAdded(label);
    addBonusRound(mt,show,redFlags||[],!!extrasBypassed,bonusSections||[],currentSlide||0,dispatch).then(function(){setTimeout(function(){setAdded(null)},1500)}).catch(function(){setAdded(null)});
  }
  function removeLast(){
    if(!queueCount)return;
    if(typeof confirm==='function'&&!confirm('Remove the last bonus round?'))return;
    dispatch({type:'REMOVE_BONUS_SECTION'});
  }
  return(
    <div style={{display:'flex',alignItems:'center',flexWrap:'wrap',gap:4,rowGap:4}}>
      <span style={{fontSize:9,color:'var(--text-secondary)',letterSpacing:'.06em',width:'100%'}}>BONUS:</span>
      {['movie','music',null,'hotseat'].map(function(mt){var label=mt==='movie'?'MOVIES':mt==='music'?'MUSIC':mt==='hotseat'?'HOTSEAT':'MIXED';return <button key={label} className="setlist-btn accent" style={{fontSize:10,padding:'4px 6px',flex:'1 1 60px',whiteSpace:'nowrap'}} onClick={function(){handleBonus(mt)}}>{added===label?'ADDED!':label}</button>})}
      {queueCount>0&&<span style={{fontSize:9,color:'var(--gold)',letterSpacing:'.06em',padding:'2px 6px',border:'1px solid var(--gold)',borderRadius:2}} title={queueCount+' bonus round'+(queueCount===1?'':'s')+' queued'}>{'×'+queueCount}</span>}
      {queueCount>0&&<button className="setlist-btn" style={{fontSize:10,padding:'4px 8px',color:'var(--signal)'}} title="Remove last bonus round" onClick={removeLast}>UNDO</button>}
    </div>
  );
}

function MobileCrowdButtons({showId,slideId}){
  var[flash,setFlash]=useState(null);
  function opVote(field){
    if(!fbdb||!showId||!slideId)return;
    fbdb.ref('live/'+showId+'/responses/'+slideId+'/'+field).transaction(function(cur){return(cur||0)+10});
    setFlash(field);
    setTimeout(function(){setFlash(null)},300);
  }
  return(
    <div style={{display:'flex',gap:8,marginBottom:'var(--space-03)'}}>
      <button style={{flex:1,border:'1px solid var(--cyan)',color:flash==='knewIt'?'#000':'var(--cyan)',background:flash==='knewIt'?'var(--cyan)':'transparent',fontFamily:"'Space Mono',monospace",fontSize:10,padding:'8px 0',cursor:'pointer',letterSpacing:'.06em',transition:'all .15s'}} onClick={function(){opVote('knewIt')}}>LANDED</button>
      <button style={{flex:1,border:'1px solid var(--signal)',color:flash==='noClue'?'#000':'var(--signal)',background:flash==='noClue'?'var(--signal)':'transparent',fontFamily:"'Space Mono',monospace",fontSize:10,padding:'8px 0',cursor:'pointer',letterSpacing:'.06em',transition:'all .15s'}} onClick={function(){opVote('noClue')}}>DUD</button>
    </div>
  );
}

function MobileOperatorLayout({state,dispatch,flat,slide,nextSlide,onTimerAction,onFx,autoTimer,autoAdvance,setAutoTimer,setAutoAdvance,autoAdvanceRef,autoTimerDuration,onPairToOther,onBackToLocal}){
  const sectionIdx=slideToSection(state.show,state.currentSlide,state.redFlags,state.extrasBypassed,state.bonusSections);
  const revealed=(state.revealState[slide.id]||'hidden')==='revealed';
  const chipsRef=useRef(null);

  // Auto-scroll active chip into view
  useEffect(function(){
    if(!chipsRef.current)return;
    var active=chipsRef.current.querySelector('.mob-chip.active');
    if(active)active.scrollIntoView({behavior:'smooth',inline:'center',block:'nearest'});
  },[sectionIdx]);

  function navPrev(){stopTimerSystem();if(onTimerAction)onTimerAction('stop');dispatch({type:'PREV_SLIDE'})}
  function navNext(){stopTimerSystem();if(onTimerAction)onTimerAction('stop');dispatch({type:'NEXT_SLIDE'})}

  return(
    <div className="mob-shell">
      {/* Top bar */}
      <div className="mob-top">
        <div className="mob-top-section">{state.show.sections[sectionIdx]?state.show.sections[sectionIdx].name:''}</div>
        <div className="mob-top-counter">{state.currentSlide+1}/{flat.length}</div>
      </div>

      {/* Scrollable body */}
      <div className="mob-scroll">
        {/* Section chips */}
        <div className="mob-section-chips" ref={chipsRef}>
          {state.show.sections.map(function(sec,i){
            return <button key={sec.id} className={'mob-chip'+(i===sectionIdx?' active':'')} onClick={function(){dispatch({type:'JUMP_SECTION',payload:i})}}>{sec.name}</button>;
          })}
        </div>

        {/* Current slide */}
        <div className="mob-card">
          <div className="mob-card-type">{slide.type}</div>
          <div className="mob-card-title">{slide.content.primary||''}</div>
          {slide.content.answer&&<div className="mob-card-answer">{slide.content.answer}</div>}
          {slide.content.source&&<div style={{fontSize:10,color:'var(--text-disabled)',textAlign:'center',marginTop:4}}>{slide.content.source}</div>}
          {slide.content.secondary&&<div style={{fontSize:10,color:'var(--text-disabled)',textAlign:'center',marginTop:4}}>{slide.content.secondary}</div>}
          <div style={{textAlign:'center'}}>
            <span className={'mob-reveal '+(revealed?'revealed':'hidden')} onClick={function(){dispatch({type:'TOGGLE_REVEAL'})}}>{revealed?'REVEALED':'HIDDEN'}</span>
          </div>
          {slide.host_notes&&<div className="mob-card-notes">// {slide.host_notes}</div>}
        </div>

        {/* Operator crowd feedback */}
        {(function(){
          var mobIsQ=['source_q','acronym_q','hotseat_prompt'].indexOf(slide.type)!==-1;
          var mobShowId=state.show&&state.show.id;
          if(!mobIsQ||!mobShowId)return null;
          return React.createElement(MobileCrowdButtons,{showId:mobShowId,slideId:slide.id});
        })()}

        {/* Next slide preview */}
        <div className="mob-next">
          <div className="mob-next-label">NEXT</div>
          {nextSlide
            ?<div><div style={{fontSize:10,color:'var(--text-secondary)',textTransform:'uppercase',letterSpacing:'.08em'}}>{nextSlide.type}</div><div className="mob-next-text">{nextSlide.content.primary||''}</div></div>
            :<div style={{fontSize:10,color:'var(--text-disabled)'}}>END OF SHOW</div>
          }
        </div>

        {/* Scores */}
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:'6px'}}>
          <div style={{fontFamily:"'Space Mono',monospace",fontSize:'10px',color:'var(--text-secondary)',textTransform:'uppercase',letterSpacing:'.08em'}}>SCOREBOARD</div>
          <div style={{display:'flex',gap:'4px'}}>
            <button onClick={function(){dispatch({type:'TOGGLE_SCOREBOARD'})}} style={{background:'none',border:'1px solid '+(state.showScoreboard?'var(--signal)':'var(--border-subtle)'),color:state.showScoreboard?'var(--signal)':'var(--text-secondary)',fontFamily:"'Space Mono',monospace",fontSize:'9px',padding:'2px 6px',cursor:'pointer',letterSpacing:'.06em',textTransform:'uppercase'}}>{state.showScoreboard?'VISIBLE':'HIDDEN'}</button>
            <button onClick={function(){dispatch({type:'TOGGLE_SCOREBOARD_POS'})}} style={{background:'none',border:'1px solid var(--border-subtle)',color:'var(--text-secondary)',fontFamily:"'Space Mono',monospace",fontSize:'9px',padding:'2px 6px',cursor:'pointer',letterSpacing:'.06em',textTransform:'uppercase'}}>{state.scoreboardPosition==='top'?'▲ TOP':'▼ BTM'}</button>
          </div>
        </div>
        <div className="mob-scores">
          <div className="mob-score-team">
            <div className="mob-score-label" style={{color:'var(--text-secondary)'}}>CYAN</div>
            <div className="mob-score-val" style={{color:'var(--cyan)',textShadow:'var(--glow-cyan)'}}>{state.scores[0]}</div>
            <div className="mob-score-btns">
              <button className="mob-score-btn" onClick={function(){dispatch({type:'SCORE_CYAN',payload:-1})}}>-</button>
              <button className="mob-score-btn" onClick={function(){dispatch({type:'SCORE_CYAN',payload:1})}}>+</button>
            </div>
          </div>
          <div className="mob-score-team">
            <div className="mob-score-label" style={{color:'var(--text-secondary)'}}>PINK</div>
            <div className="mob-score-val" style={{color:'var(--signal)',textShadow:'var(--glow-signal)'}}>{state.scores[1]}</div>
            <div className="mob-score-btns">
              <button className="mob-score-btn" onClick={function(){dispatch({type:'SCORE_PINK',payload:-1})}}>-</button>
              <button className="mob-score-btn" onClick={function(){dispatch({type:'SCORE_PINK',payload:1})}}>+</button>
            </div>
          </div>
        </div>

        {/* Active Side */}
        {(function(){var as=getActiveSide(state.show,flat,state.currentSlide);if(!as)return null;return(
          <div style={{marginBottom:'var(--space-03)'}}>
            <div className={'game-side-indicator'+(as==='cyan'?' cyan':as==='pink'?' pink':' open')}>
              {as==='open'?'OPEN':as.toUpperCase()}
            </div>
            {(as==='cyan'||as==='pink')&&<button className="game-violation-btn" onClick={function(){dispatch({type:as==='cyan'?'SCORE_CYAN':'SCORE_PINK',payload:1})}}>SILENCE VIOLATION (+1 {as.toUpperCase()})</button>}
            {as==='open'&&<div style={{display:'flex',gap:'var(--space-02)',marginTop:'var(--space-02)'}}>
              <button className="game-claim-btn cyan" onClick={function(){dispatch({type:'SCORE_CYAN',payload:1})}}>CLAIM: CYAN</button>
              <button className="game-claim-btn pink" onClick={function(){dispatch({type:'SCORE_PINK',payload:1})}}>CLAIM: PINK</button>
            </div>}
          </div>
        )})()}

        {/* Timer */}
        <TimerDisplay onTimerAction={onTimerAction} autoAdvanceRef={autoAdvanceRef} autoTimerDuration={autoTimerDuration} dispatch={dispatch}/>

        {/* Controls */}
        <div style={{marginTop:'var(--space-03)'}}>
          <AutoToggles autoTimer={autoTimer} autoAdvance={autoAdvance} setAutoTimer={setAutoTimer} setAutoAdvance={setAutoAdvance} muted={state.muted} onToggleMute={function(){dispatch({type:'TOGGLE_MUTE'})}} extrasBypassed={state.extrasBypassed} dispatch={dispatch}/>
        </div>

        {/* Setlist editor button */}
        <div style={{marginTop:'var(--space-03)'}}>
          <button className="setlist-btn" onClick={function(){dispatch({type:'TOGGLE_SETLIST_EDITOR'})}} style={{width:'100%',padding:'10px 12px',fontSize:11}}>{state.liveMode?'LIVE \uD83D\uDD34':'SETLIST EDITOR'}</button>
        </div>

        {/* Bonus round buttons */}
        <div style={{marginTop:'var(--space-03)'}}>
          <BonusButtons show={state.show} redFlags={state.redFlags} extrasBypassed={state.extrasBypassed} bonusSections={state.bonusSections} currentSlide={state.currentSlide} dispatch={dispatch}/>
        </div>

        {/* Red flag form */}
        <div style={{marginTop:'var(--space-03)'}}>
          <RedFlagForm redFlags={state.redFlags} dispatch={dispatch}/>
          <RedFlagSubmissions showId={state.show&&state.show.id} dispatch={dispatch} redFlags={state.redFlags}/>
        </div>

        {/* Connection + Sign out */}
        <div style={{marginTop:'var(--space-03)',display:'flex',alignItems:'center',justifyContent:'space-between'}}>
          <ConnectionSignal/>
          <div style={{display:'flex',gap:8}}>
            {onBackToLocal&&<button className="mob-signout" onClick={onBackToLocal} style={{borderColor:'var(--cyan)',color:'var(--cyan)'}}>BACK TO LOCAL</button>}
            {onPairToOther&&<button className="mob-signout" onClick={onPairToOther} style={{borderColor:'var(--signal)',color:'var(--signal)'}}>PAIR TO OTHER</button>}
            <button className="mob-signout" onClick={function(){if(fbauth)fbauth.signOut()}}>SIGN OUT</button>
          </div>
        </div>
      </div>

      {/* Soundboard */}
      <div className="mob-soundboard">
        <button className="mob-snd-btn" onClick={function(){onFx('airhorn')}}>HORN</button>
        <button className="mob-snd-btn" onClick={function(){onFx('rimshot')}}>RIM</button>
        <button className="mob-snd-btn" onClick={function(){onFx('crickets')}}>CRIK</button>
        <button className="mob-snd-btn" onClick={function(){onFx('scream')}}>SCRM</button>
        <button className="mob-snd-btn" onClick={function(){onFx('rain')}}>RAIN</button>
      </div>

      {/* Bottom nav */}
      <div className="mob-bottom">
        <button className="mob-nav-btn" onClick={navPrev}>&larr; PREV</button>
        <div className="mob-nav-divider"></div>
        <button className="mob-nav-btn primary" onClick={navNext}>NEXT &rarr;</button>
      </div>
      {state.showSetlistEditor&&<SetlistEditor show={state.show} dispatch={dispatch} liveMode={state.liveMode} redFlags={state.redFlags} currentSlide={state.currentSlide} extrasBypassed={state.extrasBypassed} bonusSections={state.bonusSections}/>}
    </div>
  );
}



function SetlistEditor({show,dispatch,liveMode,redFlags,currentSlide,extrasBypassed,bonusSections}){
  const sectionRefs=useRef([]);
  const sortableInstances=useRef([]);
  const[swapOpen,setSwapOpen]=useState(null); // {si,sli}
  const[saveName,setSaveName]=useState('');
  const[showLoad,setShowLoad]=useState(false);
  const[showImport,setShowImport]=useState(false);
  const[importText,setImportText]=useState('');
  const[importErr,setImportErr]=useState('');
  const[savedList,setSavedList]=useState(function(){try{return JSON.parse(localStorage.getItem('lineconic_setlists'))||[]}catch(e){return[]}});
  const[firebaseShows,setFirebaseShows]=useState([]);
  const[fbLoading,setFbLoading]=useState(false);
  const[loadStatus,setLoadStatus]=useState('');
  const fileRef=useRef(null);
  const[bonusAdded,setBonusAdded]=useState(null);

  // Attach SortableJS to each section
  useEffect(function(){
    if(liveMode)return;
    sortableInstances.current.forEach(function(s){if(s)try{s.destroy()}catch(e){}});
    sortableInstances.current=[];
    sectionRefs.current.forEach(function(el,si){
      if(!el)return;
      var inst=new Sortable(el,{
        animation:0,
        ghostClass:'sortable-ghost',
        chosenClass:'sortable-chosen',
        handle:'.setlist-slide-grip',
        onEnd:function(evt){
          var sec=show.sections[si];
          var slides=sec.slides.slice();
          var moved=slides.splice(evt.oldIndex,1)[0];
          slides.splice(evt.newIndex,0,moved);
          dispatch({type:'REORDER_SLIDES',payload:{sectionIndex:si,newOrder:slides}});
        }
      });
      sortableInstances.current.push(inst);
    });
    return function(){sortableInstances.current.forEach(function(s){if(s)try{s.destroy()}catch(e){}});sortableInstances.current=[]};
  },[show.sections,liveMode]);

  // Fetch Firebase show index when LOAD dropdown opens
  useEffect(function(){
    if(!showLoad||!fbdb)return;
    setFbLoading(true);
    fbdb.ref('show_index').orderByChild('created').once('value',function(snap){
      var list=[];
      snap.forEach(function(child){list.push({id:child.key,name:child.val().name,created:child.val().created,templateId:child.val().templateId})});
      list.reverse();
      setFirebaseShows(list);
      setFbLoading(false);
    },function(err){
      console.warn('show_index fetch failed',err);
      // Backfill: if show_index doesn't exist, read shows/ directly
      fbdb.ref('shows').once('value',function(showsSnap){
        var backfillUpdates={};var backfillList=[];
        showsSnap.forEach(function(child){
          var s=child.val();
          if(s&&s.name){
            backfillUpdates['show_index/'+child.key]={name:s.name,created:s.created||0,templateId:s.templateId||null};
            backfillList.push({id:child.key,name:s.name,created:s.created||0,templateId:s.templateId||null});
          }
        });
        if(Object.keys(backfillUpdates).length>0)fbdb.ref().update(backfillUpdates).catch(function(){});
        backfillList.sort(function(a,b){return(b.created||0)-(a.created||0)});
        setFirebaseShows(backfillList);
        setFbLoading(false);
      });
    });
  },[showLoad]);

  function handleSwap(si,sli,alt){
    dispatch({type:'SWAP_SLIDE',payload:{sectionIndex:si,slideIndex:sli,newSlide:alt}});
    setSwapOpen(null);
  }

  function handleSave(){
    if(!saveName.trim())return;
    var entry={name:saveName.trim(),savedAt:Date.now(),showId:show.id,sections:show.sections};
    var list=savedList.concat([entry]);
    localStorage.setItem('lineconic_setlists',JSON.stringify(list));
    setSavedList(list);
    setSaveName('');
  }

  function handleLoad(entry){
    var restored={id:entry.showId||show.id,name:show.name,sections:entry.sections,created:show.created};
    dispatch({type:'IMPORT_SHOW',payload:restored});
    setShowLoad(false);
  }

  function handleDeleteSave(idx){
    var list=savedList.filter(function(_,i){return i!==idx});
    localStorage.setItem('lineconic_setlists',JSON.stringify(list));
    setSavedList(list);
  }

  function handleLoadFirebase(showId){
    if(!fbdb){alert('FIREBASE NOT CONNECTED');return}
    var timeout=setTimeout(function(){alert('LOAD TIMED OUT — CHECK CONNECTION')},8000);
    fbdb.ref('shows/'+showId).once('value').then(function(snap){
      clearTimeout(timeout);
      var showData=snap.val();
      if(!showData||!showData.sections){alert('SHOW DATA MISSING');return}
      dispatch({type:'IMPORT_SHOW',payload:showData});
      try{localStorage.setItem('lineconic_show',JSON.stringify(showData))}catch(e){}
      fbdb.ref('active_show').set(showId).catch(function(){});
      setShowLoad(false);
      setLoadStatus('LOADED: '+(showData.name||showId));
      setTimeout(function(){setLoadStatus('')},3000);
    }).catch(function(e){
      clearTimeout(timeout);
      alert('LOAD FAILED: '+e.message);
    });
  }

  function handleDeleteFirebase(showId){
    if(!fbdb)return;
    var isActive=show.id===showId;
    var msg='DELETE THIS SHOW FROM FIREBASE?';
    if(isActive)msg+='\n\nWARNING: THIS IS THE CURRENTLY ACTIVE SHOW.';
    if(!confirm(msg))return;
    var updates={};
    updates['shows/'+showId]=null;
    updates['show_index/'+showId]=null;
    fbdb.ref().update(updates).then(function(){
      setFirebaseShows(function(prev){return prev.filter(function(s){return s.id!==showId})});
    }).catch(function(e){alert('DELETE FAILED: '+e.message)});
  }

  function handleDeleteAllFirebase(){
    if(!fbdb||firebaseShows.length===0)return;
    if(!confirm('DELETE ALL '+firebaseShows.length+' SHOWS FROM FIREBASE?\n\nTHIS CANNOT BE UNDONE.'))return;
    var updates={shows:null,show_index:null,active_show:null};
    fbdb.ref().update(updates).then(function(){
      setFirebaseShows([]);
    }).catch(function(e){alert('DELETE ALL FAILED: '+e.message)});
  }

  function handleLoadDefault(){
    fetch('/ros-v1.json').then(function(r){return r.json()}).then(function(showData){
      dispatch({type:'IMPORT_SHOW',payload:showData});
      try{localStorage.setItem('lineconic_show',JSON.stringify(showData))}catch(e){}
      if(fbdb)fbdb.ref('active_show').set(showData.id).catch(function(){});
      setShowLoad(false);
    }).catch(function(){alert('FAILED TO LOAD DEFAULT SHOW')});
  }

  function parseImport(raw){
    setImportErr('');
    try{
      var obj=JSON.parse(raw);
      if(!obj.sections||!Array.isArray(obj.sections)){setImportErr('MISSING SECTIONS ARRAY');return}
      if(!obj.id&&!obj.name){setImportErr('MISSING ID OR NAME');return}
      dispatch({type:'IMPORT_SHOW',payload:obj});
      localStorage.setItem('lineconic_show',JSON.stringify(obj));
      setShowImport(false);setImportText('');
    }catch(e){setImportErr('INVALID JSON: '+e.message)}
  }

  function handleFile(e){
    var f=e.target.files[0];if(!f)return;
    var reader=new FileReader();
    reader.onload=function(ev){parseImport(ev.target.result)};
    reader.readAsText(f);
  }

  function handleGoLive(){dispatch({type:'SET_LIVE_MODE'})}

  function slideLabel(s){
    var p=s.content&&s.content.primary||'';
    if(p.length>60)p=p.slice(0,57)+'...';
    return p;
  }

  return(
    <div className="setlist-overlay" onClick={function(e){if(e.target===e.currentTarget)dispatch({type:'TOGGLE_SETLIST_EDITOR'})}}>
      <div className="setlist-header">
        <div className="setlist-title">SETLIST EDITOR</div>
        <div style={{display:'flex',alignItems:'center',gap:'var(--space-03)'}}>
          {liveMode&&<div className="setlist-lock-badge">LOCKED — LIVE</div>}
          <button className="setlist-close" onClick={function(){dispatch({type:'TOGGLE_SETLIST_EDITOR'})}}>CLOSE</button>
        </div>
      </div>
      <div className={'setlist-body'+(liveMode?' setlist-locked':'')}>
        {liveMode&&<div style={{padding:'10px 14px',margin:'0 0 12px',background:'rgba(255,0,127,.08)',border:'1px solid var(--signal)',color:'var(--signal)',fontFamily:"'Space Mono',monospace",fontSize:11,letterSpacing:'.1em',textTransform:'uppercase',textAlign:'center'}}>READ-ONLY · the show is live · go offline to edit</div>}
        {show.sections.map(function(sec,si){
          return(
            <div className="setlist-section" key={sec.id||si}>
              <div className="setlist-section-head">
                <span className="setlist-section-name">{sec.name}</span>
                <span className="setlist-section-count">{sec.slides.length}</span>
              </div>
              <div ref={function(el){sectionRefs.current[si]=el}}>
                {sec.slides.map(function(slide,sli){
                  var hasAlts=slide.alternates&&slide.alternates.length>0;
                  var isSwapOpen=swapOpen&&swapOpen.si===si&&swapOpen.sli===sli;
                  return(
                    <div className="setlist-slide" key={slide.id||si+'-'+sli} data-id={slide.id}>
                      <div className="setlist-slide-grip">&#x2807;</div>
                      <div className="setlist-slide-type">{slide.type.replace(/_/g,' ')}</div>
                      <div className="setlist-slide-text">{slideLabel(slide)}</div>
                      {slide.content&&slide.content.answer&&<div className="setlist-slide-answer">{slide.content.answer}</div>}
                      <div className="setlist-slide-actions">
                        {hasAlts&&<div style={{position:'relative'}}>
                          <button className={'setlist-btn'+(isSwapOpen?' accent':'')} onClick={function(){setSwapOpen(isSwapOpen?null:{si:si,sli:sli})}}>SWAP</button>
                          {isSwapOpen&&<div className="setlist-alt-popup">
                            {slide.alternates.map(function(alt,ai){
                              return <div className="setlist-alt-item" key={alt.id||ai} onClick={function(){handleSwap(si,sli,alt)}}>
                                <span style={{flex:1}}>{alt.content&&alt.content.primary||alt.id}</span>
                                {alt.content&&alt.content.answer&&<span style={{color:'var(--text-disabled)',fontSize:9}}>{alt.content.answer}</span>}
                              </div>;
                            })}
                          </div>}
                        </div>}
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          );
        })}
      </div>
      <div className="setlist-footer">
        <div className="setlist-footer-row">
          <input className="setlist-input" placeholder="Setlist name..." value={saveName} onChange={function(e){setSaveName(e.target.value)}} onKeyDown={function(e){if(e.key==='Enter')handleSave()}}/>
          <button className="setlist-btn accent" onClick={handleSave} disabled={!saveName.trim()}>SAVE</button>
          <div style={{position:'relative'}}>
            <button className="setlist-btn" onClick={function(){setShowLoad(!showLoad);setShowImport(false)}}>LOAD</button>
          {showLoad&&<div className="setlist-load-list">
            {savedList.length>0&&<div>
              <div className="setlist-load-section-head">SAVED SETLISTS</div>
              {savedList.map(function(entry,i){
                return <div className="setlist-load-item" key={'local-'+i}>
                  <span onClick={function(){handleLoad(entry)}} style={{flex:1,cursor:'pointer'}}>{entry.name}</span>
                  <span style={{color:'var(--text-disabled)',fontSize:9}}>{new Date(entry.savedAt).toLocaleDateString()}</span>
                  <button className="setlist-load-del" onClick={function(e){e.stopPropagation();handleDeleteSave(i)}}>&times;</button>
                </div>;
              })}
            </div>}
            <div className="setlist-load-section-head" style={{display:'flex',alignItems:'center',justifyContent:'space-between'}}>SHOWS{fbLoading?' ...':''}{firebaseShows.length>0&&<button onClick={handleDeleteAllFirebase} style={{background:'none',border:'1px solid var(--signal)',color:'var(--signal)',fontSize:7,padding:'1px 6px',cursor:'pointer',fontFamily:'Space Mono,monospace',letterSpacing:'.05em'}}>DELETE ALL</button>}</div>
            {!fbdb&&<div style={{padding:8,color:'var(--text-disabled)',fontSize:10}}>FIREBASE OFFLINE</div>}
            {fbdb&&firebaseShows.length===0&&!fbLoading&&<div style={{padding:8,color:'var(--text-disabled)',fontSize:10}}>NO SHOWS IN FIREBASE</div>}
            {firebaseShows.map(function(entry){
              var isActive=show.id===entry.id;
              return <div className={'setlist-load-item'+(isActive?' active':'')} key={'fb-'+entry.id}>
                <span onClick={function(){handleLoadFirebase(entry.id)}} style={{flex:1,cursor:'pointer'}}>{entry.name||entry.id}</span>
                <span className="setlist-load-badge">FB</span>
                <span style={{color:'var(--text-disabled)',fontSize:9}}>{entry.created?new Date(entry.created).toLocaleDateString():''}</span>
                <button className="setlist-load-del" onClick={function(e){e.stopPropagation();handleDeleteFirebase(entry.id)}}>&times;</button>
              </div>;
            })}
            <div className="setlist-load-section-head">DEFAULT</div>
            <div className="setlist-load-item" key="default">
              <span onClick={handleLoadDefault} style={{flex:1,cursor:'pointer'}}>THE SPLIT — TABLE FORMAT V3</span>
              <span className="setlist-load-badge default">DEFAULT</span>
            </div>
          </div>}
          </div>
        </div>
        {loadStatus&&<div style={{padding:'4px 12px',fontSize:10,letterSpacing:'.08em',color:'var(--accent)',textAlign:'center'}}>{loadStatus}</div>}
        <div className="setlist-footer-row" style={{alignItems:'center'}}>
          <span style={{fontSize:9,color:'var(--text-secondary)',letterSpacing:'.06em',marginRight:8}}>ADD BONUS:</span>
          {['movie','music',null,'hotseat'].map(function(mt){var label=mt==='movie'?'MOVIES':mt==='music'?'MUSIC':mt==='hotseat'?'HOT SEAT':'MIXED';return <button key={label} className="setlist-btn accent" style={{fontSize:10,padding:'4px 10px'}} onClick={function(){addBonusRound(mt,show,redFlags||[],!!extrasBypassed,bonusSections||[],currentSlide||0,dispatch).then(function(name){setBonusAdded(label);setTimeout(function(){setBonusAdded(null)},1500)})}}>{bonusAdded===label?'ADDED!':label}</button>})}
        </div>
        <div className="setlist-footer-row">
          <div style={{position:'relative'}}>
            <button className="setlist-btn" onClick={function(){setShowImport(!showImport);setShowLoad(false)}}>IMPORT</button>
            {showImport&&<div className="setlist-alt-popup" style={{width:320,right:0,bottom:'100%',marginBottom:8}}>
              <div style={{padding:'8px 12px',borderBottom:'1px solid var(--border-subtle)'}}>
                <button className="setlist-btn" onClick={function(){fileRef.current&&fileRef.current.click()}} style={{width:'100%',marginBottom:8}}>CHOOSE FILE</button>
                <input ref={fileRef} type="file" accept=".json" style={{display:'none'}} onChange={handleFile}/>
                <textarea className="setlist-import-textarea" placeholder="Or paste JSON here..." value={importText} onChange={function(e){setImportText(e.target.value);setImportErr('')}} rows={4}/>
                <button className="setlist-btn accent" onClick={function(){parseImport(importText)}} style={{width:'100%',marginTop:4}} disabled={!importText.trim()}>IMPORT JSON</button>
                {importErr&&<div className="setlist-import-error">{importErr}</div>}
              </div>
            </div>}
          </div>
          <div style={{marginLeft:'auto'}}>
            {!liveMode&&<button className="setlist-live-btn" onClick={handleGoLive}>GO LIVE</button>}
          </div>
        </div>
      </div>
    </div>
  );
}
