// ═══ GENESIS BUILD gangway-fila-slim · 2026-07-11 ═══
// Tidal — Gangway · QR scan + câmera + API real
const LEITOS_GENESIS = 90; // leitos/cabines disponíveis a bordo (referência; POB pode ser maior com visitantes/terceirizados sem cabine)

const loadQRLib = () => new Promise(resolve => {
  if (window._qrgen) { resolve(true); return; }
  const s = document.createElement('script');
  s.src = 'vendor/qrcode.js';
  s.onload = () => { window._qrgen = window.qrcode; resolve(true); };
  s.onerror = () => resolve(false);
  document.head.appendChild(s);
});

const QRImg = ({url, size=160}) => {
  const [src, setSrc] = useState(null);
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    loadQRLib().then(ok => {
      setLoading(false);
      if (ok && window._qrgen) {
        try {
          const qr = window._qrgen(0, 'M');
          qr.addData(url); qr.make();
          const canvas = document.createElement('canvas');
          const n = qr.getModuleCount();
          const cell = Math.floor(size / n);
          canvas.width = n * cell; canvas.height = n * cell;
          const ctx = canvas.getContext('2d');
          ctx.fillStyle = '#ffffff'; ctx.fillRect(0, 0, canvas.width, canvas.height);
          ctx.fillStyle = '#000000';
          for (let r = 0; r < n; r++)
            for (let c = 0; c < n; c++)
              if (qr.isDark(r, c)) ctx.fillRect(c*cell, r*cell, cell, cell);
          setSrc(canvas.toDataURL('image/png'));
        } catch(e) { setSrc(null); }
      }
    });
  }, [url]);
  if (loading) return <div style={{width:size,height:size,display:'grid',placeItems:'center',background:'var(--cream-2)',borderRadius:12}}><div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)'}}>gerando QR…</div></div>;
  if (!src) return <div style={{width:size,height:size,display:'grid',placeItems:'center',background:'var(--cream-2)',borderRadius:12,padding:8}}><div style={{fontSize:10,color:'var(--muted)',fontFamily:'var(--fm)',textAlign:'center',wordBreak:'break-all'}}>{url}</div></div>;
  return <img src={src} style={{width:size,height:size,imageRendering:'pixelated',borderRadius:8,border:'1px solid var(--line-soft)'}} alt="QR Code"/>;
};

// ─── CrachaModal — mostra o crachá da pessoa (módulo novo, cracha.jsx) ───
const getCrachaQRHost = () => {
  try {
    const cfg = JSON.parse(localStorage.getItem('genesis_cracha_qr') || '{}');
    if (cfg.host && String(cfg.host).trim()) return String(cfg.host).trim();
  } catch(e) {}
  return window.location.host;
};

const CrachaModal = ({user, onClose}) => {
  const [img, setImg] = useState(null);
  const [fail, setFail] = useState(false);
  useEffect(() => {
    let alive = true;
    (async () => {
      try {
        if (!window.renderCardCanvas) throw new Error('módulo de crachá não carregado');
        const canvas = await window.renderCardCanvas(user);
        if (alive) setImg(canvas.toDataURL('image/png'));
      } catch(e) { if (alive) setFail(true); }
    })();
    return () => { alive = false; };
  }, [user]);
  const qrUrl = `${window.location.protocol}//${getCrachaQRHost()}/gangway-scan.html?scan=${user.genesis_id}`;
  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,zIndex:9500,background:'rgba(31,42,46,0.55)',display:'flex',alignItems:'center',justifyContent:'center',padding:16}}>
      <div onClick={e=>e.stopPropagation()} style={{background:'var(--cream)',borderRadius:20,padding:'18px 20px',maxWidth:340,width:'100%',textAlign:'center',boxShadow:'var(--shadow-lg)',border:'1px solid var(--line-soft)'}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:12}}>
          <div style={{fontFamily:'var(--fs)',fontSize:18}}>Crachá · {user.genesis_id}</div>
          <button className="btn ghost btn-sm" onClick={onClose}>✕</button>
        </div>
        {img ? (
          <img src={img} style={{width:'100%',borderRadius:12,border:'1px solid var(--line-soft)'}} alt={`Crachá de ${user.nome}`}/>
        ) : fail ? (
          <div style={{display:'flex',flexDirection:'column',alignItems:'center',gap:10,padding:'10px 0'}}>
            <QRImg url={qrUrl} size={220}/>
            <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>{user.genesis_id}</div>
          </div>
        ) : (
          <div style={{padding:40,fontFamily:'var(--fs)',fontStyle:'italic',color:'var(--muted)'}}>gerando crachá…</div>
        )}
        <div style={{fontSize:13,fontWeight:500,marginTop:10}}>{user.nome}</div>
        <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>{user.empresa||''}{user.funcao?` · ${user.funcao}`:''}</div>
        <div style={{display:'flex',gap:8,marginTop:14}}>
          <button className="btn btn-primary btn-sm" style={{flex:1}} onClick={()=>window.printCracha && window.printCracha(user)}>🖨 Imprimir</button>
          <button className="btn ghost btn-sm" style={{flex:1}} onClick={onClose}>Fechar</button>
        </div>
      </div>
    </div>
  );
};

const CameraScanner = ({onDetected, onClose}) => {
  const videoRef = useRef(null);
  const streamRef = useRef(null);
  const rafRef = useRef(null);
  const [error, setError] = useState(null);
  const [retrying, setRetrying] = useState(false);

  const startCamera = async () => {
    setError(null);
    setRetrying(false);
    const loadJsQR = () => new Promise(resolve => {
      if (window.jsQR) { resolve(true); return; }
      const s = document.createElement('script');
      s.src = 'https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.js';
      s.onload = () => resolve(true); s.onerror = () => resolve(false);
      document.head.appendChild(s);
    });

    const ok = await loadJsQR();
    if (!ok) { setError('lib'); return; }

    try {
      const stream = await navigator.mediaDevices.getUserMedia({
        video: { facingMode:'environment', width:{ideal:1280}, height:{ideal:720} }
      });
      streamRef.current = stream;
      if (videoRef.current) { videoRef.current.srcObject = stream; videoRef.current.play(); }
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      const tick = () => {
        if (!videoRef.current) return;
        if (videoRef.current.readyState === videoRef.current.HAVE_ENOUGH_DATA) {
          canvas.width = videoRef.current.videoWidth;
          canvas.height = videoRef.current.videoHeight;
          ctx.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);
          const imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
          const code = window.jsQR(imgData.data, imgData.width, imgData.height, {inversionAttempts:'dontInvert'});
          if (code?.data) { stopStream(); onDetected(code.data); return; }
        }
        rafRef.current = requestAnimationFrame(tick);
      };
      rafRef.current = requestAnimationFrame(tick);
    } catch(e) {
      const isIOS = /iPhone|iPad|iPod/.test(navigator.userAgent);
      setError(isIOS ? 'ios' : 'android');
    }
  };

  useEffect(() => { startCamera(); return () => stopStream(); }, []);

  const stopStream = () => {
    if (rafRef.current) cancelAnimationFrame(rafRef.current);
    if (streamRef.current) streamRef.current.getTracks().forEach(t => t.stop());
    streamRef.current = null;
  };

  const handleRetry = () => {
    setRetrying(true);
    stopStream();
    setTimeout(() => startCamera(), 300);
  };

  const steps = {
    ios: [
      { emoji:'⚙️', text:'Abra o app Ajustes do iPhone' },
      { emoji:'⬇️', text:'Role até encontrar "Safari"' },
      { emoji:'📷', text:'Toque em Câmera' },
      { emoji:'✅', text:'Selecione "Permitir"' },
    ],
    android: [
      { emoji:'⚙️', text:'Abra Configurações do celular' },
      { emoji:'📱', text:'Toque em Apps → Chrome' },
      { emoji:'🔑', text:'Toque em Permissões → Câmera' },
      { emoji:'✅', text:'Selecione "Permitir"' },
    ],
    lib: [
      { emoji:'📡', text:'Sem conexão para carregar biblioteca' },
      { emoji:'🔄', text:'Verifique o Wi-Fi e tente novamente' },
    ],
  };

  return (
    <div style={{position:'fixed',inset:0,background:'rgba(0,0,0,0.96)',zIndex:9500,display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center'}}>
      {/* Header */}
      <div style={{position:'absolute',top:0,left:0,right:0,padding:'16px 20px',display:'flex',justifyContent:'space-between',alignItems:'center'}}>
        <div style={{color:'rgba(255,255,255,0.4)',fontFamily:'var(--fm)',fontSize:11,letterSpacing:2,textTransform:'uppercase'}}>
          Leitura de QR Code
        </div>
        <button onClick={()=>{stopStream();onClose();}} style={{background:'rgba(255,255,255,0.1)',border:'none',color:'white',borderRadius:8,padding:'8px 16px',cursor:'pointer',fontFamily:'var(--fm)',fontSize:12}}>
          fechar
        </button>
      </div>

      {error ? (
        <div style={{textAlign:'center',padding:'20px 24px',maxWidth:360,width:'100%'}}>
          <div style={{fontSize:52,marginBottom:12}}>📷</div>
          <div style={{color:'white',fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.3px',marginBottom:6}}>
            Câmera bloqueada
          </div>
          <div style={{color:'rgba(255,255,255,0.5)',fontFamily:'var(--fm)',fontSize:12,marginBottom:24}}>
            siga os passos abaixo e tente novamente
          </div>

          {/* Passo a passo visual */}
          <div style={{background:'rgba(255,255,255,0.06)',borderRadius:20,padding:'20px 18px',marginBottom:20,textAlign:'left'}}>
            <div style={{color:'rgba(255,255,255,0.4)',fontFamily:'var(--fm)',fontSize:10,letterSpacing:2,textTransform:'uppercase',marginBottom:14}}>
              {error==='ios' ? '📱 iPhone / iPad' : error==='android' ? '🤖 Android' : '⚠️ Erro'}
            </div>
            {(steps[error]||[]).map((step, i) => (
              <div key={i} style={{display:'flex',alignItems:'center',gap:14,marginBottom:i<steps[error].length-1?12:0}}>
                <div style={{
                  width:38,height:38,borderRadius:12,
                  background: i===steps[error].length-1 ? 'rgba(46,123,119,0.3)' : 'rgba(255,255,255,0.08)',
                  display:'grid',placeItems:'center',fontSize:18,flexShrink:0
                }}>
                  {step.emoji}
                </div>
                <div style={{color:'rgba(255,255,255,0.85)',fontSize:14,fontFamily:'var(--fm)',lineHeight:1.4}}>
                  {step.text}
                </div>
              </div>
            ))}
          </div>

          {/* Botão tentar novamente — destaque */}
          <button onClick={handleRetry} disabled={retrying} style={{
            width:'100%', padding:'16px', borderRadius:16, border:'none', cursor:'pointer',
            background:'linear-gradient(135deg, #2E7B77, #1A5250)',
            color:'white', fontFamily:'var(--fs)', fontStyle:'italic',
            fontSize:18, letterSpacing:'-0.2px',
            boxShadow:'0 8px 24px rgba(46,123,119,0.4)',
            opacity: retrying ? 0.6 : 1, marginBottom:12
          }}>
            {retrying ? 'Abrindo câmera…' : '🔄 Já habilitei — tentar novamente'}
          </button>

          <div style={{color:'rgba(255,255,255,0.3)',fontSize:11,fontFamily:'var(--fm)'}}>
            ou feche e use a aba <strong style={{color:'rgba(255,255,255,0.5)'}}>Busca manual</strong>
          </div>
        </div>
      ) : (
        <>
          <div style={{position:'relative',width:'min(340px,90vw)',height:'min(340px,90vw)'}}>
            <video ref={videoRef} playsInline muted style={{width:'100%',height:'100%',objectFit:'cover',borderRadius:20}}/>
            <div style={{position:'absolute',inset:0,borderRadius:20,border:'2px solid rgba(255,255,255,0.2)',pointerEvents:'none'}}>
              <div style={{position:'absolute',top:0,left:0,width:28,height:28,borderTop:'3px solid #2E7B77',borderLeft:'3px solid #2E7B77',borderRadius:'20px 0 0 0'}}/>
              <div style={{position:'absolute',top:0,right:0,width:28,height:28,borderTop:'3px solid #2E7B77',borderRight:'3px solid #2E7B77',borderRadius:'0 20px 0 0'}}/>
              <div style={{position:'absolute',bottom:0,left:0,width:28,height:28,borderBottom:'3px solid #2E7B77',borderLeft:'3px solid #2E7B77',borderRadius:'0 0 0 20px'}}/>
              <div style={{position:'absolute',bottom:0,right:0,width:28,height:28,borderBottom:'3px solid #2E7B77',borderRight:'3px solid #2E7B77',borderRadius:'0 0 20px 0'}}/>
              <div style={{position:'absolute',left:10,right:10,height:2,background:'linear-gradient(90deg,transparent,#2E7B77,transparent)',animation:'scanLine 2s ease-in-out infinite'}}/>
              <style>{`@keyframes scanLine{0%{top:10px}100%{top:calc(100% - 12px)}}`}</style>
            </div>
          </div>
          <div style={{color:'rgba(255,255,255,0.4)',fontFamily:'var(--fm)',fontSize:12,marginTop:20}}>
            aponte para o QR Code do crachá
          </div>
        </>
      )}
    </div>
  );
};

// ─── PortSelect — porto atual a partir do catálogo (DB) + cadastro ───
const PortSelect = ({ isEN }) => {
  const tk = () => localStorage.getItem('genesis_token');
  const [ports, setPorts] = useState([]);
  const [current, setCurrent] = useState('');
  const [adding, setAdding] = useState(false);
  const [novo, setNovo] = useState('');
  const [busy, setBusy] = useState(false);
  const load = () => fetch('/api/ports', { headers: { Authorization: `Bearer ${tk()}` } })
    .then(r => r.ok ? r.json() : { ports: [], current: null })
    .then(d => { setPorts(d.ports || []); setCurrent(d.current || ''); });
  useEffect(() => { load(); }, []);
  const setCur = async (nome) => {
    setCurrent(nome);
    if (nome) await fetch('/api/ports/current', { method: 'PUT', headers: { Authorization: `Bearer ${tk()}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ nome }) });
  };
  const addPort = async () => {
    const nome = novo.trim(); if (!nome) return; setBusy(true);
    const r = await fetch('/api/ports', { method: 'POST', headers: { Authorization: `Bearer ${tk()}`, 'Content-Type': 'application/json' }, body: JSON.stringify({ nome }) });
    setBusy(false);
    const d = await r.json().catch(() => ({}));
    const finalName = (r.ok && d.port) ? d.port.nome : (d.port ? d.port.nome : nome);
    setNovo(''); setAdding(false); await load(); await setCur(finalName);
  };
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
      <span className="t-eyebrow">{isEN ? 'current port' : 'porto atual'}</span>
      {!adding ? (<>
        <select className="input" style={{ minWidth: 200, boxSizing: 'border-box' }} value={current} onChange={e => setCur(e.target.value)}>
          <option value="">{isEN ? '— select —' : '— selecione —'}</option>
          {ports.map(p => <option key={p.id} value={p.nome}>{p.nome}</option>)}
        </select>
        <button className="btn-icone sm" onClick={() => setAdding(true)} title={isEN ? 'register a port' : 'cadastrar porto'}>＋</button>
      </>) : (<>
        <input className="input" style={{ minWidth: 200, boxSizing: 'border-box' }} placeholder={isEN ? 'New port name' : 'Nome do novo porto'} value={novo} onChange={e => setNovo(e.target.value)} autoFocus onKeyDown={e => { if (e.key === 'Enter') addPort(); }} />
        <button className="btn btn-primary btn-sm" onClick={addPort} disabled={busy}>{busy ? '…' : (isEN ? 'Save' : 'Salvar')}</button>
        <button className="btn btn-sm" onClick={() => { setAdding(false); setNovo(''); }}>{isEN ? 'Cancel' : 'Cancelar'}</button>
      </>)}
    </div>
  );
};

const Gangway = ({state, setState, remountGangway}) => {
  const tt = useT();
  const isEN = tt.lang === 'en';
  const toast = useToast();
  const token = () => localStorage.getItem('genesis_token');
  const isMobile = useMobile();
  const [histOpen, setHistOpen] = useState(false);
  const [visOpen, setVisOpen] = useState(false);
  const [log, setLog] = useState([]);
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [qrUser, setQrUser] = useState(null);
  const [crachaUser, setCrachaUser] = useState(null);
  const [visitors, setVisitors] = useState([]);
  const [slotsLivres, setSlotsLivres] = useState([]);
  const [queue, setQueue] = useState([]);
  const [boardOpen, setBoardOpen] = useState(false);
  const [cameraOpen, setCameraOpen] = useState(false);

  const fetchData = async () => {
    try {
      const [uRes, lRes, vRes, sRes, qRes] = await Promise.all([
        fetch('/api/users', {headers:{'Authorization':`Bearer ${token()}`}}),
        fetch('/api/boarding/log', {headers:{'Authorization':`Bearer ${token()}`}}),
        fetch('/api/boarding/visitors', {headers:{'Authorization':`Bearer ${token()}`}}),
        fetch('/api/boarding/slots-livres', {headers:{'Authorization':`Bearer ${token()}`}}),
        fetch('/api/boarding/queue', {headers:{'Authorization':`Bearer ${token()}`}}),
      ]);
      if (uRes.ok) setUsers(await uRes.json());
      if (lRes.ok) setLog(await lRes.json());
      if (vRes.ok) { const vd = await vRes.json(); setVisitors(Array.isArray(vd)?vd:[]); }
      if (sRes.ok) { const sd = await sRes.json(); setSlotsLivres(Array.isArray(sd)?sd:[]); }
      if (qRes.ok) { const qd = await qRes.json(); setQueue(Array.isArray(qd)?qd:[]); }
    } catch(e) { console.error(e); }
    finally { setLoading(false); }
  };

  useEffect(() => { fetchData(); }, []);

  useEffect(() => {
    const interval = setInterval(() => fetchData(), 15000);
    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    const p = new URLSearchParams(window.location.search);
    const scanId = p.get('scan');
    if (scanId && users.length) {
      const u = users.find(x => x.genesis_id === scanId.toUpperCase());
      if (u) { setQrUser(u); window.history.replaceState({}, '', window.location.pathname); }
    }
  }, [users]);

  const handleQRDetected = (data) => {
    setCameraOpen(false);
    const match = data.match(/GEN-\d+/i);
    const gid = match ? match[0].toUpperCase() : data.trim();
    const u = users.find(x => x.genesis_id === gid);
    if (u) setQrUser(u);
    else toast.push({icon:'x', title:'QR não reconhecido', body:`ID: ${gid}`});
  };

  const handleBoarding = async (user, tipo, visitor_slot) => {
    try {
      const res = await fetch('/api/boarding/scan', {
        method:'POST',
        headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},
        body: JSON.stringify({genesis_id: user.genesis_id, tipo, visitor_slot})
      });
      const data = await res.json();
      if (res.ok) {
        toast.push({
          icon:'check',
          title:`${user.nome} · ${tipo==='embarque'?'🟢 A BORDO':'🔴 EM TERRA'}`,
          body:`${user.empresa||''} · ${new Date().toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'})}`
        });
        setUsers(prev => prev.map(u => u.id===user.id
          ? {...u, status_bordo:tipo==='embarque'?'a_bordo':'em_terra', cabine_atual:tipo==='desembarque'?null:u.cabine_atual}
          : u
        ));
        if (qrUser?.id===user.id) setQrUser(prev=>({...prev, status_bordo:tipo==='embarque'?'a_bordo':'em_terra'}));
        await fetchData();
      } else { toast.push({icon:'x', title:'Erro', body:data.error}); }
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };

  // ─── Fila ───
  const addToQueue = async (userIds, tipo, nota) => {
    try {
      const res = await fetch('/api/boarding/queue', {
        method:'POST',
        headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},
        body: JSON.stringify({user_ids: Array.isArray(userIds)?userIds:[userIds], tipo, nota})
      });
      const data = await res.json();
      if (res.ok) {
        toast.push({icon:'check', title:`${data.added} na fila`, body: data.skipped?`${data.skipped} já estavam`:''});
        await fetchData();
      } else toast.push({icon:'x', title:'Erro', body:data.error});
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };

  const completeQueueItem = async (queueId, visitor_slot) => {
    try {
      const res = await fetch(`/api/boarding/queue/${queueId}/done`, {
        method:'PATCH',
        headers:{'Content-Type':'application/json','Authorization':`Bearer ${token()}`},
        body: JSON.stringify({visitor_slot})
      });
      const data = await res.json();
      if (res.ok) { await fetchData(); return true; }
      else { toast.push({icon:'x', title:'Erro', body:data.error}); return false; }
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); return false; }
  };

  const removeQueueItem = async (queueId) => {
    try {
      await fetch(`/api/boarding/queue/${queueId}`, {
        method:'DELETE', headers:{'Authorization':`Bearer ${token()}`}
      });
      await fetchData();
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };

  const todayLog = log.filter(l => {
    if (!l.created_at) return false;
    return new Date(l.created_at).toDateString() === new Date().toDateString();
  });

  return (
    <>
      {cameraOpen && (
        <CameraScanner onDetected={handleQRDetected} onClose={()=>setCameraOpen(false)}/>
      )}
      <main style={{padding:isMobile?'16px 16px 80px':'26px 20px 56px',flex:1,maxWidth:1180,margin:'0 auto',width:'100%',boxSizing:'border-box'}}>
      {qrUser && (
        <ScanModal
          user={qrUser}
          slotsLivres={slotsLivres}
          onBoard={(slot)=>handleBoarding(qrUser,'embarque',slot)}
          onDisembark={()=>handleBoarding(qrUser,'desembarque')}
          onClose={()=>setQrUser(null)}
          isEN={isEN}
        />
      )}
      {crachaUser && (
        <CrachaModal user={crachaUser} onClose={()=>setCrachaUser(null)}/>
      )}

      {/* Cabeçalho de página */}
      <div style={{display:'flex',alignItems:'flex-end',justifyContent:'space-between',marginBottom:20,flexWrap:'wrap',gap:12}}>
        <div>
          <div className="t-eyebrow">{isEN?'gangway control':'controle de portaló'}</div>
          <h1 style={{fontFamily:'var(--fs)',fontSize:isMobile?28:36,letterSpacing:'-0.8px',lineHeight:1.05,marginTop:6}}>
            {isEN?<>Gang<em style={{fontStyle:'italic',color:'var(--accent)'}}>way.</em></>:<>Porta<em style={{fontStyle:'italic',color:'var(--accent)'}}>ló.</em></>}
          </h1>
        </div>
      </div>

      {/* Barra de ação — porto + embarque + histórico (uma tela só) */}
      <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:12,flexWrap:'wrap',marginBottom:18,background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'3px solid var(--accent)',borderRadius:16,padding:'12px 16px',boxShadow:'var(--shadow-sm)'}}>
        <PortSelect isEN={isEN}/>
        <div style={{display:'flex',gap:8,flexWrap:'wrap'}}>
          <button className="btn btn-primary btn-sm" onClick={()=>setCameraOpen(true)} style={{padding:'10px 16px'}}>📷 {isEN?'Scan QR':'Escanear QR'}</button>
          <button className="btn btn-primary btn-sm" onClick={()=>setHistOpen(true)} style={{padding:'10px 16px'}}>📜 {isEN?'History':'Histórico'}</button>
          <button className="btn btn-primary" onClick={()=>setBoardOpen(true)} style={{fontSize:14,padding:'10px 20px'}}>
            <Icon n="search" size={13}/> {isEN?'Boarding':'Embarque'}
          </button>
        </div>
      </div>
      <>
          {isMobile ? (
            <>
              <FlowTab users={users} log={todayLog} loading={loading} onBoarding={handleBoarding} onShowCracha={setCrachaUser} isEN={isEN} isMobile={isMobile}/>
              <div style={{marginTop:18,background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'3px solid var(--sun)',borderRadius:18,padding:'14px 14px',boxShadow:'var(--shadow-sm)',boxSizing:'border-box'}}>
                <FilaTab queue={queue} users={users} slotsLivres={slotsLivres} onComplete={completeQueueItem} onRemove={removeQueueItem} onAddToQueue={addToQueue} isMobile={isMobile} isEN={isEN} tableOnly/>
              </div>
            </>
          ) : (
            <div style={{display:'flex',gap:14,alignItems:'stretch',height:'calc(100vh - 320px)',minHeight:520}}>
              <div style={{flex:'0.95',minWidth:0,display:'flex',flexDirection:'column',background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'3px solid var(--sun)',borderRadius:18,padding:'14px 16px',boxShadow:'var(--shadow-sm)',boxSizing:'border-box',overflow:'hidden'}}>
                <FilaTab queue={queue} users={users} slotsLivres={slotsLivres} onComplete={completeQueueItem} onRemove={removeQueueItem} onAddToQueue={addToQueue} isMobile={isMobile} isEN={isEN} tableOnly fill/>
              </div>
              <div style={{flex:'1.7',minWidth:0}}>
                <FlowTab users={users} log={todayLog} loading={loading} onBoarding={handleBoarding} onShowCracha={setCrachaUser} isEN={isEN} isMobile={isMobile}/>
              </div>
            </div>
          )}
      </>

      {/* Visitantes — card colapsável */}
      <div style={{marginTop:18,background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'3px solid var(--sky)',borderRadius:16,boxShadow:'var(--shadow-sm)',overflow:'hidden'}}>
        <button onClick={()=>setVisOpen(v=>!v)} style={{width:'100%',border:'none',background:'transparent',cursor:'pointer',display:'flex',justifyContent:'space-between',alignItems:'center',padding:'13px 18px'}}>
          <span style={{fontFamily:'var(--fm)',fontSize:11,letterSpacing:1.2,textTransform:'uppercase',color:'var(--sky)'}}>👤 {isEN?'visitors':'visitantes'}{visitors.filter(v=>v.status_bordo==='a_bordo').length?<span style={{marginLeft:8,background:'var(--sky)',color:'#fff',borderRadius:8,padding:'1px 8px',fontSize:10}}>{visitors.filter(v=>v.status_bordo==='a_bordo').length} a bordo</span>:null}</span>
          <span style={{color:'var(--muted)',fontSize:12}}>{visOpen?'▲':'▼'}</span>
        </button>
        {visOpen ? <div style={{padding:'0 18px 18px'}}><VisitorsTab visitors={visitors} onBoarding={handleBoarding} onShowQR={setQrUser} isEN={isEN} isMobile={isMobile}/></div> : null}
      </div>

      {/* Histórico — modal */}
      {histOpen && (
        <div onClick={()=>setHistOpen(false)} style={{position:'fixed',inset:0,zIndex:8000,background:'rgba(31,42,46,0.45)',display:'flex',alignItems:'flex-start',justifyContent:'center',padding:'4vh 14px',overflowY:'auto'}}>
          <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:860,background:'var(--cream)',borderRadius:20,boxShadow:'var(--shadow-lg)',border:'1px solid var(--line-soft)',borderTop:'3px solid var(--accent)',padding:'20px 22px',marginBottom:40}}>
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:14}}>
              <div>
                <div className="t-eyebrow" style={{color:'var(--accent)',marginBottom:3}}>{isEN?'gangway':'portaló'}</div>
                <div style={{fontFamily:'var(--fs)',fontSize:22}}>📜 {isEN?'Movement history':'Histórico de movimentação'}</div>
              </div>
              <button className="btn ghost btn-sm" onClick={()=>setHistOpen(false)}>✕</button>
            </div>
            <HistoryTab log={log} isEN={isEN} isMobile={isMobile}/>
          </div>
        </div>
      )}

      {/* Modal de embarque (estilo DMS) — busca + embarque + adicionar à fila */}
      {boardOpen && (
        <div onClick={()=>setBoardOpen(false)} style={{position:'fixed',inset:0,zIndex:8000,background:'rgba(31,42,46,0.45)',display:'flex',alignItems:'flex-start',justifyContent:'center',padding:'4vh 16px',overflowY:'auto'}}>
          <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:820,background:'var(--cream)',borderRadius:20,boxShadow:'var(--shadow-lg)',border:'1px solid var(--line-soft)',marginBottom:40}}>
            <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',padding:'18px 22px',borderBottom:'1px solid var(--line-soft)'}}>
              <div style={{fontFamily:'var(--fs)',fontSize:24,letterSpacing:'-0.5px'}}>{isEN?<>Boar<em style={{fontStyle:'italic',color:'var(--accent)'}}>ding.</em></>:<>Embar<em style={{fontStyle:'italic',color:'var(--accent)'}}>que.</em></>}</div>
              <button className="btn ghost btn-sm" onClick={()=>setBoardOpen(false)}><Icon n="x" size={14}/></button>
            </div>
            <div style={{padding:'18px 22px'}}>
              <ManualTab users={users} slotsLivres={slotsLivres} onBoarding={handleBoarding} onShowQR={setQrUser} onAddToQueue={addToQueue} isEN={isEN} isMobile={isMobile}/>
            </div>
          </div>
        </div>
      )}
    </main>
    </>
  );
};

const ScanModal = ({user, slotsLivres, onBoard, onDisembark, onClose, isEN}) => {
  const abordo = user.status_bordo === 'a_bordo';
  const isGuest = user.role === 'guest';
  const [action, setAction] = useState(null);
  const [confirming, setConfirming] = useState(false);
  const [selectedSlot, setSelectedSlot] = useState('');

  useEffect(() => {
    if (action === 'embarque' && isGuest && slotsLivres?.length && !selectedSlot) {
      setSelectedSlot(String(slotsLivres[0]));
    }
  }, [action, isGuest, slotsLivres, selectedSlot]);

  const confirm = async () => {
    setConfirming(true);
    if (action==='embarque') await onBoard(isGuest ? selectedSlot : undefined);
    else await onDisembark();
    setConfirming(false); setAction(null);
  };
  return (
    <div style={{position:'fixed',inset:0,zIndex:9000,background:abordo?'linear-gradient(160deg,#0F2E2C,#0A1F1E)':'linear-gradient(160deg,#1F2A2E,#0F1518)',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',padding:32}}>
      <div style={{position:'absolute',top:0,left:0,right:0,padding:'14px 24px',background:'rgba(255,255,255,0.04)',display:'flex',justifyContent:'space-between',alignItems:'center'}}>
        <div style={{fontFamily:'var(--fm)',fontSize:11,letterSpacing:2,textTransform:'uppercase',color:'rgba(255,255,255,0.3)'}}>Genesis I · Gangway</div>
        <button onClick={onClose} style={{background:'rgba(255,255,255,0.08)',border:'1px solid rgba(255,255,255,0.12)',color:'rgba(255,255,255,0.6)',borderRadius:8,padding:'6px 14px',cursor:'pointer',fontFamily:'var(--fm)',fontSize:12}}>fechar</button>
      </div>
      {user.foto_url ? (
        <img src={user.foto_url} style={{width:110,height:110,borderRadius:'50%',objectFit:'cover',border:'3px solid rgba(255,255,255,0.15)',marginBottom:20}}/>
      ) : (
        <div style={{width:110,height:110,borderRadius:'50%',background:'rgba(255,255,255,0.08)',border:'3px solid rgba(255,255,255,0.15)',display:'grid',placeItems:'center',fontFamily:'var(--fs)',fontSize:38,color:'rgba(255,255,255,0.5)',marginBottom:20}}>{user.initials||'?'}</div>
      )}
      <div style={{fontFamily:'var(--fs)',fontSize:34,letterSpacing:'-0.8px',color:'white',lineHeight:1,marginBottom:6,textAlign:'center'}}>{user.nome}</div>
      <div style={{fontFamily:'var(--fm)',fontSize:12,color:'rgba(255,255,255,0.4)',marginBottom:4}}>{user.genesis_id} · {user.empresa}</div>
      <div style={{fontSize:13,color:'rgba(255,255,255,0.55)',fontStyle:'italic',fontFamily:'var(--fs)',marginBottom:20}}>{user.funcao}</div>
      <div style={{display:'flex',gap:8,justifyContent:'center',marginBottom:28,flexWrap:'wrap'}}>
        {user.turno && <div style={{padding:'5px 14px',borderRadius:20,background:'rgba(255,255,255,0.08)',color:'rgba(255,255,255,0.6)',fontFamily:'var(--fm)',fontSize:11}}>⏰ {user.turno}</div>}
        {user.cabine_atual && <div style={{padding:'5px 14px',borderRadius:20,background:'rgba(255,255,255,0.08)',color:'rgba(255,255,255,0.6)',fontFamily:'var(--fm)',fontSize:11}}>🛏 Cab {user.cabine_atual}</div>}
      </div>
      <div style={{padding:'10px 28px',borderRadius:50,marginBottom:36,background:abordo?'rgba(122,182,126,0.15)':'rgba(217,105,75,0.15)',border:`1px solid ${abordo?'rgba(122,182,126,0.4)':'rgba(217,105,75,0.4)'}`,fontFamily:'var(--fm)',fontSize:12,letterSpacing:2,textTransform:'uppercase',color:abordo?'#7BB67E':'#E8896A',display:'flex',alignItems:'center',gap:10}}>
        <span style={{width:8,height:8,borderRadius:'50%',display:'inline-block',background:abordo?'#7BB67E':'#E8896A',boxShadow:abordo?'0 0 10px #7BB67E':'0 0 10px #E8896A',animation:'ganPulse 1.5s ease-in-out infinite'}}/>
        {abordo?'A BORDO':'EM TERRA'}
      </div>
      <style>{`@keyframes ganPulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:0.3;transform:scale(0.8)}}`}</style>
      {!action ? (
        <div style={{display:'flex',gap:16}}>
          {!abordo ? (
            <button onClick={()=>setAction('embarque')} style={{padding:'20px 48px',borderRadius:20,border:'none',cursor:'pointer',background:'linear-gradient(135deg,#2E7B77,#1A5250)',color:'white',fontFamily:'var(--fs)',fontStyle:'italic',fontSize:24,letterSpacing:'-0.3px',boxShadow:'0 12px 40px rgba(46,123,119,0.45)'}}>🟢 Embarcar</button>
          ) : (
            <button onClick={()=>setAction('desembarque')} style={{padding:'20px 48px',borderRadius:20,border:'none',cursor:'pointer',background:'linear-gradient(135deg,#C05A3A,#9A4030)',color:'white',fontFamily:'var(--fs)',fontStyle:'italic',fontSize:24,letterSpacing:'-0.3px',boxShadow:'0 12px 40px rgba(192,90,58,0.45)'}}>🔴 Desembarcar</button>
          )}
        </div>
      ) : (
        <div style={{textAlign:'center',width:'100%',maxWidth:320}}>
          <div style={{color:'rgba(255,255,255,0.7)',fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18,marginBottom:20}}>
            {action==='embarque'?'Confirmar embarque?':'Confirmar desembarque?'}
          </div>

          {/* Seleção de slot para visitantes */}
          {action==='embarque' && isGuest && (
            <div style={{marginBottom:20}}>
              <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:10}}>
                <div style={{color:'rgba(255,255,255,0.5)',fontFamily:'var(--fm)',fontSize:11,letterSpacing:1.5,textTransform:'uppercase'}}>
                  Crachá temporário
                </div>
                <div style={{fontFamily:'var(--fm)',fontSize:11,color:'rgba(255,255,255,0.4)'}}>
                  {slotsLivres?.length || 0} {(slotsLivres?.length||0)===1?'disponível':'disponíveis'}
                </div>
              </div>
              <select
                value={selectedSlot}
                onChange={e=>setSelectedSlot(e.target.value)}
                style={{
                  width:'100%',padding:'12px 16px',borderRadius:12,
                  border:'1px solid rgba(255,255,255,0.2)',
                  background:'rgba(255,255,255,0.08)',color:'white',
                  fontFamily:'var(--fm)',fontSize:18,letterSpacing:2,
                  textAlign:'center',cursor:'pointer'
                }}
              >
                <option value="">— selecione o slot —</option>
                {(slotsLivres||[]).map(s=>{
                  const scarse = (slotsLivres||[]).length <= 3;
                  return (
                    <option key={s} value={s} style={{background: scarse ? '#3A2518' : '#1A2024', color:'white'}}>
                      Crachá #{String(s).padStart(2,'0')} {scarse ? '⚠️' : ''}
                    </option>
                  );
                })}
              </select>
              {(slotsLivres||[]).length === 0 && (
                <div style={{color:'#E8896A',fontSize:13,fontFamily:'var(--fm)',marginTop:12,background:'rgba(217,105,75,0.12)',padding:'10px 14px',borderRadius:10}}>
                  ⚠️ Nenhum crachá disponível. Não é possível embarcar um visitante sem crachá físico.
                </div>
              )}
              {(slotsLivres||[]).length > 0 && (slotsLivres||[]).length <= 3 && (
                <div style={{color:'#E8C45A',fontSize:12,fontFamily:'var(--fm)',marginTop:8}}>
                  ⚠️ Poucos crachás disponíveis
                </div>
              )}
            </div>
          )}

          <div style={{display:'flex',gap:12,justifyContent:'center'}}>
            <button onClick={()=>setAction(null)} style={{padding:'12px 24px',borderRadius:12,border:'1px solid rgba(255,255,255,0.15)',background:'transparent',color:'rgba(255,255,255,0.6)',cursor:'pointer',fontSize:14,fontFamily:'var(--fm)'}}>Cancelar</button>
            <button onClick={confirm} disabled={confirming||(action==='embarque'&&isGuest&&!selectedSlot)} style={{padding:'12px 30px',borderRadius:12,border:'none',cursor:'pointer',fontSize:14,fontWeight:600,fontFamily:'var(--fm)',background:action==='embarque'?'#2E7B77':'#C05A3A',color:'white',opacity:(confirming||(action==='embarque'&&isGuest&&!selectedSlot))?0.4:1}}>
              {confirming?'…':'Confirmar'}
            </button>
          </div>
        </div>
      )}
    </div>
  );
};

const FlowTab = ({users, log, loading, onBoarding, onShowCracha, isEN, isMobile}) => {
  const abordo = users.filter(u=>u.status_bordo==='a_bordo');
  const [grupoSel, setGrupoSel] = useState(null);   // filtro por grupo (clica no chip)
  const GRUPOS = [
    {id:'trip', label:isEN?'Crew':'Tripulação',   tone:'var(--teal)', test:u=>u.role==='crew'||u.role==='admin'||(u.role==='ops'&&u.is_staff)},
    {id:'ops',  label:isEN?'Operations':'Operações', tone:'var(--leaf)', test:u=>u.role==='ops'&&!u.is_staff},
    {id:'cat',  label:'Catering',                 tone:'var(--sun)',  test:u=>u.role==='catering'},
    {id:'terc', label:isEN?'Contractors':'Terceirizados', tone:'var(--sky)', test:u=>u.role==='terceirizada'},
    {id:'vis',  label:isEN?'Visitors':'Visitantes', tone:'var(--rose)', test:u=>u.role==='guest'},
  ];
  const grupoAtivo = GRUPOS.find(g=>g.id===grupoSel);
  const abordoVisivel = grupoAtivo ? abordo.filter(grupoAtivo.test) : abordo;
  return (
    <div style={{display:'flex',flexDirection:isMobile?'column':'row',gap:isMobile?16:16,height:isMobile?'auto':'100%',minHeight:0,alignItems:'stretch'}}>
      {/* POB — pessoas a bordo agora */}
      <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'3px solid var(--teal)',borderRadius:18,padding:isMobile?'18px 16px':'18px 20px',display:'flex',flexDirection:'column',minHeight:0,flex:isMobile?'none':'1.2',minWidth:0,boxSizing:'border-box'}}>
        <div className="t-eyebrow" style={{marginBottom:6}}>{isEN?'People aboard now':'Pessoas a bordo'}</div>
        <div style={{fontFamily:'var(--fs)',fontSize:isMobile?52:64,letterSpacing:'-2.5px',lineHeight:0.9,marginBottom:12,color:'var(--accent)'}}>
          {abordo.length}<span style={{fontSize:isMobile?16:20,color:"var(--muted)",letterSpacing:0,fontStyle:"italic"}}>/{LEITOS_GENESIS} cabines</span>
        </div>
        <div style={{display:'flex',gap:6,marginBottom:14,flexWrap:'wrap',flexShrink:0}}>
          {GRUPOS.map(g=>{
            const n = abordo.filter(g.test).length;
            const on = grupoSel===g.id;
            return (
              <button key={g.id} onClick={()=>setGrupoSel(on?null:g.id)} title={on?'Mostrar todos':'Filtrar '+g.label}
                style={{padding:'5px 10px',borderRadius:10,cursor:'pointer',minWidth:0,
                  background: on ? g.tone : 'var(--cream-2)',
                  border:'1px solid '+(on ? g.tone : 'var(--line-soft)'),
                  color: on ? '#fff' : 'var(--ink)'}}>
                <span style={{fontFamily:'var(--fs)',fontSize:15,fontWeight:600,lineHeight:1}}>{n}</span>
                <span style={{fontFamily:'var(--fm)',fontSize:9,letterSpacing:0.4,textTransform:'uppercase',marginLeft:5,color:on?'rgba(255,255,255,0.9)':'var(--muted)'}}>{g.label}</span>
              </button>
            );
          })}
        </div>
        <div style={{flex:isMobile?'none':1,minHeight:0,maxHeight:isMobile?240:'none',overflowY:'auto',marginRight:-6,paddingRight:6}}>
          {loading ? <div style={{padding:30,textAlign:'center',color:'var(--muted)',fontFamily:'var(--fs)',fontStyle:'italic'}}>{isEN?'loading…':'carregando…'}</div>
          : abordoVisivel.length===0 ? <div className="empty" style={{padding:40}}>{grupoAtivo?(isEN?'nobody in this group aboard.':'ninguém desse grupo a bordo.'):(isEN?'nobody aboard.':'ninguém a bordo.')}</div>
          : abordoVisivel.map((u,i)=>(
            <div key={u.id} style={{display:'flex',alignItems:'center',gap:10,padding:'10px 0',borderTop:i===0?'none':'1px solid var(--line-soft)'}}>
              <Avatar user={u} size="sm"/>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontSize:13,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{u.nome}</div>
                <div style={{fontSize:10.5,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:1}}>{u.empresa}{u.turno?` · ${u.turno}`:''}{u.cabine_atual?` · Cab ${u.cabine_atual}`:''}</div>
              </div>
              <button onClick={()=>onShowCracha(u)} title="Ver crachá" style={{padding:'4px 8px',borderRadius:7,border:'1px solid var(--line-soft)',background:'var(--cream-2)',cursor:'pointer',fontSize:10,fontFamily:'var(--fm)'}}>QR</button>
              <button onClick={()=>onBoarding(u,'desembarque')} style={{padding:'5px 8px',borderRadius:8,border:'1px solid #FDECEA',background:'transparent',cursor:'pointer',fontSize:11,color:'#C0392B',fontFamily:'var(--fm)'}}>{isEN?'exit':'saída'}</button>
            </div>
          ))}
        </div>
      </div>

      {/* Hoje na gangway — compacto */}
      <aside style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:'3px solid var(--leaf)',borderRadius:18,padding:isMobile?'16px 14px':'14px 16px',display:'flex',flexDirection:'column',minHeight:0,flex:isMobile?'none':'0.72',minWidth:0,boxSizing:'border-box'}}>
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:12,flexShrink:0}}>
          <h2 style={{fontFamily:'var(--fs)',fontSize:19,letterSpacing:'-0.4px'}}>
            {isEN?<>Today at the <em style={{fontStyle:'italic',color:'var(--accent)'}}>gangway</em></>:<>Hoje na <em style={{fontStyle:'italic',color:'var(--accent)'}}>gangway</em></>}
          </h2>
          <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',background:'var(--cream-2)',padding:'4px 10px',borderRadius:8}}>{log.length}</div>
        </div>
        <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:8,marginBottom:14,flexShrink:0}}>
          <div style={{padding:'10px',background:'var(--leaf-soft)',borderRadius:12,textAlign:'center'}}>
            <div style={{fontFamily:'var(--fs)',fontSize:26,color:'var(--leaf)'}}>{log.filter(l=>l.tipo==='embarque').length}</div>
            <div style={{fontSize:10,color:'var(--leaf)',fontFamily:'var(--fm)',letterSpacing:0.8,textTransform:'uppercase',marginTop:2}}>{isEN?'Boardings':'Embarques'}</div>
          </div>
          <div style={{padding:'10px',background:'#FDECEA',borderRadius:12,textAlign:'center'}}>
            <div style={{fontFamily:'var(--fs)',fontSize:26,color:'#C0392B'}}>{log.filter(l=>l.tipo==='desembarque').length}</div>
            <div style={{fontSize:10,color:'#C0392B',fontFamily:'var(--fm)',letterSpacing:0.8,textTransform:'uppercase',marginTop:2}}>{isEN?'Exits':'Saídas'}</div>
          </div>
        </div>
        <div style={{flex:isMobile?'none':1,minHeight:0,maxHeight:isMobile?200:'none',overflowY:'auto',marginRight:-6,paddingRight:6}}>
          {log.length===0 ? <div className="empty" style={{padding:'24px 12px',fontSize:13}}>{isEN?'waiting for the first movement today.':'aguardando o primeiro movimento de hoje.'}</div>
          : log.map((entry,i)=>{
            const isOut=entry.tipo==='desembarque';
            const dt=entry.created_at?new Date(entry.created_at):null;
            return (
              <div key={entry.id||i} style={{display:'flex',alignItems:'center',gap:10,padding:'9px 0',borderTop:i===0?'none':'1px solid var(--line-soft)'}}>
                <div style={{width:26,height:26,borderRadius:8,display:'grid',placeItems:'center',background:isOut?'#FDECEA':'var(--leaf-soft)',color:isOut?'#C0392B':'var(--leaf)',flexShrink:0}}>
                  <Icon n={isOut?'arrow-up':'arrow-down'} size={13}/>
                </div>
                <div style={{flex:1,minWidth:0}}>
                  <div style={{fontSize:12.5,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{entry.nome||entry.genesis_id}</div>
                  <div style={{fontSize:10,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:1}}>
                    {entry.empresa||''}
                    {entry.origem && entry.origem !== 'gangway' && entry.origem !== 'scan' && (
                      <span style={{marginLeft:6,padding:'1px 5px',borderRadius:4,background:'var(--sky-soft)',color:'var(--teal)',fontSize:9,letterSpacing:0.5}}>
                        {entry.origem === 'bulk' ? (isEN?'bulk':'em massa') : entry.origem === 'import' ? (isEN?'import':'importação') : entry.origem}
                      </span>
                    )}
                  </div>
                </div>
                <div style={{fontFamily:'var(--fm)',fontSize:10,color:'var(--muted)'}}>{dt?dt.toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'}):'—'}</div>
              </div>
            );
          })}
        </div>
      </aside>
    </div>
  );
};

// ─── VisitorsTab — crachás temporários de visitantes ───
const VisitorsTab = ({visitors, onBoarding, isEN, isMobile}) => {
  const ocupados = visitors.filter(v=>v.ocupado).sort((a,b)=>a.slot-b.slot);
  const livres   = visitors.filter(v=>!v.ocupado).sort((a,b)=>a.slot-b.slot);

  return (
    <div>
      {/* Contadores */}
      <div style={{display:'flex',gap:12,marginBottom:20,flexWrap:'wrap'}}>
        <div style={{padding:'14px 20px',background:'var(--leaf-soft)',borderRadius:16,textAlign:'center',minWidth:100}}>
          <div style={{fontFamily:'var(--fs)',fontSize:32,color:'var(--leaf)'}}>{ocupados.length}</div>
          <div style={{fontSize:10,color:'var(--leaf)',fontFamily:'var(--fm)',letterSpacing:0.8,textTransform:'uppercase',marginTop:2}}>A bordo</div>
        </div>
        <div style={{padding:'14px 20px',background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:16,textAlign:'center',minWidth:100}}>
          <div style={{fontFamily:'var(--fs)',fontSize:32,color:'var(--muted)'}}>{livres.length}</div>
          <div style={{fontSize:10,color:'var(--muted)',fontFamily:'var(--fm)',letterSpacing:0.8,textTransform:'uppercase',marginTop:2}}>{isEN?'Available':'Disponíveis'}</div>
        </div>
      </div>

      {/* Tabela de visitantes a bordo — ordenada por slot */}
      {ocupados.length > 0 && (
        <div style={{marginBottom:28}}>
          <div className="t-eyebrow" style={{marginBottom:12}}>Visitantes a bordo</div>
          <div style={{background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:20,overflow:'hidden'}}>
            {/* Cabeçalho */}
            {!isMobile && (
              <div style={{display:'grid',gridTemplateColumns:'80px 1.5fr 1fr 1fr 1fr',padding:'11px 20px',background:'var(--cream-2)',fontFamily:'var(--fm)',fontSize:10,letterSpacing:1.2,textTransform:'uppercase',color:'var(--muted)'}}>
                <div>{isEN?'Badge':'Crachá'}</div><div>{isEN?'Name':'Nome'}</div><div>{isEN?'Company':'Empresa'}</div><div>{isEN?'Entry':'Entrada'}</div><div></div>
              </div>
            )}
            {ocupados.map((v,i)=>(
              <div key={v.slot} style={{
                display:'grid',
                gridTemplateColumns:isMobile?'60px 1fr auto':'80px 1.5fr 1fr 1fr 1fr',
                padding:'12px 20px',alignItems:'center',
                borderTop:i===0?'none':'1px solid var(--line-soft)',
                gap:10
              }}>
                {/* Número do crachá */}
                <div style={{
                  width:44,height:44,borderRadius:12,flexShrink:0,
                  background:'linear-gradient(135deg,var(--sun-soft),var(--cream-2))',
                  border:'1px solid var(--line)',
                  display:'grid',placeItems:'center',
                  fontFamily:'var(--fm)',fontSize:14,fontWeight:700,color:'var(--ink)'
                }}>
                  #{String(v.slot).padStart(2,'0')}
                </div>
                <div style={{minWidth:0}}>
                  <div style={{fontSize:13.5,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{v.nome||'—'}</div>
                  {isMobile && <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:1}}>{v.empresa||''}</div>}
                </div>
                {!isMobile && <div style={{fontSize:12,color:'var(--muted)'}}>{v.empresa||'—'}</div>}
                {!isMobile && (
                  <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>
                    {v.atribuido_em ? new Date(v.atribuido_em).toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'}) : '—'}
                  </div>
                )}
                <div style={{display:'flex',justifyContent:'flex-end'}}>
                  <button onClick={()=>onBoarding({genesis_id:v.genesis_id,nome:v.nome,empresa:v.empresa,id:v.user_id},'desembarque')}
                    style={{padding:'6px 12px',borderRadius:8,border:'1px solid #FDECEA',background:'transparent',cursor:'pointer',fontSize:11,color:'#C0392B',fontFamily:'var(--fm)',whiteSpace:'nowrap'}}>
                    🔴 saída
                  </button>
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {ocupados.length === 0 && (
        <div className="empty" style={{padding:40,textAlign:'center',marginBottom:20}}>
          nenhum visitante a bordo no momento.
        </div>
      )}

      {/* Slots livres */}
      <div>
        <div className="t-eyebrow" style={{marginBottom:12}}>{isEN?'Available badges':'Crachás disponíveis'}</div>
        <div style={{display:'flex',flexWrap:'wrap',gap:8}}>
          {livres.map(v=>(
            <div key={v.slot} style={{
              width:48,height:48,borderRadius:12,
              background:'var(--cream-2)',border:'1px solid var(--line-soft)',
              display:'grid',placeItems:'center',
              fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)'
            }}>
              #{String(v.slot).padStart(2,'0')}
            </div>
          ))}
          {livres.length === 0 && (
            <div style={{fontSize:13,color:'var(--coral-2)',fontFamily:'var(--fm)',fontStyle:'italic'}}>
              ⚠️ Todos os crachás em uso
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// ─── ManualTab — Embarque rápido (modo porto) ───
const ManualTab = ({users, slotsLivres, onBoarding, onShowQR, onAddToQueue, isEN, isMobile}) => {
  const [search, setSearch] = useState('');
  const [empresaFilter, setEmpresaFilter] = useState('');
  const [confirm, setConfirm] = useState(null); // {user, tipo}
  const [slotSel, setSlotSel] = useState(null);

  // Lista de empresas para filtro rápido
  const empresas = [...new Set(users.filter(u=>u.status_bordo==='a_bordo'||search).map(u=>u.empresa).filter(Boolean))].sort();

  const filtered = (search.length > 0 || empresaFilter)
    ? users.filter(u => {
        if (empresaFilter && u.empresa !== empresaFilter) return false;
        if (search) {
          const s = search.toLowerCase();
          return (u.nome||'').toLowerCase().includes(s) ||
                 (u.genesis_id||'').toLowerCase().includes(s) ||
                 (u.empresa||'').toLowerCase().includes(s) ||
                 (u.cpf||'').includes(s);
        }
        return true;
      }).slice(0, 30)
    : [];

  const openConfirm = (user) => {
    const tipo = user.status_bordo === 'a_bordo' ? 'desembarque' : 'embarque';
    setConfirm({user, tipo});
    // Pré-seleciona menor slot livre para visitante embarcando
    if (tipo === 'embarque' && user.role === 'guest' && slotsLivres.length) {
      setSlotSel(slotsLivres[0]);
    } else {
      setSlotSel(null);
    }
  };

  const doConfirm = () => {
    if (!confirm) return;
    onBoarding(confirm.user, confirm.tipo, slotSel);
    setConfirm(null);
    setSlotSel(null);
    setSearch('');
  };

  const isGuestBoarding = confirm && confirm.tipo === 'embarque' && confirm.user.role === 'guest';

  return (
    <div style={{maxWidth:760}}>
      {/* Card de confirmação */}
      {confirm && (
        <div onClick={()=>{setConfirm(null);setSlotSel(null);}} style={{position:'fixed',inset:0,background:'rgba(31,42,46,0.5)',backdropFilter:'blur(8px)',zIndex:9000,display:'grid',placeItems:'center',padding:20}}>
          <div onClick={e=>e.stopPropagation()} style={{
            width:'100%',maxWidth:420,background:'var(--cream)',borderRadius:28,
            padding:'30px 32px',boxShadow:'0 32px 80px -20px rgba(31,42,46,0.3)',animation:'slideIn .2s both'
          }}>
            <div style={{display:'flex',alignItems:'center',gap:14,marginBottom:22}}>
              <Avatar user={confirm.user} size="lg"/>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.4px',lineHeight:1.1}}>{confirm.user.nome}</div>
                <div style={{fontSize:12,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:3}}>
                  {confirm.user.genesis_id} · {confirm.user.empresa||'sem empresa'}
                </div>
              </div>
            </div>

            {/* Seletor de slot para visitante */}
            {isGuestBoarding && (
              <div style={{marginBottom:20}}>
                <label className="label" style={{marginBottom:8,display:'block'}}>{isEN?'Visitor badge — choose the physical number':'Crachá do visitante — escolha o número físico'}</label>
                {slotsLivres.length === 0 ? (
                  <div style={{padding:'12px 16px',background:'var(--coral-soft)',borderRadius:12,fontSize:12.5,color:'var(--accent-2)'}}>
                    ⚠️ Nenhum crachá livre no momento.
                  </div>
                ) : (
                  <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(52px, 1fr))',gap:8,maxHeight:180,overflowY:'auto',padding:4}}>
                    {slotsLivres.map(slot=>(
                      <button key={slot} onClick={()=>setSlotSel(slot)}
                        style={{
                          padding:'12px 0',borderRadius:12,cursor:'pointer',
                          border: slotSel===slot ? '2px solid var(--teal)' : '1px solid var(--line)',
                          background: slotSel===slot ? 'var(--teal)' : 'var(--cream-2)',
                          color: slotSel===slot ? 'white' : 'var(--ink)',
                          fontFamily:'var(--fm)',fontSize:16,fontWeight:600,
                        }}>
                        {slot}
                      </button>
                    ))}
                  </div>
                )}
              </div>
            )}

            {/* Botões */}
            <div style={{display:'flex',gap:10}}>
              <button className="btn" style={{flex:1}} onClick={()=>{setConfirm(null);setSlotSel(null);}}>
                Cancelar
              </button>
              <button
                onClick={doConfirm}
                disabled={isGuestBoarding && slotsLivres.length > 0 && !slotSel}
                style={{
                  flex:2,padding:'14px',borderRadius:14,border:'none',cursor:'pointer',
                  background: confirm.tipo==='embarque' ? 'var(--leaf)' : '#C0392B',
                  color:'white',fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18,fontWeight:600,
                  opacity:(isGuestBoarding && slotsLivres.length > 0 && !slotSel)?0.4:1,
                }}>
                {confirm.tipo==='embarque' ? '🟢 Embarcar' : '🔴 Desembarcar'}
                {isGuestBoarding && slotSel ? ` · crachá ${slotSel}` : ''}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Crachás livres */}
      {slotsLivres.length > 0 && (
        <div style={{marginBottom:16,fontSize:12,color:'var(--muted)',fontFamily:'var(--fm)'}}>
          🪪 {slotsLivres.length} crachá{slotsLivres.length!==1?'s':''} livre{slotsLivres.length!==1?'s':''} para visitantes
        </div>
      )}

      {/* Busca grande */}
      <div style={{marginBottom:12}}>
        <input className="input" style={{width:'100%',boxSizing:'border-box',fontSize:17,padding:'16px 20px',borderRadius:16}}
          placeholder="Buscar nome, CPF, GEN-ID ou empresa…"
          value={search} onChange={e=>setSearch(e.target.value)} autoFocus/>
      </div>

      {/* Filtro rápido por empresa */}
      {empresas.length > 1 && (
        <div style={{display:'flex',gap:6,marginBottom:16,flexWrap:'wrap',overflowX:'auto'}}>
          <button onClick={()=>setEmpresaFilter('')}
            className={!empresaFilter?'btn btn-primary btn-sm':'btn btn-sm'}>Todas</button>
          {empresas.slice(0,6).map(emp=>(
            <button key={emp} onClick={()=>setEmpresaFilter(emp===empresaFilter?'':emp)}
              className={empresaFilter===emp?'btn btn-primary btn-sm':'btn btn-sm'}
              style={{whiteSpace:'nowrap'}}>{emp}</button>
          ))}
        </div>
      )}

      {/* Resultados — cards grandes */}
      {filtered.map(u=>{
        const abordo = u.status_bordo==='a_bordo';
        return (
          <div key={u.id} style={{display:'flex',alignItems:'center',gap:12,padding:'14px 16px',background:'var(--surface)',border:'1px solid var(--line-soft)',borderLeft:'3px solid var(--accent)',borderRadius:18,marginBottom:8,boxShadow:'var(--shadow-sm)',flexWrap:isMobile?'wrap':'nowrap'}}>
            <Avatar user={u} size="md"/>
            <div style={{flex:1,minWidth:isMobile?'calc(100% - 64px)':0}}>
              <div style={{fontSize:14.5,fontWeight:500}}>{u.nome}</div>
              <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>
                {u.genesis_id} · {u.empresa||'—'}{u.role==='guest'?' · 🎫 visitante':''}
              </div>
            </div>
            <div style={{padding:'4px 10px',borderRadius:10,fontFamily:'var(--fm)',fontSize:10,letterSpacing:0.5,background:abordo?'var(--leaf-soft)':'#FDECEA',color:abordo?'var(--leaf)':'#C0392B'}}>
              {abordo?'A BORDO':'EM TERRA'}
            </div>
            <button onClick={()=>onAddToQueue(u.id)} title="Adicionar à fila"
              style={{padding:'9px 12px',borderRadius:10,border:'1px solid var(--line-soft)',background:'var(--cream-2)',cursor:'pointer',fontSize:11,fontFamily:'var(--fm)'}}>
              + fila
            </button>
            <button onClick={()=>openConfirm(u)}
              style={{padding:'11px 18px',borderRadius:12,border:'none',background:abordo?'#C0392B':'var(--leaf)',color:'white',cursor:'pointer',fontSize:13.5,fontFamily:'var(--fm)',fontWeight:600,whiteSpace:'nowrap'}}>
              {abordo?'Desembarcar':'Embarcar'}
            </button>
          </div>
        );
      })}
      {search.length>0 && filtered.length===0 && (
        <div className="empty" style={{padding:40,textAlign:'center'}}>ninguém encontrado para "{search}".</div>
      )}
      {search.length===0 && !empresaFilter && (
        <div className="empty" style={{padding:40,textAlign:'center'}}>digite para buscar e embarcar rapidamente.</div>
      )}
    </div>
  );
};

// ─── Gangway Visitors Record Book (.doc via HTML — sem dependência de CDN) ───
// Robusto pra internet de bordo: gera um Word a partir da fila, sem baixar libs.
const gerarGangwayRecordBookDoc = (people) => {
  const HDR = ["Visitor\u2019s Name","Visitor\u2019s Company / business onboard","Visitor\u2019s Pass No.","Date / Time On","Personal belongings checked Yes/No","Visitor\u2019s Signature","Date / Time Off","Duty Crew Signature"];
  const esc = (s) => String(s==null?'':s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
  const today = new Date().toLocaleDateString('pt-BR');
  const named = (people||[]).filter(p => (p.name||'').trim());
  const minRows = 14, spare = 5;
  const total = Math.max(named.length + spare, minRows);
  const rows = named.slice();
  while (rows.length < total) rows.push({name:'', company:''});
  const bodyRows = rows.map(p => `<tr>
    <td class="nm">${esc(p.name)}</td>
    <td class="co">${esc(p.company)}</td>
    <td></td><td></td><td></td><td></td><td></td><td></td>
  </tr>`).join('');
  const thead = `<tr>${HDR.map(h=>`<th>${esc(h)}</th>`).join('')}</tr>`;
  const html = `<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40">
<head><meta charset="utf-8"><title>Gangway Visitors Record Book - Genesis I</title>
<!--[if gte mso 9]><xml><w:WordDocument><w:View>Print</w:View><w:Zoom>100</w:Zoom></w:WordDocument></xml><![endif]-->
<style>
@page { size: 29.7cm 21.0cm; mso-page-orientation: landscape; margin: 1.0cm 0.8cm; }
body { font-family: 'Times New Roman', serif; font-size: 11pt; }
.title { font-family: Arial, sans-serif; font-size: 17pt; font-weight: bold; text-align: center; margin: 0; }
.sub { font-family: Arial, sans-serif; font-size: 11pt; text-align: center; margin: 2px 0 0; }
.meta { font-family: Arial, sans-serif; font-size: 9pt; text-align: right; margin: 4px 0 6px; }
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #000; padding: 5px 6px; font-size: 9.5pt; vertical-align: middle; height: 26pt; }
th { background: #E6E6E6; font-family: Arial, sans-serif; font-size: 8.5pt; text-align: center; }
td.nm { font-weight: bold; white-space: nowrap; }
td.co { font-size: 8.5pt; }
</style></head>
<body>
<p class="title">Gangway Visitors Record Book</p>
<p class="sub">MPSV Genesis I &nbsp;&middot;&nbsp; Marsec Level 1</p>
<p class="meta">Data / Date: ${esc(today)}</p>
<table><thead>${thead}</thead><tbody>${bodyRows}</tbody></table>
</body></html>`;
  try {
    const blob = new Blob(['\ufeff', html], { type: 'application/msword' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `Gangway_Visitors_Genesis_${new Date().toISOString().slice(0,10)}.doc`;
    document.body.appendChild(a); a.click();
    setTimeout(() => { try { document.body.removeChild(a); } catch(e){} URL.revokeObjectURL(url); }, 600);
    return true;
  } catch (e) { console.error('record book:', e); return false; }
};

// ─── FilaTab — fila de embarque persistente ───
const FilaTab = ({queue, users, slotsLivres, onComplete, onRemove, onAddToQueue, isMobile, tableOnly, isEN, fill}) => {
  const toast = useToast();
  const [addSearch, setAddSearch] = useState('');
  const [slotModal, setSlotModal] = useState(null); // {item}
  const [slotSel, setSlotSel] = useState(null);

  const queueUserIds = new Set(queue.map(q=>q.user_id));
  const addResults = addSearch.length > 0
    ? users.filter(u =>
        !queueUserIds.has(u.id) && (
          (u.nome||'').toLowerCase().includes(addSearch.toLowerCase()) ||
          (u.genesis_id||'').toLowerCase().includes(addSearch.toLowerCase()) ||
          (u.empresa||'').toLowerCase().includes(addSearch.toLowerCase())
        )
      ).slice(0, 8)
    : [];

  const handleComplete = async (item) => {
    const estaABordo = item.status_bordo === 'a_bordo';
    // Visitante embarcando (não está a bordo) precisa de slot
    if (!estaABordo && item.role === 'guest') {
      setSlotModal(item);
      setSlotSel(slotsLivres[0] || null);
      return;
    }
    await onComplete(item.id);
  };

  const confirmSlot = async () => {
    if (!slotModal) return;
    const ok = await onComplete(slotModal.id, slotSel);
    if (ok) { setSlotModal(null); setSlotSel(null); }
  };

  const aguardando = queue.length;

  return (
    <div style={fill ? {display:'flex',flexDirection:'column',height:'100%',width:'100%',minHeight:0,minWidth:0,overflow:'hidden',boxSizing:'border-box'} : {maxWidth:760}}>
      {/* Modal de slot para visitante */}
      {slotModal && (
        <div onClick={()=>setSlotModal(null)} style={{position:'fixed',inset:0,background:'rgba(31,42,46,0.5)',backdropFilter:'blur(8px)',zIndex:9000,display:'grid',placeItems:'center',padding:20}}>
          <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:420,background:'var(--cream)',borderRadius:28,padding:'28px 30px',boxShadow:'0 32px 80px -20px rgba(31,42,46,0.3)'}}>
            <div style={{fontFamily:'var(--fs)',fontSize:20,letterSpacing:'-0.4px',marginBottom:6}}>{slotModal.nome}</div>
            <label className="label" style={{marginBottom:10,display:'block'}}>{isEN?'Choose the physical badge':'Escolha o crachá físico'}</label>
            {slotsLivres.length === 0 ? (
              <div style={{padding:'12px 16px',background:'var(--coral-soft)',borderRadius:12,fontSize:12.5,color:'var(--accent-2)'}}>{isEN?'⚠️ No free badge.':'⚠️ Nenhum crachá livre.'}</div>
            ) : (
              <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(52px, 1fr))',gap:8,maxHeight:200,overflowY:'auto',padding:4}}>
                {slotsLivres.map(slot=>(
                  <button key={slot} onClick={()=>setSlotSel(slot)}
                    style={{padding:'12px 0',borderRadius:12,cursor:'pointer',
                      border:slotSel===slot?'2px solid var(--teal)':'1px solid var(--line)',
                      background:slotSel===slot?'var(--teal)':'var(--cream-2)',
                      color:slotSel===slot?'white':'var(--ink)',fontFamily:'var(--fm)',fontSize:16,fontWeight:600}}>
                    {slot}
                  </button>
                ))}
              </div>
            )}
            <div style={{display:'flex',gap:10,marginTop:20}}>
              <button className="btn" style={{flex:1}} onClick={()=>setSlotModal(null)}>Cancelar</button>
              <button onClick={confirmSlot} disabled={slotsLivres.length>0 && !slotSel}
                style={{flex:2,padding:'13px',borderRadius:14,border:'none',cursor:'pointer',background:'var(--leaf)',color:'white',fontFamily:'var(--fs)',fontStyle:'italic',fontSize:17,fontWeight:600,opacity:(slotsLivres.length>0&&!slotSel)?0.4:1}}>
                🟢 Embarcar · crachá {slotSel||'?'}
              </button>
            </div>
          </div>
        </div>
      )}

      {/* Adicionar à fila */}
      {!tableOnly && (
      <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:18,padding:'16px 18px',marginBottom:18,boxShadow:'var(--shadow-sm)'}}>
        <div className="t-eyebrow" style={{marginBottom:10}}>Adicionar à fila</div>
        <input className="input" style={{width:'100%',boxSizing:'border-box',fontSize:15}}
          placeholder="Buscar pessoa para adicionar…"
          value={addSearch} onChange={e=>setAddSearch(e.target.value)}/>
        {addResults.length > 0 && (
          <div style={{marginTop:10,display:'grid',gap:6}}>
            {addResults.map(u=>(
              <div key={u.id} style={{display:'flex',alignItems:'center',gap:10,padding:'8px 10px',background:'var(--cream-2)',borderRadius:12}}>
                <Avatar user={u} size="sm"/>
                <div style={{flex:1,minWidth:0}}>
                  <div style={{fontSize:13,fontWeight:500}}>{u.nome}</div>
                  <div style={{fontSize:10.5,color:'var(--muted)',fontFamily:'var(--fm)'}}>{u.empresa||u.genesis_id} · {u.status_bordo==='a_bordo'?'a bordo':'em terra'}</div>
                </div>
                <button onClick={()=>{onAddToQueue(u.id);setAddSearch('');}}
                  className="btn-tidal" style={{height:30,fontSize:13.5}}>+ adicionar</button>
              </div>
            ))}
          </div>
        )}
      </div>
      )}

      {/* Cabeçalho da fila */}
      <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:14,gap:10,flexWrap:'wrap',flexShrink:0}}>
        <h2 style={{fontFamily:'var(--fs)',fontSize:20,letterSpacing:'-0.4px'}}>
          {tableOnly ? (isEN?'Boarding queue':'Fila de embarque') : (isEN?'Movement queue':'Fila de movimentação')}
        </h2>
        <div style={{display:'flex',alignItems:'center',gap:10}}>
          <button
            onClick={() => {
              const people = queue.filter(q => q.role === 'guest').map(q => ({ name: q.nome || '', company: q.empresa || '' }));
              if (!people.length) {
                toast.push({icon:'x', title:'Nenhum visitante na fila', body:'O Record Book lista só visitantes (classe Visitante).'});
                return;
              }
              const ok = gerarGangwayRecordBookDoc(people);
              toast.push(ok
                ? {icon:'check', title:'Record Book gerado', body:`${people.length} visitante(s) · abra no Word`}
                : {icon:'x', title:'Erro ao gerar o documento'});
            }}
            disabled={queue.length===0}
            title="Gerar o Gangway Visitors Record Book (.doc) com a fila atual"
            style={{padding:'9px 14px',borderRadius:12,border:'1px solid var(--line-soft)',
              background: queue.length===0 ? 'var(--cream-2)' : 'var(--surface)',
              color: queue.length===0 ? 'var(--muted)' : 'var(--ink)',
              cursor: queue.length===0 ? 'default' : 'pointer',
              fontFamily:'var(--fm)',fontSize:12,fontWeight:600,whiteSpace:'nowrap',
              opacity: queue.length===0 ? 0.55 : 1}}>
            📄 Record Book
          </button>
          <div style={{fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)'}}>
            {aguardando} aguardando
          </div>
        </div>
      </div>

      {/* Lista da fila */}
      <div style={fill ? {flex:1,overflowY:'auto',overflowX:'hidden',minHeight:0,minWidth:0} : {}}>
      {queue.length === 0 ? (
        <div className="empty" style={{padding:'48px 20px',textAlign:'center'}}>
          {tableOnly ? (isEN?'the queue is empty. Use the “Boarding” button to add people.':'a fila está vazia. Use o botão “Embarque” para adicionar pessoas.') : (isEN?'the queue is empty. Add people above to prepare boarding.':'a fila está vazia. Adicione pessoas acima para preparar o embarque.')}
        </div>
      ) : (
        <div style={{display:'grid',gap:8}}>
          {queue.map((item, idx)=>{
            // Deriva o tipo do status REAL atual, não do gravado
            const estaABordo = item.status_bordo === 'a_bordo';
            const isEmb = !estaABordo;
            return (
              <div key={item.id} style={{display:'flex',alignItems:'center',gap:8,padding:'8px 2px',borderTop:idx===0?'none':'1px solid var(--line-soft)',minWidth:0,boxSizing:'border-box'}}>
                <span style={{fontFamily:'var(--fm)',fontSize:10,color:'var(--muted)',width:16,textAlign:'right',flexShrink:0}}>{idx+1}</span>
                <Avatar user={item} size="sm"/>
                <div style={{flex:1,minWidth:0}}>
                  <div style={{fontSize:13,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{item.nome}</div>
                  <div style={{fontSize:10.5,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:1,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
                    {item.empresa||item.genesis_id}{item.role==='guest'?' · 🎫':''}{item.slot_atual?` · crachá ${item.slot_atual}`:''}{!isEmb?' · a bordo':''}
                  </div>
                </div>
                <button onClick={()=>onRemove(item.id)} title="Remover da fila"
                  style={{width:26,height:26,borderRadius:8,border:'none',background:'var(--rose)',color:'#fff',cursor:'pointer',fontSize:12,lineHeight:1,display:'grid',placeItems:'center',flexShrink:0}}>
                  ✕
                </button>
                <button onClick={()=>handleComplete(item)} title={isEmb?'Confirmar embarque':'Confirmar saída'}
                  style={{width:30,height:26,borderRadius:8,border:'none',background:'var(--leaf)',color:'#fff',cursor:'pointer',fontSize:13,lineHeight:1,display:'grid',placeItems:'center',flexShrink:0}}>
                  ✓
                </button>
              </div>
            );
          })}
        </div>
      )}
      </div>
    </div>
  );
};

// ─── HistoryTab — período, paginação, exportação ───
const HistoryTab = ({log, isEN, isMobile}) => {
  const token = () => localStorage.getItem('genesis_token');
  const now = new Date();
  const yesterday = new Date(now - 24*60*60*1000);
  const fmt = d => d.toISOString().split('T')[0];
  const [dateFrom, setDateFrom] = useState(fmt(yesterday));
  const [dateTo,   setDateTo]   = useState(fmt(now));
  const [showPeriod, setShowPeriod] = useState(false);
  const [pendingFrom, setPendingFrom] = useState(fmt(yesterday));
  const [pendingTo,   setPendingTo]   = useState(fmt(now));
  const [tipo, setTipo] = useState('all');
  const [search, setSearch] = useState('');
  const [page, setPage] = useState(1);
  const [showExport, setShowExport] = useState(false);
  const [exporting, setExporting] = useState(false);
  const PER_PAGE = 50;

  const filtered = log.filter(l => {
    if (!l.created_at) return false;
    const dt = new Date(l.created_at);
    const from = new Date(dateFrom + 'T00:00:00');
    const to   = new Date(dateTo   + 'T23:59:59');
    if (dt < from || dt > to) return false;
    if (tipo !== 'all' && l.tipo !== tipo) return false;
    if (search) {
      const s = search.toLowerCase();
      if (!(l.nome||'').toLowerCase().includes(s) && !(l.genesis_id||'').toLowerCase().includes(s)) return false;
    }
    return true;
  });

  const totalPages = Math.ceil(filtered.length / PER_PAGE) || 1;
  const paginated  = filtered.slice((page-1)*PER_PAGE, page*PER_PAGE);

  const applyPeriod = () => { setDateFrom(pendingFrom); setDateTo(pendingTo); setPage(1); setShowPeriod(false); };

  const exportExcel = async () => {
    setExporting(true); setShowExport(false);
    try {
      if (!window.XLSX) await new Promise(resolve => {
        const s = document.createElement('script');
        s.src = 'https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js';
        s.onload = resolve; document.head.appendChild(s);
      });
      const rows = filtered.map(l => {
        const dt = l.created_at ? new Date(l.created_at) : null;
        return {'GEN-ID':l.genesis_id||'—','Nome':l.nome||'—','Empresa':l.empresa||'—','Tipo':l.tipo,'Origem':l.origem||'gangway','Data':dt?dt.toLocaleDateString('pt-BR'):'—','Hora':dt?dt.toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'}):'—'};
      });
      const ws = window.XLSX.utils.json_to_sheet(rows);
      const wb = window.XLSX.utils.book_new();
      window.XLSX.utils.book_append_sheet(wb, ws, 'Log Gangway');
      ws['!cols'] = [10,28,22,14,12,12,10].map(w=>({wch:w}));
      window.XLSX.writeFile(wb, `log_gangway_${dateFrom}_a_${dateTo}.xlsx`);
    } catch(e) { console.error(e); }
    setExporting(false);
  };

  const exportPDF = () => {
    setShowExport(false);
    const periodoLabel = `${new Date(dateFrom+'T12:00:00').toLocaleDateString('pt-BR')} a ${new Date(dateTo+'T12:00:00').toLocaleDateString('pt-BR')}`;
    const geradoEm = new Date().toLocaleString('pt-BR');
    const origemLabel = {gangway:'Gangway', scan:'QR Code', bulk:isEN?'Bulk':'Em massa', import:isEN?'Import':'Importação', fila:isEN?'Queue':'Fila'};
    const rows = filtered.map(l => {
      const dt = l.created_at ? new Date(l.created_at) : null;
      const isOut = l.tipo === 'desembarque';
      return `<tr><td>${l.genesis_id||'—'}</td><td>${l.nome||'—'}</td><td>${l.empresa||'—'}</td><td><span class="${isOut?'out':'in'}">${l.tipo}</span></td><td>${origemLabel[l.origem]||l.origem||'gangway'}</td><td>${dt?dt.toLocaleDateString('pt-BR'):'—'}</td><td>${dt?dt.toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'}):'—'}</td></tr>`;
    }).join('');
    const html = `<!DOCTYPE html><html lang="pt-BR"><head><meta charset="UTF-8"><title>Log Gangway</title><style>*{box-sizing:border-box;margin:0;padding:0}body{font-family:'Helvetica Neue',Arial,sans-serif;color:#1F2A2E;padding:32px}.header{margin-bottom:28px;border-bottom:2px solid #2E7B77;padding-bottom:16px}.header h1{font-size:24px;font-weight:700;color:#2E7B77}.header .sub{font-size:13px;color:#6B7B82;margin-top:4px}.meta{display:flex;gap:32px;margin-bottom:20px;flex-wrap:wrap}.meta-item{font-size:12px;color:#6B7B82}.meta-item strong{display:block;font-size:15px;color:#1F2A2E}table{width:100%;border-collapse:collapse;font-size:12px}th{background:#F0EDE8;padding:10px 12px;text-align:left;font-size:10px;letter-spacing:.8px;text-transform:uppercase;color:#6B7B82;border-bottom:1px solid #DDD8D0}td{padding:9px 12px;border-bottom:1px solid #EDE9E3}tr:nth-child(even) td{background:#FAF8F5}.in{background:#D4EDDA;color:#2E7B50;padding:2px 8px;border-radius:4px;font-size:10px}.out{background:#FDECEA;color:#C0392B;padding:2px 8px;border-radius:4px;font-size:10px}.footer{margin-top:20px;font-size:10px;color:#9BA8AD;text-align:right}@media print{body{padding:16px}}</style></head><body><div class="header"><h1>Genesis I · Log de Gangway</h1><div class="sub">Relatório de embarques e desembarques</div></div><div class="meta"><div class="meta-item"><strong>${periodoLabel}</strong>Período</div><div class="meta-item"><strong>${filtered.length}</strong>Registros</div><div class="meta-item"><strong>${filtered.filter(l=>l.tipo==='embarque').length}</strong>Embarques</div><div class="meta-item"><strong>${filtered.filter(l=>l.tipo==='desembarque').length}</strong>Desembarques</div><div class="meta-item"><strong>${geradoEm}</strong>Gerado em</div></div><table><thead><tr><th>GEN-ID</th><th>Nome</th><th>Empresa</th><th>Tipo</th><th>Origem</th><th>Data</th><th>Hora</th></tr></thead><tbody>${rows}</tbody></table><div class="footer">Portal Genesis · PRIO · ${new Date().getFullYear()}</div></body></html>`;
    const w = window.open('', '_blank');
    w.document.write(html); w.document.close();
    setTimeout(() => w.print(), 500);
  };

  const origemLabel = {gangway:'Gangway', scan:'QR Code', bulk:isEN?'Bulk':'Em massa', import:isEN?'Import':'Importação', fila:isEN?'Queue':'Fila'};

  return (
    <div>
      <div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap',alignItems:'center'}}>
        {/* Período */}
        <div style={{position:'relative'}}>
          <button className="btn btn-sm" onClick={()=>setShowPeriod(v=>!v)} style={{display:'flex',alignItems:'center',gap:6}}>
            <Icon n="calendar" size={12}/>
            {new Date(dateFrom+'T12:00:00').toLocaleDateString('pt-BR')} → {new Date(dateTo+'T12:00:00').toLocaleDateString('pt-BR')}
            <span style={{fontSize:9,opacity:0.6}}>▾</span>
          </button>
          {showPeriod && (
            <div onClick={e=>e.stopPropagation()} style={{position:'absolute',top:'calc(100% + 8px)',left:0,zIndex:200,background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:18,padding:'20px 22px',boxShadow:'0 16px 48px -12px rgba(31,42,46,0.18)',minWidth:300}}>
              <div className="t-eyebrow" style={{marginBottom:14}}>{isEN?'Select period':'Selecionar período'}</div>
              <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:12,marginBottom:12}}>
                <div><label className="label">{isEN?'From':'De'}</label><input type="date" className="input" style={{width:'100%',boxSizing:'border-box'}} value={pendingFrom} onChange={e=>setPendingFrom(e.target.value)}/></div>
                <div><label className="label">{isEN?'To':'Até'}</label><input type="date" className="input" style={{width:'100%',boxSizing:'border-box'}} value={pendingTo} onChange={e=>setPendingTo(e.target.value)}/></div>
              </div>
              <div style={{display:'flex',gap:6,marginBottom:14,flexWrap:'wrap'}}>
                {[{l:'Hoje',from:fmt(now),to:fmt(now)},{l:'24h',from:fmt(yesterday),to:fmt(now)},{l:'7 dias',from:fmt(new Date(now-7*86400000)),to:fmt(now)},{l:'30 dias',from:fmt(new Date(now-30*86400000)),to:fmt(now)}].map(p=>(
                  <button key={p.l} className="btn btn-sm" onClick={()=>{setPendingFrom(p.from);setPendingTo(p.to);}}>{p.l}</button>
                ))}
              </div>
              <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
                <button className="btn btn-sm" onClick={()=>setShowPeriod(false)}>Cancelar</button>
                <button className="btn btn-primary btn-sm" onClick={applyPeriod}>Aplicar</button>
              </div>
            </div>
          )}
        </div>
        <input className="input" placeholder="Buscar…" value={search} onChange={e=>{setSearch(e.target.value);setPage(1);}} style={{width:isMobile?'100%':180,boxSizing:'border-box'}}/>
        <div style={{display:'flex',gap:4}}>
          {['all','embarque','desembarque'].map(t=>(
            <button key={t} onClick={()=>{setTipo(t);setPage(1);}} className={tipo===t?'btn btn-primary btn-sm':'btn btn-sm'}>{t==='all'?'todos':t}</button>
          ))}
        </div>
        <div style={{marginLeft:'auto',fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)'}}>{filtered.length} registros</div>
        {/* Exportar */}
        <div style={{position:'relative'}}>
          <button className="btn-icone" onClick={()=>setShowExport(v=>!v)} title={exporting?'exportando…':'exportar (PDF / Excel)'}><Icon n="upload" size={15}/></button>
          {showExport && (
            <div style={{position:'absolute',top:'calc(100% + 6px)',right:0,zIndex:200,background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:14,overflow:'hidden',boxShadow:'0 12px 32px -8px rgba(31,42,46,0.18)',minWidth:160}}>
              {[{label:'📊 Excel (.xlsx)',action:exportExcel},{label:'📄 PDF',action:exportPDF}].map(o=>(
                <button key={o.label} onClick={o.action} style={{display:'block',width:'100%',padding:'12px 18px',border:'none',background:'transparent',cursor:'pointer',textAlign:'left',fontSize:13,fontFamily:'var(--f)',borderBottom:'1px solid var(--line-soft)'}} onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'} onMouseLeave={e=>e.currentTarget.style.background='transparent'}>{o.label}</button>
              ))}
            </div>
          )}
        </div>
      </div>

      <div style={{background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:20,overflow:'hidden'}}>
        {!isMobile && (
          <div style={{display:'grid',gridTemplateColumns:'1.4fr 1fr 0.7fr 0.8fr 0.8fr 0.7fr',padding:'11px 20px',background:'var(--cream-2)',fontFamily:'var(--fm)',fontSize:10,letterSpacing:1.2,textTransform:'uppercase',color:'var(--muted)'}}>
            <div>Pessoa</div><div>Empresa</div><div>Tipo</div><div>Origem</div><div>Data</div><div>Hora</div>
          </div>
        )}
        {paginated.length===0 && <div className="empty" style={{padding:40,textAlign:'center'}}>{isEN?'no records in this period.':'sem registros neste período.'}</div>}
        {paginated.map((l,i)=>{
          const isOut=l.tipo==='desembarque';
          const dt=l.created_at?new Date(l.created_at):null;
          return isMobile ? (
            <div key={l.id||i} style={{display:'flex',alignItems:'center',gap:10,padding:'12px 16px',borderTop:i===0?'none':'1px solid var(--line-soft)'}}>
              <div style={{width:26,height:26,borderRadius:8,display:'grid',placeItems:'center',background:isOut?'#FDECEA':'var(--leaf-soft)',color:isOut?'#C0392B':'var(--leaf)',flexShrink:0}}><Icon n={isOut?'arrow-up':'arrow-down'} size={13}/></div>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontSize:13,fontWeight:500}}>{l.nome||l.genesis_id||'—'}</div>
                <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)'}}>{l.empresa||''}</div>
              </div>
              <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',textAlign:'right',flexShrink:0}}>
                <div>{dt?dt.toLocaleDateString('pt-BR'):'—'}</div>
                <div>{dt?dt.toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'}):'—'}</div>
              </div>
            </div>
          ) : (
            <div key={l.id||i} style={{display:'grid',gridTemplateColumns:'1.4fr 1fr 0.7fr 0.8fr 0.8fr 0.7fr',padding:'12px 20px',alignItems:'center',borderTop:'1px solid var(--line-soft)',fontSize:13}}>
              <div style={{fontWeight:500}}>{l.nome||l.genesis_id||'—'}</div>
              <div style={{fontSize:12,color:'var(--muted)'}}>{l.empresa||'—'}</div>
              <div><span style={{fontFamily:'var(--fm)',fontSize:10,padding:'3px 8px',borderRadius:6,background:isOut?'#FDECEA':'var(--leaf-soft)',color:isOut?'#C0392B':'var(--leaf)'}}>{l.tipo}</span></div>
              <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>{origemLabel[l.origem]||l.origem||'gangway'}</div>
              <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>{dt?dt.toLocaleDateString('pt-BR'):'—'}</div>
              <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>{dt?dt.toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'}):'—'}</div>
            </div>
          );
        })}
      </div>

      {totalPages > 1 && (
        <div style={{display:'flex',justifyContent:'center',alignItems:'center',gap:6,marginTop:16,flexWrap:'wrap'}}>
          <button className="btn btn-sm" onClick={()=>setPage(p=>Math.max(1,p-1))} disabled={page===1}>←</button>
          {Array.from({length:Math.min(totalPages,7)},(_,i)=>{
            const p = totalPages<=7?i+1:page<=4?i+1:page>=totalPages-3?totalPages-6+i:page-3+i;
            return <button key={p} onClick={()=>setPage(p)} className={page===p?'btn btn-primary btn-sm':'btn btn-sm'} style={{minWidth:32}}>{p}</button>;
          })}
          <button className="btn btn-sm" onClick={()=>setPage(p=>Math.min(totalPages,p+1))} disabled={page===totalPages}>→</button>
          <span style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',marginLeft:4}}>{page}/{totalPages} · {filtered.length} total</span>
        </div>
      )}
    </div>
  );
};

