// ═══ GENESIS BUILD conquistas-f2-criador · 2026-07-12 ═══
// ═══ GENESIS BUILD rbac-nav-fix · 2026-07-10 ═══
// Tidal — Pessoas (admin CRUD), Cinema, Música, Avisos, Manifesto, Importar, Conquistas

// ─── PESSOAS (admin) — conectado à API real ───
// ─── 🆘 Contatos de emergência múltiplos (estilo UniSea) ───
const CE_VAZIO = { nome: '', telefone: '', parentesco: '', lingua: '', endereco: '', cidade: '', pais: '', principal: false };
const ContatosEmergencia = ({ userId, toast }) => {
  const H = () => ({ 'Authorization': `Bearer ${localStorage.getItem('genesis_token')}`, 'Content-Type': 'application/json' });
  const [lista, setLista] = useState(null);
  const [edit, setEdit] = useState(null);   // null | {**CE_VAZIO} | contato existente
  const [salvando, setSalvando] = useState(false);
  const carregar = () => fetch(`/api/users/${userId}/contatos-emergencia`, { headers: H() })
    .then(r => r.ok ? r.json() : []).then(d => setLista(Array.isArray(d) ? d : [])).catch(() => setLista([]));
  useEffect(() => { carregar(); }, [userId]);

  const salvar = async () => {
    if (!edit.nome.trim()) { toast.push({ icon: 'x', title: 'Nome do contato é obrigatório' }); return; }
    setSalvando(true);
    const r = edit.id
      ? await fetch(`/api/users/contatos-emergencia/${edit.id}`, { method: 'PUT', headers: H(), body: JSON.stringify(edit) })
      : await fetch(`/api/users/${userId}/contatos-emergencia`, { method: 'POST', headers: H(), body: JSON.stringify(edit) });
    setSalvando(false);
    if (r.ok) { toast.push({ icon: 'check', title: edit.id ? 'Contato atualizado' : 'Contato adicionado 🆘' }); setEdit(null); carregar(); }
    else toast.push({ icon: 'x', title: 'Erro ao salvar contato' });
  };
  const excluir = async (c) => {
    if (!confirm(`Remover "${c.nome}" dos contatos de emergência?`)) return;
    const r = await fetch(`/api/users/contatos-emergencia/${c.id}`, { method: 'DELETE', headers: H() });
    if (r.ok) { toast.push({ icon: 'check', title: 'Contato removido' }); carregar(); }
  };
  const tornarPrincipal = async (c) => {
    await fetch(`/api/users/contatos-emergencia/${c.id}`, { method: 'PUT', headers: H(), body: JSON.stringify({ principal: true }) });
    carregar();
  };
  const campo = (rot, key, extra) => (
    <div style={{ minWidth: 0 }}>
      <label className="label">{rot}</label>
      <input className="input" style={{ width: '100%', boxSizing: 'border-box' }} {...(extra || {})}
        value={edit[key] || ''} onChange={e => setEdit(p => ({ ...p, [key]: e.target.value }))} />
    </div>
  );
  return (
    <div style={{ display: 'grid', gap: 10 }}>
      <div style={{ fontSize: 11, color: 'var(--muted)', fontFamily: 'var(--fm)' }}>
        quem a gente chama se algo acontecer — cadastra quantos quiser; a ⭐ marca o principal. Conta pra medalha 🪪.
      </div>
      {lista === null ? <div className="empty" style={{ padding: 14 }}>carregando…</div>
        : !lista.length && !edit ? <div className="empty" style={{ padding: 16, textAlign: 'center' }}>nenhum contato ainda — adiciona o primeiro 👇</div>
        : lista.map(c => (
          <div key={c.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '10px 12px', background: 'var(--cream-2)', borderRadius: 12, border: c.principal ? '1px solid var(--sun)' : '1px solid var(--line-soft)', minWidth: 0 }}>
            <button title={c.principal ? 'Contato principal' : 'Tornar principal'} onClick={() => !c.principal && tornarPrincipal(c)}
              style={{ border: 'none', background: 'transparent', cursor: c.principal ? 'default' : 'pointer', fontSize: 15, flexShrink: 0, opacity: c.principal ? 1 : 0.3 }}>⭐</button>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                {c.nome}{c.parentesco ? <span style={{ fontWeight: 400, color: 'var(--muted)' }}> · {c.parentesco}</span> : null}
              </div>
              <div style={{ fontFamily: 'var(--fm)', fontSize: 10.5, color: 'var(--muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                {c.telefone || 'sem telefone ⚠'}{c.lingua ? ` · ${c.lingua}` : ''}{c.cidade ? ` · ${c.cidade}` : ''}{c.pais ? `/${c.pais}` : ''}
              </div>
            </div>
            <button className="btn ghost btn-sm" style={{ flexShrink: 0, padding: '3px 8px' }} onClick={() => setEdit({ ...CE_VAZIO, ...c })}>✎</button>
            <button className="btn ghost btn-sm" style={{ flexShrink: 0, padding: '3px 8px', color: 'var(--rose)' }} onClick={() => excluir(c)}>✕</button>
          </div>
        ))}

      {edit ? (
        <div style={{ background: 'var(--cream-2)', borderRadius: 14, padding: '12px 14px', border: '1px solid var(--line-soft)', display: 'grid', gap: 10 }}>
          <div className="t-eyebrow">{edit.id ? '✎ editar contato' : '＋ novo contato'}</div>
          <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 10 }}>
            {campo('Nome', 'nome')}
            {campo('Parentesco', 'parentesco', { placeholder: 'esposa, pai, amigo…', list: 'dl-parentesco' })}
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }}>
            {campo('Telefone', 'telefone', { type: 'tel', inputMode: 'tel', placeholder: '+55 21 99999-9999' })}
            {campo('Idioma do contato', 'lingua', { placeholder: 'Português, Français…' })}
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr 1fr', gap: 10 }}>
            {campo('Endereço', 'endereco')}
            {campo('Cidade', 'cidade')}
            {campo('País', 'pais', { list: 'dl-paises' })}
          </div>
          <label style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 12.5, cursor: 'pointer' }}>
            <input type="checkbox" checked={!!edit.principal} onChange={e => setEdit(p => ({ ...p, principal: e.target.checked }))} />
            ⭐ Este é o contato principal
          </label>
          <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
            <button className="btn btn-sm" onClick={() => setEdit(null)}>Cancelar</button>
            <button className="btn btn-primary btn-sm" onClick={salvar} disabled={salvando}>{salvando ? '…' : 'Salvar contato'}</button>
          </div>
        </div>
      ) : (
        <button className="btn btn-sm" style={{ justifySelf: 'start' }} onClick={() => setEdit({ ...CE_VAZIO })}>＋ Adicionar contato</button>
      )}
      <datalist id="dl-parentesco">
        {['Esposa','Marido','Companheiro(a)','Mãe','Pai','Filho(a)','Irmão(ã)','Amigo(a)'].map(p => <option key={p} value={p} />)}
      </datalist>
    </div>
  );
};

const Pessoas = ({state, setState}) => {
  const toast = useToast();
  const isMobile = useMobile();
  const [filterModal, setFilterModal] = useState(false);
  const [activeFilters, setActiveFilters] = useState({
    status: 'all',    // all | a_bordo | em_terra
    classe: 'all',    // all | crew | ops | guest | staff
    empresa: '',
    funcao: '',
  });
  const [search, setSearch] = useState('');
  const [editing, setEditing] = useState(null);
  const [adding, setAdding] = useState(false);
  const [importOpen, setImportOpen] = useState(false);
  const [inviteOpen, setInviteOpen] = useState(false);
  const [exportOpen, setExportOpen] = useState(false);
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [selected, setSelected] = useState(new Set());
  const [sort, setSort] = useState({ key: 'nome', dir: 1 }); // ordenação da tabela
  const [toolsMenuOpen, setToolsMenuOpen] = useState(false);

  // Busca usuários da API
  const fetchUsers = async () => {
    setLoading(true);
    try {
      const token = localStorage.getItem('genesis_token');
      const res = await fetch('/api/users', {
        headers: { 'Authorization': `Bearer ${token}` }
      });
      const data = await res.json();
      if (res.ok) setUsers(data);
      else toast.push({icon:'x', title:'Erro ao carregar pessoas', body: data.error});
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    } finally {
      setLoading(false);
    }
  };

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

  const toggleSelect = (id) => {
    setSelected(s => {
      const n = new Set(s);
      n.has(id) ? n.delete(id) : n.add(id);
      return n;
    });
  };

  const toggleSelectAll = () => {
    if (selected.size === filtered.length) {
      setSelected(new Set());
    } else {
      setSelected(new Set(filtered.map(u => u.id)));
    }
  };

  // Exclui admins da lista — eles não são pessoas físicas
  const nonAdminUsers = users.filter(u => u.role !== 'admin');

  const selectedUsers = nonAdminUsers.filter(u => selected.has(u.id));
  const [bulkModal, setBulkModal] = useState(null);
  const [bulkMenuOpen, setBulkMenuOpen] = useState(false);

  const filtered = nonAdminUsers.filter(u => {
    const { status, classe, empresa, funcao } = activeFilters;
    if (status === 'a_bordo'  && u.status_bordo !== 'a_bordo')  return false;
    if (status === 'em_terra' && u.status_bordo !== 'em_terra') return false;
    if (classe === 'crew'  && u.role !== 'crew')  return false;
    if (classe === 'ops'   && u.role !== 'ops')   return false;
    if (classe === 'catering' && u.role !== 'catering') return false;
    if (classe === 'terceirizada' && u.role !== 'terceirizada') return false;
    if (classe === 'guest' && u.role !== 'guest') return false;
    if (classe === 'staff' && !u.is_staff)        return false;
    if (empresa && !(u.empresa||'').toLowerCase().includes(empresa.toLowerCase())) return false;
    if (funcao  && !(u.funcao||'').toLowerCase().includes(funcao.toLowerCase()))   return false;
    if (search) {
      const q = search.toLowerCase();
      if (!(u.nome||'').toLowerCase().includes(q) &&
          !(u.genesis_id||'').toLowerCase().includes(q) &&
          !(u.email||'').toLowerCase().includes(q) &&
          !(u.empresa||'').toLowerCase().includes(q) &&
          !(u.funcao||'').toLowerCase().includes(q)) return false;
    }
    return true;
  });

  // Ordenação clicável do cabeçalho (padrão: nome A→Z)
  const sorted = [...filtered].sort((a, b) => {
    const va = String(a[sort.key] || ''), vb = String(b[sort.key] || '');
    return va.localeCompare(vb, 'pt-BR', { numeric: true, sensitivity: 'base' }) * sort.dir;
  });
  const toggleSort = (k) => setSort(s => ({ key: k, dir: s.key === k ? -s.dir : 1 }));
  const SortHead = ({label, k}) => (
    <div onClick={()=>toggleSort(k)} title="Ordenar"
      style={{cursor:'pointer',userSelect:'none',display:'flex',alignItems:'center',gap:4}}>
      {label}
      <span style={{fontSize:8,opacity:sort.key===k?0.9:0.3}}>{sort.key===k && sort.dir===-1?'▼':'▲'}</span>
    </div>
  );

  const hasActiveFilters = activeFilters.status !== 'all' || activeFilters.classe !== 'all' ||
    activeFilters.empresa !== '' || activeFilters.funcao !== '';

  const handleDelete = async (userId) => {
    try {
      const token = localStorage.getItem('genesis_token');
      const res = await fetch(`/api/users/${userId}`, {
        method: 'DELETE',
        headers: { 'Authorization': `Bearer ${token}` }
      });
      if (res.ok) {
        setUsers(u => u.filter(x => x.id !== userId));
        setEditing(null);
        toast.push({icon:'check', title:'Pessoa removida'});
      } else {
        const d = await res.json();
        toast.push({icon:'x', title:'Erro ao remover', body: d.error});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    }
  };

  const addSelectedToQueue = async () => {
    const token = localStorage.getItem('genesis_token');
    const ids = [...selected];
    try {
      const res = await fetch('/api/boarding/queue', {
        method: 'POST',
        headers: {'Content-Type':'application/json','Authorization':`Bearer ${token}`},
        body: JSON.stringify({ user_ids: ids })
      });
      const data = await res.json();
      if (res.ok) {
        toast.push({icon:'check', title:`${data.added} na fila da gangway`, body: data.skipped ? `${data.skipped} já estavam na fila` : 'embarque/desembarque definido automaticamente'});
        setSelected(new Set());
      } else toast.push({icon:'x', title:'Erro', body:data.error});
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };

  const executeBulk = async (action, data) => {
    const token = localStorage.getItem('genesis_token');
    const ids = [...selected];
    try {
      const res = await fetch('/api/users/bulk', {
        method: 'POST',
        headers: {'Content-Type':'application/json','Authorization':`Bearer ${token}`},
        body: JSON.stringify({action, ids, ...data})
      });
      const result = await res.json();
      if (res.ok) {
        const verbo = action === 'delete' ? 'Removidas' : action === 'embarque' ? 'Embarcadas' : action === 'desembarque' ? 'Desembarcadas' : 'Atualizadas';
        const detalhes = [];
        if (result.skipped) detalhes.push(`${result.skipped} ignorada(s)`);
        if (result.falhas?.length) detalhes.push(`${result.falhas.length} FALHARAM: ${result.falhas.map(f => f.nome).join(', ')}`);
        toast.push({
          icon: result.falhas?.length ? 'x' : 'check',
          title: `${verbo}: ${result.ok ?? ids.length} pessoa${(result.ok ?? ids.length)!==1?'s':''}`,
          body: detalhes.join(' · ') || undefined
        });
        setSelected(new Set());
        setBulkModal(null);
        await fetchUsers();
      } else {
        toast.push({icon:'x', title:'Erro', body: result.error});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    }
  };

  return (
    <main style={{padding: isMobile ? '16px 16px 80px' : '30px 36px 56px', flex:1}}>

      {/* Modal de confirmação de ação em massa */}
      {bulkModal && (
        <BulkConfirmModal
          modal={bulkModal}
          selectedUsers={selectedUsers}
          onConfirm={executeBulk}
          onClose={()=>setBulkModal(null)}
        />
      )}

      {/* Modal de filtro avançado */}
      {filterModal && (
        <FilterModal
          filters={activeFilters}
          users={nonAdminUsers}
          onApply={(f)=>{ setActiveFilters(f); setFilterModal(false); }}
          onClose={()=>setFilterModal(false)}
        />
      )}

      {/* Cabeçalho de página */}
      <div style={{marginBottom:20}}>
        <div className="t-eyebrow">tripulação e visitantes</div>
        <h1 style={{fontFamily:'var(--fs)',fontSize:isMobile?30:38,letterSpacing:'-0.8px',lineHeight:1.05,marginTop:6}}>
          Pess<em style={{fontStyle:'italic',color:'var(--accent)'}}>oas.</em>
        </h1>
      </div>

      <div style={{display:'flex',gap:8,marginBottom:16,flexWrap:'wrap',alignItems:'center'}}>
        {/* Botão Filtro */}
        <button className={hasActiveFilters?'btn btn-primary btn-sm':'btn btn-sm'}
          onClick={()=>setFilterModal(true)}
          style={{display:'flex',alignItems:'center',gap:6}}>
          <Icon n="search" size={12}/>
          Filtro
          {hasActiveFilters && <span style={{background:'rgba(255,255,255,0.25)',borderRadius:6,padding:'1px 6px',fontSize:10}}>ativo</span>}
          <span style={{fontSize:9,opacity:0.6}}>▾</span>
        </button>
        {/* Limpar filtros */}
        {hasActiveFilters && (
          <button className="btn btn-sm" onClick={()=>setActiveFilters({status:'all',classe:'all',empresa:'',funcao:''})}>
            × limpar
          </button>
        )}
        <input className="input" placeholder="Buscar por nome ou GEN-ID…"
          value={search} onChange={e=>setSearch(e.target.value)}
          style={{flex:1,minWidth:160,boxSizing:'border-box'}}/>
        <div style={{position:'relative'}}>
          <button className="btn btn-sm" onClick={()=>setToolsMenuOpen(o=>!o)} title="Ferramentas (importar, exportar, convites)">
            ⚙️{isMobile?'':'Ferramentas'} <span style={{fontSize:9,opacity:.7}}>▼</span>
          </button>
          {toolsMenuOpen && (
            <React.Fragment>
              <div onClick={()=>setToolsMenuOpen(false)} style={{position:'fixed',inset:0,zIndex:40}}/>
              <div style={{position:'absolute',top:'calc(100% + 8px)',left:0,zIndex:41,background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:16,boxShadow:'0 18px 44px -12px rgba(31,42,46,.30)',minWidth:252,padding:8,animation:'slideIn .18s both'}}>
                <div style={{padding:'6px 12px 8px',fontFamily:'var(--fm)',fontSize:10,fontWeight:600,textTransform:'uppercase',letterSpacing:'.06em',color:'var(--muted-2)',borderBottom:'1px solid var(--line-soft)',marginBottom:6}}>Ferramentas</div>
                {[
                  state.currentUser?.role === 'admin' && {icon:'⬆️',label:'Importar planilha',fn:()=>setImportOpen(true)},
                  {icon:'📤',label:'Exportar dados (Excel/PDF)',fn:()=>setExportOpen(true)},
                  (state.currentUser?.role === 'admin' || state.currentUser?.is_staff) && {icon:'🔗',label:'Cadastro externo (convites)',fn:()=>setInviteOpen(true)},
                ].filter(Boolean).map((it,i)=>(
                  <button key={i} onClick={()=>{ setToolsMenuOpen(false); it.fn(); }}
                    style={{display:'flex',alignItems:'center',gap:10,width:'100%',padding:'8px 10px',border:'none',background:'transparent',cursor:'pointer',fontFamily:'var(--fm)',fontSize:13,color:'var(--ink)',borderRadius:10,textAlign:'left'}}
                    onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'}
                    onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
                    <span style={{width:28,height:28,borderRadius:9,background:'var(--cream-2)',display:'grid',placeItems:'center',fontSize:13,flexShrink:0}}>{it.icon}</span>{it.label}
                  </button>
                ))}
              </div>
            </React.Fragment>
          )}
        </div>
        <div style={{position:'relative'}}>
          <button className="btn btn-sm" disabled={selected.size===0}
            onClick={()=>setBulkMenuOpen(o=>!o)}
            style={{opacity: selected.size===0 ? 0.45 : 1, cursor: selected.size===0 ? 'not-allowed' : 'pointer'}}
            title={selected.size===0 ? 'Selecione um ou mais perfis' : 'Ações em massa'}>
            {isMobile?'Ações':'Ações em massa'}{selected.size>0?` · ${selected.size}`:''} <span style={{fontSize:9,opacity:.7}}>▼</span>
          </button>
          {bulkMenuOpen && selected.size>0 && (
            <React.Fragment>
              <div onClick={()=>setBulkMenuOpen(false)} style={{position:'fixed',inset:0,zIndex:40}}/>
              <div style={{position:'absolute',top:'calc(100% + 8px)',right:0,zIndex:41,background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:16,boxShadow:'0 18px 44px -12px rgba(31,42,46,.30)',minWidth:260,padding:8,animation:'slideIn .18s both'}}>
                <div style={{padding:'6px 12px 8px',fontFamily:'var(--fm)',fontSize:10,fontWeight:600,textTransform:'uppercase',letterSpacing:'.06em',color:'var(--muted-2)',borderBottom:'1px solid var(--line-soft)',marginBottom:6}}>
                  Ações em massa · {selected.size} selecionado{selected.size!==1?'s':''}
                </div>
                {[
                  {icon:'🟢',label:'Embarcar',fn:()=>setBulkModal({action:'embarque'})},
                  {icon:'🔴',label:'Desembarcar',fn:()=>setBulkModal({action:'desembarque'})},
                  {icon:'⚓',label:'Adicionar à fila da gangway',fn:addSelectedToQueue},
                  {icon:'🎭',label:'Mudar classe',fn:()=>setBulkModal({action:'classe'})},
                  {icon:'🪪',label:'Gerar ZIP de crachás',fn:async()=>{ await loadCrachaModule(); window.exportCrachasZip(selectedUsers, toast); }},
                ].map((it,i)=>(
                  <button key={i} onClick={()=>{ setBulkMenuOpen(false); it.fn(); }}
                    style={{display:'flex',alignItems:'center',gap:10,width:'100%',padding:'8px 10px',border:'none',background:'transparent',cursor:'pointer',fontFamily:'var(--fm)',fontSize:13,color:'var(--ink)',borderRadius:10,textAlign:'left'}}
                    onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'}
                    onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
                    <span style={{width:28,height:28,borderRadius:9,background:'var(--cream-2)',display:'grid',placeItems:'center',fontSize:13,flexShrink:0}}>{it.icon}</span>{it.label}
                  </button>
                ))}
                <div style={{height:1,background:'var(--line-soft)',margin:'6px 8px'}}/>
                <button onClick={()=>{ setBulkMenuOpen(false); setBulkModal({action:'delete'}); }}
                  style={{display:'flex',alignItems:'center',gap:10,width:'100%',padding:'8px 10px',border:'none',background:'transparent',cursor:'pointer',fontFamily:'var(--fm)',fontSize:13,color:'var(--accent-2)',borderRadius:10,textAlign:'left'}}
                  onMouseEnter={e=>e.currentTarget.style.background='rgba(217,105,75,.10)'}
                  onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
                  <span style={{width:28,height:28,borderRadius:9,background:'var(--accent-soft)',display:'grid',placeItems:'center',fontSize:13,flexShrink:0}}>🗑️</span>Excluir selecionados
                </button>
                <button onClick={()=>{ setSelected(new Set()); setBulkMenuOpen(false); }}
                  style={{display:'flex',alignItems:'center',gap:10,width:'100%',padding:'8px 10px',border:'none',background:'transparent',cursor:'pointer',fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)',borderRadius:10,textAlign:'left'}}>
                  <span style={{width:28,height:28,borderRadius:9,display:'grid',placeItems:'center',fontSize:12,flexShrink:0}}>✕</span>Limpar seleção
                </button>
              </div>
            </React.Fragment>
          )}
        </div>
        <button className="btn btn-primary btn-sm" onClick={()=>setAdding(true)}>
          <Icon n="plus" size={13}/>{isMobile?'Novo':'Nova pessoa'}
        </button>
      </div>

      <div className="t-eyebrow" style={{marginBottom:10,color:'var(--muted)'}}>
        {loading ? 'carregando…' : `${filtered.length} de ${nonAdminUsers.length} pessoas`}
      </div>

      {loading ? (
        // Skeleton loaders
        isMobile ? (
          <div style={{display:'flex',flexDirection:'column',gap:10}}>
            {[1,2,3,4,5].map(i=>(
              <div key={i} style={{height:74,borderRadius:18,background:'linear-gradient(90deg,var(--cream-2) 25%,var(--cream-3) 50%,var(--cream-2) 75%)',backgroundSize:'200% 100%',animation:'shimmer 1.5s infinite'}}/>
            ))}
          </div>
        ) : (
          <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:24,overflow:'hidden',boxShadow:'var(--shadow-md)'}}>
            {[1,2,3,4,5,6].map(i=>(
              <div key={i} style={{height:56,borderTop:i>1?'1px solid var(--line-soft)':'none',background:'linear-gradient(90deg,var(--cream) 25%,var(--cream-2) 50%,var(--cream) 75%)',backgroundSize:'200% 100%',animation:'shimmer 1.5s infinite'}}/>
            ))}
          </div>
        )
      ) : isMobile ? (
        // ─── Vista mobile: cards ───
        <div style={{display:'flex',flexDirection:'column',gap:10}}>
          {sorted.map(u => (
            <div key={u.id} onClick={()=>setEditing(u)} style={{
              display:'flex',alignItems:'center',gap:12,padding:'14px 16px',
              background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:18,cursor:'pointer'
            }}>
              <Avatar user={u} size="md"/>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontSize:14,fontWeight:500,lineHeight:1.2}}>{u.nome}</div>
                <div style={{fontSize:11.5,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>{u.funcao} · {u.empresa}</div>
                <div style={{fontSize:11,color:'var(--muted-2)',fontFamily:'var(--fm)',marginTop:2}}>{u.genesis_id||'—'} {u.cabine_atual?`· Cab ${u.cabine_atual}`:''}</div>
              </div>
              <Pill tone={u.status_bordo==='a_bordo'?'leaf':'coral'} live={u.status_bordo==='a_bordo'}>
                {u.status_bordo==='a_bordo'?'bordo':'terra'}
              </Pill>
            </div>
          ))}
          {!filtered.length && <div className="empty" style={{padding:60,textAlign:'center'}}>nenhuma pessoa encontrada.</div>}
        </div>
      ) : (
        // ─── Vista desktop: tabela ───
        <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:24,overflow:'hidden',boxShadow:'var(--shadow-md)'}}>
          <div style={{display:'grid',gridTemplateColumns:'40px 1.5fr 1fr 1fr 0.8fr 0.8fr 50px',padding:'14px 22px',background:'var(--cream-2)',fontFamily:'var(--fm)',fontSize:10.5,letterSpacing:1.2,textTransform:'uppercase',color:'var(--muted)'}}>
            <div>
              <input type="checkbox" checked={selected.size === filtered.length && filtered.length > 0}
                onChange={toggleSelectAll} style={{cursor:'pointer'}}/>
            </div>
            <SortHead label="Nome" k="nome"/><SortHead label="Função / Empresa" k="funcao"/><SortHead label="GEN-ID" k="genesis_id"/><SortHead label="Cabine" k="cabine_atual"/><SortHead label="Status" k="status_bordo"/><div></div>
          </div>
          {sorted.map((u, idx) => (
            <div key={u.id}
              style={{display:'grid',gridTemplateColumns:'40px 1.5fr 1fr 1fr 0.8fr 0.8fr 50px',padding:'14px 22px',alignItems:'center',borderTop:'1px solid var(--line-soft)',cursor:'pointer',transition:'background .15s',background:selected.has(u.id)?'var(--accent-soft)':(idx%2?'rgba(0,0,0,0.012)':'transparent')}}
              onMouseEnter={e=>{if(!selected.has(u.id))e.currentTarget.style.background='var(--cream-2)'}}
              onMouseLeave={e=>{if(!selected.has(u.id))e.currentTarget.style.background=idx%2?'rgba(0,0,0,0.012)':'transparent'}}>
              <div onClick={e=>e.stopPropagation()}>
                <input type="checkbox" checked={selected.has(u.id)} onChange={()=>toggleSelect(u.id)} style={{cursor:'pointer'}}/>
              </div>
              <div style={{display:'flex',alignItems:'center',gap:11}} onClick={()=>setEditing(u)}>
                <Avatar user={{...u, nome: u.nome, initials: u.initials, role: u.role}} size="sm"/>
                <div>
                  <div style={{fontSize:13.5,fontWeight:500,lineHeight:1.2,display:'flex',alignItems:'center',gap:6}}>
                    {u.nome}
                    {u.genero==='F' && <span style={{fontSize:10,color:'#C36474'}}>♀</span>}
                  </div>
                  {u.email && !u.email.includes('@genesis.temp') && (
                    <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)'}}>{u.email}</div>
                  )}
                </div>
              </div>
              <div style={{fontSize:13}} onClick={()=>setEditing(u)}>{u.funcao}<div style={{fontSize:11,color:'var(--muted)'}}>{u.empresa}</div></div>
              <div style={{fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)'}}>{u.genesis_id||'—'}</div>
              <div style={{fontFamily:'var(--fm)',fontSize:12}}>{u.cabine_atual||'—'}</div>
              <div><Pill tone={u.status_bordo==='a_bordo'?'leaf':'coral'} live={u.status_bordo==='a_bordo'}>{u.status_bordo==='a_bordo'?'a bordo':'em terra'}</Pill></div>
              <div style={{color:'var(--muted-2)',textAlign:'right'}}><Icon n="chev_r" size={14}/></div>
            </div>
          ))}
          {!filtered.length && <div className="empty" style={{padding:60}}>nenhuma pessoa encontrada.</div>}
        </div>
      )}

      {editing && (
        <PersonDrawer
          user={editing}
          onClose={()=>setEditing(null)}
          onDelete={()=>handleDelete(editing.id)}
          onSaved={()=>{ fetchUsers(); setEditing(null); }}
        />
      )}
      {adding && (
        <NewPersonDrawer
          onClose={()=>setAdding(false)}
          onSaved={()=>{ fetchUsers(); setAdding(false); }}
        />
      )}
      {importOpen && <Importar state={state} onClose={()=>{ setImportOpen(false); fetchUsers(); }}/>}
      {inviteOpen && <CadastroExternoModal onClose={()=>{ setInviteOpen(false); fetchUsers(); }}/>}
      {exportOpen && <ExportarPessoasModal users={nonAdminUsers} filtered={filtered} selected={selected} onClose={()=>setExportOpen(false)}/>}
    </main>
  );
};

// ─── ExportarPessoasModal — escopo (grupos) + campos + formato (Excel/PDF) ───
const EXPORT_ROLE_LABELS = { admin:'Administrador', staff:'Staff', crew:'Tripulação', ops:'Operações', catering:'Catering', terceirizada:'Terceirizada', guest:'Visitante' };
const EXPORT_FIELDS = [
  {k:'genesis_id',      label:'GEN-ID'},
  {k:'nome',            label:'Nome'},
  {k:'email',           label:'Email'},
  {k:'funcao',          label:'Função'},
  {k:'empresa',         label:'Empresa'},
  {k:'role',            label:'Classe'},
  {k:'status_bordo',    label:'Status'},
  {k:'cabine_atual',    label:'Cabine'},
  {k:'turno',           label:'Turno'},
  {k:'genero',          label:'Gênero'},
  {k:'data_nascimento', label:'Data nascimento'},
  {k:'nacionalidade',   label:'Nacionalidade'},
  {k:'cpf',             label:'CPF'},
  {k:'passaporte',      label:'Passaporte'},
  {k:'telefone',        label:'Telefone'},
  {k:'tipo_sanguineo',  label:'Tipo sanguíneo'},
  {k:'epi',             label:'EPI (macacão · calçado)'},
  {k:'emergencia',      label:'Contato emergência'},
  {k:'embarque',        label:'Data embarque'},
  {k:'created_at',      label:'Cadastrado em'},
];
const EXPORT_FIELDS_DEFAULT = ['genesis_id','nome','funcao','empresa','role','status_bordo','cabine_atual','turno'];

const fmtDateExport = (v) => {
  if (!v) return '—';
  const d = new Date(v);
  return isNaN(d) ? String(v) : d.toLocaleDateString('pt-BR');
};
const exportFieldValue = (u, k) => {
  switch (k) {
    case 'email':        return (u.email && !u.email.includes('@genesis.temp')) ? u.email : '—';
    case 'role':         return EXPORT_ROLE_LABELS[u.role] || u.role || '—';
    case 'status_bordo': return u.status_bordo === 'a_bordo' ? 'A bordo' : 'Em terra';
    case 'data_nascimento':
    case 'embarque':
    case 'created_at':   return fmtDateExport(u[k]);
    case 'epi': {
      const m = u.epi_macacao, c = u.epi_calcado;
      return (m || c) ? `Macacão ${m || '—'} · Calçado ${c || '—'}` : '—';
    }
    case 'emergencia': {
      const n = u.em_nome, t = u.em_telefone;
      return (n || t) ? [n, t].filter(Boolean).join(' · ') : '—';
    }
    default: return u[k] || '—';
  }
};
const escHtml = (s) => String(s ?? '').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');

const ExportarPessoasModal = ({users, filtered, selected, onClose}) => {
  const toast = useToast();
  const isMobile = useMobile();
  const [escopo, setEscopo] = useState(selected.size ? 'selecionados' : 'filtrados');
  const [grpClasses, setGrpClasses] = useState(new Set(['crew','ops','catering','terceirizada','guest']));
  const [grpStaff, setGrpStaff] = useState(false);
  const [grpStatus, setGrpStatus] = useState(new Set(['a_bordo','em_terra']));
  const [grpEmpresas, setGrpEmpresas] = useState(new Set()); // vazio = todas
  const [fieldsSel, setFieldsSel] = useState(new Set(EXPORT_FIELDS_DEFAULT));
  const [fieldsOpen, setFieldsOpen] = useState(false);
  const [empresasOpen, setEmpresasOpen] = useState(false);
  const [busy, setBusy] = useState(false);

  const empresas = [...new Set(users.map(u => u.empresa).filter(Boolean))].sort();

  // ─── Lista final conforme o escopo ───
  let list = users;
  let escopoDesc = 'todas as pessoas';
  if (escopo === 'filtrados') { list = filtered; escopoDesc = 'lista atual (filtros da tela)'; }
  else if (escopo === 'selecionados') { list = users.filter(u => selected.has(u.id)); escopoDesc = 'marcados na tela'; }
  else if (escopo === 'grupos') {
    list = users.filter(u => {
      const classeOk = grpClasses.has(u.role) || (grpStaff && u.is_staff);
      const statusOk = grpStatus.size === 2 || grpStatus.has(u.status_bordo === 'a_bordo' ? 'a_bordo' : 'em_terra');
      const empOk = !grpEmpresas.size || grpEmpresas.has(u.empresa || '');
      return classeOk && statusOk && empOk;
    });
    escopoDesc = 'grupos escolhidos';
  }

  const toggleSet = (setFn, val) => setFn(s => { const n = new Set(s); n.has(val) ? n.delete(val) : n.add(val); return n; });

  const fieldsOrdenados = EXPORT_FIELDS.filter(f => fieldsSel.has(f.k));
  const today = new Date().toISOString().slice(0, 10);

  const doExcel = async () => {
    if (!list.length) { toast.push({icon:'x', title:'Nenhuma pessoa no escopo'}); return; }
    if (!fieldsOrdenados.length) { toast.push({icon:'x', title:'Escolha ao menos um campo'}); return; }
    setBusy(true);
    try {
      if (!window.XLSX) await new Promise((resolve, reject) => {
        const s = document.createElement('script');
        s.src = 'https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js';
        s.onload = resolve; s.onerror = reject; document.head.appendChild(s);
      });
      const rows = list.map(u => {
        const o = {};
        fieldsOrdenados.forEach(f => { o[f.label] = exportFieldValue(u, f.k); });
        return o;
      });
      const ws = window.XLSX.utils.json_to_sheet(rows);
      ws['!cols'] = fieldsOrdenados.map(f => ({
        wch: Math.min(44, Math.max(f.label.length + 2, ...list.slice(0, 200).map(u => String(exportFieldValue(u, f.k)).length + 2), 12))
      }));
      const wb = window.XLSX.utils.book_new();
      window.XLSX.utils.book_append_sheet(wb, ws, 'Pessoas');
      window.XLSX.writeFile(wb, `pessoas_genesis_${today}.xlsx`);
      toast.push({icon:'check', title:`${list.length} pessoa(s) exportadas`, body:'Arquivo Excel baixado.'});
      onClose();
    } catch(e) { toast.push({icon:'x', title:'Erro ao gerar Excel', body:e.message}); }
    finally { setBusy(false); }
  };

  const doPDF = () => {
    if (!list.length) { toast.push({icon:'x', title:'Nenhuma pessoa no escopo'}); return; }
    if (!fieldsOrdenados.length) { toast.push({icon:'x', title:'Escolha ao menos um campo'}); return; }
    const landscape = fieldsOrdenados.length > 6;
    const w = window.open('', '_blank', 'width=1000,height=800');
    if (!w) { toast.push({icon:'x', title:'Pop-up bloqueado', body:'Permita pop-ups para exportar o PDF.'}); return; }
    const head = fieldsOrdenados.map(f => `<th>${escHtml(f.label)}</th>`).join('');
    const body = list.map(u => `<tr>${fieldsOrdenados.map(f => `<td>${escHtml(exportFieldValue(u, f.k))}</td>`).join('')}</tr>`).join('');
    w.document.write(`<!doctype html><html><head><meta charset="utf-8"><title>Pessoas · ${today} · Genesis</title>
<style>
  @page{margin:14mm${landscape ? ';size:A4 landscape' : ''}}
  body{font-family:Georgia,serif;color:#1F2A2E}
  h1{font-size:30px;font-weight:400;letter-spacing:-0.5px;margin:0 0 4px}
  h1 em{color:#D87C5A}
  .meta{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:#7A8388;margin-bottom:22px}
  table{width:100%;border-collapse:collapse;font-size:10.5px}
  th{text-align:left;font-family:'IBM Plex Mono',monospace;font-size:8.5px;letter-spacing:1px;text-transform:uppercase;color:#7A8388;padding:8px 7px;border-bottom:1px solid #1F2A2E}
  td{padding:8px 7px;border-bottom:1px solid #EAE3D6;vertical-align:top}
  tr:nth-child(even) td{background:#F7F3EC}
  .footer{margin-top:32px;font-family:'IBM Plex Mono',monospace;font-size:9px;color:#7A8388;letter-spacing:1px;text-transform:uppercase}
</style></head><body>
<h1>Pess<em>oas</em></h1>
<div class="meta">${today} · ${list.length} pessoas · ${escHtml(escopoDesc)} · Genesis</div>
<table><thead><tr>${head}</tr></thead><tbody>${body}</tbody></table>
<div class="footer">Pessoas · Portal Genesis · uso interno</div>
</body></html>`);
    w.document.close();
    setTimeout(() => w.print(), 300);
    onClose();
  };

  const Chip = ({on, label, onClick}) => (
    <button onClick={onClick} style={{
      padding:'6px 12px',borderRadius:20,cursor:'pointer',fontFamily:'var(--fm)',fontSize:11.5,
      border:'1px solid '+(on?'var(--teal)':'var(--line-soft)'),
      background:on?'var(--teal)':'var(--cream-2)', color:on?'#fff':'var(--ink)'
    }}>{label}</button>
  );

  // Item de checkbox (linha) e dropdown sanfonado com checkboxes
  const CheckItem = ({label, checked, onChange}) => (
    <label style={{display:'flex',alignItems:'center',gap:9,padding:'7px 10px',borderRadius:8,cursor:'pointer',fontSize:13,color:'var(--ink)'}}
      onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'}
      onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
      <input type="checkbox" checked={checked} onChange={onChange} style={{cursor:'pointer',accentColor:'var(--teal)',width:15,height:15}}/>
      {label}
    </label>
  );
  const CheckDrop = ({title, resumo, open, onToggle, onAll, onNone, allLabel, noneLabel, children}) => (
    <div style={{border:'1px solid var(--line-soft)',borderRadius:12,background:'var(--surface)',overflow:'hidden'}}>
      <button onClick={onToggle} style={{width:'100%',display:'flex',justifyContent:'space-between',alignItems:'center',padding:'11px 14px',border:'none',background:'transparent',cursor:'pointer',fontFamily:'var(--fm)',fontSize:13,color:'var(--ink)'}}>
        <span>{title}</span>
        <span style={{color:'var(--muted)',fontSize:11.5}}>{resumo} <span style={{fontSize:9,opacity:.6}}>{open?'▲':'▼'}</span></span>
      </button>
      {open && (
        <div style={{borderTop:'1px solid var(--line-soft)',padding:'6px 8px 10px',maxHeight:230,overflowY:'auto'}}>
          <div style={{display:'flex',gap:14,padding:'2px 10px 8px'}}>
            <button onClick={onAll} style={{border:'none',background:'none',cursor:'pointer',fontFamily:'var(--fm)',fontSize:11,color:'var(--teal)',padding:0}}>{allLabel||'todos'}</button>
            <button onClick={onNone} style={{border:'none',background:'none',cursor:'pointer',fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',padding:0}}>{noneLabel||'nenhum'}</button>
          </div>
          <div style={{display:'grid',gridTemplateColumns:isMobile?'1fr':'1fr 1fr',gap:1}}>
            {children}
          </div>
        </div>
      )}
    </div>
  );
  const miniLabel = {fontFamily:'var(--fm)',fontSize:10,letterSpacing:1,textTransform:'uppercase',color:'var(--muted)',marginBottom:6};

  return (
    <div onClick={onClose} 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:640,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}}>pessoas</div>
            <div style={{fontFamily:'var(--fs)',fontSize:22}}>📤 Exportar dados</div>
          </div>
          <button className="btn ghost btn-sm" onClick={onClose}>✕</button>
        </div>

        {/* 1 — Quem exportar */}
        <div className="t-eyebrow" style={{marginBottom:8}}>Quem exportar</div>
        <div style={{display:'flex',gap:8,flexWrap:'wrap',marginBottom:10}}>
          <Chip on={escopo==='filtrados'} label={`Lista atual (${filtered.length})`} onClick={()=>setEscopo('filtrados')}/>
          {selected.size > 0 && <Chip on={escopo==='selecionados'} label={`Marcados (${selected.size})`} onClick={()=>setEscopo('selecionados')}/>}
          <Chip on={escopo==='grupos'} label="Escolher grupos" onClick={()=>setEscopo('grupos')}/>
        </div>
        {escopo === 'grupos' && (
          <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:14,padding:'12px 14px',marginBottom:10,display:'grid',gap:12}}>
            <div>
              <div style={miniLabel}>Classes</div>
              <div style={{display:'grid',gridTemplateColumns:isMobile?'1fr':'1fr 1fr',gap:1}}>
                {[['crew','Tripulação'],['ops','Operações'],['catering','Catering'],['terceirizada','Terceirizadas'],['guest','Visitantes']].map(([id,l])=>(
                  <CheckItem key={id} label={l} checked={grpClasses.has(id)} onChange={()=>toggleSet(setGrpClasses, id)}/>
                ))}
                <CheckItem label="Com acesso à gestão" checked={grpStaff} onChange={()=>setGrpStaff(v=>!v)}/>
              </div>
            </div>
            <div>
              <div style={miniLabel}>Status</div>
              <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:1}}>
                <CheckItem label="A bordo" checked={grpStatus.has('a_bordo')} onChange={()=>toggleSet(setGrpStatus,'a_bordo')}/>
                <CheckItem label="Em terra" checked={grpStatus.has('em_terra')} onChange={()=>toggleSet(setGrpStatus,'em_terra')}/>
              </div>
            </div>
            <div>
              <div style={miniLabel}>Empresas</div>
              <CheckDrop title="Selecionar empresas"
                resumo={grpEmpresas.size ? `${grpEmpresas.size} de ${empresas.length}` : 'todas'}
                open={empresasOpen} onToggle={()=>setEmpresasOpen(o=>!o)}
                onAll={()=>setGrpEmpresas(new Set(empresas))} onNone={()=>setGrpEmpresas(new Set())}
                allLabel="marcar todas" noneLabel="limpar">
                {empresas.map(e=>(
                  <CheckItem key={e} label={e} checked={grpEmpresas.has(e)} onChange={()=>toggleSet(setGrpEmpresas, e)}/>
                ))}
              </CheckDrop>
            </div>
          </div>
        )}

        {/* 2 — Quais dados */}
        <div className="t-eyebrow" style={{marginBottom:8}}>Quais dados exportar</div>
        <div style={{marginBottom:16}}>
          <CheckDrop title="Selecionar dados"
            resumo={`${fieldsSel.size} de ${EXPORT_FIELDS.length} campos`}
            open={fieldsOpen} onToggle={()=>setFieldsOpen(o=>!o)}
            onAll={()=>setFieldsSel(new Set(EXPORT_FIELDS.map(f=>f.k)))} onNone={()=>setFieldsSel(new Set())}>
            {EXPORT_FIELDS.map(f=>(
              <CheckItem key={f.k} label={f.label} checked={fieldsSel.has(f.k)} onChange={()=>toggleSet(setFieldsSel, f.k)}/>
            ))}
          </CheckDrop>
        </div>

        {/* 3 — Formato */}
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',gap:10,flexWrap:'wrap',borderTop:'1px solid var(--line-soft)',paddingTop:14}}>
          <div style={{fontFamily:'var(--fm)',fontSize:11.5,color:'var(--muted)'}}>
            {list.length} pessoa{list.length!==1?'s':''} · {fieldsOrdenados.length} campo{fieldsOrdenados.length!==1?'s':''}
          </div>
          <div style={{display:'flex',gap:8,flexDirection:isMobile?'column':'row',width:isMobile?'100%':'auto'}}>
            <button className="btn btn-sm" onClick={doPDF} disabled={busy} style={{opacity:busy?0.5:1}}>📄 PDF</button>
            <button className="btn btn-primary btn-sm" onClick={doExcel} disabled={busy} style={{opacity:busy?0.5:1}}>{busy?'gerando…':'📊 Excel (.xlsx)'}</button>
          </div>
        </div>
      </div>
    </div>
  );
};

// editor de permissões da pessoa: perfil + ajustes finos (herdar/conceder/negar)
const PermPessoaSecao = ({userId, token, toast}) => {
  const [perfis, setPerfis] = useState([]);
  const [catalogo, setCatalogo] = useState(null);
  const [estado, setEstado] = useState(null); // {perm_perfil_id, grant, deny}
  const [aberto, setAberto] = useState(false);
  const [salvando, setSalvando] = useState(false);
  const H = () => ({'Authorization':`Bearer ${localStorage.getItem('genesis_token')}`});
  useEffect(() => {
    fetch('/api/users/perm/perfis', {headers:H()}).then(r=>r.ok?r.json():[]).then(d=>setPerfis(Array.isArray(d)?d:[])).catch(()=>{});
    fetch('/api/users/perm/catalogo', {headers:H()}).then(r=>r.ok?r.json():null).then(d=>d&&setCatalogo(d.catalogo)).catch(()=>{});
    fetch(`/api/users/perm/usuario/${userId}`, {headers:H()}).then(r=>r.ok?r.json():null).then(d=>d&&setEstado({perm_perfil_id:d.perm_perfil_id, grant:new Set(d.grant), deny:new Set(d.deny)})).catch(()=>{});
  }, [userId]);
  if (!estado) return null;
  const perfilAtual = perfis.find(p => p.id === estado.perm_perfil_id);
  const doPerfil = new Set(perfilAtual ? perfilAtual.permissoes : []);
  // 3 estados por chave: herdar (segue o perfil) · conceder · negar
  const estadoDe = (k) => estado.grant.has(k) ? 'grant' : estado.deny.has(k) ? 'deny' : 'herda';
  const girar = (k) => setEstado(e => {
    const g = new Set(e.grant), d = new Set(e.deny);
    const atual = g.has(k) ? 'grant' : d.has(k) ? 'deny' : 'herda';
    g.delete(k); d.delete(k);
    if (atual === 'herda') g.add(k);
    else if (atual === 'grant') d.add(k);
    return {...e, grant:g, deny:d};
  });
  const salvar = async () => {
    setSalvando(true);
    const r = await fetch(`/api/users/perm/usuario/${userId}`, {method:'PUT', headers:{...H(),'Content-Type':'application/json'},
      body: JSON.stringify({ perm_perfil_id: estado.perm_perfil_id, grant: [...estado.grant], deny: [...estado.deny] })});
    setSalvando(false);
    if (r.ok) toast.push({icon:'check', title:'Permissões salvas 🔐'});
    else toast.push({icon:'x', title:'Erro ao salvar permissões'});
  };
  return (
    <div style={{background:'var(--cream-2)',borderRadius:14,padding:'14px 16px'}}>
      <div className="t-eyebrow" style={{marginBottom:10}}>Permissões</div>
      <div style={{display:'grid',gap:10}}>
        <div>
          <label className="label">Perfil de permissões</label>
          <select className="select" value={estado.perm_perfil_id || ''} onChange={e=>setEstado(x=>({...x, perm_perfil_id: e.target.value ? parseInt(e.target.value) : null}))} style={{width:'100%'}}>
            <option value="">— sem perfil (só o básico) —</option>
            {perfis.map(p => <option key={p.id} value={p.id}>{p.nome} ({p.permissoes.length} autorizações)</option>)}
          </select>
          <div style={{fontFamily:'var(--fm)',fontSize:9.5,color:'var(--muted)',marginTop:4}}>admin e staff continuam podendo tudo, independente do perfil. Perfis se criam em Módulos → Permissões.</div>
        </div>
        <button className="btn btn-sm" type="button" onClick={()=>setAberto(a=>!a)} style={{justifySelf:'start'}}>
          {aberto ? '▾' : '▸'} ajustes finos desta pessoa {(estado.grant.size || estado.deny.size) ? `(${estado.grant.size + estado.deny.size} ativos)` : ''}
        </button>
        {aberto && catalogo && (
          <div style={{display:'grid',gap:7,maxHeight:280,overflowY:'auto',paddingRight:4}}>
            <div style={{fontFamily:'var(--fm)',fontSize:9.5,color:'var(--muted)'}}>toque pra girar: <b>herda do perfil</b> → <b style={{color:'var(--leaf)'}}>✓ concede</b> → <b style={{color:'var(--rose)'}}>✕ nega</b></div>
            {Object.entries(catalogo).map(([mod, info]) => Object.keys(info.acoes).map(acao => {
              const k = `${mod}.${acao}`;
              const st = estadoDe(k);
              const noPerfil = doPerfil.has(k);
              const cor = st === 'grant' ? 'var(--leaf)' : st === 'deny' ? 'var(--rose)' : 'var(--muted)';
              return (
                <div key={k} onClick={()=>girar(k)} style={{display:'flex',alignItems:'center',gap:8,padding:'6px 10px',borderRadius:9,cursor:'pointer',border:`1px solid ${st==='herda'?'var(--line-soft)':cor}`,background:st==='herda'?'var(--surface)':(st==='grant'?'rgba(92,126,79,0.08)':'rgba(177,78,92,0.08)')}}>
                  <span style={{fontFamily:'var(--fm)',fontSize:11,flex:1}}>{info.nome} · <b>{acao}</b></span>
                  <span style={{fontFamily:'var(--fm)',fontSize:9.5,color:cor,fontWeight:700,flexShrink:0}}>
                    {st === 'herda' ? (noPerfil ? 'herda: ✓' : 'herda: —') : st === 'grant' ? '✓ concede' : '✕ nega'}
                  </span>
                </div>
              );
            }))}
          </div>
        )}
        <button className="btn btn-primary btn-sm" type="button" disabled={salvando} onClick={salvar} style={{justifySelf:'end'}}>{salvando?'…':'Salvar permissões'}</button>
      </div>
    </div>
  );
};

const PersonDrawer = ({user, onClose, onDelete, onSaved}) => {
  const toast = useToast();
  const euUser = JSON.parse(localStorage.getItem('genesis_user') || '{}');
  const euAdmin = (euUser.role === 'admin');
  // Só admin/staff podem ver e editar permissões (endpoints exigem 'manage') — evita 403 pro usuário comum
  const euManage = euAdmin || euUser.is_staff === true;
  const isMobile = useMobile();
  const [tab, setTab] = useState('perfil'); // 'perfil' | 'senha' | 'historico'
  const [history, setHistory] = useState(null);
  const [loadingHistory, setLoadingHistory] = useState(false);
  const [form, setForm] = useState({
    nome:              user.nome||'',
    email:             user.email||'',
    funcao:            user.funcao||'',
    empresa:           user.empresa||'',
    role:              user.role==='admin'?'admin':(user.role||'crew'),
    is_staff:          user.is_staff||false,
    turno:             user.turno||'',
    foto_url:          user.foto_url||'',
    cpf:               user.cpf||'',
    passaporte:        user.passaporte||'',
    data_nascimento:   user.data_nascimento ? user.data_nascimento.split('T')[0] : '',
    nacionalidade:     user.nacionalidade||'',
    telefone:          user.telefone||'',
    bio:               user.bio||'',
    genero:            user.genero||'',
    cidade_nascimento: user.cidade_nascimento||'',
    estado_nascimento: user.estado_nascimento||'',
    end_logradouro:    user.end_logradouro||'',
    end_numero:        user.end_numero||'',
    end_complemento:   user.end_complemento||'',
    end_cidade:        user.end_cidade||'',
    end_estado:        user.end_estado||'',
    end_cep:           user.end_cep||'',
    end_pais:          user.end_pais||'',
    tipo_sanguineo:    user.tipo_sanguineo||'',
    epi_macacao:       user.epi_macacao||'',
    epi_calcado:       user.epi_calcado||'',
  });
  const [sub, setSub] = useState('dados');       // dados · endereco · emergencia · conta
  const [fotoEdit, setFotoEdit] = useState(false);
  const [saving, setSaving] = useState(false);
  const [changingStatus, setChangingStatus] = useState(false);
  const [statusBordo, setStatusBordo] = useState(user.status_bordo||'em_terra');
  // Senha
  const [senhaNova, setSenhaNova] = useState('');
  const [senhaConfirma, setSenhaConfirma] = useState('');
  const [savingPass, setSavingPass] = useState(false);

  const handleSavePassword = async () => {
    if (senhaNova.length < 6) { toast.push({icon:'x', title:'Mínimo 6 caracteres'}); return; }
    if (senhaNova !== senhaConfirma) { toast.push({icon:'x', title:'Senhas não coincidem'}); return; }
    setSavingPass(true);
    try {
      const token = localStorage.getItem('genesis_token');
      const res = await fetch(`/api/users/${user.id}/reset-password`, {
        method:'POST',
        headers:{'Content-Type':'application/json','Authorization':`Bearer ${token}`},
        body: JSON.stringify({nova_senha: senhaNova})
      });
      const data = await res.json();
      if (res.ok) { toast.push({icon:'check', title:'Senha alterada!'}); setSenhaNova(''); setSenhaConfirma(''); }
      else toast.push({icon:'x', title:'Erro', body:data.error});
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
    finally { setSavingPass(false); }
  };

  // Carrega histórico de gangway quando a aba é aberta
  const loadHistory = async () => {
    if (history !== null) return; // já carregado
    setLoadingHistory(true);
    try {
      const token = localStorage.getItem('genesis_token');
      const res = await fetch(`/api/boarding/history/${user.id}`, {
        headers: { 'Authorization': `Bearer ${token}` }
      });
      if (res.ok) setHistory(await res.json());
      else setHistory([]);
    } catch(e) { setHistory([]); }
    finally { setLoadingHistory(false); }
  };

  React.useEffect(() => {
    if (tab === 'historico') loadHistory();
  }, [tab]);

  // Fecha com confirmação se houver alterações não salvas
  const handleClose = () => {
    const initial = JSON.stringify({
      nome:user.nome||'', funcao:user.funcao||'', empresa:user.empresa||'',
      role:user.role==='admin'?'admin':(user.role||'crew'), is_staff:user.is_staff||false,
      turno:user.turno||'', cpf:user.cpf||'', passaporte:user.passaporte||'',
    });
    const current = JSON.stringify({
      nome:form.nome, funcao:form.funcao, empresa:form.empresa,
      role:form.role, is_staff:form.is_staff,
      turno:form.turno, cpf:form.cpf, passaporte:form.passaporte,
    });
    if (initial !== current) {
      if (window.confirm('Há alterações não salvas. Deseja descartá-las?')) onClose();
    } else {
      onClose();
    }
  };

  const handleSave = async () => {
    if (!form.cpf && !form.passaporte) {
      toast.push({icon:'x', title:'CPF ou Passaporte obrigatório'});
      return;
    }
    if (form.cpf && form.cpf.length !== 11) {
      toast.push({icon:'x', title:'CPF inválido', body:'Deve ter exatamente 11 dígitos'});
      return;
    }
    setSaving(true);
    try {
      const token = localStorage.getItem('genesis_token');
      const res = await fetch(`/api/users/${user.id}`, {
        method: 'PUT',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token}` },
        body: JSON.stringify(form)
      });
      const data = await res.json();
      if (res.ok) {
        if (data.warning) {
          toast.push({icon:'x', title:'Classe alterada com aviso', body: data.warning});
        } else {
          toast.push({icon:'check', title:'Alterações salvas'});
        }
        onSaved();
      } else {
        toast.push({icon:'x', title:'Erro ao salvar', body: data.error});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    } finally {
      setSaving(false);
    }
  };

  const handleStatus = async (novoStatus) => {
    setChangingStatus(true);
    try {
      const token = localStorage.getItem('genesis_token');
      const res = await fetch(`/api/users/${user.id}/status`, {
        method: 'PATCH',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token}` },
        body: JSON.stringify({ status: novoStatus })
      });
      const data = await res.json();
      if (res.ok) {
        setStatusBordo(novoStatus);
        toast.push({icon:'check', title: novoStatus === 'a_bordo' ? `${user.nome} embarcou! 🚢` : `${user.nome} desembarcou.`});
        onSaved();
      } else {
        toast.push({icon:'x', title:'Erro ao mudar status', body: data.error});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    } finally {
      setChangingStatus(false);
    }
  };

  const swipe = useSwipeToClose(handleClose, 'right');

  return (
    <div onClick={handleClose} style={{position:'fixed',inset:0,background:'rgba(31,42,46,0.4)',backdropFilter:'blur(6px)',zIndex:8000,display:'flex',alignItems:'flex-start',justifyContent:'center',padding:'5vh 16px',overflowY:'auto'}}>
      <div onClick={e=>e.stopPropagation()}
        style={{width:'100%',maxWidth:isMobile?'100%':560,maxHeight:'90vh',background:'var(--cream)',borderRadius:isMobile?18:22,display:'flex',flexDirection:'column',animation:'slideIn .25s cubic-bezier(.2,.7,.3,1) both',boxShadow:'var(--shadow-lg)',border:'1px solid var(--line-soft)',overflow:'hidden'}}>
        {/* Conteúdo rolável */}
        <div style={{flex:1,overflowY:'auto',padding:'30px 32px 24px'}}>
          <div style={{display:'flex',justifyContent:'space-between',marginBottom:18}}>
            <div className="t-eyebrow">Editar pessoa · {user.genesis_id||'sem ID'}</div>
            <button className="btn ghost" onClick={handleClose}><Icon n="x" size={14}/></button>
          </div>

          {/* Tabs perfil / senha / histórico */}
          <div style={{display:'flex',gap:6,padding:4,background:'var(--cream-2)',borderRadius:12,marginBottom:20}}>
            {[
              {id:'perfil',    label:'Perfil'},
              {id:'senha',     label:'Senha'},
              {id:'historico', label:'Histórico'},
            ].map(t=>(
              <button key={t.id} onClick={()=>setTab(t.id)}
                style={{flex:1,padding:'8px',borderRadius:9,border:'none',cursor:'pointer',fontSize:12,fontFamily:'var(--fm)',
                  background:tab===t.id?'var(--cream)':'transparent',
                  color:tab===t.id?'var(--ink)':'var(--muted)',
                  boxShadow:tab===t.id?'0 1px 4px rgba(31,42,46,0.1)':'none',
                  transition:'all .15s'}}>
                {t.label}
              </button>
            ))}
          </div>

          {tab==='perfil' ? (
            <>
              <div style={{display:'flex',gap:18,alignItems:'center',marginBottom:14}}>
                <button onClick={()=>setFotoEdit(v=>!v)} title="Clica pra trocar a foto"
                  style={{position:'relative',border:'none',background:'transparent',padding:0,cursor:'pointer',flexShrink:0}}>
                  {form.foto_url ? (
                    <img src={form.foto_url} style={{width:52,height:52,borderRadius:'50%',objectFit:'cover',display:'block'}} onError={e=>e.target.style.display='none'}/>
                  ) : (
                    <Avatar user={{...user}} size="lg"/>
                  )}
                  <span style={{position:'absolute',bottom:-3,right:-3,width:20,height:20,borderRadius:'50%',background:'var(--ink)',color:'#fff',display:'grid',placeItems:'center',fontSize:10,border:'2px solid var(--surface)'}}>📷</span>
                </button>
                <div>
                  <div style={{fontFamily:'var(--fs)',fontSize:24,letterSpacing:'-0.5px',lineHeight:1.1}}>{user.nome}</div>
                  <div style={{fontSize:12,color:'var(--muted)',marginTop:3,fontStyle:'italic',fontFamily:'var(--fs)'}}>{user.funcao} · {user.empresa}</div>
                </div>
              </div>
              {fotoEdit && (
                <div style={{marginBottom:14,background:'var(--cream-2)',borderRadius:14,padding:'12px 14px'}}>
                  <PhotoUploader userId={user.id} currentUrl={form.foto_url} onUploaded={(url)=>{ setForm(p=>({...p,foto_url:url})); setFotoEdit(false); }}/>
                </div>
              )}

              {/* ── sub-abas do formulário ── */}
              <div style={{display:'flex',gap:2,marginBottom:16,background:'var(--cream-2)',borderRadius:12,padding:3}}>
                {[['dados','📇 Dados'],['endereco','🏠 Endereço'],['emergencia','🆘 Emergência'],['conta','⚙ Conta']].map(([id,rot])=>(
                  <button key={id} onClick={()=>setSub(id)}
                    style={{flex:1,border:'none',cursor:'pointer',padding:'8px 4px',borderRadius:10,fontSize:12,fontFamily:'var(--f)',
                      fontWeight:sub===id?700:400,background:sub===id?'var(--surface)':'transparent',
                      color:sub===id?'var(--teal)':'var(--muted)',boxShadow:sub===id?'var(--shadow-sm)':'none'}}>{rot}</button>
                ))}
              </div>

              {/* ═══ ABA 📇 DADOS ═══ */}
              {sub==='dados' && (
              <div style={{display:'grid',gap:12}}>
                <div>
                  <label className="label">Nome completo</label>
                  <input className="input" autoComplete="name" style={{width:'100%',boxSizing:'border-box'}} value={form.nome} onChange={e=>setForm(p=>({...p,nome:e.target.value}))}/>
                </div>

                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Função</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}} placeholder="OIM, DPO, Cozinheiro…" value={form.funcao} onChange={e=>setForm(p=>({...p,funcao:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">Empresa</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}} value={form.empresa} onChange={e=>setForm(p=>({...p,empresa:e.target.value}))}/>
                  </div>
                </div>

                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Gênero</label>
                    <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={form.genero} onChange={e=>setForm(p=>({...p,genero:e.target.value}))}>
                      <option value="">—</option>
                      <option value="M">Masculino</option>
                      <option value="F">Feminino</option>
                      <option value="O">Outro</option>
                    </select>
                  </div>
                  <div>
                    <label className="label">Nascimento</label>
                    <input className="input" type="date" max={new Date().toISOString().split('T')[0]} style={{width:'100%',boxSizing:'border-box'}}
                      value={form.data_nascimento} onChange={e=>setForm(p=>({...p,data_nascimento:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">Tipo sanguíneo 🩸</label>
                    <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={form.tipo_sanguineo} onChange={e=>setForm(p=>({...p,tipo_sanguineo:e.target.value}))}>
                      <option value="">—</option>
                      {['A+','A-','B+','B-','AB+','AB-','O+','O-'].map(t=><option key={t} value={t}>{t}</option>)}
                    </select>
                  </div>
                </div>

                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">CPF</label>
                    <input className="input" inputMode="numeric" style={{width:'100%',boxSizing:'border-box'}}
                      placeholder="00000000000"
                      maxLength={11}
                      value={form.cpf} onChange={e=>setForm(p=>({...p,cpf:e.target.value.replace(/\D/g,'')}))}/>
                    {form.cpf && form.cpf.length===11 && (
                      <div style={{fontSize:10.5,color:'var(--teal)',fontFamily:'var(--fm)',marginTop:3}}>
                        {form.cpf.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/,'$1.$2.$3-$4')}
                      </div>
                    )}
                    {form.cpf && form.cpf.length>0 && form.cpf.length!==11 && (
                      <div style={{fontSize:10.5,color:'var(--accent-2)',fontFamily:'var(--fm)',marginTop:3}}>
                        CPF deve ter 11 dígitos
                      </div>
                    )}
                  </div>
                  <div>
                    <label className="label">Passaporte</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                      placeholder="Ex: AB123456"
                      value={form.passaporte} onChange={e=>setForm(p=>({...p,passaporte:e.target.value.toUpperCase()}))}/>
                  </div>
                </div>
                {!form.cpf && !form.passaporte && (
                  <div style={{fontSize:11.5,color:'var(--accent-2)',fontFamily:'var(--fm)'}}>
                    ⚠️ Pelo menos CPF ou Passaporte é obrigatório
                  </div>
                )}

                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Nacionalidade</label>
                    <input className="input" list="dl-nacionalidades" style={{width:'100%',boxSizing:'border-box'}}
                      value={form.nacionalidade} onChange={e=>setForm(p=>({...p,nacionalidade:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">Telefone / Satphone</label>
                    <input className="input" type="tel" inputMode="tel" autoComplete="tel" style={{width:'100%',boxSizing:'border-box'}} placeholder="+55 21 99999-9999"
                      value={form.telefone} onChange={e=>setForm(p=>({...p,telefone:e.target.value}))}/>
                  </div>
                </div>

                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Cidade de nascimento</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                      value={form.cidade_nascimento} onChange={e=>setForm(p=>({...p,cidade_nascimento:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">Estado / província</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                      value={form.estado_nascimento} onChange={e=>setForm(p=>({...p,estado_nascimento:e.target.value}))}/>
                  </div>
                </div>

                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Turno</label>
                    <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={form.turno} onChange={e=>setForm(p=>({...p,turno:e.target.value}))}>
                      <option value="">—</option>
                      <option value="00:00-12:00">00:00–12:00</option>
                      <option value="06:00-18:00">06:00–18:00</option>
                      <option value="07:00-19:00">07:00–19:00</option>
                      <option value="12:00-00:00">12:00–00:00</option>
                      <option value="18:00-06:00">18:00–06:00</option>
                      <option value="19:00-07:00">19:00–07:00</option>
                    </select>
                  </div>
                  <div>
                    <label className="label">Macacão (EPI)</label>
                    <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={form.epi_macacao} onChange={e=>setForm(p=>({...p,epi_macacao:e.target.value}))}>
                      <option value="">—</option>
                      {['PP','P','M','G','GG','XG','XXG'].map(t=><option key={t} value={t}>{t}</option>)}
                    </select>
                  </div>
                  <div>
                    <label className="label">Calçado (EPI)</label>
                    <input className="input" inputMode="numeric" maxLength={2} style={{width:'100%',boxSizing:'border-box'}} placeholder="42"
                      value={form.epi_calcado} onChange={e=>setForm(p=>({...p,epi_calcado:e.target.value.replace(/\D/g,'')}))}/>
                  </div>
                </div>

                <div>
                  <label className="label">Sobre mim (bio do perfil)</label>
                  <textarea className="input" maxLength={400} style={{width:'100%',boxSizing:'border-box',minHeight:56,resize:'vertical'}}
                    placeholder="Apaixonado por operações, segurança e pessoas…"
                    value={form.bio} onChange={e=>setForm(p=>({...p,bio:e.target.value}))}/>
                </div>
                <datalist id="dl-nacionalidades">
                  {['Brasil','França','Noruega','Filipinas','Portugal','Índia','Reino Unido','Estados Unidos','Angola','Argentina'].map(p=><option key={p} value={p}/>)}
                </datalist>
              </div>
              )}

              {/* ═══ ABA 🏠 ENDEREÇO ═══ */}
              {sub==='endereco' && (
              <div style={{display:'grid',gap:12}}>
                <div>
                  <label className="label">Rua / Av.</label>
                  <input className="input" autoComplete="street-address" style={{width:'100%',boxSizing:'border-box'}}
                    value={form.end_logradouro} onChange={e=>setForm(p=>({...p,end_logradouro:e.target.value}))}/>
                </div>
                <div style={{display:'grid',gridTemplateColumns:'1fr 2fr',gap:10}}>
                  <div>
                    <label className="label">Número</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                      value={form.end_numero} onChange={e=>setForm(p=>({...p,end_numero:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">Complemento</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}} placeholder="Apto, bloco, sala…"
                      value={form.end_complemento} onChange={e=>setForm(p=>({...p,end_complemento:e.target.value}))}/>
                  </div>
                </div>
                <div style={{display:'grid',gridTemplateColumns:'2fr 1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Cidade</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                      value={form.end_cidade} onChange={e=>setForm(p=>({...p,end_cidade:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">Estado</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                      value={form.end_estado} onChange={e=>setForm(p=>({...p,end_estado:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">CEP</label>
                    <input className="input" inputMode="numeric" maxLength={9} style={{width:'100%',boxSizing:'border-box'}} placeholder="00000-000"
                      value={form.end_cep} onChange={e=>{
                        const d = e.target.value.replace(/\D/g,'').slice(0,8);
                        setForm(p=>({...p,end_cep: d.length>5 ? d.slice(0,5)+'-'+d.slice(5) : d}));
                      }}/>
                  </div>
                </div>
                <div>
                  <label className="label">País</label>
                  <input className="input" list="dl-paises" style={{width:'100%',boxSizing:'border-box'}} placeholder="Brasil"
                    value={form.end_pais} onChange={e=>setForm(p=>({...p,end_pais:e.target.value}))}/>
                </div>
                <datalist id="dl-paises">
                  {['Brasil','França','Noruega','Filipinas','Portugal','Índia','Reino Unido','Estados Unidos','Angola','Argentina'].map(p=><option key={p} value={p}/>)}
                </datalist>
              </div>
              )}

              {/* ═══ ABA 🆘 EMERGÊNCIA ═══ */}
              {sub==='emergencia' && <ContatosEmergencia userId={user.id} toast={toast}/>}

              {/* ═══ ABA ⚙ CONTA ═══ */}
              {sub==='conta' && (
              <div style={{display:'grid',gap:12}}>
                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Papel {!euAdmin && <span style={{color:'var(--sun)'}}>🔒</span>}</label>
                    <select className="input select" disabled={!euAdmin} title={!euAdmin ? 'Somente a conta administradora altera o papel' : ''} style={{width:'100%',boxSizing:'border-box',opacity:!euAdmin?0.55:1,cursor:!euAdmin?'not-allowed':'pointer'}} value={form.role} onChange={e=>setForm(p=>({...p,role:e.target.value}))}>
                      <option value="crew">Tripulação</option>
                      <option value="ops">Operações</option>
                      <option value="catering">Catering</option>
                      <option value="terceirizada">Terceirizada</option>
                      <option value="guest">Visitante</option>
                    </select>
                  </div>
                  <div>
                    <label className="label">E-mail</label>
                    <input className="input" type="email" autoComplete="email" style={{width:'100%',boxSizing:'border-box'}} placeholder="email@empresa.com" value={form.email} onChange={e=>setForm(f=>({...f,email:e.target.value}))}/>
                  </div>
                </div>

                {(form.role === 'crew' || form.role === 'ops') && (
                  <div style={{
                    display:'flex',alignItems:'center',justifyContent:'space-between',
                    padding:'12px 14px',background:'var(--cream-2)',borderRadius:14,
                    border:'1px solid var(--line-soft)'
                  }}>
                    <div>
                      <div style={{fontSize:13.5,fontWeight:500}}>Acesso à gestão {!euAdmin && '🔒'}</div>
                      <div style={{fontSize:11,color:!euAdmin?'var(--sun)':'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>
                        {!euAdmin ? 'Somente a conta administradora pode alterar papel e acesso à gestão' : 'Permite visualizar e operar gangway, cabines e manifesto'}
                      </div>
                    </div>
                    <button disabled={!euAdmin} onClick={()=>{ if (!euAdmin) { toast.push({icon:'lock', title:'Somente a conta administradora altera isso'}); return; } setForm(p=>({...p,is_staff:!p.is_staff})); }}
                      style={{
                        width:44,height:24,borderRadius:12,border:'none',
                        background:form.is_staff?'var(--teal)':'var(--line)',
                        position:'relative',transition:'background .2s',flexShrink:0,
                        opacity:!euAdmin?0.45:1,cursor:!euAdmin?'not-allowed':'pointer'
                      }}>
                      <div style={{
                        position:'absolute',top:2,
                        left:form.is_staff?'calc(100% - 22px)':2,
                        width:20,height:20,borderRadius:'50%',
                        background:'white',transition:'left .2s'
                      }}/>
                    </button>
                  </div>
                )}

                <div>
                  <label className="label">Status a bordo</label>
                  <div style={{display:'flex',gap:8}}>
                    {['em_terra','a_bordo'].map(s=>(
                      <button key={s} onClick={()=>handleStatus(s)} disabled={changingStatus||statusBordo===s}
                        style={{flex:1,padding:'10px',borderRadius:12,border:'1px solid var(--line)',cursor:statusBordo===s?'default':'pointer',transition:'all .2s',
                          background:statusBordo===s?(s==='a_bordo'?'var(--leaf-soft)':'var(--accent-soft)'):'var(--cream-2)',
                          color:statusBordo===s?(s==='a_bordo'?'var(--leaf)':'var(--accent-2)'):'var(--muted)',
                          fontFamily:'var(--f)',fontWeight:statusBordo===s?600:400,fontSize:13}}>
                        {statusBordo===s?'● ':''}{s==='a_bordo'?'A bordo':'Em terra'}
                      </button>
                    ))}
                  </div>
                  {changingStatus && <div style={{fontSize:11,color:'var(--muted)',marginTop:5,fontFamily:'var(--fm)'}}>atualizando…</div>}
                </div>

                <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
                  <div><label className="label">GEN-ID</label><div style={{fontSize:13,fontFamily:'var(--fm)',padding:'10px 0'}}>{user.genesis_id||'—'}</div></div>
                  <div><label className="label">Cabine atual</label><div style={{fontSize:13,color:'var(--muted)',fontFamily:'var(--fm)',padding:'10px 0'}}>{user.cabine_atual||'—'}</div></div>
                </div>

                {euManage && <PermPessoaSecao userId={user.id} token={()=>localStorage.getItem('genesis_token')} toast={toast}/>}
              </div>
              )}
            </>
          ) : tab==='senha' ? (
            /* Tab senha */
            <div style={{display:'grid',gap:14}}>
              <div style={{padding:'14px 16px',background:'var(--sky-soft)',borderRadius:14,fontSize:13,color:'var(--teal)',lineHeight:1.5}}>
                Como admin, você pode redefinir a senha desta pessoa. Ela precisará usar a nova senha no próximo login.
              </div>
              <div>
                <label className="label">Nova senha</label>
                <input className="input" type="password" style={{width:'100%',boxSizing:'border-box'}}
                  placeholder="mínimo 6 caracteres"
                  value={senhaNova} onChange={e=>setSenhaNova(e.target.value)}/>
              </div>
              <div>
                <label className="label">Confirmar nova senha</label>
                <input className="input" type="password" style={{width:'100%',boxSizing:'border-box',
                  borderColor:senhaConfirma.length>0?(senhaNova===senhaConfirma?'var(--leaf)':'var(--accent-2)'):'var(--line)'}}
                  placeholder="repita a senha"
                  value={senhaConfirma} onChange={e=>setSenhaConfirma(e.target.value)}/>
                {senhaConfirma.length>0&&senhaNova!==senhaConfirma&&(
                  <div style={{fontSize:11,color:'var(--accent-2)',fontFamily:'var(--fm)',marginTop:4}}>senhas não coincidem</div>
                )}
              </div>
              <button className="btn btn-primary" style={{padding:'12px'}}
                onClick={handleSavePassword} disabled={savingPass||senhaNova.length<6||senhaNova!==senhaConfirma}>
                {savingPass?'Salvando…':'Redefinir senha'}
              </button>
            </div>
          ) : (
            /* Tab histórico */
            <div>
              <div className="t-eyebrow" style={{marginBottom:14}}>Passagens pela gangway</div>
              {loadingHistory ? (
                <div style={{display:'grid',gap:8}}>
                  {[1,2,3].map(i=>(
                    <div key={i} style={{height:52,borderRadius:12,background:'linear-gradient(90deg,var(--cream-2) 25%,var(--cream-3) 50%,var(--cream-2) 75%)',backgroundSize:'200% 100%',animation:'shimmer 1.5s infinite'}}/>
                  ))}
                </div>
              ) : !history || history.length === 0 ? (
                <div style={{padding:'40px 20px',textAlign:'center'}}>
                  <div style={{fontSize:32,marginBottom:10,opacity:0.4}}>⚓</div>
                  <div style={{fontSize:13,color:'var(--muted)',fontStyle:'italic',fontFamily:'var(--fs)'}}>
                    nenhuma passagem registrada ainda.
                  </div>
                </div>
              ) : (
                <div style={{display:'grid',gap:8}}>
                  {history.map((h,i)=>{
                    const isOut = h.tipo==='desembarque';
                    const dt = h.created_at ? new Date(h.created_at) : null;
                    const origemLabel = {gangway:'Gangway', scan:'QR Code', bulk:'Em massa', import:'Importação', fila:'Fila'};
                    return (
                      <div key={h.id||i} style={{
                        display:'flex',alignItems:'center',gap:12,padding:'12px 14px',
                        background:'var(--cream-2)',borderRadius:14
                      }}>
                        <div style={{width:32,height:32,borderRadius:9,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={15}/>
                        </div>
                        <div style={{flex:1,minWidth:0}}>
                          <div style={{fontSize:13,fontWeight:500}}>{isOut?'Desembarque':'Embarque'}</div>
                          <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:1}}>
                            {origemLabel[h.origem] || h.origem || 'gangway'}
                          </div>
                        </div>
                        <div style={{textAlign:'right',fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>
                          <div>{dt?dt.toLocaleDateString('pt-BR'):'—'}</div>
                          <div>{dt?dt.toLocaleTimeString('pt-BR',{hour:'2-digit',minute:'2-digit'}):''}</div>
                        </div>
                      </div>
                    );
                  })}
                </div>
              )}
            </div>
          )}
        </div>

        {/* Botões fixos no fundo */}
        {tab==='perfil' && (
          <div style={{padding:'16px 32px 28px',borderTop:'1px solid var(--line-soft)',background:'var(--cream)',display:'flex',gap:10,flexShrink:0,alignItems:'stretch'}}>
            <button className="btn btn-primary" style={{flex:1,padding:'15px',fontSize:15,fontWeight:600}} onClick={handleSave} disabled={saving}>
              {saving?'Salvando…':'💾 Salvar alterações'}
            </button>
            <button className="btn" onClick={async()=>{ await loadCrachaModule(); window.printCracha(user); }} title="Ver crachá" style={{display:'flex',alignItems:'center',gap:6}}>
              <Icon n="upload" size={14}/>Crachá
            </button>
            <button className="btn" onClick={()=>{
              if(window.confirm(`Remover ${user.nome} permanentemente?`)) onDelete();
            }} title="Excluir pessoa"
              style={{color:'#C0392B',borderColor:'#F0C4BC',background:'#FDECEA',display:'flex',alignItems:'center',gap:6,fontWeight:600}}>
              🗑 Excluir
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

const NewPersonDrawer = ({onClose, onSaved}) => {
  const toast = useToast();
  const isMobile = useMobile();
  const [form, setForm] = useState({
    nome:'', email:'', funcao:'', empresa:'', role:'crew',
    cpf:'', passaporte:'', data_nascimento:'', turno:''
  });
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState('');

  const isGuest = form.role === 'guest';
  const swipe = useSwipeToClose(onClose, 'right');

  const handleSave = async () => {
    if (!form.nome) { setError('Nome é obrigatório.'); return; }
    if (!form.cpf && !form.passaporte) { setError('CPF ou Passaporte é obrigatório.'); return; }
    if (form.cpf && form.cpf.length !== 11) { setError('CPF deve ter exatamente 11 dígitos numéricos.'); return; }
    setSaving(true); setError('');
    try {
      const token = localStorage.getItem('genesis_token');
      // Email é opcional para todos — gera temporário se vazio
      const payload = {
        ...form,
        email: form.email || `user-${Date.now()}@genesis.temp`,
      };
      const res = await fetch('/api/users', {
        method: 'POST',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token}` },
        body: JSON.stringify(payload)
      });
      const data = await res.json();
      if (res.ok) {
        toast.push({
          icon:'check',
          title:`${form.nome} adicionado`,
          body: data.genesis_id ? `GEN-ID: ${data.genesis_id} · senha: ${data.genesis_id}` : 'cadastrado com sucesso'
        });
        onSaved();
      } else {
        setError(data.error || 'Erro ao criar pessoa.');
      }
    } catch(e) {
      setError('Erro de conexão.');
    } finally {
      setSaving(false);
    }
  };

  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(31,42,46,0.4)',backdropFilter:'blur(6px)',zIndex:8000,display:'flex',alignItems:'flex-start',justifyContent:'center',padding:'5vh 16px',overflowY:'auto'}}>
      <div onClick={e=>e.stopPropagation()}
        style={{width:'100%',maxWidth:isMobile?'100%':560,maxHeight:'90vh',background:'var(--cream)',borderRadius:isMobile?18:22,display:'flex',flexDirection:'column',animation:'slideIn .25s cubic-bezier(.2,.7,.3,1) both',boxShadow:'var(--shadow-lg)',border:'1px solid var(--line-soft)',overflow:'hidden'}}>
        {/* Conteúdo rolável */}
        <div style={{flex:1,overflowY:'auto',padding:'30px 32px 24px'}}>
          <div style={{display:'flex',justifyContent:'space-between',marginBottom:20}}>
            <div className="t-eyebrow">Nova pessoa</div>
            <button className="btn ghost" onClick={onClose}><Icon n="x" size={14}/></button>
          </div>
          <div style={{fontFamily:'var(--fs)',fontSize:26,letterSpacing:'-0.4px',marginBottom:22}}>
            Adicionar <em style={{fontStyle:'italic',color:'var(--accent)'}}>
              {isGuest ? 'visitante.' : 'tripulante.'}
            </em>
          </div>

          <div style={{display:'grid',gap:13}}>
            {/* Papel — primeiro para adaptar o formulário */}
            <div>
              <label className="label">Papel *</label>
              <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={form.role} onChange={e=>setForm(f=>({...f,role:e.target.value}))}>
                <option value="crew">Tripulação</option>
                <option value="ops">Operações</option>
                <option value="catering">Catering</option>
                <option value="terceirizada">Terceirizada</option>
                <option value="guest">Visitante</option>
              </select>
            </div>

            <div>
              <label className="label">Nome completo *</label>
              <input className="input" style={{width:'100%',boxSizing:'border-box'}} placeholder="Nome Sobrenome" value={form.nome} onChange={e=>setForm(f=>({...f,nome:e.target.value}))}/>
            </div>

            {/* Email — opcional para todos */}
            <div>
              <label className="label">E-mail (opcional)</label>
              <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                placeholder="email@empresa.com — deixe vazio se não tiver"
                type="email" value={form.email} onChange={e=>setForm(f=>({...f,email:e.target.value}))}/>
            </div>

            <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
              <div>
                <label className="label">Função</label>
                <input className="input" style={{width:'100%',boxSizing:'border-box'}} placeholder="Ex: Engenheiro" value={form.funcao} onChange={e=>setForm(f=>({...f,funcao:e.target.value}))}/>
              </div>
              <div>
                <label className="label">Empresa</label>
                <input className="input" style={{width:'100%',boxSizing:'border-box'}} placeholder="Ex: Logística Marítima SA" value={form.empresa} onChange={e=>setForm(f=>({...f,empresa:e.target.value}))}/>
              </div>
            </div>

            {/* CPF + Passaporte separados */}
            <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:10}}>
              <div>
                <label className="label">CPF</label>
                <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                  placeholder="00000000000" maxLength={11}
                  value={form.cpf} onChange={e=>setForm(f=>({...f,cpf:e.target.value.replace(/\D/g,'')}))}/>
                {form.cpf.length===11 && (
                  <div style={{fontSize:10,color:'var(--teal)',fontFamily:'var(--fm)',marginTop:3}}>
                    {form.cpf.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/,'$1.$2.$3-$4')}
                  </div>
                )}
              </div>
              <div>
                <label className="label">Passaporte</label>
                <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                  placeholder="Ex: AB123456"
                  value={form.passaporte} onChange={e=>setForm(f=>({...f,passaporte:e.target.value.toUpperCase()}))}/>
              </div>
            </div>
            {!form.cpf && !form.passaporte && (
              <div style={{fontSize:11,color:'var(--accent-2)',fontFamily:'var(--fm)'}}>
                ⚠️ Pelo menos um é obrigatório
              </div>
            )}

            {/* Data de nascimento */}
            <div>
              <label className="label">Data de nascimento</label>
              <input className="input" style={{width:'100%',boxSizing:'border-box'}}
                type="date" value={form.data_nascimento}
                onChange={e=>setForm(f=>({...f,data_nascimento:e.target.value}))}/>
              <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:4}}>
                usada como senha provisória (DDMMAAAA)
              </div>
            </div>

            {/* Turno — só para não-visitantes */}
            {!isGuest && (
              <div>
                <label className="label">Turno</label>
                <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={form.turno} onChange={e=>setForm(f=>({...f,turno:e.target.value}))}>
                  <option value="">— não definido —</option>
                  <option value="00:00-12:00">00:00 às 12:00</option>
                  <option value="06:00-18:00">06:00 às 18:00</option>
                  <option value="07:00-19:00">07:00 às 19:00</option>
                  <option value="12:00-00:00">12:00 às 00:00</option>
                  <option value="18:00-06:00">18:00 às 06:00</option>
                  <option value="19:00-07:00">19:00 às 07:00</option>
                </select>
              </div>
            )}
          </div>

          {error && (
            <div style={{marginTop:14,padding:'10px 14px',background:'rgba(217,105,75,0.08)',border:'1px solid rgba(217,105,75,0.2)',borderRadius:10,fontSize:13,color:'var(--accent-2)'}}>
              {error}
            </div>
          )}

          <div style={{marginTop:16,padding:'12px 14px',background:'var(--sky-soft)',borderRadius:14,fontSize:12,color:'var(--teal)',lineHeight:1.6}}>
            {form.data_nascimento
              ? `Senha provisória: ${form.data_nascimento.split('-').reverse().join('')}`
              : isGuest
                ? 'Senha provisória: genesis2026'
                : 'Informe a data de nascimento para gerar a senha provisória.'}
          </div>
        </div>

        {/* Botões fixos */}
        <div style={{padding:'16px 32px 28px',borderTop:'1px solid var(--line-soft)',display:'flex',gap:10,flexShrink:0}}>
          <button className="btn btn-primary" style={{flex:1,padding:'12px'}} onClick={handleSave} disabled={saving}>
            {saving ? 'Criando…' : isGuest ? 'Cadastrar visitante' : 'Criar tripulante'}
          </button>
          <button className="btn" onClick={onClose}>Cancelar</button>
        </div>
      </div>
    </div>
  );
};

// ─── Lazy loader do módulo de crachá ───
// cracha.jsx agora é carregado no index.html; aqui só garantimos JSZip para exportação em ZIP
let crachaLoaded = false;
const loadCrachaModule = () => new Promise((resolve) => {
  if (window.printCracha && window.exportCrachasZip) { resolve(); return; }
  const loadJSZip = () => new Promise(r => {
    if (window.JSZip) { r(); return; }
    const s = document.createElement('script');
    s.src = 'https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js';
    s.onload = r;
    s.onerror = () => r();
    document.head.appendChild(s);
  });
  loadJSZip().then(() => {
    const wait = setInterval(() => {
      if (window.printCracha && window.exportCrachasZip) { clearInterval(wait); resolve(); }
    }, 50);
    setTimeout(() => { clearInterval(wait); resolve(); }, 2000);
  });
});
const PhotoUploader = ({userId, currentUrl, onUploaded}) => {
  const toast = useToast();
  const [cropSrc, setCropSrc] = useState(null);
  const [uploading, setUploading] = useState(false);
  const cropperRef = useRef(null);
  const cropperInstance = useRef(null);
  const inputRef = useRef(null);

  // Carrega Cropper.js dinamicamente se necessário
  const loadCropper = () => new Promise((resolve) => {
    if (window.Cropper) { resolve(); return; }
    const link = document.createElement('link');
    link.rel = 'stylesheet';
    link.href = 'https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.css';
    document.head.appendChild(link);
    const script = document.createElement('script');
    script.src = 'https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.6.2/cropper.min.js';
    script.onload = resolve;
    document.head.appendChild(script);
  });

  const handleFileChange = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    await loadCropper();
    const reader = new FileReader();
    reader.onload = (ev) => setCropSrc(ev.target.result);
    reader.readAsDataURL(file);
    e.target.value = '';
  };

  // Inicia cropper quando modal abre
  useEffect(() => {
    if (!cropSrc || !cropperRef.current) return;
    setTimeout(() => {
      if (cropperInstance.current) cropperInstance.current.destroy();
      cropperInstance.current = new window.Cropper(cropperRef.current, {
        aspectRatio: 1,
        viewMode: 1,
        dragMode: 'move',
        autoCropArea: 0.9,
        restore: false,
        guides: true,
        center: true,
        highlight: false,
        cropBoxMovable: true,
        cropBoxResizable: true,
        toggleDragModeOnDblclick: false,
      });
    }, 100);
    return () => { if (cropperInstance.current) cropperInstance.current.destroy(); };
  }, [cropSrc]);

  const handleConfirm = async () => {
    if (!cropperInstance.current) return;
    setUploading(true);
    try {
      // Gera canvas 400x400
      const canvas = cropperInstance.current.getCroppedCanvas({ width: 400, height: 400 });
      const blob = await new Promise(r => canvas.toBlob(r, 'image/jpeg', 0.92));
      const formData = new FormData();
      formData.append('photo', blob, `photo_${userId}.jpg`);
      const token = localStorage.getItem('genesis_token');
      const res = await fetch('/api/upload/photo', {
        method: 'POST',
        headers: { 'Authorization': `Bearer ${token}` },
        body: formData
      });
      const data = await res.json();
      if (res.ok) {
        onUploaded(data.url);
        setCropSrc(null);
        toast.push({icon:'check', title:'Foto atualizada!'});
      } else {
        toast.push({icon:'x', title:'Erro no upload', body: data.error});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    } finally {
      setUploading(false);
    }
  };

  return (
    <>
      <div style={{display:'flex',alignItems:'center',gap:14,marginTop:4}}>
        {currentUrl ? (
          <img src={currentUrl} style={{width:56,height:56,borderRadius:'50%',objectFit:'cover',border:'2px solid var(--line-soft)',flexShrink:0}}
            onError={e=>e.target.style.display='none'}/>
        ) : (
          <div style={{width:56,height:56,borderRadius:'50%',background:'var(--cream-2)',border:'1px dashed var(--line)',display:'grid',placeItems:'center',color:'var(--muted-2)',flexShrink:0}}>
            <Icon n="users" size={22}/>
          </div>
        )}
        <div style={{flex:1}}>
          <button className="btn" style={{width:'100%'}} onClick={()=>inputRef.current?.click()}>
            <Icon n="upload" size={14}/>
            {currentUrl ? 'Trocar foto' : 'Escolher foto'}
          </button>
          {currentUrl && (
            <div style={{fontSize:10.5,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:5,textAlign:'center'}}>
              foto atual · clique para trocar
            </div>
          )}
        </div>
        <input ref={inputRef} type="file" accept="image/jpeg,image/png,image/webp"
          onChange={handleFileChange} style={{display:'none'}}/>
      </div>

      {/* Modal do cropper */}
      {cropSrc && (
        <div style={{
          position:'fixed', inset:0, background:'rgba(31,42,46,0.85)',
          zIndex:9000, display:'flex', flexDirection:'column',
          alignItems:'center', justifyContent:'center', padding:20
        }}>
          <div style={{
            background:'var(--cream)', borderRadius:20, overflow:'hidden',
            width:'min(520px, 95vw)', maxHeight:'90vh',
            display:'flex', flexDirection:'column',
            boxShadow:'0 32px 80px rgba(0,0,0,0.5)'
          }}>
            {/* Header */}
            <div style={{padding:'18px 22px',borderBottom:'1px solid var(--line-soft)',display:'flex',alignItems:'center',justifyContent:'space-between'}}>
              <div>
                <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.3px'}}>
                  Ajustar <em style={{fontStyle:'italic',color:'var(--accent)'}}>foto.</em>
                </div>
                <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>
                  arraste para reposicionar · 1:1 · 400×400px
                </div>
              </div>
              <button className="btn ghost" onClick={()=>setCropSrc(null)}><Icon n="x" size={14}/></button>
            </div>

            {/* Área do cropper */}
            <div style={{flex:1,overflow:'hidden',maxHeight:400,background:'#1a1a1a'}}>
              <img ref={cropperRef} src={cropSrc} style={{maxWidth:'100%',display:'block'}}/>
            </div>

            {/* Botões */}
            <div style={{padding:'16px 22px',display:'flex',gap:10}}>
              <button className="btn" style={{flex:1}} onClick={()=>setCropSrc(null)}>
                Cancelar
              </button>
              <button className="btn btn-primary" style={{flex:2,padding:'12px'}}
                onClick={handleConfirm} disabled={uploading}>
                {uploading ? 'Enviando…' : '✓ Confirmar e salvar'}
              </button>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

const Field2 = ({label, value}) => (
  <div>
    <div style={{fontFamily:'var(--fm)',fontSize:10.5,letterSpacing:1,textTransform:'uppercase',color:'var(--muted)',marginBottom:6}}>{label}</div>
    <div style={{padding:'11px 14px',background:'var(--cream-2)',borderRadius:14,fontSize:13.5}}>{value||'—'}</div>
  </div>
);

// ─── CINEMA ───
// ─── CINEMA (integrado ao Jellyfin) ───
const cinAuthH = () => ({ 'Authorization': `Bearer ${localStorage.getItem('genesis_token')}`, 'Content-Type': 'application/json' });
const cinCapa = (id, w=400) => `/api/cinema/capa/${id}?w=${w}`;
const cinFilmColor = (s='') => { const c=['#B14E36','#4E7A57','#7FA8C9','#2E3A3F','#C68A1F','#5F7A8C']; let h=0; for(const ch of String(s)) h=(h*31+ch.charCodeAt(0))>>>0; return c[h%c.length]; };

// Modal: criar nova votação do Clube do Filme (admin escolhe filmes da biblioteca)
const CinemaVotacaoModal = ({onClose, onCreated}) => {
  const toast = useToast();
  const [busca, setBusca] = useState('');
  const [itens, setItens] = useState([]);
  const [sel, setSel] = useState([]);   // [{jellyfin_id, nome, ano}]
  const [titulo, setTitulo] = useState('Clube do Filme');
  const [loading, setLoading] = useState(true);
  useEffect(() => {
    fetch('/api/cinema/biblioteca?tipo=Movie&limit=120', {headers:cinAuthH()})
      .then(r=>r.json()).then(d=>{ setItens(d.itens||[]); setLoading(false); });
  }, []);
  const toggle = (it) => setSel(s => s.find(x=>x.jellyfin_id===it.id)
    ? s.filter(x=>x.jellyfin_id!==it.id)
    : s.length>=6 ? s : [...s, {jellyfin_id:it.id, nome:it.nome, ano:it.ano}]);
  const filtrados = itens.filter(i => (i.nome||'').toLowerCase().includes(busca.toLowerCase()));
  const criar = async () => {
    if (sel.length < 2) { toast.push({icon:'x', title:'Escolha pelo menos 2 filmes'}); return; }
    const r = await fetch('/api/cinema/votacao', {method:'POST', headers:cinAuthH(), body:JSON.stringify({titulo, opcoes:sel})}).then(r=>r.json());
    if (r.ok) { toast.push({icon:'check', title:'Votação criada'}); onCreated && onCreated(); onClose(); }
    else toast.push({icon:'x', title:r.error||'Erro'});
  };
  return (
    <div style={{position:'fixed',inset:0,zIndex:200,background:'rgba(31,42,46,0.55)',backdropFilter:'blur(3px)',display:'flex',alignItems:'flex-start',justifyContent:'center',padding:'5vh 16px',overflowY:'auto'}} onClick={onClose}>
      <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:560,maxHeight:'90vh',background:'var(--surface)',borderRadius:22,padding:'24px',display:'flex',flexDirection:'column',gap:14,overflow:'hidden'}}>
        <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.4px'}}>Nova <em style={{fontStyle:'italic',color:'var(--accent)'}}>votação.</em></div>
        <input value={titulo} onChange={e=>setTitulo(e.target.value)} placeholder="Título da votação" style={{padding:'11px 13px',borderRadius:11,border:'1px solid var(--line-soft)',fontFamily:'var(--fm)',fontSize:13}}/>
        <input value={busca} onChange={e=>setBusca(e.target.value)} placeholder="🔍 buscar filme…" style={{padding:'11px 13px',borderRadius:11,border:'1px solid var(--line-soft)',fontFamily:'var(--fm)',fontSize:13}}/>
        <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted-2)'}}>{sel.length} selecionado(s) · escolha de 2 a 6</div>
        <div style={{overflowY:'auto',display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(96px,1fr))',gap:8,flex:1}}>
          {loading ? <div style={{color:'var(--muted)',fontFamily:'var(--fm)',fontSize:12}}>carregando…</div> :
            filtrados.slice(0,60).map(it => {
              const on = sel.find(x=>x.jellyfin_id===it.id);
              return (
                <div key={it.id} onClick={()=>toggle(it)} style={{cursor:'pointer',borderRadius:10,overflow:'hidden',border:on?'2px solid var(--accent)':'2px solid transparent',position:'relative'}}>
                  <div style={{aspectRatio:'2/3',background: it.temCapa?`center/cover url(${cinCapa(it.id,200)})`:cinFilmColor(it.nome)}}/>
                  <div style={{fontSize:9.5,fontFamily:'var(--fm)',padding:'4px 5px',lineHeight:1.2,color:'var(--ink)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{it.nome}</div>
                  {on && <div style={{position:'absolute',top:4,right:4,background:'var(--accent)',color:'#fff',borderRadius:'50%',width:18,height:18,display:'grid',placeItems:'center',fontSize:11}}>✓</div>}
                </div>
              );
            })}
        </div>
        <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" onClick={criar}>Criar votação</button>
        </div>
      </div>
    </div>
  );
};

// Modal: editar o aviso do hero
const CinemaAvisoModal = ({aviso, onClose, onSaved}) => {
  const toast = useToast();
  const [ativo, setAtivo] = useState(aviso?.ativo || false);
  const [titulo, setTitulo] = useState(aviso?.titulo || '');
  const [subtitulo, setSubtitulo] = useState(aviso?.subtitulo || '');
  const salvar = async () => {
    const r = await fetch('/api/cinema/aviso', {method:'POST', headers:cinAuthH(), body:JSON.stringify({ativo, titulo, subtitulo, jellyfin_id:aviso?.jellyfin_id||null})}).then(r=>r.json());
    if (r.ok) { toast.push({icon:'check', title:'Aviso salvo'}); onSaved && onSaved(); onClose(); }
    else toast.push({icon:'x', title:r.error||'Erro'});
  };
  return (
    <div style={{position:'fixed',inset:0,zIndex:200,background:'rgba(31,42,46,0.55)',backdropFilter:'blur(3px)',display:'flex',alignItems:'center',justifyContent:'center',padding:'5vh 16px'}} onClick={onClose}>
      <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:440,background:'var(--surface)',borderRadius:22,padding:'24px',display:'flex',flexDirection:'column',gap:14}}>
        <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.4px'}}>Aviso do <em style={{fontStyle:'italic',color:'var(--accent)'}}>cinema.</em></div>
        <label style={{display:'flex',alignItems:'center',gap:10,fontFamily:'var(--fm)',fontSize:13,cursor:'pointer'}}>
          <input type="checkbox" checked={ativo} onChange={e=>setAtivo(e.target.checked)}/> Mostrar aviso no topo
        </label>
        <input value={titulo} onChange={e=>setTitulo(e.target.value)} placeholder="Título (ex.: Cine Genesis)" style={{padding:'11px 13px',borderRadius:11,border:'1px solid var(--line-soft)',fontFamily:'var(--fm)',fontSize:13}}/>
        <input value={subtitulo} onChange={e=>setSubtitulo(e.target.value)} placeholder="Subtítulo (ex.: sexta 21:00 · convés 7)" style={{padding:'11px 13px',borderRadius:11,border:'1px solid var(--line-soft)',fontFamily:'var(--fm)',fontSize:13}}/>
        <div style={{display:'flex',gap:8,justifyContent:'flex-end'}}>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" onClick={salvar}>Salvar</button>
        </div>
      </div>
    </div>
  );
};

// Card fixo "Teu acesso ao Cinema" (mostra login Jellyfin + instruções)
const CinemaAcessoCard = ({acesso}) => {
  if (!acesso || !acesso.temAcesso) return null;
  return (
    <div className="card" style={{padding:'16px 20px',marginBottom:24,border:'1px solid var(--line-soft)',display:'flex',gap:16,alignItems:'center',flexWrap:'wrap'}}>
      <div style={{fontSize:28}}>🎟️</div>
      <div style={{flex:1,minWidth:220}}>
        <div style={{fontFamily:'var(--fs)',fontSize:17,letterSpacing:'-0.3px'}}>Teu acesso ao <em style={{fontStyle:'italic',color:'var(--accent)'}}>Cinema</em></div>
        <div style={{fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)',marginTop:5,lineHeight:1.6}}>
          App <b>Jellyfin</b> · servidor <b style={{color:'var(--ink)'}}>{acesso.servidor}</b> · usuário <b style={{color:'var(--ink)'}}>{acesso.login}</b> · senha <b>em branco</b> (defina no 1º acesso).
        </div>
      </div>
    </div>
  );
};

// Modal de boas-vindas (1ª vez que o acesso é criado)
const CinemaWelcomeModal = ({acesso, onClose}) => (
  <div style={{position:'fixed',inset:0,zIndex:200,background:'rgba(31,42,46,0.55)',backdropFilter:'blur(3px)',display:'flex',alignItems:'center',justifyContent:'center',padding:'5vh 16px'}} onClick={onClose}>
    <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:440,background:'var(--surface)',borderRadius:22,padding:'28px 26px'}}>
      <div style={{fontSize:38,textAlign:'center',marginBottom:10}}>🎬</div>
      <h3 style={{fontFamily:'var(--fs)',fontSize:23,letterSpacing:'-0.5px',textAlign:'center',marginBottom:8}}>Teu acesso ao <em style={{fontStyle:'italic',color:'var(--accent)'}}>Cinema</em> está pronto!</h3>
      <p style={{fontFamily:'var(--fm)',fontSize:12.5,color:'var(--muted)',textAlign:'center',marginBottom:18,lineHeight:1.5}}>Criamos teu usuário no Jellyfin automaticamente. É só configurar o app:</p>
      <ol style={{fontFamily:'var(--fm)',fontSize:13,color:'var(--ink)',lineHeight:1.7,paddingLeft:20,marginBottom:20}}>
        <li>Baixe o app <b>Jellyfin</b> (App Store / Play Store).</li>
        <li>Adicione o servidor: <b style={{color:'var(--accent)'}}>{acesso?.servidor||'http://192.168.20.2:8096'}</b></li>
        <li>Entre com o usuário <b style={{color:'var(--accent)'}}>{acesso?.login||'—'}</b> e <b>senha em branco</b>.</li>
        <li>Defina uma senha sua no primeiro acesso.</li>
      </ol>
      <button onClick={onClose} className="btn btn-primary" style={{width:'100%',padding:'12px'}}>Entendi</button>
    </div>
  </div>
);

// (componente Cinema extraído para cinema.jsx — dono único do modo Netflix)

// ─── MÚSICA ───
const Musica = ({state, setState}) => {
  const toast = useToast();
  const usersById = Object.fromEntries(state.users.map(u=>[u.id,u]));
  const r = state.radioNow;
  return (
    <main style={{padding:'30px 36px 56px',flex:1}}>
      <section style={{background:'linear-gradient(155deg, var(--ink) 0%, var(--teal) 100%)',color:'var(--cream)',borderRadius:32,padding:'36px 40px',marginBottom:30,position:'relative',overflow:'hidden'}}>
        <div style={{position:'absolute',right:-60,top:-60,width:300,height:300,borderRadius:'50%',background:'rgba(232,180,83,0.2)',animation:'pulse 4s infinite'}}/>
        <div style={{display:'flex',alignItems:'center',gap:20,position:'relative'}}>
          <div style={{width:96,height:96,borderRadius:'50%',background:'var(--cream)',color:'var(--ink)',display:'grid',placeItems:'center',animation:'spin 8s linear infinite'}}><Icon n="radio" size={40}/></div>
          <style>{`@keyframes spin{from{transform:rotate(0)}to{transform:rotate(360deg)}}`}</style>
          <div style={{flex:1}}>
            <div className="t-eyebrow" style={{color:'var(--sun-soft)',marginBottom:8}}>Tocando agora · rádio do Genesis</div>
            <div style={{fontFamily:'var(--fs)',fontSize:42,letterSpacing:'-1px',lineHeight:1.05}}>
              <em style={{fontStyle:'italic',color:'var(--sun)'}}>{r.track}</em>
            </div>
            <div style={{fontSize:14,opacity:0.7,marginTop:6}}>{r.artist} · {r.title}</div>
            <div style={{display:'flex',alignItems:'center',gap:14,marginTop:18}}>
              <div style={{flex:1,height:4,background:'rgba(255,255,255,0.15)',borderRadius:2}}>
                <div style={{width:`${r.progress*100}%`,height:'100%',background:'var(--sun)',borderRadius:2,transition:'width 1s'}}/>
              </div>
              <Pill tone="sun" live>{r.listeners} ouvindo</Pill>
            </div>
          </div>
        </div>
      </section>

      <div style={{display:'grid',gridTemplateColumns:'1.4fr 1fr',gap:24}}>
        <div>
          <SectionH title="Playlists" em="da casa." meta={`${state.playlists.length} colaborativas`}/>
          <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(200px, 1fr))',gap:14}}>
            {state.playlists.map(p => {
              const cu = usersById[p.curator];
              return (
                <div key={p.id} className="card interactive" style={{padding:16,background:`linear-gradient(160deg, ${p.color}40, var(--cream))`}}>
                  <div style={{height:90,borderRadius:12,background:p.color,marginBottom:12,display:'grid',placeItems:'center',color:'rgba(255,255,255,0.6)'}}><Icon n="music" size={32}/></div>
                  <div style={{fontFamily:'var(--fs)',fontSize:18,letterSpacing:'-0.3px'}}>{p.title}</div>
                  <div style={{display:'flex',alignItems:'center',gap:6,marginTop:8}}>
                    <Avatar user={cu} size="sm"/>
                    <div style={{fontSize:11.5,color:'var(--muted)',fontFamily:'var(--fm)'}}>{p.tracks} faixas · {p.listeners} ouvindo</div>
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        <aside>
          <SectionH title="Mural do" em="jantar." meta="pedidos para hoje"/>
          <div style={{background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:22,padding:'18px 20px'}}>
            {state.dinnerRequests.map((d,i) => {
              const u = usersById[d.user];
              return (
                <div key={d.id} style={{display:'flex',alignItems:'center',gap:11,padding:'12px 0',borderTop:i?'1px solid var(--line-soft)':'none'}}>
                  <Avatar user={u} size="sm"/>
                  <div style={{flex:1,minWidth:0}}>
                    <div style={{fontSize:13.5,fontWeight:500,lineHeight:1.2,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{d.track}</div>
                    <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>{u?.nome} · {d.time}</div>
                  </div>
                  <button onClick={()=>toast.push({icon:'check',title:'+ 1 voto'})} className="btn btn-sm" style={{padding:'5px 11px'}}>♥ {d.votes}</button>
                </div>
              );
            })}
            <button className="btn btn-primary" style={{width:'100%',marginTop:14}}><Icon n="plus" size={14}/>Pedir música</button>
          </div>
        </aside>
      </div>
    </main>
  );
};

// ─── AVISOS ───
const CATEGORIAS = [
  {id:'all',         label:'Todos',         label_en:'All',          tone:'cream',    color:'var(--ink)'},
  {id:'emergencia',  label:'Emergência',    label_en:'Emergency',    tone:'coral',    color:'var(--accent-2)'},
  {id:'operacional', label:'Operacional',   label_en:'Operations',   tone:'sky',      color:'var(--teal)'},
  {id:'manutencao',  label:'Manutenção',    label_en:'Maintenance',  tone:'sun',      color:'#8B6313'},
  {id:'convivencia', label:'Convivência',   label_en:'Community',    tone:'leaf',     color:'#2F5C3D'},
  {id:'casa',        label:'Recados de casa', label_en:'From home',  tone:'rose',     color:'#9C3F4D'},
];

const catMeta = id => CATEGORIAS.find(c=>c.id===id) || CATEGORIAS[0];

const isExpired = (a, today) => {
  if (!a.expira) return false;
  return a.expira < today;
};

const Avisos = ({state, setState}) => {
  const t = useT();
  const toast = useToast();
  const [cat, setCat] = useState('all');
  const [composing, setComposing] = useState(false);
  const [avisos, setAvisos] = useState([]);
  const [loading, setLoading] = useState(true);
  const today = new Date().toISOString().split('T')[0];
  const can = state.currentUser?.role === 'admin' || state.currentUser?.is_staff === true || state.role === 'admin';

  const token = () => localStorage.getItem('genesis_token');

  const fetchAvisos = async () => {
    try {
      const res = await fetch('/api/announcements', {
        headers: { 'Authorization': `Bearer ${token()}` }
      });
      if (res.ok) setAvisos(await res.json());
    } catch(e) { console.error(e); }
    finally { setLoading(false); }
  };

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

  const handleDelete = async (id) => {
    try {
      const res = await fetch(`/api/announcements/${id}`, {
        method: 'DELETE',
        headers: { 'Authorization': `Bearer ${token()}` }
      });
      if (res.ok) {
        setAvisos(a => a.filter(x => x.id !== id));
        toast.push({icon:'check', title:'Aviso removido'});
      }
    } catch(e) { toast.push({icon:'x', title:'Erro ao remover'}); }
  };

  const visible = avisos.filter(a => cat === 'all' || a.categoria === cat);

  return (
    <main style={{padding:'30px 36px 56px',flex:1}}>
      <div style={{display:'flex',gap:10,flexWrap:'wrap',alignItems:'center',marginBottom:22,maxWidth:840,margin:'0 auto 22px'}}>
        {CATEGORIAS.map(c => {
          const count = c.id==='all' ? avisos.length : avisos.filter(a=>a.categoria===c.id).length;
          return (
            <button key={c.id} onClick={()=>setCat(c.id)}
              className={cat===c.id?'btn btn-primary':'btn'}
              style={cat===c.id?{}:{background:`var(--${c.tone}-soft, var(--cream-2))`}}>
              {t.lang==='pt'?c.label:c.label_en} <span style={{opacity:0.6,marginLeft:4,fontFamily:'var(--fm)',fontSize:11}}>{count}</span>
            </button>
          );
        })}
        <div style={{flex:1}}/>
        {can && <button className="btn btn-primary" onClick={()=>setComposing(true)}><Icon n="plus" size={14}/>{t('new_notice')}</button>}
      </div>

      {loading ? (
        <div style={{padding:60,textAlign:'center',color:'var(--muted)',fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18}}>carregando avisos…</div>
      ) : (
        <div style={{display:'flex',flexDirection:'column',gap:12,maxWidth:840,margin:'0 auto'}}>
          {visible.map(a => {
            const meta = catMeta(a.categoria);
            const dataFmt = a.created_at ? new Date(a.created_at).toLocaleString('pt-BR',{
              year:'numeric',month:'2-digit',day:'2-digit',hour:'2-digit',minute:'2-digit'
            }).replace(',','') : '';
            const expiraFmt = a.expires_at ? new Date(a.expires_at).toISOString().split('T')[0] : null;
            return (
              <article key={a.id} style={{
                background:'var(--cream)',
                border:'1px solid var(--line-soft)',
                borderRadius:22, padding:'22px 26px',
                transition:'box-shadow .2s'
              }}
                onMouseEnter={e=>e.currentTarget.style.boxShadow='0 8px 32px -16px rgba(31,42,46,0.12)'}
                onMouseLeave={e=>e.currentTarget.style.boxShadow=''}
              >
                {/* Linha de meta: categoria · autor · setor · data · expira */}
                <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:10,flexWrap:'wrap'}}>
                  {/* Pill de categoria */}
                  <span style={{
                    fontFamily:'var(--fm)',fontSize:10,letterSpacing:0.8,textTransform:'uppercase',
                    padding:'3px 10px',borderRadius:20,fontWeight:500,
                    background:`var(--${meta.tone}-soft, var(--cream-2))`,
                    color: meta.color || `var(--${meta.tone})`
                  }}>
                    {t.lang==='pt'?meta.label:meta.label_en}
                  </span>
                  {/* Autor · Setor */}
                  {a.from_name && (
                    <span style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',letterSpacing:0.5,textTransform:'uppercase'}}>
                      {a.from_name}
                    </span>
                  )}
                  {a.from_name && <span style={{color:'var(--muted-2)',fontSize:11}}>·</span>}
                  {a.setor && (
                    <span style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',letterSpacing:0.5,textTransform:'uppercase'}}>
                      {a.setor}
                    </span>
                  )}
                  <div style={{flex:1}}/>
                  {/* Data */}
                  <span style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>{dataFmt}</span>
                  {expiraFmt && (
                    <>
                      <span style={{color:'var(--muted-2)',fontSize:11}}>·</span>
                      <span style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>expira em {expiraFmt}</span>
                    </>
                  )}
                  {can && (
                    <button onClick={()=>handleDelete(a.id)} style={{background:'none',border:'none',cursor:'pointer',color:'var(--muted-2)',padding:'2px 4px',lineHeight:1}}>
                      <Icon n="x" size={12}/>
                    </button>
                  )}
                </div>
                {/* Título grande */}
                <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.4px',lineHeight:1.3,color:'var(--ink)',marginBottom:a.conteudo?8:0}}>
                  {a.titulo}
                </div>
                {/* Conteúdo */}
                {a.conteudo && (
                  <div style={{fontSize:14,color:'var(--ink-2)',lineHeight:1.65}}>
                    {t.lang==='en' && a.conteudo_en ? a.conteudo_en : a.conteudo}
                  </div>
                )}
              </article>
            );
          })}
          {!visible.length && <div className="empty" style={{padding:60,textAlign:'center'}}>{t('no_notices')}</div>}
        </div>
      )}

      {composing && <NoticeComposer onClose={()=>setComposing(false)} onPost={async (notice) => {
        try {
          const res = await fetch('/api/announcements', {
            method: 'POST',
            headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token()}` },
            body: JSON.stringify(notice)
          });
          if (res.ok) {
            toast.push({icon:'check', title:t('notice_posted')});
            await fetchAvisos();
            setComposing(false);
          } else {
            const d = await res.json();
            toast.push({icon:'x', title:'Erro ao publicar', body: d.error});
          }
        } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
      }}/>}
    </main>
  );
};

const NoticeComposer = ({onClose, onPost}) => {
  const t = useT();
  const [cat, setCat] = useState('convivencia');
  const [titulo, setTitulo] = useState('');
  const [conteudo, setConteudo] = useState('');
  const [priority, setPriority] = useState('mid');
  const [expira, setExpira] = useState('');
  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(31,42,46,0.4)',backdropFilter:'blur(6px)',zIndex:8000,display:'grid',placeItems:'center',padding:20}}>
      <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:560,background:'var(--cream)',borderRadius:24,padding:'30px 32px',animation:'slideIn .3s both'}}>
        <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:24}}>
          <div style={{fontFamily:'var(--fs)',fontSize:28,letterSpacing:'-0.5px'}}>Novo aviso</div>
          <button className="btn ghost" onClick={onClose}><Icon n="x" size={14}/></button>
        </div>
        <div style={{display:'grid',gap:14}}>
          <div>
            <label className="label">{t('category')}</label>
            <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={cat} onChange={e=>setCat(e.target.value)}>
              {CATEGORIAS.filter(c=>c.id!=='all').map(c => (
                <option key={c.id} value={c.id}>{t.lang==='pt'?c.label:c.label_en}</option>
              ))}
            </select>
          </div>
          <div>
            <label className="label">Título *</label>
            <input className="input" style={{width:'100%',boxSizing:'border-box'}} value={titulo} onChange={e=>setTitulo(e.target.value)} placeholder="Ex: Manutenção no convés 7..."/>
          </div>
          <div>
            <label className="label">{t('message')}</label>
            <textarea className="input" value={conteudo} onChange={e=>setConteudo(e.target.value)} placeholder={t('msg_placeholder')} style={{width:'100%',boxSizing:'border-box',minHeight:90,resize:'vertical',fontFamily:'var(--fs)',fontSize:15}}/>
          </div>
          <div>
            <label className="label">Prioridade</label>
            <select className="input select" style={{width:'100%',boxSizing:'border-box'}} value={priority} onChange={e=>setPriority(e.target.value)}>
              <option value="low">Baixa</option>
              <option value="mid">Média</option>
              <option value="high">Alta</option>
            </select>
          </div>
          <div>
            <label className="label">{t('expires_optional')}</label>
            <input className="input" type="date" style={{width:'100%',boxSizing:'border-box'}} value={expira} onChange={e=>setExpira(e.target.value)}/>
          </div>
        </div>
        <div style={{marginTop:24,display:'flex',gap:10,justifyContent:'flex-end'}}>
          <button className="btn" onClick={onClose}>{t('cancel')}</button>
          <button className="btn btn-primary" disabled={!titulo.trim()} onClick={()=>onPost({titulo, conteudo, categoria:cat, priority, expires_at:expira||null})}>
            <Icon n="check" size={14}/>{t('post')}
          </button>
        </div>
      </div>
    </div>
  );
};

const Input2 = ({label, value, onChange, placeholder, type='text', multiline=false}) => (
  <div>
    <div style={{fontFamily:'var(--fm)',fontSize:10.5,letterSpacing:1,textTransform:'uppercase',color:'var(--muted)',marginBottom:6}}>{label}</div>
    {multiline
      ? <textarea className="input" value={value} onChange={e=>onChange(e.target.value)} placeholder={placeholder} style={{minHeight:90,resize:'vertical',fontFamily:'var(--fs)',fontSize:15}}/>
      : <input className="input" type={type} value={value} onChange={e=>onChange(e.target.value)} placeholder={placeholder}/>}
  </div>
);

// ─── MANIFESTO (sensível — fica dentro de Operação) ───
const Manifesto = ({state}) => {
  const tt = useT();
  const isMobile = useMobile();
  const [view, setView] = useState('cards');
  const [doc, setDoc] = useState('manifesto'); // manifesto | anvisa | dms | gangway
  const [search, setSearch] = useState('');
  const [abordo, setAbordo] = useState([]);
  const [loading, setLoading] = useState(true);

  const fetchManifesto = async () => {
    try {
      const token = localStorage.getItem('genesis_token');
      const res = await fetch('/api/boarding/manifesto', {
        headers: { 'Authorization': `Bearer ${token}` }
      });
      if (res.ok) {
        const data = await res.json();
        setAbordo(data.abordo || []);
      }
    } catch(e) { console.error(e); }
    finally { setLoading(false); }
  };

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

  const today = new Date().toISOString().split('T')[0];

  const filtered = abordo.filter(u => {
    if (!search) return true;
    const q = search.toLowerCase();
    return (u.nome||'').toLowerCase().includes(q)
      || (u.genesis_id||'').toLowerCase().includes(q)
      || (u.cabine||'').toLowerCase().includes(q)
      || (u.empresa||'').toLowerCase().includes(q);
  });

  const handlePrint = () => {
    const w = window.open('', '_blank', 'width=800,height=900');
    const rows = filtered.map(u => `
      <tr>
        <td>${u.genesis_id||''}</td>
        <td><strong>${u.nome}</strong></td>
        <td>${u.funcao||''}<br><small>${u.empresa||''}</small></td>
        <td>${u.cabine||'—'}</td>
        <td>${u.embarque ? new Date(u.embarque).toLocaleDateString('pt-BR') : '—'}</td>
      </tr>`).join('');
    w.document.write(`
<!doctype html><html><head><meta charset="utf-8"><title>Manifesto · ${today} · Genesis</title>
<style>
  @page{margin:18mm}
  body{font-family:Georgia,serif;color:#1F2A2E}
  h1{font-size:32px;font-weight:400;letter-spacing:-0.5px;margin:0 0 4px}
  h1 em{color:#D87C5A}
  .meta{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:#7A8388;margin-bottom:24px}
  table{width:100%;border-collapse:collapse;font-size:11px}
  th{text-align:left;font-family:'IBM Plex Mono',monospace;font-size:9px;letter-spacing:1.2px;text-transform:uppercase;color:#7A8388;padding:10px 8px;border-bottom:1px solid #1F2A2E}
  td{padding:10px 8px;border-bottom:1px solid #EAE3D6;vertical-align:top}
  small{color:#7A8388}
  .footer{margin-top:36px;font-family:'IBM Plex Mono',monospace;font-size:9px;color:#7A8388;letter-spacing:1px;text-transform:uppercase}
</style></head><body>
<h1>Manifesto a <em>bordo</em></h1>
<div class="meta">${today} · ${filtered.length} pessoas · Genesis · documento operacional</div>
<table>
  <thead><tr><th>GEN-ID</th><th>Nome</th><th>Função / Empresa</th><th>Cabine</th><th>Embarcou</th></tr></thead>
  <tbody>${rows}</tbody>
</table>
<div class="footer">Manifesto · Portal Genesis · uso interno</div>
</body></html>`);
    w.document.close();
    setTimeout(() => w.print(), 300);
  };

  // Resumo por turno e empresa
  const porTurno = {};
  const porEmpresa = {};
  abordo.forEach(u => {
    if (u.turno) porTurno[u.turno] = (porTurno[u.turno]||0) + 1;
    const emp = u.empresa || '—';
    porEmpresa[emp] = (porEmpresa[emp]||0) + 1;
  });
  const topEmpresas = Object.entries(porEmpresa).sort((a,b)=>b[1]-a[1]).slice(0,4);

  // Contagens a bordo por classe
  const mTrip = abordo.filter(u => u.role==='crew' || u.role==='admin' || (u.role==='ops' && u.is_staff)).length;
  const mOps  = abordo.filter(u => u.role==='ops' && !u.is_staff).length;
  const mCat  = abordo.filter(u => u.role==='catering').length;
  const mTerc = abordo.filter(u => u.role==='terceirizada').length;
  const mVis  = abordo.filter(u => u.role==='guest').length;
  const aboardCards = [
    {n:abordo.length, l:'a bordo',      c:'var(--leaf)'},
    {n:mTrip,         l:'tripulação',   c:'var(--teal)'},
    {n:mOps,          l:'operações',    c:'var(--accent)'},
    {n:mCat,          l:'catering',     c:'#7A6EA6'},
    {n:mTerc,         l:'terceirizadas',c:'#5F7A8C'},
    {n:mVis,          l:'visitantes',   c:'var(--sun)'},
  ];

  // Componentes das sub-abas (expostos por manifesto-docs.jsx)
  const CrewListC = (typeof CrewListAnvisa !== 'undefined' && CrewListAnvisa) || window.CrewListAnvisa;
  const DmsC      = (typeof DmsView !== 'undefined' && DmsView) || window.DmsView;
  const GangwayC  = (typeof GangwayRecordBook !== 'undefined' && GangwayRecordBook) || window.GangwayRecordBook;

  return (
    <main style={{padding: isMobile ? '16px 16px 80px' : '30px 36px 56px', flex:1}}>
      {/* Cabeçalho da página */}
      <div style={{marginBottom:24}}>
        <div className="t-eyebrow">{today} · documento operacional</div>
        <h1 style={{fontFamily:'var(--fs)',fontSize:isMobile?30:38,letterSpacing:'-0.8px',lineHeight:1.05,marginTop:6}}>
          Manifesto <em style={{fontStyle:'italic',color:'var(--accent)'}}>a bordo.</em>
        </h1>
      </div>

      {/* Sub-abas do Manifesto */}
      <div style={{display:'flex',gap:6,padding:5,background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:14,flexWrap:'wrap',marginBottom:20}}>
        {[
          {id:'manifesto', label:'Manifesto'},
          {id:'anvisa',    label:'Crew List (ANVISA)'},
          {id:'dms',       label:'DMS'},
          {id:'gangway',   label:'Gangway'},
        ].map(t=>(
          <button key={t.id} onClick={()=>setDoc(t.id)}
            className={doc===t.id?'btn btn-primary btn-sm':'btn btn-sm'}
            style={{borderRadius:9,fontSize:isMobile?10.5:12}}>
            {t.label}
          </button>
        ))}
      </div>

      {doc==='anvisa'  && (CrewListC ? <CrewListC abordo={abordo} loading={loading}/> : <div className="empty" style={{padding:30}}>Crew List indisponível.</div>)}
      {doc==='dms'     && (DmsC      ? <DmsC abordo={abordo}/>      : <div className="empty" style={{padding:30}}>DMS indisponível.</div>)}
      {doc==='gangway' && (GangwayC  ? <GangwayC abordo={abordo}/>  : <div className="empty" style={{padding:30}}>Gangway indisponível.</div>)}

      {doc==='manifesto' && (<>

      {!loading && (
        <div style={{display:'flex',gap:10,flexWrap:'wrap',marginBottom:20}}>
          {aboardCards.map((c,i)=>(
            <div key={i} style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:`3px solid ${c.c}`,borderRadius:16,padding:'12px 18px',display:'flex',alignItems:'baseline',gap:8,minWidth:isMobile?'calc(50% - 5px)':'auto',flex:isMobile?'1':'none',boxSizing:'border-box',boxShadow:'var(--shadow-sm)'}}>
              <span style={{fontFamily:'var(--fs)',fontSize:26,fontWeight:600,letterSpacing:'-0.6px',color:c.c,lineHeight:1}}>{c.n}</span>
              <span style={{fontSize:12,color:'var(--muted)',fontFamily:'var(--fm)'}}>{c.l}</span>
            </div>
          ))}
        </div>
      )}

      {/* Resumo em destaque */}
      {!loading && abordo.length > 0 && (
        <div style={{display:'grid',gridTemplateColumns:isMobile?'1fr 1fr':'repeat(auto-fit, minmax(160px, 1fr))',gap:12,marginBottom:24}}>
          {Object.keys(porTurno).length > 0 && (
            <div style={{background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:20,padding:'18px 22px'}}>
              <div className="t-eyebrow" style={{marginBottom:8}}>Por turno</div>
              <div style={{display:'flex',gap:12,flexWrap:'wrap'}}>
                {Object.entries(porTurno).map(([t,n])=>(
                  <div key={t} style={{display:'flex',alignItems:'center',gap:5}}>
                    <span style={{fontFamily:'var(--fs)',fontSize:20,fontWeight:600}}>{n}</span>
                    <span style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)'}}>{t}</span>
                  </div>
                ))}
              </div>
            </div>
          )}
          {topEmpresas.length > 0 && !isMobile && (
            <div style={{background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:20,padding:'18px 22px',gridColumn:isMobile?'auto':'span 2'}}>
              <div className="t-eyebrow" style={{marginBottom:8}}>Principais empresas</div>
              <div style={{display:'flex',gap:14,flexWrap:'wrap'}}>
                {topEmpresas.map(([emp,n])=>(
                  <div key={emp} style={{display:'flex',alignItems:'center',gap:6}}>
                    <span style={{fontFamily:'var(--fs)',fontSize:18,fontWeight:600}}>{n}</span>
                    <span style={{fontSize:11.5,color:'var(--muted)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',maxWidth:120}}>{emp}</span>
                  </div>
                ))}
              </div>
            </div>
          )}
        </div>
      )}

      {/* Toolbar */}
      <div style={{display:'flex',alignItems:'center',gap:10,flexWrap:'wrap',marginBottom:18}}>
        <input className="input" placeholder={tt('search')+'…'} value={search} onChange={e=>setSearch(e.target.value)} style={{maxWidth:isMobile?'100%':280,flex:isMobile?'1 1 100%':'none',boxSizing:'border-box'}}/>
        <div style={{flex:1}}/>
        <div style={{display:'flex',gap:4,padding:4,background:'var(--cream-2)',borderRadius:12}}>
          <button className={view==='cards'?'btn btn-primary btn-sm':'btn btn-sm'} onClick={()=>setView('cards')} style={{borderRadius:8}}>{tt('card_view')}</button>
          <button className={view==='list'?'btn btn-primary btn-sm':'btn btn-sm'} onClick={()=>setView('list')} style={{borderRadius:8}}>{tt('list_view')}</button>
        </div>
        <ExportDropdown
          onPDF={handlePrint}
          onExcel={async () => {
            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(u => ({
              'GEN-ID': u.genesis_id||'—',
              'Nome': u.nome||'—',
              'Função': u.funcao||'—',
              'Empresa': u.empresa||'—',
              'Cabine': u.cabine||'—',
              'Turno': u.turno||'—',
              'Embarque': u.embarque ? new Date(u.embarque).toLocaleDateString('pt-BR') : '—',
            }));
            const ws = window.XLSX.utils.json_to_sheet(rows);
            const wb = window.XLSX.utils.book_new();
            window.XLSX.utils.book_append_sheet(wb, ws, 'Manifesto');
            ws['!cols'] = [12,28,22,22,10,14,14].map(w=>({wch:w}));
            window.XLSX.writeFile(wb, `manifesto_${today}.xlsx`);
          }}
        />
      </div>

      <div className="t-eyebrow" style={{marginBottom:14,color:'var(--muted)'}}>
        {loading ? 'carregando…' : `${filtered.length} ${tt.lang==='pt'?'exibidas':'shown'}`}
      </div>

      {loading ? (
        // Skeleton loaders
        <div style={{display:'grid',gridTemplateColumns:isMobile?'1fr':'repeat(auto-fill, minmax(280px, 1fr))',gap:12}}>
          {[1,2,3,4,5,6].map(i=>(
            <div key={i} style={{height:80,borderRadius:18,background:'linear-gradient(90deg,var(--cream-2) 25%,var(--cream-3) 50%,var(--cream-2) 75%)',backgroundSize:'200% 100%',animation:'shimmer 1.5s infinite'}}/>
          ))}
        </div>
      ) : filtered.length === 0 ? (
        <div className="empty" style={{padding:'60px 20px',textAlign:'center',fontSize:16}}>
          {abordo.length === 0
            ? (tt.lang==='pt'?'ninguém a bordo no momento.':'no one on board right now.')
            : (tt.lang==='pt'?`ninguém encontrado para "${search}".`:`no one found for "${search}".`)}
        </div>
      ) : view==='cards' ? (
        <div style={{display:'grid',gridTemplateColumns:isMobile?'1fr':'repeat(auto-fill, minmax(280px, 1fr))',gap:12}}>
          {filtered.map(u => (
            <div key={u.id} style={{padding:16,display:'flex',alignItems:'center',gap:12,background:'var(--surface)',border:'1px solid var(--line-soft)',borderLeft:'3px solid var(--accent)',borderRadius:18,boxShadow:'var(--shadow-sm)',transition:'transform .15s ease, box-shadow .15s ease'}}
              onMouseEnter={e=>{e.currentTarget.style.transform='translateY(-2px)';e.currentTarget.style.boxShadow='0 12px 28px -16px rgba(31,42,46,0.2)';}}
              onMouseLeave={e=>{e.currentTarget.style.transform='none';e.currentTarget.style.boxShadow='none';}}>
              <Avatar user={u} size="md"/>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontSize:14,fontWeight:500,lineHeight:1.2,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',display:'flex',alignItems:'center',gap:6}}>
                  {u.nome}
                  {u.genero==='F' && <span style={{fontSize:11,color:'#C36474'}}>♀</span>}
                </div>
                <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>{u.genesis_id} · {tt('cab')} {u.cabine||'—'}</div>
                <div style={{display:'flex',gap:8,marginTop:5,alignItems:'center',flexWrap:'wrap'}}>
                  <span style={{fontSize:11,color:'var(--muted-2)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis',maxWidth:140}}>{u.funcao}</span>
                  {u.turno && <span style={{fontFamily:'var(--fm)',fontSize:9.5,padding:'1px 6px',borderRadius:6,background:'var(--sky-soft)',color:'var(--teal)'}}>{u.turno}</span>}
                </div>
              </div>
            </div>
          ))}
        </div>
      ) : isMobile ? (
        <div style={{display:'flex',flexDirection:'column',gap:8}}>
          {filtered.map(u=>(
            <div key={u.id} style={{display:'flex',alignItems:'center',gap:12,padding:'12px 14px',background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:16,boxShadow:'var(--shadow-sm)'}}>
              <Avatar user={u} size="sm"/>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontSize:13.5,fontWeight:500}}>{u.nome}</div>
                <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:1}}>{u.empresa} {u.cabine?`· Cab ${u.cabine}`:''}</div>
              </div>
              {u.turno && <span style={{fontFamily:'var(--fm)',fontSize:10,padding:'2px 7px',borderRadius:6,background:'var(--sky-soft)',color:'var(--teal)',flexShrink:0}}>{u.turno}</span>}
            </div>
          ))}
        </div>
      ) : (
        <div style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderRadius:24,overflow:'hidden',boxShadow:'var(--shadow-md)'}}>
          <div style={{display:'grid',gridTemplateColumns:'1.3fr 1fr 1fr 0.7fr 0.8fr 0.9fr',padding:'12px 22px',background:'var(--cream-2)',fontFamily:'var(--fm)',fontSize:10.5,letterSpacing:1.2,textTransform:'uppercase',color:'var(--muted)'}}>
            <div>{tt.lang==='pt'?'Nome':'Name'}</div>
            <div>GEN-ID</div>
            <div>{tt.lang==='pt'?'Função':'Role'}</div>
            <div>{tt('cab')}</div>
            <div>Turno</div>
            <div>{tt.lang==='pt'?'Embarque':'Boarded'}</div>
          </div>
          {filtered.map((u,i) => (
            <div key={u.id} style={{display:'grid',gridTemplateColumns:'1.3fr 1fr 1fr 0.7fr 0.8fr 0.9fr',padding:'12px 22px',alignItems:'center',borderTop:'1px solid var(--line-soft)',fontSize:13,background:i%2?'rgba(0,0,0,0.012)':'transparent'}}>
              <div style={{display:'flex',alignItems:'center',gap:10}}>
                <Avatar user={u} size="sm"/>
                <span style={{fontWeight:500}}>{u.nome}</span>
              </div>
              <div style={{fontFamily:'var(--fm)',fontSize:12,color:'var(--muted)'}}>{u.genesis_id}</div>
              <div>{u.funcao}<div style={{fontSize:11,color:'var(--muted)'}}>{u.empresa}</div></div>
              <div style={{fontFamily:'var(--fm)',fontSize:12}}>{u.cabine||'—'}</div>
              <div style={{fontFamily:'var(--fm)',fontSize:10.5}}>
                {u.turno
                  ? <span style={{padding:'2px 7px',borderRadius:6,background:'var(--sky-soft)',color:'var(--teal)'}}>{u.turno}</span>
                  : <span style={{color:'var(--muted-2)'}}>—</span>}
              </div>
              <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>{u.embarque ? new Date(u.embarque).toLocaleDateString('pt-BR') : '—'}</div>
            </div>
          ))}
        </div>
      )}
      </>)}
    </main>
  );
};

// ─── IMPORTAR ───
// ─── Helper CPF format ───
const fmtCPF = (v) => v ? v.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4') : '';

// ─── IMPORTAR ───
const Importar = ({state, onClose}) => {
  const toast = useToast();
  const isMobile = useMobile();
  const inputRef = useRef(null);
  const [fase, setFase] = useState('upload'); // upload | mapear | preview | resultado
  const [loading, setLoading] = useState(false);
  const [preview, setPreview] = useState(null);
  const [resultado, setResultado] = useState(null);
  const [opcoes, setOpcoes] = useState({
    cadastrar: true,
    atualizar_perfil: true,
    atualizar_turno: true,
    atualizar_cabine: true,
  });
  const [dragOver, setDragOver] = useState(false);
  const [globalRole, setGlobalRole] = useState('crew');
  const [rowRoles, setRowRoles] = useState({}); // { linha: role }

  // Mapeamento de colunas
  const [rawHeaders, setRawHeaders] = useState([]); // cabeçalhos do arquivo
  const [rawRows, setRawRows] = useState([]);        // linhas de dados (arrays)
  const [colMap, setColMap] = useState({});          // {campo: indiceColuna}
  const [editRows, setEditRows] = useState([]);      // linhas editáveis na conferência

  const token = () => localStorage.getItem('genesis_token');

  // Listas padrão (turnos, cabines, empresas, funções, nacionalidades) para os dropdowns da conferência
  const [options, setOptions] = useState({ turnos: [], cabines: [], empresas: [], funcoes: [], nacionalidades: [] });
  useEffect(() => {
    fetch('/api/users/import-options', { headers: { Authorization: `Bearer ${token()}` } })
      .then(r => r.ok ? r.json() : null)
      .then(d => { if (d) setOptions({ turnos: d.turnos || [], cabines: d.cabines || [], empresas: d.empresas || [], funcoes: d.funcoes || [], nacionalidades: d.nacionalidades || [] }); })
      .catch(() => {});
  }, []);

  const downloadTemplate = async () => {
    const res = await fetch('/api/users/template', {
      headers: { 'Authorization': `Bearer ${token()}` }
    });
    if (!res.ok) { toast.push({icon:'x', title:'Erro ao gerar template'}); return; }
    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = 'template_genesis.xlsx'; a.click();
    URL.revokeObjectURL(url);
  };

  // Campos do sistema que podem ser mapeados
  const CAMPOS = [
    {key:'nome',                  label:'Nome completo',       required:true},
    {key:'documento',             label:'CPF ou Passaporte',   required:true},
    {key:'empresa',               label:'Empresa',             required:false},
    {key:'funcao',                label:'Função',              required:false},
    {key:'turno',                 label:'Turno',               required:false},
    {key:'cabine',                label:'Cabine',              required:false},
    {key:'genero',                label:'Gênero',              required:false},
    {key:'data_nascimento',       label:'Data nascimento',     required:false},
    {key:'nacionalidade',         label:'Nacionalidade',       required:false},
    {key:'email',                 label:'Email',               required:false},
    {key:'data_prevista_embarque',label:'Data prev. embarque', required:false},
  ];

  // Auto-detecção: analisa cabeçalho + conteúdo das colunas
  const autoDetect = (headers, rows) => {
    const map = {};
    const used = new Set();
    const norm = s => String(s||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'').trim();

    // Palavras-chave por campo
    const keywords = {
      nome:            ['nome','name','completo'],
      documento:       ['cpf','passaporte','passport','documento','doc','rg'],
      empresa:         ['empresa','company','employer'],
      funcao:          ['funcao','função','cargo','role','position','rank','title'],
      turno:           ['turno','shift','watch'],
      cabine:          ['cabine','cabin','camarote','berth','quarto','room'],
      genero:          ['genero','gênero','gender','sexo','sex'],
      data_nascimento: ['nasc','birth','nascimento','dob','aniversario'],
      nacionalidade:   ['nacional','country','pais','país','nationality'],
      email:           ['email','e-mail','mail'],
      data_prevista_embarque: ['embarque','boarding','prev','previsto','estimated','eta','chegada'],
    };

    // 1ª passada: casa pelo cabeçalho, mas valida o conteúdo
    const looksLikeDoc = (vals) => vals.some(v => /cpf|^\d{3}\.?\d{3}\.?\d{3}-?\d{2}$|^[A-Za-z]{2}\d{5,}|^\d{9,11}$/i.test(String(v).trim()));
    const colVals = (idx) => rows.slice(0,10).map(r=>r[idx]).filter(v=>v!==null&&v!==undefined&&String(v).trim());

    headers.forEach((h, idx) => {
      const hn = norm(h);
      if (!hn) return;
      for (const [campo, kws] of Object.entries(keywords)) {
        if (used.has(idx) || map[campo] !== undefined) continue;
        if (kws.some(kw => hn.includes(kw))) {
          // Se for documento, valida que o conteúdo parece documento
          if (campo === 'documento' && !looksLikeDoc(colVals(idx))) continue;
          map[campo] = idx; used.add(idx); break;
        }
      }
    });

    // 2ª passada: detecção por conteúdo (para colunas sem header reconhecido)
    const sample = rows.slice(0, 10);
    headers.forEach((h, idx) => {
      if (used.has(idx)) return;
      const vals = sample.map(r => r[idx]).filter(v => v !== null && v !== undefined && String(v).trim());
      if (!vals.length) return;
      // Gênero: maioria Male/Female/M/F
      if (map.genero === undefined && vals.every(v => /^(m|f|o|male|female|masculino|feminino|outro)$/i.test(String(v).trim()))) {
        map.genero = idx; used.add(idx); return;
      }
      // Documento: contém "CPF:" ou padrão de passaporte/cpf
      if (map.documento === undefined && vals.some(v => /cpf|^\d{3}\.?\d{3}\.?\d{3}-?\d{2}$|^[A-Z]{2}\d{6}/i.test(String(v)))) {
        map.documento = idx; used.add(idx); return;
      }
    });

    return map;
  };

  const parseFile = async (file) => {
    if (!file) return;
    setLoading(true);
    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 buf = await file.arrayBuffer();
      const wb = window.XLSX.read(buf, { type: 'array', cellDates: true });
      const sheetName = wb.SheetNames.find(n => n !== 'Listas') || wb.SheetNames[0];
      const ws = wb.Sheets[sheetName];

      // Parse como array de arrays — preserva posição das colunas
      const matrix = window.XLSX.utils.sheet_to_json(ws, { header: 1, defval: null, raw: false });
      if (!matrix.length) { toast.push({icon:'x', title:'Planilha vazia'}); setLoading(false); return; }

      // Acha a linha de cabeçalho de verdade (o modelo novo tem título e instrução acima dela)
      let headerIdx = matrix.findIndex(r =>
        r.some(c => /(^|\s)nome/i.test(String(c))) &&
        r.some(c => /documento|cpf|passaporte|empresa/i.test(String(c)))
      );
      if (headerIdx < 0) headerIdx = matrix.findIndex(r => r.some(c => c !== null && String(c).trim()));
      const headers = matrix[headerIdx] || [];
      const dataRows = matrix.slice(headerIdx + 1).filter(r => r.some(c => c !== null && String(c).trim()));

      setRawHeaders(headers);
      setRawRows(dataRows);
      setColMap(autoDetect(headers, dataRows));
      setFase('mapear');
    } catch(e) {
      toast.push({icon:'x', title:'Erro ao ler arquivo', body: e.message});
    } finally {
      setLoading(false);
    }
  };

  // Monta objeto de entrada (para /preview) a partir de uma linha editável
  const toInput = (er) => ({
    nome: er.nome, documento: er.documento, empresa: er.empresa, funcao: er.funcao,
    turno: er.turno, cabine: er.cabine, genero: er.genero, data_nascimento: er.data_nascimento,
    nacionalidade: er.nacionalidade, email: er.email,
  });
  // Converte rows validadas (do backend) em linhas editáveis, preservando classe por índice
  const initEditRows = (rows, prevRoles) => {
    setEditRows(rows.map((r, i) => ({
      linha: r.linha,
      nome: r.nome || '',
      documento: r.cpf_fmt || r.passaporte || '',
      empresa: r.empresa || '', funcao: r.funcao || '',
      turno: r.turno || '', cabine: r.cabine || '',
      genero: r.genero || '',
      data_nascimento: r.data_nascimento || '',
      nacionalidade: r.nacionalidade || '',
      email: (r.email && !String(r.email).includes('@genesis.temp')) ? r.email : '',
      role: (prevRoles && prevRoles[i]) || globalRole,
      status: r.status, erros: r.erros || [], existente_nome: r.existente_nome,
    })));
  };
  const updateEdit = (i, key, val) => setEditRows(rows => rows.map((r, j) => j === i ? { ...r, [key]: val } : r));

  // Converte linhas + mapa para objetos e envia ao preview (1ª análise)
  const confirmarMapeamento = async () => {
    setLoading(true);
    try {
      const objRows = rawRows.map(r => {
        const obj = {};
        CAMPOS.forEach(c => {
          const idx = colMap[c.key];
          if (idx !== undefined && idx !== null && idx !== '') {
            let v = r[idx];
            if (v instanceof Date) {
              v = `${String(v.getDate()).padStart(2,'0')}/${String(v.getMonth()+1).padStart(2,'0')}/${v.getFullYear()}`;
            }
            obj[c.key] = v;
          }
        });
        return obj;
      });

      const res = await fetch('/api/users/preview', {
        method: 'POST',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token()}` },
        body: JSON.stringify({ rows: objRows })
      });
      const data = await res.json();
      if (res.ok) {
        setPreview(data);
        initEditRows(data.rows);
        setFase('preview');
      } else {
        toast.push({icon:'x', title:'Erro ao analisar', body: data.error});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão', body: e.message});
    } finally {
      setLoading(false);
    }
  };

  const handleDrop = (e) => {
    e.preventDefault(); setDragOver(false);
    const file = e.dataTransfer.files[0];
    if (file) parseFile(file);
  };

  // Re-analisa os valores editados (atualiza status/erros sem importar)
  const revalidar = async () => {
    setLoading(true);
    try {
      const roles = editRows.map(r => r.role);
      const res = await fetch('/api/users/preview', {
        method: 'POST',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token()}` },
        body: JSON.stringify({ rows: editRows.map(toInput) })
      });
      const data = await res.json();
      if (res.ok) { setPreview(data); initEditRows(data.rows, roles); }
      else toast.push({icon:'x', title:'Erro ao revalidar', body: data.error});
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
    finally { setLoading(false); }
  };

  const confirmarImport = async () => {
    setLoading(true);
    try {
      const roles = editRows.map(r => r.role);
      // 1) revalida com os valores editados (garante cpf/passaporte/cabine corretos)
      const pv = await fetch('/api/users/preview', {
        method: 'POST',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token()}` },
        body: JSON.stringify({ rows: editRows.map(toInput) })
      });
      const pdata = await pv.json();
      if (!pv.ok) { toast.push({icon:'x', title:'Erro ao validar', body: pdata.error}); setLoading(false); return; }
      setPreview(pdata); initEditRows(pdata.rows, roles);
      // 2) importa (backend ignora linhas com erro e conta como rejeitadas)
      const rowsComRole = pdata.rows.map((r, i) => ({ ...r, role: roles[i] || globalRole, _role: roles[i] || globalRole }));
      const res = await fetch('/api/users/import', {
        method: 'POST',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token()}` },
        body: JSON.stringify({ rows: rowsComRole, opcoes })
      });
      const data = await res.json();
      if (res.ok) { setResultado(data); setFase('resultado'); }
      else toast.push({icon:'x', title:'Erro na importação', body: data.error});
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    } finally {
      setLoading(false);
    }
  };

  const resetar = () => {
    setFase('upload'); setPreview(null); setResultado(null); setRowRoles({}); setEditRows([]);
    setRawHeaders([]); setRawRows([]); setColMap({});
    setOpcoes({ cadastrar:true, atualizar_perfil:true, atualizar_turno:true, atualizar_cabine:true });
  };

  // ─── RENDER: modal estilo DMS ───
  const fieldByCol = {};
  Object.entries(colMap).forEach(([k, v]) => { if (v !== undefined && v !== null && v !== '') fieldByCol[Number(v)] = k; });
  const campoLabel = Object.fromEntries(CAMPOS.map(c => [c.key, c.label]));
  const COLS = [
    { key:'nome', label:'Nome', w:160 },
    { key:'documento', label:'Documento', w:150 },
    { key:'empresa', label:'Empresa', w:130 },
    { key:'funcao', label:'Função', w:120 },
    { key:'turno', label:'Turno', w:90 },
    { key:'cabine', label:'Cabine', w:90 },
    { key:'genero', label:'Gênero', w:120, kind:'genero' },
    { key:'data_nascimento', label:'Nascimento', w:120 },
    { key:'nacionalidade', label:'Nacionalidade', w:120 },
    { key:'email', label:'Email', w:180 },
    { key:'role', label:'Classe', w:120, kind:'role' },
  ];
  const gridCols = `48px ${COLS.map(c => c.w + 'px').join(' ')} 100px`;
  const tableMinW = 48 + COLS.reduce((a, c) => a + c.w, 0) + 100;
  const ci = { padding:'5px 7px', fontSize:12, width:'100%', boxSizing:'border-box', minWidth:0 };
  const statusColor = { novo:'var(--leaf)', atualizar:'var(--teal)', erro:'var(--coral)' };
  const statusBg = { novo:'var(--leaf-soft)', atualizar:'var(--sky-soft)', erro:'var(--accent-soft)' };
  const statusLabel = { novo:'Novo', atualizar:'Atualizar', erro:'Erro' };

  const stepLabel = fase === 'upload' ? '1 · Planilha' : fase === 'mapear' ? '2 · Colunas' : fase === 'preview' ? '3 · Conferir' : 'Concluído';
  const wide = fase === 'preview';

  return (
    <div onClick={() => onClose && onClose()} style={{ position:'fixed', inset:0, zIndex:9000, background:'rgba(31,42,46,0.5)', display:'flex', alignItems:'flex-start', justifyContent:'center', padding:'3vh 16px', overflowY:'auto' }}>
      <div onClick={e => e.stopPropagation()} style={{ width:'100%', maxWidth: wide ? 1140 : 720, 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 24px', borderBottom:'1px solid var(--line-soft)' }}>
          <div style={{ display:'flex', alignItems:'baseline', gap:12, flexWrap:'wrap' }}>
            <div style={{ fontFamily:'var(--fs)', fontSize:24, letterSpacing:'-0.5px' }}>Impor<em style={{ fontStyle:'italic', color:'var(--accent)' }}>tar.</em></div>
            <span className="t-eyebrow" style={{ color:'var(--accent)' }}>{stepLabel}</span>
          </div>
          <button className="btn ghost btn-sm" onClick={() => onClose && onClose()}><Icon n="x" size={14}/></button>
        </div>

        <div style={{ padding:'20px 24px' }}>
          {/* ── FASE UPLOAD ── */}
          {fase === 'upload' && (
            <div>
              <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between', marginBottom:20, padding:'16px 20px', background:'var(--sky-soft)', borderRadius:16, border:'1px solid rgba(92,141,174,0.2)', flexWrap:'wrap', gap:10 }}>
                <div>
                  <div style={{ fontFamily:'var(--fs)', fontSize:18, letterSpacing:'-0.3px', color:'var(--teal)' }}>Baixe a planilha <em style={{ fontStyle:'italic' }}>modelo</em></div>
                  <div style={{ fontSize:12, color:'var(--teal)', opacity:0.8, marginTop:3, fontFamily:'var(--fm)' }}>uma coluna de Documento (CPF ou Passaporte) + droplists de turno, cabine e gênero</div>
                </div>
                <button className="btn" onClick={downloadTemplate} style={{ background:'var(--teal)', color:'var(--cream)', border:'none' }}><Icon n="upload" size={14}/>Baixar template</button>
              </div>

              <div onDragOver={e => { e.preventDefault(); setDragOver(true); }} onDragLeave={() => setDragOver(false)} onDrop={handleDrop}
                style={{ border:`2px dashed ${dragOver ? 'var(--accent)' : 'var(--line)'}`, borderRadius:20, padding:'48px 32px', textAlign:'center', background: dragOver ? 'var(--accent-soft)' : 'var(--cream-2)', transition:'all .2s', cursor:'pointer' }}
                onClick={() => inputRef.current?.click()}>
                <div style={{ color: dragOver ? 'var(--accent)' : 'var(--muted)', marginBottom:14, display:'inline-flex' }}><Icon n="upload" size={40}/></div>
                <div style={{ fontFamily:'var(--fs)', fontSize:24, letterSpacing:'-0.6px', lineHeight:1.1 }}>{dragOver ? 'Solte aqui!' : <>Solte seu Excel <em style={{ fontStyle:'italic', color:'var(--accent)' }}>aqui</em></>}</div>
                <div style={{ fontSize:13, color:'var(--muted)', marginTop:8 }}>ou clique para selecionar · .xlsx · .csv</div>
                {loading && <div style={{ marginTop:14, fontSize:13, color:'var(--accent)', fontFamily:'var(--fm)' }}>analisando planilha…</div>}
              </div>
              <input ref={inputRef} type="file" accept=".xlsx,.xls,.csv" onChange={e => parseFile(e.target.files[0])} style={{ display:'none' }}/>

              <div style={{ marginTop:20, padding:'18px 22px', background:'var(--cream)', borderRadius:16, border:'1px solid var(--line-soft)' }}>
                <div className="t-eyebrow" style={{ marginBottom:14 }}>O que fazer com a planilha?</div>
                <div style={{ display:'grid', gap:8 }}>
                  {[
                    { key:'cadastrar', label:'Cadastrar pessoas novas', desc:'Cria perfil para quem ainda não está no sistema' },
                    { key:'atualizar_perfil', label:'Atualizar perfil dos existentes', desc:'Atualiza função, empresa, gênero, documento' },
                    { key:'atualizar_turno', label:'Atualizar turno', desc:'Aplica o turno da planilha ao perfil' },
                    { key:'atualizar_cabine', label:'Atribuir cabines', desc:'Coloca cada pessoa na cabine indicada na planilha' },
                  ].map(({ key, label, desc }) => (
                    <label key={key} style={{ display:'flex', alignItems:'flex-start', gap:12, cursor:'pointer', padding:'11px 13px', borderRadius:12, background: opcoes[key] ? 'var(--accent-soft)' : 'var(--cream-2)' }}>
                      <input type="checkbox" checked={opcoes[key]} onChange={e => setOpcoes(o => ({ ...o, [key]: e.target.checked }))} style={{ marginTop:2, accentColor:'var(--accent)', cursor:'pointer' }}/>
                      <div>
                        <div style={{ fontSize:13.5, fontWeight:500, color: opcoes[key] ? 'var(--accent-2)' : 'var(--ink)' }}>{label}</div>
                        <div style={{ fontSize:11.5, color:'var(--muted)', marginTop:2 }}>{desc}</div>
                      </div>
                    </label>
                  ))}
                </div>
              </div>
            </div>
          )}

          {/* ── FASE MAPEAR (com preview da planilha) ── */}
          {fase === 'mapear' && (() => {
            const usedReq = CAMPOS.filter(c => c.required && (colMap[c.key] === undefined || colMap[c.key] === '' || colMap[c.key] === null));
            const colSample = (idx) => {
              if (idx === undefined || idx === '' || idx === null) return '';
              return rawRows.slice(0, 3).map(r => r[idx]).filter(v => v !== null && v !== undefined && String(v).trim())
                .slice(0, 2).map(v => v instanceof Date ? v.toLocaleDateString('pt-BR') : String(v).slice(0, 24)).join(' · ');
            };
            return (
              <div>
                <p style={{ fontSize:13.5, color:'var(--muted)', marginBottom:14, lineHeight:1.5 }}>
                  Confira a planilha abaixo e diga de qual coluna vem cada informação. Já tentei adivinhar — só <strong>Nome</strong> e <strong>Documento</strong> são obrigatórios.
                </p>

                {/* Preview da planilha */}
                <div style={{ border:'1px solid var(--line-soft)', borderRadius:14, overflow:'hidden', marginBottom:20 }}>
                  <div style={{ overflowX:'auto' }}>
                    <table style={{ borderCollapse:'collapse', width:'100%', minWidth: Math.max(rawHeaders.length * 130, 400) }}>
                      <thead>
                        <tr>
                          {rawHeaders.map((h, idx) => (
                            <th key={idx} style={{ textAlign:'left', padding:'8px 10px', background:'var(--cream-2)', borderBottom:'1px solid var(--line)', borderRight:'1px solid var(--line-soft)', minWidth:130, position:'sticky', top:0 }}>
                              <div style={{ fontSize:12, fontWeight:600, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis', maxWidth:160 }}>{h ? String(h) : `Coluna ${idx + 1}`}</div>
                              {fieldByCol[idx] && <div style={{ fontFamily:'var(--fm)', fontSize:9, letterSpacing:0.5, textTransform:'uppercase', color:'var(--accent)', marginTop:2 }}>→ {campoLabel[fieldByCol[idx]]}</div>}
                            </th>
                          ))}
                        </tr>
                      </thead>
                      <tbody>
                        {rawRows.slice(0, 6).map((r, ri) => (
                          <tr key={ri}>
                            {rawHeaders.map((h, ci2) => {
                              const v = r[ci2];
                              const txt = v instanceof Date ? v.toLocaleDateString('pt-BR') : (v == null ? '' : String(v));
                              return <td key={ci2} style={{ padding:'6px 10px', borderBottom:'1px solid var(--line-soft)', borderRight:'1px solid var(--line-soft)', fontSize:11.5, color:'var(--ink-2)', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis', maxWidth:180, background: fieldByCol[ci2] ? 'var(--accent-soft)' : 'transparent' }}>{txt}</td>;
                            })}
                          </tr>
                        ))}
                      </tbody>
                    </table>
                  </div>
                  {rawRows.length > 6 && <div style={{ padding:'6px 10px', fontSize:11, color:'var(--muted)', fontFamily:'var(--fm)', background:'var(--cream-2)' }}>+ {rawRows.length - 6} linhas</div>}
                </div>

                {/* Mapeamento campo → coluna */}
                <div className="t-eyebrow" style={{ color:'var(--accent)', marginBottom:10 }}>de qual coluna vem cada campo</div>
                <div style={{ display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(240px, 1fr))', gap:10 }}>
                  {CAMPOS.map(campo => {
                    const selected = colMap[campo.key];
                    const hasValue = selected !== undefined && selected !== null && selected !== '';
                    return (
                      <div key={campo.key} style={{ border:'1px solid var(--line-soft)', borderRadius:12, padding:'10px 12px' }}>
                        <div style={{ fontSize:13, fontWeight:500, display:'flex', alignItems:'center', gap:6, marginBottom:6 }}>
                          {campo.label}{campo.required && <span style={{ color:'var(--accent)' }}>*</span>}
                        </div>
                        <select className="input" style={{ width:'100%', boxSizing:'border-box', borderColor: campo.required && !hasValue ? 'var(--coral)' : (hasValue ? 'var(--leaf)' : 'var(--line)') }}
                          value={selected === undefined ? '' : selected}
                          onChange={e => { const v = e.target.value; setColMap(m => ({ ...m, [campo.key]: v === '' ? undefined : Number(v) })); }}>
                          <option value="">— não importar —</option>
                          {rawHeaders.map((h, idx) => <option key={idx} value={idx}>{h ? String(h).slice(0, 40) : `Coluna ${idx + 1}`}</option>)}
                        </select>
                        {hasValue && colSample(selected) && <div style={{ fontSize:10.5, color:'var(--muted)', fontFamily:'var(--fm)', marginTop:4, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>ex: {colSample(selected)}</div>}
                      </div>
                    );
                  })}
                </div>

                {usedReq.length > 0 && <div style={{ marginTop:14, padding:'10px 14px', background:'var(--accent-soft)', border:'1px solid var(--coral)', borderRadius:12, fontSize:13, color:'var(--accent-2)' }}>⚠️ Selecione a coluna para: {usedReq.map(c => c.label).join(', ')}</div>}

                <div style={{ display:'flex', gap:8, justifyContent:'space-between', marginTop:18, flexWrap:'wrap', borderTop:'1px solid var(--line-soft)', paddingTop:16 }}>
                  <button className="btn" onClick={resetar}>← Trocar arquivo</button>
                  <button className="btn btn-primary" onClick={confirmarMapeamento} disabled={loading || usedReq.length > 0}>{loading ? 'Analisando…' : 'Conferir dados →'}</button>
                </div>
              </div>
            );
          })()}

          {/* ── FASE PREVIEW (tabela editável) ── */}
          {fase === 'preview' && preview && (
            <div>
              <div style={{ display:'flex', gap:10, marginBottom:16, flexWrap:'wrap', alignItems:'center' }}>
                {[
                  { label:'Total', value:preview.resumo.total, tone:'cream' },
                  { label:'Novos', value:preview.resumo.novos, tone:'leaf' },
                  { label:'Atualizar', value:preview.resumo.atualizar, tone:'sky' },
                  { label:'Erros', value:preview.resumo.erros, tone:'coral' },
                ].map(({ label, value, tone }) => (
                  <div key={label} style={{ padding:'10px 16px', background:`var(--${tone}-soft, var(--cream-2))`, borderRadius:12, minWidth:84, textAlign:'center' }}>
                    <div style={{ fontFamily:'var(--fs)', fontSize:26, letterSpacing:'-0.5px' }}>{value}</div>
                    <div className="t-eyebrow" style={{ marginTop:2 }}>{label}</div>
                  </div>
                ))}
                <div style={{ flex:1 }}/>
                <div style={{ display:'flex', alignItems:'center', gap:8, flexWrap:'wrap' }}>
                  <span style={{ fontSize:11.5, color:'var(--muted)', fontFamily:'var(--fm)' }}>classe p/ todos:</span>
                  {['crew','ops','catering','terceirizada','guest'].map(r => (
                    <button key={r} className="btn btn-sm" onClick={() => { setGlobalRole(r); setEditRows(rows => rows.map(x => ({ ...x, role:r }))); }}
                      style={ globalRole === r ? { background:'var(--accent)', color:'#fff' } : {} }>
                      {r === 'crew' ? 'Tripulação' : r === 'ops' ? 'Operações' : r === 'catering' ? 'Catering' : r === 'terceirizada' ? 'Terceirizada' : 'Visitante'}
                    </button>
                  ))}
                </div>
              </div>

              <p style={{ fontSize:12.5, color:'var(--muted)', marginBottom:10 }}>Edite qualquer célula para corrigir. Depois clique <strong>Revalidar</strong> para conferir o status, ou <strong>Confirmar</strong> (que já revalida e importa).</p>

              {/* Tabela editável estilo planilha */}
              <style>{`
                .xlcell{width:100%;border:none;background:transparent;padding:7px 9px;font-size:12px;outline:none;border-radius:0;font-family:inherit;color:var(--ink);box-sizing:border-box;}
                select.xlcell{cursor:pointer;-webkit-appearance:none;appearance:none;}
                .xlcell:focus{background:var(--accent-soft);box-shadow:inset 0 0 0 2px var(--accent);position:relative;z-index:1;}
              `}</style>
              <datalist id="imp-empresas">{options.empresas.map(e => <option key={e} value={e}/>)}</datalist>
              <datalist id="imp-funcoes">{options.funcoes.map(e => <option key={e} value={e}/>)}</datalist>
              <datalist id="imp-nacionalidades">{options.nacionalidades.map(e => <option key={e} value={e}/>)}</datalist>
              <div style={{ border:'1px solid var(--line)', borderRadius:8, overflow:'hidden' }}>
                <div style={{ overflowX:'auto' }}>
                  <div style={{ minWidth: tableMinW }}>
                    <div style={{ display:'grid', gridTemplateColumns: gridCols, background:'var(--cream-2)', fontFamily:'var(--fm)', fontSize:9.5, letterSpacing:0.6, textTransform:'uppercase', color:'var(--accent)', borderBottom:'1px solid var(--line)' }}>
                      <div style={{ padding:'8px 9px', borderRight:'1px solid var(--line)' }}>#</div>
                      {COLS.map(c => <div key={c.key} style={{ padding:'8px 9px', borderRight:'1px solid var(--line)' }}>{c.label}</div>)}
                      <div style={{ padding:'8px 9px' }}>Status</div>
                    </div>
                    {editRows.map((er, i) => {
                      const cell = { borderRight:'1px solid var(--line-soft)', display:'flex', alignItems:'stretch', minWidth:0 };
                      return (
                      <div key={i} style={{ display:'grid', gridTemplateColumns: gridCols, borderBottom:'1px solid var(--line-soft)', alignItems:'stretch', background: er.status === 'erro' ? 'rgba(217,105,75,0.06)' : (i % 2 ? 'var(--cream)' : 'transparent') }}>
                        <div style={{ display:'flex', alignItems:'center', padding:'0 9px', borderRight:'1px solid var(--line-soft)', fontFamily:'var(--fm)', fontSize:10, color:'var(--muted)' }}>{er.linha}</div>
                        {COLS.map(c => {
                          if (c.key === 'genero') return <div key={c.key} style={cell}><select className="xlcell" value={er.genero || ''} onChange={e => updateEdit(i, 'genero', e.target.value)}><option value="">—</option><option value="M">Masculino</option><option value="F">Feminino</option><option value="O">Outro</option></select></div>;
                          if (c.key === 'role') return <div key={c.key} style={cell}><select className="xlcell" value={er.role || 'crew'} onChange={e => updateEdit(i, 'role', e.target.value)}><option value="crew">Tripulação</option><option value="ops">Operações</option><option value="catering">Catering</option><option value="terceirizada">Terceirizada</option><option value="guest">Visitante</option></select></div>;
                          if (c.key === 'turno') return <div key={c.key} style={cell}><select className="xlcell" value={er.turno || ''} onChange={e => updateEdit(i, 'turno', e.target.value)}><option value="">—</option>{options.turnos.map(t => <option key={t} value={t}>{t}</option>)}{er.turno && !options.turnos.includes(er.turno) && <option value={er.turno}>{er.turno}</option>}</select></div>;
                          if (c.key === 'cabine') return <div key={c.key} style={cell}><select className="xlcell" value={er.cabine || ''} onChange={e => updateEdit(i, 'cabine', e.target.value)}><option value="">—</option>{options.cabines.map(cb => <option key={cb.id} value={cb.id}>{cb.id}{cb.livre ? '' : ' (ocupada)'}</option>)}{er.cabine && !options.cabines.some(cb => cb.id === er.cabine) && <option value={er.cabine}>{er.cabine}</option>}</select></div>;
                          if (c.key === 'empresa') return <div key={c.key} style={cell}><input className="xlcell" list="imp-empresas" value={er.empresa || ''} onChange={e => updateEdit(i, 'empresa', e.target.value)} /></div>;
                          if (c.key === 'funcao') return <div key={c.key} style={cell}><input className="xlcell" list="imp-funcoes" value={er.funcao || ''} onChange={e => updateEdit(i, 'funcao', e.target.value)} /></div>;
                          if (c.key === 'nacionalidade') return <div key={c.key} style={cell}><input className="xlcell" list="imp-nacionalidades" value={er.nacionalidade || ''} onChange={e => updateEdit(i, 'nacionalidade', e.target.value)} /></div>;
                          return <div key={c.key} style={cell}><input className="xlcell" value={er[c.key] || ''} onChange={e => updateEdit(i, c.key, e.target.value)} /></div>;
                        })}
                        <div style={{ display:'flex', alignItems:'center', padding:'0 8px' }}>
                          <span style={{ fontFamily:'var(--fm)', fontSize:9, letterSpacing:0.5, padding:'3px 7px', borderRadius:6, textTransform:'uppercase', background: statusBg[er.status], color: statusColor[er.status] }} title={(er.erros || []).join(' · ')}>{statusLabel[er.status]}</span>
                        </div>
                      </div>
                      );
                    })}
                  </div>
                </div>
              </div>

              {preview.resumo.erros > 0 && <div style={{ marginTop:12, padding:'10px 14px', background:'var(--accent-soft)', borderRadius:12, fontSize:12.5, color:'var(--accent-2)' }}>⚠️ {preview.resumo.erros} linha(s) com erro serão ignoradas na importação. Corrija e revalide se quiser incluí-las.</div>}

              <div style={{ display:'flex', gap:8, justifyContent:'space-between', marginTop:18, flexWrap:'wrap', borderTop:'1px solid var(--line-soft)', paddingTop:16 }}>
                <button className="btn" onClick={() => setFase('mapear')}>← Colunas</button>
                <div style={{ display:'flex', gap:8 }}>
                  <button className="btn" onClick={revalidar} disabled={loading}>{loading ? '…' : 'Revalidar'}</button>
                  <button className="btn btn-primary" onClick={confirmarImport} disabled={loading || editRows.length === 0}>{loading ? 'Importando…' : 'Confirmar importação'}</button>
                </div>
              </div>
            </div>
          )}

          {/* ── FASE RESULTADO ── */}
          {fase === 'resultado' && resultado && (
            <div style={{ textAlign:'center', padding:'24px 8px' }}>
              <div style={{ fontSize:56, marginBottom:12 }}>🎉</div>
              <div style={{ fontFamily:'var(--fs)', fontSize:30, letterSpacing:'-0.8px', lineHeight:1.1, marginBottom:22 }}>Importação <em style={{ fontStyle:'italic', color:'var(--accent)' }}>concluída.</em></div>
              <div style={{ display:'flex', gap:12, justifyContent:'center', flexWrap:'wrap', marginBottom:28 }}>
                <div style={{ padding:'14px 22px', background:'var(--leaf-soft)', borderRadius:14, minWidth:104 }}>
                  <div style={{ fontFamily:'var(--fs)', fontSize:32 }}>{resultado.criados}</div>
                  <div className="t-eyebrow" style={{ color:'var(--leaf)', marginTop:4 }}>Criados</div>
                </div>
                <div style={{ padding:'14px 22px', background:'var(--sky-soft)', borderRadius:14, minWidth:104 }}>
                  <div style={{ fontFamily:'var(--fs)', fontSize:32 }}>{resultado.atualizados}</div>
                  <div className="t-eyebrow" style={{ color:'var(--teal)', marginTop:4 }}>Atualizados</div>
                </div>
                {resultado.rejeitados > 0 && (
                  <div style={{ padding:'14px 22px', background:'var(--accent-soft)', borderRadius:14, minWidth:104 }}>
                    <div style={{ fontFamily:'var(--fs)', fontSize:32 }}>{resultado.rejeitados}</div>
                    <div className="t-eyebrow" style={{ color:'var(--accent)', marginTop:4 }}>Ignorados</div>
                  </div>
                )}
              </div>
              {resultado.motivos && resultado.motivos.length > 0 && (
                <div style={{ textAlign:'left', maxWidth:520, margin:'0 auto 24px', background:'var(--accent-soft)', borderRadius:14, padding:'14px 16px' }}>
                  <div className="t-eyebrow" style={{ color:'var(--accent)', marginBottom:8 }}>Por que foram ignorados</div>
                  {resultado.motivos.map((m, i) => (
                    <div key={i} style={{ fontFamily:'var(--fm)', fontSize:11.5, color:'var(--ink)', marginBottom:5, lineHeight:1.45 }}>
                      <b>Linha {m.linha}{m.nome?` · ${m.nome}`:''}:</b> <span style={{ color:'var(--accent-2)' }}>{m.erro}</span>
                    </div>
                  ))}
                </div>
              )}
              <div style={{ display:'flex', gap:8, justifyContent:'center' }}>
                <button className="btn" onClick={resetar}>Nova importação</button>
                <button className="btn btn-primary" onClick={() => onClose && onClose()}>Fechar</button>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// ─── CONQUISTAS ───
const SECTIONS = [
  {id:'cinema',      label:'Cinema',        label_en:'Cinema',       icon:'film',   tone:'sun'},
  {id:'musica',      label:'Música',        label_en:'Music',        icon:'music',  tone:'sky'},
  {id:'eventos',     label:'Eventos',       label_en:'Events',       icon:'calendar',tone:'leaf'},
  {id:'bemestar',    label:'Bem-estar',     label_en:'Wellness',     icon:'heart',  tone:'rose'},
  {id:'aprendizado', label:'Aprendizado',   label_en:'Learning',     icon:'book',   tone:'sky'},
  {id:'refeitorio',  label:'Refeitório',    label_en:'Mess hall',    icon:'fork',   tone:'sun'},
  {id:'vidaabordo',  label:'Vida a bordo',  label_en:'Life on board',icon:'anchor', tone:'leaf'},
];

// ═══ 🏭 Criador de Conquistas — o admin fabrica medalhas manuais ou automáticas por métrica ═══
const CQ_TIERS = ['🥉 Bronze', '🥈 Prata', '🥇 Ouro'];
const CQ_CATEGORIAS = ['Operação', 'Saúde', 'Convivência', 'Especial'];
const cqH = () => ({ 'Authorization': `Bearer ${localStorage.getItem('genesis_token')}`, 'Content-Type': 'application/json' });

const CriadorConquistaModal = ({ cq, metricas, toast, onClose, onSaved }) => {
  const [d, setD] = useState(cq ? { ...cq, marcos: (() => { try { return JSON.parse(cq.marcos || '[]'); } catch (e) { return []; } })() }
    : { nome: '', descricao: '', emoji: '🏆', imagem_url: '', categoria: 'Especial', tipo: 'manual', metrica: '', marcos: [] });
  const [salvando, setSalvando] = useState(false);
  const [subindo, setSubindo] = useState(false);
  const [sim, setSim] = useState(null);          // null · 'rodando' · resultado
  const up = (k, v) => setD(p => ({ ...p, [k]: v }));
  const mSel = metricas.find(m => m.id === d.metrica);
  const marcosNum = d.marcos.map(Number).filter(x => Number.isFinite(x) && x > 0);
  const marcosOk = d.tipo !== 'metrica' || (marcosNum.length > 0 && marcosNum.every((v, i) => i === 0 || v > marcosNum[i - 1]));

  const subirImagem = async (e) => {
    const file = e.target.files[0]; if (!file) return;
    setSubindo(true);
    const fd = new FormData(); fd.append('photo', file);
    try {
      const r = await fetch('/api/upload/photo', { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('genesis_token')}` }, body: fd });
      const j = await r.json().catch(() => ({}));
      if (r.ok && j.url) { up('imagem_url', j.url); toast.push({ icon: 'check', title: 'Imagem no lugar 🖼' }); }
      else toast.push({ icon: 'x', title: j.error || 'Erro no upload' });
    } catch (err) { toast.push({ icon: 'x', title: 'Erro no upload' }); }
    setSubindo(false); e.target.value = '';
  };

  const simular = async () => {
    if (!d.metrica || !marcosOk) return;
    setSim('rodando');
    const r = await fetch('/api/users/conquistas-custom/simular', { method: 'POST', headers: cqH(), body: JSON.stringify({ metrica: d.metrica, marcos: marcosNum }) });
    const j = await r.json().catch(() => null);
    setSim(r.ok ? j : null);
    if (!r.ok) toast.push({ icon: 'x', title: (j && j.error) || 'Simulação falhou' });
  };

  const salvar = async () => {
    if (!d.nome.trim()) { toast.push({ icon: 'x', title: 'Nome obrigatório' }); return; }
    if (d.tipo === 'metrica' && (!d.metrica || !marcosOk)) { toast.push({ icon: 'x', title: 'Escolhe a métrica e marcos crescentes' }); return; }
    setSalvando(true);
    const corpo = { nome: d.nome, descricao: d.descricao, emoji: d.emoji, imagem_url: d.imagem_url || null, categoria: d.categoria, tipo: d.tipo, metrica: d.tipo === 'metrica' ? d.metrica : null, marcos: d.tipo === 'metrica' ? marcosNum : null };
    const r = cq && cq.id
      ? await fetch(`/api/users/conquistas-custom/${cq.id}`, { method: 'PUT', headers: cqH(), body: JSON.stringify(corpo) })
      : await fetch('/api/users/conquistas-custom', { method: 'POST', headers: cqH(), body: JSON.stringify(corpo) });
    setSalvando(false);
    if (r.ok) { toast.push({ icon: 'check', title: cq ? 'Conquista atualizada' : 'Conquista criada 🏭' }); onSaved(); onClose(); }
    else { const j = await r.json().catch(() => ({})); toast.push({ icon: 'x', title: j.error || 'Erro ao salvar' }); }
  };

  return (
    <div onClick={onClose} style={{ position: 'fixed', inset: 0, zIndex: 9200, background: 'rgba(31,42,46,0.5)', display: 'flex', alignItems: 'flex-start', justifyContent: 'center', padding: '5vh 14px', overflowY: 'auto' }}>
      <div onClick={e => e.stopPropagation()} style={{ width: '100%', maxWidth: 540, background: 'var(--cream)', borderRadius: 18, border: '1px solid var(--line-soft)', borderTop: '3px solid var(--sun)', padding: '18px 20px', boxShadow: 'var(--shadow-lg)', marginBottom: 40 }}>
        <div style={{ fontFamily: 'var(--fs)', fontSize: 20, marginBottom: 12 }}>🏭 {cq ? 'Editar conquista' : 'Nova conquista'}</div>

        <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 10, marginBottom: 10 }}>
          <div>
            <label className="label">Nome</label>
            <input className="input" style={{ width: '100%', boxSizing: 'border-box' }} placeholder="Mobilização Wahoo 2026" value={d.nome} onChange={e => up('nome', e.target.value)} />
          </div>
          <div>
            <label className="label">Categoria</label>
            <select className="input select" style={{ width: '100%', boxSizing: 'border-box' }} value={d.categoria || 'Especial'} onChange={e => up('categoria', e.target.value)}>
              {CQ_CATEGORIAS.map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          </div>
        </div>

        <label className="label">Descrição</label>
        <input className="input" style={{ width: '100%', boxSizing: 'border-box', marginBottom: 10 }} placeholder="o que essa medalha celebra" value={d.descricao || ''} onChange={e => up('descricao', e.target.value)} />

        <div style={{ display: 'flex', gap: 10, alignItems: 'flex-end', marginBottom: 12 }}>
          <div style={{ width: 90 }}>
            <label className="label">Emoji</label>
            <input className="input" style={{ width: '100%', boxSizing: 'border-box', textAlign: 'center', fontSize: 18 }} maxLength={4} value={d.emoji || ''} onChange={e => up('emoji', e.target.value)} />
          </div>
          <div style={{ flex: 1 }}>
            <label className="label">Imagem da medalha (opcional — vale mais que o emoji)</label>
            <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
              {d.imagem_url ? <img src={d.imagem_url} alt="" style={{ width: 38, height: 38, borderRadius: '50%', objectFit: 'cover', flexShrink: 0 }} /> : null}
              <label className="btn btn-sm" style={{ cursor: 'pointer', margin: 0 }}>
                {subindo ? '…' : d.imagem_url ? '🖼 Trocar' : '🖼 Subir imagem'}
                <input type="file" accept="image/*" style={{ display: 'none' }} onChange={subirImagem} />
              </label>
              {d.imagem_url ? <button className="btn ghost btn-sm" onClick={() => up('imagem_url', '')}>✕</button> : null}
            </div>
          </div>
        </div>

        <label className="label">Como ela é conquistada?</label>
        <div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
          {[['manual', '🤝 Concedida na mão'], ['metrica', '📈 Automática por métrica']].map(([id, rot]) => (
            <button key={id} onClick={() => up('tipo', id)} className={d.tipo === id ? 'btn btn-primary btn-sm' : 'btn btn-sm'} style={{ flex: 1 }}>{rot}</button>
          ))}
        </div>

        {d.tipo === 'metrica' ? (
          <div style={{ background: 'var(--cream-2)', borderRadius: 14, padding: '12px 14px', marginBottom: 12 }}>
            <label className="label">Métrica (dados reais do banco de bordo)</label>
            <select className="input select" style={{ width: '100%', boxSizing: 'border-box', marginBottom: 4 }} value={d.metrica} onChange={e => { up('metrica', e.target.value); setSim(null); }}>
              <option value="">— escolhe a métrica —</option>
              {metricas.map(m => <option key={m.id} value={m.id}>{m.nome}</option>)}
            </select>
            {mSel ? <div style={{ fontFamily: 'var(--fm)', fontSize: 10.5, color: 'var(--muted)', marginBottom: 10 }}>{mSel.desc}</div> : <div style={{ marginBottom: 10 }} />}

            <label className="label">Marcos ({mSel ? mSel.unidade : 'valor'} mínimo pra cada tier — deixa vazio pra não usar o tier)</label>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 8, marginBottom: 10 }}>
              {CQ_TIERS.map((rot, i) => (
                <div key={i}>
                  <div style={{ fontFamily: 'var(--fm)', fontSize: 10, color: 'var(--muted)', marginBottom: 3 }}>{rot}</div>
                  <input className="input" type="number" inputMode="numeric" min="1" style={{ width: '100%', boxSizing: 'border-box' }} placeholder={String([5, 15, 30][i])}
                    value={d.marcos[i] == null || d.marcos[i] === '' ? '' : d.marcos[i]}
                    onChange={e => setD(p => { const m = [...p.marcos]; m[i] = e.target.value; while (m.length && (m[m.length-1] === '' || m[m.length-1] == null)) m.pop(); return { ...p, marcos: m }; })} />
                </div>
              ))}
            </div>
            {!marcosOk && d.metrica ? <div style={{ fontSize: 11, color: 'var(--rose)', fontFamily: 'var(--fm)', marginBottom: 8 }}>⚠ marcos precisam ser números crescentes (ex.: 5 · 15 · 30)</div> : null}

            <button className="btn btn-sm" style={{ background: 'var(--sky)', color: '#fff', borderColor: 'transparent' }} onClick={simular} disabled={!d.metrica || !marcosOk || sim === 'rodando'}>
              {sim === 'rodando' ? '…' : '🔍 Simular: quem bateria hoje?'}
            </button>
            {sim && sim !== 'rodando' ? (
              <div style={{ marginTop: 10, fontSize: 12 }}>
                <div style={{ fontFamily: 'var(--fm)', fontSize: 10.5, color: 'var(--muted)', marginBottom: 4 }}>
                  {sim.avaliados} pessoa(s) avaliada(s) · {(sim.por_tier || []).map((n, i) => `${['🥉','🥈','🥇'][i]} ${n}`).join(' · ')}
                </div>
                {(sim.top || []).filter(t => t.valor > 0).slice(0, 5).map((t, i) => (
                  <div key={i} style={{ display: 'flex', gap: 8, padding: '2px 0', fontFamily: 'var(--fm)', fontSize: 11 }}>
                    <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{t.nivel > 0 ? ['🥉','🥈','🥇'][t.nivel - 1] : '·'} {t.nome}</span>
                    <b>{t.valor}</b>
                  </div>
                ))}
              </div>
            ) : null}
          </div>
        ) : (
          <div style={{ fontSize: 11.5, color: 'var(--muted)', fontFamily: 'var(--fm)', marginBottom: 12 }}>medalha de mérito: tu concede pessoa por pessoa na lista do criador.</div>
        )}

        <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, borderTop: '1px solid var(--line-soft)', paddingTop: 12 }}>
          <button className="btn" onClick={onClose}>Cancelar</button>
          <button className="btn btn-primary" onClick={salvar} disabled={salvando}>{salvando ? '…' : cq ? 'Salvar' : 'Criar conquista'}</button>
        </div>
      </div>
    </div>
  );
};

const CriadorConquistas = ({ pessoas, toast }) => {
  const [lista, setLista] = useState(null);
  const [metricas, setMetricas] = useState([]);
  const [modal, setModal] = useState(null);      // null | {} | {cq}
  const [avaliando, setAvaliando] = useState(false);
  const carregar = () => {
    fetch('/api/users/conquistas-custom', { headers: cqH() }).then(r => r.ok ? r.json() : []).then(d => setLista(Array.isArray(d) ? d : [])).catch(() => setLista([]));
  };
  useEffect(() => {
    carregar();
    fetch('/api/users/conquistas-custom/metricas', { headers: cqH() }).then(r => r.ok ? r.json() : []).then(d => setMetricas(Array.isArray(d) ? d : [])).catch(() => {});
  }, []);

  const avaliar = async () => {
    setAvaliando(true);
    const r = await fetch('/api/users/conquistas-custom/avaliar', { method: 'POST', headers: cqH() });
    const j = await r.json().catch(() => ({}));
    setAvaliando(false);
    if (r.ok) { toast.push({ icon: 'check', title: `⚡ Motor rodou: ${j.concessoes} concessão(ões) nova(s)` }); carregar(); }
    else toast.push({ icon: 'x', title: j.error || 'Erro ao avaliar' });
  };

  return (
    <section style={{ marginTop: 40, background: 'var(--surface)', border: '1px solid var(--line-soft)', borderTop: '3px solid var(--sun)', borderRadius: 18, padding: '18px 22px', boxShadow: 'var(--shadow-sm)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap', marginBottom: 4 }}>
        <div style={{ fontFamily: 'var(--fs)', fontSize: 19, flex: 1 }}>🏭 Criador de conquistas</div>
        <button className="btn btn-sm" style={{ background: 'var(--sky)', color: '#fff', borderColor: 'transparent' }} onClick={avaliar} disabled={avaliando}>{avaliando ? '…' : '⚡ Avaliar agora'}</button>
        <button className="btn btn-primary btn-sm" onClick={() => setModal({})}>＋ Nova conquista</button>
      </div>
      <div style={{ fontFamily: 'var(--fm)', fontSize: 10.5, color: 'var(--muted)', marginBottom: 12 }}>
        medalhas manuais (🤝 tu concede) ou automáticas (📈 o motor concede sozinho todo dia às 03:10, pelos dados reais do banco).
      </div>

      {lista === null ? <div className="empty" style={{ padding: 16 }}>carregando…</div>
        : !lista.length ? <div className="empty" style={{ padding: 20, textAlign: 'center' }}>nenhuma conquista criada ainda — inaugura a fábrica 👆</div>
        : (
          <div style={{ display: 'grid', gap: 8 }}>
            {lista.map(cq => {
              const marcos = (() => { try { return JSON.parse(cq.marcos || '[]'); } catch (e) { return []; } })();
              return (
                <div key={cq.id} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '9px 12px', borderRadius: 12, border: '1px solid var(--line-soft)', background: 'var(--cream)', minWidth: 0 }}>
                  <div style={{ width: 38, height: 38, borderRadius: '50%', overflow: 'hidden', display: 'grid', placeItems: 'center', fontSize: 17, flexShrink: 0, background: cq.imagem_url ? 'var(--ink)' : 'linear-gradient(135deg, var(--teal), var(--sky))' }}>
                    {cq.imagem_url ? <img src={cq.imagem_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }} /> : (cq.emoji || '🏆')}
                  </div>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                      {cq.nome}
                      <span style={{ fontFamily: 'var(--fm)', fontSize: 9.5, fontWeight: 400, color: 'var(--muted)' }}> · {cq.categoria || 'Especial'} · {(cq.pessoas || []).length} pessoa(s)</span>
                    </div>
                    <div style={{ fontFamily: 'var(--fm)', fontSize: 10, color: 'var(--muted)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
                      {cq.tipo === 'metrica'
                        ? `📈 ${(metricas.find(m => m.id === cq.metrica) || { nome: cq.metrica }).nome} · marcos ${marcos.join(' / ')}`
                        : '🤝 concedida na mão'}
                      {cq.descricao ? ` — ${cq.descricao}` : ''}
                    </div>
                  </div>
                  {cq.tipo !== 'metrica' ? (
                    <select className="select" value="" onChange={async e => {
                      const uid = parseInt(e.target.value); if (isNaN(uid)) return;
                      await fetch(`/api/users/conquistas-custom/${cq.id}/atribuir`, { method: 'POST', headers: cqH(), body: JSON.stringify({ user_ids: [uid] }) });
                      toast.push({ icon: 'check', title: 'Concedida 🏆' }); carregar();
                    }} style={{ fontFamily: 'var(--fm)', fontSize: 10.5, maxWidth: 130, flexShrink: 0 }}>
                      <option value="">+ conceder a…</option>
                      {pessoas.filter(p => !(cq.pessoas || []).includes(p.id)).map(p => <option key={p.id} value={p.id}>{p.nome}</option>)}
                    </select>
                  ) : null}
                  <button className="btn ghost btn-sm" style={{ flexShrink: 0, padding: '3px 8px' }} onClick={() => setModal({ cq })}>✎</button>
                  <button className="btn ghost btn-sm" style={{ flexShrink: 0, padding: '3px 8px', color: 'var(--rose)' }} onClick={async () => {
                    if (!confirm(`Excluir "${cq.nome}" de todo mundo? Não tem volta.`)) return;
                    await fetch(`/api/users/conquistas-custom/${cq.id}`, { method: 'DELETE', headers: cqH() });
                    toast.push({ icon: 'check', title: 'Conquista excluída' }); carregar();
                  }}>✕</button>
                </div>
              );
            })}
          </div>
        )}
      {modal ? <CriadorConquistaModal cq={modal.cq} metricas={metricas} toast={toast} onClose={() => setModal(null)} onSaved={carregar} /> : null}
    </section>
  );
};

const Conquistas = ({state, setState}) => {
  const t = useT();
  const toastCq = useToast();
  const isEN = t.lang === 'en';
  const usersById = useMemo(() => Object.fromEntries(state.users.map(u=>[u.id,u])), [state.users]);
  const [section, setSection] = useState('all');
  const [reacted, setReacted] = useState({}); // local per-session

  const week = state.achievements.filter(a => a.week);
  const filtered = state.achievements.filter(a => section === 'all' || a.sec === section);

  const react = (id, kind) => {
    const key = id+'_'+kind;
    if (reacted[key]) return;
    setReacted(r => ({...r, [key]:true}));
    setState(s => ({...s, achievements: s.achievements.map(a => a.id===id ? {...a, reactions:{...(a.reactions||{}), [kind]:((a.reactions||{})[kind]||0)+1}} : a)}));
  };

  const sectionMeta = (id) => SECTIONS.find(s=>s.id===id) || SECTIONS[0];

  return (
    <main style={{padding:'30px 36px 56px', flex:1}}>
      {/* Mural semanal — full-bleed editorial */}
      <section style={{marginBottom:36}}>
        <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',marginBottom:16,gap:14}}>
          <div>
            <div className="t-eyebrow" style={{color:'var(--accent)',marginBottom:4}}>{isEN?'this week':'esta semana'}</div>
            <h1 style={{fontFamily:'var(--fs)',fontSize:46,letterSpacing:'-1px',lineHeight:1,fontWeight:400}}>
              <em style={{fontStyle:'italic',color:'var(--accent)'}}>{isEN?'Mural':'Mural'}</em> {isEN?'of accomplishments':'de conquistas'}
            </h1>
            <div style={{fontSize:14,color:'var(--muted)',marginTop:6,fontStyle:'italic',fontFamily:'var(--fs)'}}>
              {isEN?'celebrate together — give a clap, a fire, a heart.':'celebrar junto — dê uma palma, um fogo, um coração.'}
            </div>
          </div>
          <Pill tone="leaf" live>{week.length} {isEN?'this week':'esta semana'}</Pill>
        </div>

        <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(300px, 1fr))',gap:14}}>
          {week.map(a => {
            const u = usersById[a.by];
            const meta = sectionMeta(a.sec);
            return (
              <article key={a.id} style={{
                background:`linear-gradient(160deg, var(--${meta.tone}-soft) 0%, var(--cream) 80%)`,
                border:'1px solid var(--line-soft)', borderRadius:22, padding:'22px 24px',
                display:'flex',flexDirection:'column',gap:14
              }}>
                <div style={{display:'flex',alignItems:'center',gap:10}}>
                  <div style={{width:28,height:28,borderRadius:8,display:'grid',placeItems:'center',background:'var(--cream)',color:'var(--ink-2)'}}>
                    <Icon n={meta.icon} size={15}/>
                  </div>
                  <div className="t-eyebrow">{isEN?meta.label_en:meta.label}</div>
                  <div style={{flex:1}}/>
                  <div style={{fontFamily:'var(--fm)',fontSize:10.5,color:'var(--muted)'}}>{a.when?.slice(5)||''}</div>
                </div>
                <div>
                  <div style={{fontFamily:'var(--fs)',fontSize:24,letterSpacing:'-0.4px',lineHeight:1.15}}>{a.titulo}</div>
                  <div style={{fontSize:13,color:'var(--muted)',marginTop:4,fontStyle:'italic',fontFamily:'var(--fs)'}}>{a.desc}</div>
                </div>
                <div style={{display:'flex',alignItems:'center',gap:10,marginTop:'auto',paddingTop:8,borderTop:'1px solid var(--line-soft)'}}>
                  {u && <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)'}}>{u?.funcao}</div>
                  </div>
                </div>
                <div style={{display:'flex',gap:6}}>
                  {[
                    {kind:'clap', emoji:'👏'},
                    {kind:'fire', emoji:'🔥'},
                    {kind:'heart', emoji:'❤️'},
                  ].map(r => {
                    const did = reacted[a.id+'_'+r.kind];
                    return (
                      <button key={r.kind} onClick={()=>react(a.id, r.kind)} disabled={did}
                        style={{
                          flex:1, border:'1px solid var(--line-soft)',
                          background: did ? 'var(--accent-soft, var(--cream-2))' : 'var(--cream)',
                          color:'var(--ink-2)', borderRadius:10, padding:'7px 10px',
                          fontFamily:'var(--fm)', fontSize:12, cursor: did?'default':'pointer',
                          display:'flex',alignItems:'center',justifyContent:'center',gap:6,
                          transition:'all .15s', opacity: did?0.85:1
                        }}>
                        <span style={{fontSize:14}}>{r.emoji}</span>
                        <span>{(a.reactions||{})[r.kind] || 0}</span>
                      </button>
                    );
                  })}
                </div>
              </article>
            );
          })}
          {!week.length && <div className="empty" style={{padding:60,gridColumn:'1/-1'}}>{isEN?'no highlights this week.':'sem destaques esta semana.'}</div>}
        </div>
      </section>

      {/* Section filter */}
      <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',marginBottom:14,gap:14,flexWrap:'wrap'}}>
        <h2 style={{fontFamily:'var(--fs)',fontSize:28,letterSpacing:'-0.6px',fontWeight:400}}>
          {isEN?'All achievements':'Todas as conquistas'}
        </h2>
        <div style={{display:'flex',gap:6,flexWrap:'wrap'}}>
          <button onClick={()=>setSection('all')} className={section==='all'?'btn btn-primary btn-sm':'btn btn-sm'}>
            {isEN?'All':'Todas'}
          </button>
          {SECTIONS.map(s => (
            <button key={s.id} onClick={()=>setSection(s.id)} className={section===s.id?'btn btn-primary btn-sm':'btn btn-sm'}>
              <Icon n={s.icon} size={12}/>{isEN?s.label_en:s.label}
            </button>
          ))}
        </div>
      </div>

      {section === 'all' ? (
        <div style={{display:'flex',flexDirection:'column',gap:30}}>
          {SECTIONS.map(s => {
            const items = state.achievements.filter(a => a.sec === s.id);
            if (!items.length) return null;
            return <SectionRow key={s.id} section={s} items={items} usersById={usersById} isEN={isEN}/>;
          })}
        </div>
      ) : (
        <SectionRow section={sectionMeta(section)} items={filtered} usersById={usersById} isEN={isEN} expanded/>
      )}

      {(state.currentUser && (state.currentUser.role === 'admin' || state.currentUser.is_staff === true)) ? (
        <CriadorConquistas toast={toastCq} pessoas={(state.users || []).filter(u => u.nome).sort((a, b) => a.nome.localeCompare(b.nome))} />
      ) : null}
    </main>
  );
};

const SectionRow = ({section, items, usersById, isEN, expanded=false}) => (
  <div>
    <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:14,paddingBottom:10,borderBottom:'1px solid var(--line-soft)'}}>
      <div style={{width:32,height:32,borderRadius:10,display:'grid',placeItems:'center',background:`var(--${section.tone}-soft)`,color:'var(--ink-2)'}}>
        <Icon n={section.icon} size={16}/>
      </div>
      <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.4px'}}>{isEN?section.label_en:section.label}</div>
      <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>{items.length} {isEN?'badges':'conquistas'}</div>
    </div>
    <div style={{display:'grid',gridTemplateColumns:`repeat(auto-fill, minmax(${expanded?260:240}px, 1fr))`,gap:12}}>
      {items.map(a => {
        const u = usersById[a.by];
        return (
          <div key={a.id} style={{
            background:'var(--cream)', border:'1px solid var(--line-soft)', borderRadius:16,
            padding:'18px 20px', display:'flex',flexDirection:'column',gap:10
          }}>
            <div>
              <div style={{fontFamily:'var(--fs)',fontSize:18,letterSpacing:'-0.3px',lineHeight:1.2}}>{a.titulo}</div>
              <div style={{fontSize:12,color:'var(--muted)',marginTop:3,fontStyle:'italic',fontFamily:'var(--fs)'}}>{a.desc}</div>
            </div>
            {u && (
              <div style={{display:'flex',alignItems:'center',gap:8,marginTop:'auto',paddingTop:8,borderTop:'1px solid var(--line-soft)'}}>
                <Avatar user={u} size="sm"/>
                <div style={{flex:1,minWidth:0}}>
                  <div style={{fontSize:12.5,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{u.nome}</div>
                  <div style={{fontSize:10,color:'var(--muted)',fontFamily:'var(--fm)'}}>{u.funcao}</div>
                </div>
              </div>
            )}
          </div>
        );
      })}
    </div>
  </div>
);

// ─── FilterModal — filtro avançado de pessoas ───
const FilterModal = ({filters, users, onApply, onClose}) => {
  const [local, setLocal] = useState({...filters});
  const [empresaSugg, setEmpresaSugg] = useState([]);
  const [funcaoSugg, setFuncaoSugg] = useState([]);
  const [showEmpresa, setShowEmpresa] = useState(false);
  const [showFuncao, setShowFuncao] = useState(false);

  // Listas únicas para autocomplete
  const allEmpresas = [...new Set(users.map(u=>u.empresa).filter(Boolean))].sort();
  const allFuncoes  = [...new Set(users.map(u=>u.funcao).filter(Boolean))].sort();

  const updateEmpresa = (v) => {
    setLocal(l=>({...l,empresa:v}));
    setEmpresaSugg(v.length > 0 ? allEmpresas.filter(e=>e.toLowerCase().includes(v.toLowerCase())).slice(0,6) : []);
    setShowEmpresa(true);
  };
  const updateFuncao = (v) => {
    setLocal(l=>({...l,funcao:v}));
    setFuncaoSugg(v.length > 0 ? allFuncoes.filter(f=>f.toLowerCase().includes(v.toLowerCase())).slice(0,6) : []);
    setShowFuncao(true);
  };

  const activeCount = [
    local.status !== 'all', local.classe !== 'all',
    local.empresa !== '', local.funcao !== ''
  ].filter(Boolean).length;

  // Contadores por opção + total ao vivo conforme o filtro sendo montado
  const nAbordo = users.filter(u=>u.status_bordo==='a_bordo').length;
  const nTerra  = users.length - nAbordo;
  const classeCounts = { crew:0, ops:0, catering:0, terceirizada:0, guest:0, staff:0 };
  users.forEach(u => { if (classeCounts[u.role] !== undefined) classeCounts[u.role]++; if (u.is_staff) classeCounts.staff++; });
  const liveCount = users.filter(u => {
    if (local.status==='a_bordo'  && u.status_bordo!=='a_bordo')  return false;
    if (local.status==='em_terra' && u.status_bordo!=='em_terra') return false;
    if (local.classe==='crew'  && u.role!=='crew')  return false;
    if (local.classe==='ops'   && u.role!=='ops')   return false;
    if (local.classe==='catering' && u.role!=='catering') return false;
    if (local.classe==='terceirizada' && u.role!=='terceirizada') return false;
    if (local.classe==='guest' && u.role!=='guest') return false;
    if (local.classe==='staff' && !u.is_staff) return false;
    if (local.empresa && !(u.empresa||'').toLowerCase().includes(local.empresa.toLowerCase())) return false;
    if (local.funcao  && !(u.funcao||'').toLowerCase().includes(local.funcao.toLowerCase()))   return false;
    return true;
  }).length;

  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(31,42,46,0.45)',backdropFilter:'blur(6px)',zIndex:8000,display:'grid',placeItems:'center',padding:20}}>
      <div onClick={e=>e.stopPropagation()} style={{
        width:'100%',maxWidth:480,background:'var(--cream)',
        borderRadius:28,padding:'30px 32px',
        boxShadow:'0 32px 80px -20px rgba(31,42,46,0.25)',
        animation:'slideIn .25s both'
      }}>
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',marginBottom:24}}>
          <div style={{fontFamily:'var(--fs)',fontSize:26,letterSpacing:'-0.5px'}}>
            Filtrar <em style={{fontStyle:'italic',color:'var(--accent)'}}>pessoas.</em>
          </div>
          <button className="btn ghost" onClick={onClose}><Icon n="x" size={14}/></button>
        </div>

        <div style={{display:'grid',gap:18}}>
          {/* Status de embarque */}
          <div>
            <label className="label">Status de embarque</label>
            <div style={{display:'flex',gap:8,marginTop:6}}>
              {[
                {id:'all',      label:'Todos',       n:users.length},
                {id:'a_bordo',  label:'🟢 A bordo',  n:nAbordo},
                {id:'em_terra', label:'🔴 Em terra', n:nTerra},
              ].map(o=>(
                <button key={o.id} onClick={()=>setLocal(l=>({...l,status:o.id}))}
                  className={local.status===o.id?'btn btn-primary btn-sm':'btn btn-sm'}
                  style={{flex:1}}>
                  {o.label} <span style={{opacity:.65,fontSize:10}}>{o.n}</span>
                </button>
              ))}
            </div>
          </div>

          {/* Classe */}
          <div>
            <label className="label">Classe do perfil</label>
            <div style={{display:'flex',gap:8,marginTop:6,flexWrap:'wrap'}}>
              {[
                {id:'all',   label:'Todas',        n:users.length},
                {id:'crew',  label:'Tripulação',   n:classeCounts.crew},
                {id:'ops',   label:'Operações',    n:classeCounts.ops},
        {id:'catering', label:'Catering',          n:classeCounts.catering},
        {id:'terceirizada', label:'Terceirizadas', n:classeCounts.terceirizada},
                {id:'guest', label:'Visitante',    n:classeCounts.guest},
                {id:'staff', label:'Gestão',       n:classeCounts.staff},
              ].map(o=>(
                <button key={o.id} onClick={()=>setLocal(l=>({...l,classe:o.id}))}
                  className={local.classe===o.id?'btn btn-primary btn-sm':'btn btn-sm'}>
                  {o.label} <span style={{opacity:.65,fontSize:10}}>{o.n}</span>
                </button>
              ))}
            </div>
          </div>

          {/* Empresa — autocomplete */}
          <div style={{position:'relative'}}>
            <label className="label">Empresa</label>
            <input className="input" style={{width:'100%',boxSizing:'border-box',marginTop:6}}
              placeholder="Digite para filtrar…"
              value={local.empresa}
              onChange={e=>updateEmpresa(e.target.value)}
              onFocus={()=>setShowEmpresa(true)}
              onBlur={()=>setTimeout(()=>setShowEmpresa(false),150)}
              autoComplete="off"/>
            {showEmpresa && empresaSugg.length > 0 && (
              <div style={{position:'absolute',top:'100%',left:0,right:0,background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:14,zIndex:10,overflow:'hidden',boxShadow:'0 8px 24px -8px rgba(31,42,46,0.15)',marginTop:4}}>
                {empresaSugg.map(e=>(
                  <div key={e} onMouseDown={()=>{setLocal(l=>({...l,empresa:e}));setShowEmpresa(false);}}
                    style={{padding:'10px 16px',cursor:'pointer',fontSize:13,borderBottom:'1px solid var(--line-soft)'}}
                    onMouseEnter={ev=>ev.currentTarget.style.background='var(--cream-2)'}
                    onMouseLeave={ev=>ev.currentTarget.style.background='transparent'}>
                    {e}
                  </div>
                ))}
              </div>
            )}
          </div>

          {/* Função — autocomplete */}
          <div style={{position:'relative'}}>
            <label className="label">Função</label>
            <input className="input" style={{width:'100%',boxSizing:'border-box',marginTop:6}}
              placeholder="Digite para filtrar…"
              value={local.funcao}
              onChange={e=>updateFuncao(e.target.value)}
              onFocus={()=>setShowFuncao(true)}
              onBlur={()=>setTimeout(()=>setShowFuncao(false),150)}
              autoComplete="off"/>
            {showFuncao && funcaoSugg.length > 0 && (
              <div style={{position:'absolute',top:'100%',left:0,right:0,background:'var(--cream)',border:'1px solid var(--line-soft)',borderRadius:14,zIndex:10,overflow:'hidden',boxShadow:'0 8px 24px -8px rgba(31,42,46,0.15)',marginTop:4}}>
                {funcaoSugg.map(f=>(
                  <div key={f} onMouseDown={()=>{setLocal(l=>({...l,funcao:f}));setShowFuncao(false);}}
                    style={{padding:'10px 16px',cursor:'pointer',fontSize:13,borderBottom:'1px solid var(--line-soft)'}}
                    onMouseEnter={ev=>ev.currentTarget.style.background='var(--cream-2)'}
                    onMouseLeave={ev=>ev.currentTarget.style.background='transparent'}>
                    {f}
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>

        <div style={{display:'flex',gap:10,marginTop:28}}>
          <button className="btn" style={{flex:1}} onClick={()=>{
            setLocal({status:'all',classe:'all',empresa:'',funcao:''});
          }}>
            Limpar filtros
          </button>
          <button className="btn btn-primary" style={{flex:2,padding:'12px'}}
            onClick={()=>onApply(local)}>
            {activeCount > 0
              ? `Aplicar · ${liveCount} pessoa${liveCount!==1?'s':''}`
              : `Ver todos · ${users.length}`}
          </button>
        </div>
      </div>
    </div>
  );
};

// ─── BulkConfirmModal ───
const BulkConfirmModal = ({modal, selectedUsers, onConfirm, onClose}) => {
  const [newRole, setNewRole] = useState('crew');
  const [confirmText, setConfirmText] = useState('');
  const [loading, setLoading] = useState(false);
  const { action } = modal;

  const guests = selectedUsers.filter(u => u.role === 'guest');
  const nonGuests = selectedUsers.filter(u => u.role !== 'guest');

  const config = {
    embarque: {
      title: 'Embarcar em massa',
      em: 'selecionados',
      color: 'var(--leaf)',
      bg: 'var(--leaf-soft)',
      icon: '🟢',
      warn: guests.length > 0 ? `${guests.length} visitante${guests.length!==1?'s':''} serão ignorados — precisam de crachá individual na gangway.` : null,
      desc: `${nonGuests.length} pessoa${nonGuests.length!==1?'s':''} serão marcadas como a bordo.`,
      needsConfirmText: false,
    },
    desembarque: {
      title: 'Desembarcar em massa',
      em: 'selecionados',
      color: '#C0392B',
      bg: '#FDECEA',
      icon: '🔴',
      warn: null,
      desc: `${selectedUsers.length} pessoa${selectedUsers.length!==1?'s':''} serão marcadas como em terra e removidas das cabines.`,
      needsConfirmText: false,
    },
    classe: {
      title: 'Mudar classe',
      em: 'selecionados',
      color: 'var(--teal)',
      bg: 'var(--sky-soft)',
      icon: '🎭',
      warn: null,
      desc: `${selectedUsers.length} pessoa${selectedUsers.length!==1?'s':''} terão a classe alterada.`,
      needsConfirmText: false,
    },
    delete: {
      title: 'Excluir pessoas',
      em: 'selecionados',
      color: 'var(--accent)',
      bg: 'var(--accent-soft)',
      icon: '🗑️',
      warn: 'Esta ação é irreversível. Todo o histórico dessas pessoas será perdido.',
      desc: `${selectedUsers.filter(u=>u.role!=='admin').length} pessoa${selectedUsers.filter(u=>u.role!=='admin').length!==1?'s':''} serão excluídas permanentemente.`,
      needsConfirmText: true,
    },
  };

  const c = config[action] || {};

  const handleConfirm = async () => {
    if (c.needsConfirmText && confirmText !== 'CONFIRMAR') return;
    setLoading(true);
    await onConfirm(action, action === 'classe' ? {role: newRole} : {});
    setLoading(false);
  };

  return (
    <div onClick={onClose} 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:460,background:'var(--cream)',
        borderRadius:28,padding:'30px 32px',
        boxShadow:'0 32px 80px -20px rgba(31,42,46,0.3)',
        animation:'slideIn .25s both'
      }}>
        {/* Header */}
        <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:20}}>
          <div style={{width:44,height:44,borderRadius:14,background:c.bg,display:'grid',placeItems:'center',fontSize:22,flexShrink:0}}>
            {c.icon}
          </div>
          <div>
            <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.4px'}}>{c.title}</div>
            <div style={{fontSize:12,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>
              {selectedUsers.length} {c.em}
            </div>
          </div>
        </div>

        {/* Lista de pessoas selecionadas */}
        <div style={{background:'var(--cream-2)',borderRadius:14,padding:'12px 14px',marginBottom:16,maxHeight:160,overflowY:'auto'}}>
          {selectedUsers.slice(0,8).map(u=>(
            <div key={u.id} style={{display:'flex',alignItems:'center',gap:8,padding:'5px 0',borderTop:'1px solid var(--line-soft)'}}>
              <Avatar user={u} size="sm"/>
              <div style={{flex:1,minWidth:0}}>
                <div style={{fontSize:12.5,fontWeight:500,whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>{u.nome}</div>
                <div style={{fontSize:10.5,color:'var(--muted)',fontFamily:'var(--fm)'}}>{u.role}</div>
              </div>
            </div>
          ))}
          {selectedUsers.length > 8 && (
            <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',padding:'6px 0',textAlign:'center'}}>
              +{selectedUsers.length - 8} mais…
            </div>
          )}
        </div>

        {/* Descrição */}
        <div style={{fontSize:13.5,color:'var(--ink)',marginBottom:c.warn?10:16,lineHeight:1.5}}>
          {c.desc}
        </div>

        {/* Aviso */}
        {c.warn && (
          <div style={{padding:'10px 14px',background:'rgba(217,105,75,0.08)',border:'1px solid rgba(217,105,75,0.2)',borderRadius:12,fontSize:12.5,color:'var(--accent-2)',marginBottom:16,lineHeight:1.5}}>
            ⚠️ {c.warn}
          </div>
        )}

        {/* Seletor de classe */}
        {action === 'classe' && (
          <div style={{marginBottom:16}}>
            <label className="label">Nova classe</label>
            <select className="input select" style={{width:'100%',boxSizing:'border-box'}}
              value={newRole} onChange={e=>setNewRole(e.target.value)}>
              <option value="crew">Tripulação</option>
              <option value="ops">Operações</option>
              <option value="catering">Catering</option>
              <option value="terceirizada">Terceirizada</option>
              <option value="guest">Visitante</option>
            </select>
          </div>
        )}

        {/* Campo de confirmação para delete */}
        {c.needsConfirmText && (
          <div style={{marginBottom:16}}>
            <label className="label">Digite CONFIRMAR para prosseguir</label>
            <input className="input" style={{width:'100%',boxSizing:'border-box',letterSpacing:2,fontFamily:'var(--fm)'}}
              placeholder="CONFIRMAR"
              value={confirmText} onChange={e=>setConfirmText(e.target.value.toUpperCase())}/>
          </div>
        )}

        {/* Botões */}
        <div style={{display:'flex',gap:10}}>
          <button className="btn" onClick={onClose} style={{flex:1}}>Cancelar</button>
          <button
            onClick={handleConfirm}
            disabled={loading || (c.needsConfirmText && confirmText !== 'CONFIRMAR')}
            style={{
              flex:2,padding:'12px',borderRadius:14,border:'none',cursor:'pointer',
              background:c.color,color:'white',
              fontFamily:'var(--fs)',fontStyle:'italic',fontSize:16,
              opacity:(loading||(c.needsConfirmText&&confirmText!=='CONFIRMAR'))?0.4:1,
              transition:'opacity .2s'
            }}>
            {loading ? 'Processando…' : `Confirmar ${c.title.toLowerCase()}`}
          </button>
        </div>
      </div>
    </div>
  );
};

// ─── ExportDropdown — reutilizável ───
const ExportDropdown = ({onPDF, onExcel}) => {
  const [open, setOpen] = useState(false);
  return (
    <div style={{position:'relative'}}>
      <button className="btn btn-sm" onClick={()=>setOpen(v=>!v)}
        style={{display:'flex',alignItems:'center',gap:6}}>
        <Icon n="upload" size={12}/>Exportar<span style={{fontSize:9,opacity:0.6}}>▾</span>
      </button>
      {open && (
        <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:()=>{setOpen(false);onExcel();}},
            {label:'📄 PDF',          action:()=>{setOpen(false);onPDF();}},
          ].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>
  );
};

// ─── CadastroExternoModal ─ painel de convites e aprovações ───
// Abas: Empresas (links + pedidos de listas), Colaboradores (link geral de auto-cadastro + pedidos individuais), Histórico (por empresa).
// Aprovar NÃO embarca ninguém: apenas cria o cadastro e marca embarque_pendente — o embarque acontece no portaló, via crachá.
// Agrupa linhas de cadastro_pendentes pelo lote (pedido) — registros antigos sem lote_id viram grupo próprio
const agruparLotes = (rows) => {
  const map = new Map();
  rows.forEach(r => { const k = r.lote_id || `single-${r.id}`; if (!map.has(k)) map.set(k, []); map.get(k).push(r); });
  return [...map.values()];
};

// Classes atribuídas pelo admin no momento da aprovação (users.role)
const CLASSES_CADASTRO = [
  { id: 'crew', label: 'Tripulação' },
  { id: 'ops', label: 'Operação' },
  { id: 'catering', label: 'Catering' },
  { id: 'terceirizada', label: 'Terceirizada' },
  { id: 'guest', label: 'Visitante' },
];

const CadastroExternoModal = ({onClose}) => {
  const toast = useToast();
  const token = localStorage.getItem('genesis_token');
  const H = { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' };
  const [aba, setAba] = useState('empresas');
  const [convites, setConvites] = useState([]);
  const [pendentes, setPendentes] = useState([]);
  const [historico, setHistorico] = useState([]);
  const [loading, setLoading] = useState(true);
  const [criando, setCriando] = useState(false);
  const [form, setForm] = useState({ empresa: '', max_usos: 1, expira_em_dias: 7 });
  const [detalhe, setDetalhe] = useState(null);
  const [copiado, setCopiado] = useState(false);
  const [ultimoAprovado, setUltimoAprovado] = useState(null);
  const [showRevogados, setShowRevogados] = useState(false);
  const [rolesSel, setRolesSel] = useState({});
  const [listas, setListas] = useState({ empresas: [], funcoes: [] });
  const [cadNovo, setCadNovo] = useState({}); // id -> { empresa: bool, funcao: bool } (default de cada flag = true)
  const [pedido, setPedido] = useState(null); // { loteId, rows, loading }
  const [editando, setEditando] = useState(null);
  const [editForm, setEditForm] = useState({});
  const [salvandoEdicao, setSalvandoEdicao] = useState(false);
  const [processandoLote, setProcessandoLote] = useState(null);

  const baseUrl = `${window.location.origin}/cadastro-externo.html`;
  const baseUrlTrip = `${window.location.origin}/cadastro-tripulante.html`;

  const loadConvites = async () => {
    const r = await fetch('/api/cadastro-externo/convites', { headers: H });
    if (r.ok) setConvites(await r.json());
  };
  const loadPendentes = async () => {
    const r = await fetch('/api/cadastro-externo/pendentes', { headers: H });
    if (r.ok) setPendentes(await r.json());
  };
  const loadHistorico = async () => {
    const r = await fetch('/api/cadastro-externo/historico-admin', { headers: H });
    if (r.ok) setHistorico(await r.json());
  };
  const loadListas = async () => {
    const r = await fetch('/api/cadastro-externo/listas-admin', { headers: H });
    if (r.ok) {
      const d = await r.json();
      setListas({ empresas: d.empresas || [], funcoes: d.funcoes || [] });
    }
  };

  useEffect(() => {
    (async () => {
      setLoading(true);
      await Promise.all([loadConvites(), loadPendentes(), loadHistorico(), loadListas()]);
      setLoading(false);
    })();
  }, []);

  // ─── Convites de empresas ───
  const criarConvite = async () => {
    setCriando(true);
    try {
      const r = await fetch('/api/cadastro-externo/convites', {
        method: 'POST', headers: H,
        body: JSON.stringify(form)
      });
      if (r.ok) {
        toast.push({icon:'check', title:'Convite criado'});
        setForm({ empresa: '', max_usos: 1, expira_em_dias: 7 });
        await loadConvites();
      } else {
        const d = await r.json();
        toast.push({icon:'x', title:'Erro ao criar convite', body: d.error});
      }
    } finally { setCriando(false); }
  };

  const revogar = async (id) => {
    if (!confirm('Revogar este convite? O link não funcionará mais.')) return;
    const r = await fetch(`/api/cadastro-externo/convites/${id}/revogar`, { method: 'POST', headers: H });
    if (r.ok) { toast.push({icon:'check', title:'Convite revogado'}); loadConvites(); }
    else toast.push({icon:'x', title:'Erro ao revogar'});
  };

  // ─── Link geral de auto-cadastro (colaboradores) ───
  // Gera o primeiro link ou rotaciona: o link anterior deixa de funcionar na hora
  const rotacionarLink = async () => {
    if (linkTrip && !confirm('Gerar um novo link de auto-cadastro? O link atual deixará de funcionar imediatamente.')) return;
    const r = await fetch('/api/cadastro-externo/convites/tripulantes/rotacionar', { method: 'POST', headers: H });
    if (r.ok) { toast.push({icon:'check', title:'Novo link gerado'}); loadConvites(); }
    else toast.push({icon:'x', title:'Erro ao gerar link'});
  };

  // ─── Aprovação / rejeição ───
  const rolePara = (p) => rolesSel[p.id] || (p.origem === 'autocadastro' ? 'crew' : 'terceirizada');

  const postAprovar = async (p) => {
    const body = p.tipo === 'novo' ? {
      role: rolePara(p),
      cadastrar_empresa: !!(p.empresa && !listas.empresas.includes(p.empresa) && (cadNovo[p.id]?.empresa !== false)),
      cadastrar_funcao: !!(p.funcao && !listas.funcoes.includes(p.funcao) && (cadNovo[p.id]?.funcao !== false)),
    } : {};
    const r = await fetch(`/api/cadastro-externo/pendentes/${p.id}/aprovar`, { method: 'POST', headers: H, body: JSON.stringify(body) });
    const d = await r.json().catch(() => ({}));
    return { ok: r.ok, data: d };
  };
  const postRejeitar = async (id, motivo) => {
    const r = await fetch(`/api/cadastro-externo/pendentes/${id}/rejeitar`, { method: 'POST', headers: H, body: JSON.stringify({ motivo: motivo || '' }) });
    return r.ok;
  };

  const aprovar = async (p) => {
    const r = await postAprovar(p);
    if (r.ok) {
      const d = r.data;
      toast.push({icon:'check', title:'Aprovado', body: d.genesis_id ? `Genesis ID: ${d.genesis_id}` : 'Embarque aprovado'});
      if (d.user_id) setUltimoAprovado({ id: p.id, user_id: d.user_id, genesis_id: d.genesis_id, nome: p.nome || '', temp_pass: d.temp_pass, email: d.email, autocadastro: !!d.autocadastro });
      setRolesSel(rs => { const n = {...rs}; delete n[p.id]; return n; });
      setCadNovo(m => { const n = {...m}; delete n[p.id]; return n; });
      loadPendentes(); loadConvites(); loadHistorico();
      if (pedido) abrirPedido(pedido.loteId);
    } else {
      toast.push({icon:'x', title:'Erro ao aprovar', body: r.data.error});
    }
  };

  const rejeitar = async (id) => {
    const motivo = prompt('Motivo da rejeição (opcional):');
    if (motivo === null) return;
    if (await postRejeitar(id, motivo)) {
      toast.push({icon:'check', title:'Rejeitado'});
      loadPendentes(); loadHistorico();
      if (pedido) abrirPedido(pedido.loteId);
    } else toast.push({icon:'x', title:'Erro ao rejeitar'});
  };

  // Aceite total/parcial: aprova ou rejeita todas as pessoas do pedido de uma vez
  const aprovarLote = async (g) => {
    if (!confirm(`Aprovar ${g.length} pessoa(s) deste pedido com as classes selecionadas?`)) return;
    const key = g[0].lote_id || g[0].id;
    setProcessandoLote(key);
    let ok = 0, falhas = 0, ultimo = null;
    for (const p of g) {
      const r = await postAprovar(p);
      if (r.ok) { ok++; if (r.data.user_id) ultimo = { p, d: r.data }; }
      else falhas++;
    }
    setProcessandoLote(null);
    setRolesSel({});
    setCadNovo({});
    if (ultimo) setUltimoAprovado({ id: ultimo.p.id, user_id: ultimo.d.user_id, genesis_id: ultimo.d.genesis_id, nome: ultimo.p.nome, temp_pass: ultimo.d.temp_pass, email: ultimo.d.email, autocadastro: !!ultimo.d.autocadastro });
    toast.push({icon: falhas ? 'x' : 'check', title: `${ok} aprovado(s)`, body: falhas ? `${falhas} falha(s) — revise na lista` : undefined});
    loadPendentes(); loadConvites(); loadHistorico();
    if (pedido) abrirPedido(pedido.loteId);
  };

  const rejeitarLote = async (g) => {
    const motivo = prompt(`Motivo da rejeição em lote de ${g.length} pessoa(s) (opcional):`);
    if (motivo === null) return;
    const key = g[0].lote_id || g[0].id;
    setProcessandoLote(key);
    let ok = 0, falhas = 0;
    for (const p of g) {
      if (await postRejeitar(p.id, motivo)) ok++; else falhas++;
    }
    setProcessandoLote(null);
    toast.push({icon: falhas ? 'x' : 'check', title: `${ok} rejeitado(s)`, body: falhas ? `${falhas} falha(s)` : undefined});
    loadPendentes(); loadHistorico();
    if (pedido) abrirPedido(pedido.loteId);
  };

  // ─── Pedido completo (todas as pessoas, qualquer status) ───
  const abrirPedido = async (loteId) => {
    setPedido({ loteId, rows: [], loading: true });
    const r = await fetch(`/api/cadastro-externo/lote/${encodeURIComponent(loteId)}`, { headers: H });
    if (r.ok) setPedido({ loteId, rows: await r.json(), loading: false });
    else { setPedido(null); toast.push({icon:'x', title:'Erro ao carregar pedido'}); }
  };

  // ─── Edição de dados do pendente ───
  const abrirEdicao = (p) => {
    setEditForm({
      nome: p.nome || '', email: p.email || '', empresa: p.empresa || '', funcao: p.funcao || '',
      turno: p.turno || '', genero: p.genero || '',
      data_nascimento: String(p.data_nascimento || '').slice(0, 10),
      nacionalidade: p.nacionalidade || '', cpf: p.cpf || '', passaporte: p.passaporte || '',
      telefone: p.telefone || '', data_prevista_embarque: String(p.data_prevista_embarque || '').slice(0, 10),
    });
    setEditando(p);
  };

  const salvarEdicao = async () => {
    setSalvandoEdicao(true);
    try {
      const r = await fetch(`/api/cadastro-externo/pendentes/${editando.id}`, { method: 'PUT', headers: H, body: JSON.stringify(editForm) });
      const d = await r.json().catch(() => ({}));
      if (r.ok) {
        toast.push({icon:'check', title:'Dados atualizados'});
        setEditando(null);
        loadPendentes();
        if (pedido) abrirPedido(pedido.loteId);
      } else toast.push({icon:'x', title:'Erro ao salvar', body: d.error});
    } finally { setSalvandoEdicao(false); }
  };

  // ─── Crachá e boas-vindas pós-aprovação ───
  const gerarCrachaAprovado = async (userId, nome) => {
    try {
      if (!window.renderCardCanvas) { toast.push({icon:'x', title:'Módulo de crachá não carregado'}); return; }
      const r = await fetch(`/api/users/${userId}`, { headers: H });
      if (!r.ok) throw new Error('Erro ao buscar usuário');
      const u = await r.json();
      const canvas = await window.renderCardCanvas(u);
      const dataUrl = canvas.toDataURL('image/png');
      const a = document.createElement('a');
      a.href = dataUrl;
      a.download = `cracha_${String(u.nome||u.genesis_id||userId).replace(/\s+/g,'_')}.png`;
      a.click();
      toast.push({icon:'check', title:'Crachá baixado'});
      const email = detalhe?.email || u.email;
      if (email && email.includes('@') && !email.includes('@genesis.local')) {
        const assunto = encodeURIComponent(`Crachá de acesso · ${u.nome}`);
        const corpo = encodeURIComponent(`Olá,\n\nSegue o crachá de acesso em anexo (arquivo PNG). Favor anexá-lo manualmente a este e-mail.\n\nGenesis ID: ${u.genesis_id || '—'}\nNome: ${u.nome}\n\nAtenciosamente,\nPortal Genesis`);
        window.location.href = `mailto:${email}?subject=${assunto}&body=${corpo}`;
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro ao gerar crachá', body: e.message});
    }
  };

  // E-mail de boas-vindas do auto-cadastro: abre o cliente de e-mail do admin já preenchido
  const enviarBoasVindas = (ap) => {
    const portal = window.location.origin;
    const assunto = encodeURIComponent('Acesso ao Portal Genesis — cadastro aprovado');
    const corpo = encodeURIComponent(
`Olá, ${ap.nome || ''}!

Seu cadastro no Portal Genesis foi aprovado. Siga os passos para o primeiro acesso:

1. Abra o portal: ${portal}
   (conectado à rede da embarcação, ou pelo endereço externo informado pela administração)
2. Entre com:
   Usuário: ${ap.genesis_id} (ou o seu e-mail)
   Senha temporária: ${ap.temp_pass}
3. No primeiro acesso, o portal pedirá que você crie uma nova senha pessoal — guarde-a com cuidado.

Qualquer problema, procure a administração do sistema.

Atenciosamente,
Portal Genesis`);
    window.location.href = `mailto:${ap.email}?subject=${assunto}&body=${corpo}`;
  };

  // fmt de data vinda do banco (DATE) sem problema de fuso
  const fmtD = (s) => { const m = String(s || '').slice(0, 10).split('-'); return m.length === 3 ? `${m[2]}/${m[1]}/${m[0]}` : (s || '—'); };

  const copiarLink = (tok, tipo, key) => {
    const pagina = tipo === 'trip' ? '/cadastro-tripulante.html' : '/cadastro-externo.html';
    const url = `${window.location.origin}${pagina}?token=${encodeURIComponent(tok)}`;
    navigator.clipboard.writeText(url).then(() => {
      setCopiado(key || tipo || 'emp'); setTimeout(() => setCopiado(false), 1500);
    }).catch(() => {
      prompt('Copie o link:', url);
    });
  };

  // ─── Derivações ───
  const convitesEmpresa = convites.filter(c => (c.tipo || 'empresa') === 'empresa');
  const revogadosCount = convitesEmpresa.filter(c => c.status === 'revogado').length;
  const convitesVisiveis = convitesEmpresa.filter(c => showRevogados || c.status !== 'revogado');
  const linkTrip = convites.find(c => c.tipo === 'tripulantes' && c.status === 'ativo');
  const pendentesEmpresa = pendentes.filter(p => p.origem !== 'autocadastro');
  const pendentesColab = pendentes.filter(p => p.origem === 'autocadastro');

  // ─── Render helpers ───
  const statusBadge = (st) => st === 'aprovado'
    ? <span style={{background:'var(--leaf-soft)',color:'var(--leaf)',fontSize:10,padding:'2px 8px',borderRadius:8}}>Aprovado</span>
    : st === 'rejeitado'
      ? <span style={{background:'var(--accent-soft)',color:'var(--accent-2)',fontSize:10,padding:'2px 8px',borderRadius:8}}>Rejeitado</span>
      : null;

  // Seletor de classe (só faz sentido para cadastro novo; embarque usa usuário existente)
  const roleSelect = (p) => p.tipo !== 'novo' ? null : (
    <select value={rolePara(p)} onChange={e=>setRolesSel(rs=>({...rs,[p.id]:e.target.value}))}
      title="Classe atribuída na aprovação"
      style={{fontSize:11,padding:'5px 6px',borderRadius:8,border:'1px solid var(--line-soft)',background:'var(--surface)',fontFamily:'var(--fm)',color:'var(--ink)'}}>
      {CLASSES_CADASTRO.map(c=><option key={c.id} value={c.id}>{c.label}</option>)}
    </select>
  );

  // Checkboxes "cadastrar no sistema" — aparecem quando a empresa/função enviada ainda não existe nas listas
  const cadNovoChecks = (p) => {
    if (p.tipo !== 'novo') return null;
    const itens = [
      ['empresa', p.empresa, listas.empresas],
      ['funcao', p.funcao, listas.funcoes],
    ].filter(([, valor, lista]) => valor && !lista.includes(valor));
    if (!itens.length) return null;
    return (
      <div style={{display:'flex',flexDirection:'column',gap:4,marginTop:6}}>
        {itens.map(([campo, valor]) => (
          <label key={campo} style={{display:'flex',alignItems:'center',gap:6,fontSize:11,color:'var(--muted)',cursor:'pointer'}}>
            <input type="checkbox"
              checked={cadNovo[p.id]?.[campo] !== false}
              onChange={e=>setCadNovo(m=>({...m,[p.id]:{...(m[p.id]||{}),[campo]:e.target.checked}}))}/>
            <span>🆕 {campo === 'empresa' ? 'empresa' : 'função'} nova “{valor}” — cadastrar no sistema</span>
          </label>
        ))}
      </div>
    );
  };

  // Linha de pessoa — usada nas listas de pendentes e no modal de pedido completo
  const renderPessoa = (p, idx, acoes) => (
    <div key={p.id} style={{display:'flex',alignItems:'flex-start',gap:12,padding:14,borderTop:idx?'1px solid var(--line-soft)':'none'}}>
      {p.foto_url ? (
        <img src={p.foto_url} style={{width:44,height:44,borderRadius:'50%',objectFit:'cover',flexShrink:0}} onError={e=>e.target.style.display='none'}/>
      ) : (
        <div style={{width:44,height:44,borderRadius:'50%',background:'var(--accent-soft)',display:'grid',placeItems:'center',fontSize:14,fontWeight:600,color:'var(--accent-2)',flexShrink:0}}>
          {p.nome ? p.nome.split(' ').slice(0,2).map(x=>x[0]).join('').toUpperCase() : '?'}
        </div>
      )}
      <div style={{flex:1,minWidth:0}}>
        <div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'wrap'}}>
          <span style={{fontSize:14,fontWeight:500}}>{p.nome}</span>
          <span style={{background:p.tipo==='novo'?'var(--sky-soft)':'var(--leaf-soft)',color:p.tipo==='novo'?'var(--sky)':'var(--leaf)',fontSize:10,padding:'2px 8px',borderRadius:8}}>{p.tipo==='novo'?(p.origem==='autocadastro'?'Auto-cadastro':'Novo cadastro'):'Embarque'}</span>
          {statusBadge(p.status)}
        </div>
        <div style={{fontSize:12,color:'var(--muted)',marginTop:4}}>{[p.funcao, p.empresa].filter(Boolean).join(' · ') || '—'}</div>
        <div style={{fontSize:11,fontFamily:'var(--fm)',color:'var(--ink-2)',marginTop:4}}>
          {p.cpf ? `CPF: ${p.cpf} ` : ''}{p.passaporte ? `Passaporte: ${p.passaporte}` : ''}
          {p.data_prevista_embarque ? ` · Previsão: ${fmtD(p.data_prevista_embarque)}` : ''}
        </div>
        {cadNovoChecks(p)}
      </div>
      {acoes && (
        <div style={{display:'flex',gap:6,flexShrink:0,flexWrap:'wrap',alignItems:'center',justifyContent:'flex-end'}}>
          {roleSelect(p)}
          <button className="btn btn-sm" onClick={()=>setDetalhe(p)}>Ver</button>
          <button className="btn btn-sm" onClick={()=>abrirEdicao(p)}>Editar</button>
          <button className="btn btn-primary btn-sm" disabled={!!processandoLote} onClick={()=>aprovar(p)}>Aprovar</button>
          <button className="btn btn-sm" style={{color:'var(--accent-2)'}} disabled={!!processandoLote} onClick={()=>rejeitar(p.id)}>Rejeitar</button>
        </div>
      )}
    </div>
  );

  // Cabeçalho de um pedido (lote) — empresa/auto-cadastro, data, contato e ações em lote
  const renderLoteHeader = (g, acoes) => {
    const first = g[0];
    const isAuto = first.origem === 'autocadastro';
    return (
      <div style={{padding:'10px 16px',background:'var(--cream-2)',borderBottom:'1px solid var(--line-soft)',display:'flex',justifyContent:'space-between',gap:8,flexWrap:'wrap',alignItems:'center'}}>
        <div style={{minWidth:0}}>
          <div style={{fontSize:12.5,fontWeight:600}}>
            {isAuto ? '🙋 Auto-cadastro de tripulante' : `🏢 ${first.convite_empresa || first.empresa || 'Empresa não identificada'}`}
          </div>
          <div style={{fontSize:11,color:'var(--muted)',marginTop:2}}>
            {new Date(first.criado_em).toLocaleString('pt-BR')}
            {first.email_empresa ? ` · ${first.email_empresa}` : ''}
            {first.telefone_empresa ? ` · ${first.telefone_empresa}` : ''}
            {g.length > 1 ? ` · ${g.length} pessoas` : ''}
          </div>
        </div>
        {acoes && (
          <div style={{display:'flex',gap:6,flexShrink:0,flexWrap:'wrap'}}>
            {first.lote_id && <button className="btn btn-sm" onClick={()=>abrirPedido(first.lote_id)}>📋 Pedido completo</button>}
            <button className="btn btn-primary btn-sm" disabled={!!processandoLote} onClick={()=>aprovarLote(g)}>{processandoLote ? 'Processando…' : 'Aprovar todos'}</button>
            <button className="btn btn-sm" style={{color:'var(--accent-2)'}} disabled={!!processandoLote} onClick={()=>rejeitarLote(g)}>Rejeitar todos</button>
          </div>
        )}
      </div>
    );
  };

  // Campo do modal de edição
  const campoEdit = (key, label, opts) => {
    const o = opts || {};
    return (
      <div style={o.full ? {gridColumn:'1 / -1'} : undefined}>
        <label className="label">{label}</label>
        {o.select ? (
          <select className="input" style={{width:'100%',boxSizing:'border-box'}} value={editForm[key] || ''} onChange={e=>setEditForm(f=>({...f,[key]:e.target.value}))}>
            {o.select.map(op=><option key={op.v} value={op.v}>{op.l}</option>)}
          </select>
        ) : (
          <input className="input" type={o.type || 'text'} style={{width:'100%',boxSizing:'border-box'}} value={editForm[key] || ''} onChange={e=>setEditForm(f=>({...f,[key]:e.target.value}))}/>
        )}
      </div>
    );
  };

  return (
    <div onClick={onClose} style={{position:'fixed',inset:0,background:'rgba(31,42,46,0.45)',backdropFilter:'blur(6px)',zIndex:9000,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:24,boxShadow:'var(--shadow-lg)',border:'1px solid var(--line-soft)',marginBottom:40,overflow:'hidden'}}>
        <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',padding:'18px 22px',borderBottom:'1px solid var(--line-soft)'}}>
          <div>
            <h3 className="t-h3" style={{fontFamily:'var(--fs)',fontSize:22,fontWeight:400}}>Cadastro externo</h3>
            <div style={{fontSize:12,color:'var(--muted)'}}>Links para empresas e colaboradores · avaliação de pedidos (aprovar não embarca ninguém)</div>
          </div>
          <button className="btn ghost btn-sm" onClick={onClose}><Icon n="x" size={14}/></button>
        </div>

        <div style={{display:'flex',gap:6,padding:4,background:'var(--cream-2)',borderRadius:12,margin:20}}>
          {[{id:'empresas',label:`Empresas (${pendentesEmpresa.length})`},{id:'colaboradores',label:`Colaboradores (${pendentesColab.length})`},{id:'historico',label:'Histórico'}].map(t=>(
            <button key={t.id} onClick={()=>setAba(t.id)}
              style={{flex:1,padding:'8px',borderRadius:9,border:'none',cursor:'pointer',fontSize:13,fontFamily:'var(--fm)',
                background:aba===t.id?'var(--cream)':'transparent',
                color:aba===t.id?'var(--ink)':'var(--muted)',
                boxShadow:aba===t.id?'0 1px 4px rgba(31,42,46,0.1)':'none'}}>{t.label}</button>
          ))}
        </div>

        {ultimoAprovado && (
          <div style={{margin:'0 22px 16px',padding:14,background:'var(--leaf-soft)',borderRadius:14,border:'1px solid var(--leaf-soft)',display:'flex',alignItems:'center',justifyContent:'space-between',gap:12,flexWrap:'wrap'}}>
            <div style={{minWidth:0}}>
              <div style={{fontSize:13,fontWeight:500,color:'var(--leaf)'}}>Aprovado · {ultimoAprovado.genesis_id}</div>
              <div style={{fontSize:11,color:'var(--ink-2)'}}>
                {ultimoAprovado.autocadastro
                  ? 'Envie o e-mail de boas-vindas com o login e a senha temporária.'
                  : 'Baixe o crachá e anexe no e-mail da empresa.'}
              </div>
            </div>
            <div style={{display:'flex',gap:6,flexShrink:0,flexWrap:'wrap'}}>
              {ultimoAprovado.autocadastro && ultimoAprovado.email && (
                <button className="btn btn-primary btn-sm" onClick={()=>enviarBoasVindas(ultimoAprovado)}>✉️ Boas-vindas</button>
              )}
              <button className="btn btn-sm" onClick={()=>gerarCrachaAprovado(ultimoAprovado.user_id, ultimoAprovado.nome)}>🪪 Gerar crachá</button>
              <button className="btn ghost btn-sm" onClick={()=>setUltimoAprovado(null)}><Icon n="x" size={12}/></button>
            </div>
          </div>
        )}

        <div style={{padding:'0 22px 28px'}}>
          {aba==='empresas' ? (
            <div style={{display:'grid',gap:18}}>
              <div style={{background:'var(--cream-2)',borderRadius:16,padding:16,border:'1px solid var(--line-soft)'}}>
                <div className="t-eyebrow" style={{marginBottom:10}}>Novo convite para empresa</div>
                <div style={{display:'grid',gridTemplateColumns:'2fr 1fr 1fr',gap:10}}>
                  <div>
                    <label className="label">Empresa</label>
                    <input className="input" style={{width:'100%',boxSizing:'border-box'}} placeholder="Nome da empresa" value={form.empresa} onChange={e=>setForm(f=>({...f,empresa:e.target.value}))}/>
                  </div>
                  <div>
                    <label className="label">Limite de usos</label>
                    <input className="input" type="number" min="1" style={{width:'100%',boxSizing:'border-box'}} value={form.max_usos} onChange={e=>setForm(f=>({...f,max_usos:parseInt(e.target.value)||1}))}/>
                  </div>
                  <div>
                    <label className="label">Expira em (dias)</label>
                    <input className="input" type="number" min="1" style={{width:'100%',boxSizing:'border-box'}} value={form.expira_em_dias} onChange={e=>setForm(f=>({...f,expira_em_dias:parseInt(e.target.value)||1}))}/>
                  </div>
                </div>
                <button className="btn btn-primary btn-sm" style={{marginTop:12}} onClick={criarConvite} disabled={criando||!form.empresa.trim()}>{criando?'Criando…':'Criar convite'}</button>
                <div style={{fontSize:11,color:'var(--muted)',marginTop:8,lineHeight:1.5}}>
                  Gera um link para a empresa montar a lista de pessoas (manualmente ou por planilha), com foto de cada um.
                  A aprovação cria os cadastros e libera a emissão dos crachás — <strong>não embarca</strong> ninguém automaticamente: o embarque é feito no portaló, com o crachá.
                </div>
              </div>

              <div>
                <div style={{display:'flex',justifyContent:'space-between',alignItems:'center',marginBottom:8,gap:8,flexWrap:'wrap'}}>
                  <div className="t-eyebrow">Links de empresas</div>
                  {revogadosCount>0 && (
                    <button className="btn ghost btn-sm" onClick={()=>setShowRevogados(s=>!s)}>{showRevogados?'Ocultar revogados':`Mostrar revogados (${revogadosCount})`}</button>
                  )}
                </div>
                {loading ? <div className="empty">carregando…</div> : (
                  <div style={{display:'grid',gap:8}}>
                    {convitesVisiveis.length===0 && <div className="empty" style={{padding:30,textAlign:'center'}}>Nenhum convite de empresa criado.</div>}
                    {convitesVisiveis.map(c=>{
                      const url = `${baseUrl}?token=${encodeURIComponent(c.token)}`;
                      const expirado = c.expira_em && new Date(c.expira_em) < new Date();
                      const esgotado = c.max_usos>0 && c.usos >= c.max_usos;
                      const ativo = c.status==='ativo' && !expirado && !esgotado;
                      return (
                        <div key={c.id} style={{display:'flex',alignItems:'center',gap:12,padding:14,background:'var(--surface)',borderRadius:14,border:'1px solid var(--line-soft)',opacity:c.status==='revogado'?0.6:1}}>
                          <div style={{flex:1,minWidth:0}}>
                            <div style={{fontSize:13.5,fontWeight:500}}>{c.empresa || 'Sem empresa'}</div>
                            <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>
                              {c.status==='revogado'
                                ? <span style={{color:'var(--accent-2)'}}>● revogado</span>
                                : ativo ? <span style={{color:'var(--leaf)'}}>● ativo</span> : <span style={{color:'var(--accent-2)'}}>● inativo</span>}
                              {' · '}usos {c.usos}/{c.max_usos>0?c.max_usos:'∞'}
                              {c.expira_em && ` · expira ${new Date(c.expira_em).toLocaleDateString('pt-BR')}`}
                            </div>
                            {c.status!=='revogado' && <div style={{fontSize:11,fontFamily:'var(--fm)',color:'var(--ink-2)',marginTop:4,wordBreak:'break-all'}}>{url}</div>}
                          </div>
                          <div style={{display:'flex',gap:6,flexShrink:0,flexWrap:'wrap'}}>
                            {c.status!=='revogado' && <button className="btn btn-sm" onClick={()=>copiarLink(c.token,'emp','emp-'+c.id)}>{copiado==='emp-'+c.id?'Copiado!':'Copiar link'}</button>}
                            {c.status==='ativo' && <button className="btn btn-sm" style={{color:'var(--accent-2)'}} onClick={()=>revogar(c.id)}>Revogar</button>}
                          </div>
                        </div>
                      );
                    })}
                  </div>
                )}
              </div>

              <div>
                <div className="t-eyebrow" style={{marginBottom:8}}>Pedidos aguardando avaliação ({pendentesEmpresa.length})</div>
                {loading ? <div className="empty">carregando…</div> : pendentesEmpresa.length===0 ? (
                  <div className="empty" style={{padding:30,textAlign:'center'}}>Nenhuma solicitação de empresa pendente.</div>
                ) : (
                  <div style={{display:'grid',gap:12}}>
                    {agruparLotes(pendentesEmpresa).map(g=>(
                      <div key={g[0].lote_id || g[0].id} style={{background:'var(--surface)',borderRadius:16,border:'1px solid var(--line-soft)',overflow:'hidden'}}>
                        {renderLoteHeader(g, true)}
                        {g.map((p, idx)=>renderPessoa(p, idx, true))}
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </div>
          ) : aba==='colaboradores' ? (
            <div style={{display:'grid',gap:18}}>
              <div style={{background:'var(--cream-2)',borderRadius:16,padding:16,border:'1px solid var(--line-soft)'}}>
                <div className="t-eyebrow" style={{marginBottom:10}}>Link geral de auto-cadastro</div>
                {linkTrip ? (
                  <div>
                    <div style={{fontSize:11,fontFamily:'var(--fm)',color:'var(--ink-2)',wordBreak:'break-all',background:'var(--surface)',padding:'10px 12px',borderRadius:10,border:'1px solid var(--line-soft)'}}>
                      {`${baseUrlTrip}?token=${encodeURIComponent(linkTrip.token)}`}
                    </div>
                    <div style={{display:'flex',gap:6,marginTop:10,flexWrap:'wrap'}}>
                      <button className="btn btn-primary btn-sm" onClick={()=>copiarLink(linkTrip.token,'trip')}>{copiado==='trip'?'Copiado!':'Copiar link'}</button>
                      <button className="btn btn-sm" onClick={rotacionarLink}>🔄 Gerar novo link</button>
                    </div>
                    <div style={{fontSize:11,color:'var(--muted)',marginTop:8,lineHeight:1.5}}>
                      Envie este link aos colaboradores para que façam o próprio cadastro, com foto. Ao gerar um novo link, o anterior deixa de funcionar na hora.
                    </div>
                  </div>
                ) : (
                  <div>
                    <div style={{fontSize:12,color:'var(--muted)',marginBottom:10,lineHeight:1.5}}>Nenhum link ativo no momento. Gere um para liberar o auto-cadastro dos colaboradores.</div>
                    <button className="btn btn-primary btn-sm" onClick={rotacionarLink}>Gerar link de auto-cadastro</button>
                  </div>
                )}
              </div>

              <div>
                <div className="t-eyebrow" style={{marginBottom:8}}>Pedidos aguardando avaliação ({pendentesColab.length})</div>
                {loading ? <div className="empty">carregando…</div> : pendentesColab.length===0 ? (
                  <div className="empty" style={{padding:30,textAlign:'center'}}>Nenhum auto-cadastro pendente.</div>
                ) : (
                  <div style={{display:'grid',gap:10}}>
                    {pendentesColab.map(p=>(
                      <div key={p.id} style={{display:'flex',alignItems:'flex-start',gap:14,padding:14,background:'var(--surface)',borderRadius:16,border:'1px solid var(--line-soft)'}}>
                        {p.foto_url ? (
                          <img src={p.foto_url} style={{width:64,height:64,borderRadius:14,objectFit:'cover',flexShrink:0}} onError={e=>e.target.style.display='none'}/>
                        ) : (
                          <div style={{width:64,height:64,borderRadius:14,background:'var(--accent-soft)',display:'grid',placeItems:'center',fontSize:18,fontWeight:600,color:'var(--accent-2)',flexShrink:0}}>
                            {p.nome ? p.nome.split(' ').slice(0,2).map(x=>x[0]).join('').toUpperCase() : '?'}
                          </div>
                        )}
                        <div style={{flex:1,minWidth:0}}>
                          <div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'wrap'}}>
                            <span style={{fontSize:14,fontWeight:500}}>{p.nome}</span>
                            <span style={{background:'var(--sky-soft)',color:'var(--sky)',fontSize:10,padding:'2px 8px',borderRadius:8}}>Auto-cadastro</span>
                          </div>
                          <div style={{fontSize:12,color:'var(--muted)',marginTop:4}}>{[p.funcao, p.empresa].filter(Boolean).join(' · ') || '—'}</div>
                          <div style={{fontSize:11,fontFamily:'var(--fm)',color:'var(--ink-2)',marginTop:4}}>
                            {p.cpf ? `CPF: ${p.cpf} ` : ''}{p.passaporte ? `Passaporte: ${p.passaporte}` : ''}
                            {p.email ? ` · ${p.email}` : ''}
                          </div>
                          {cadNovoChecks(p)}
                          <div style={{fontSize:11,color:'var(--muted)',marginTop:4}}>Enviado em {new Date(p.criado_em).toLocaleString('pt-BR')}</div>
                        </div>
                        <div style={{display:'flex',gap:6,flexShrink:0,flexWrap:'wrap',alignItems:'center',justifyContent:'flex-end'}}>
                          {roleSelect(p)}
                          <button className="btn btn-sm" onClick={()=>setDetalhe(p)}>Ver</button>
                          <button className="btn btn-sm" onClick={()=>abrirEdicao(p)}>Editar</button>
                          <button className="btn btn-primary btn-sm" disabled={!!processandoLote} onClick={()=>aprovar(p)}>Aprovar</button>
                          <button className="btn btn-sm" style={{color:'var(--accent-2)'}} disabled={!!processandoLote} onClick={()=>rejeitar(p.id)}>Rejeitar</button>
                        </div>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </div>
          ) : (
            <div style={{display:'grid',gap:16}}>
              {loading ? <div className="empty">carregando…</div> : historico.length===0 ? (
                <div className="empty" style={{padding:40,textAlign:'center'}}>Nenhum pedido decidido ainda.</div>
              ) : (() => {
                const porEmpresa = new Map();
                historico.forEach(r => {
                  const k = r.origem==='autocadastro' ? '🙋 Auto-cadastro de tripulantes' : (r.convite_empresa || r.empresa || 'Empresa não identificada');
                  if (!porEmpresa.has(k)) porEmpresa.set(k, []);
                  porEmpresa.get(k).push(r);
                });
                return [...porEmpresa.entries()].map(([emp, rows]) => {
                  const apT = rows.filter(r=>r.status==='aprovado').length;
                  const rjT = rows.filter(r=>r.status==='rejeitado').length;
                  return (
                    <div key={emp} style={{display:'grid',gap:10}}>
                      <div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'wrap',padding:'2px 4px'}}>
                        <span style={{fontSize:13,fontWeight:600}}>{emp}</span>
                        {apT>0 && <span style={{background:'var(--leaf-soft)',color:'var(--leaf)',fontSize:10,padding:'2px 8px',borderRadius:8}}>{apT} aprovado(s)</span>}
                        {rjT>0 && <span style={{background:'var(--accent-soft)',color:'var(--accent-2)',fontSize:10,padding:'2px 8px',borderRadius:8}}>{rjT} rejeitado(s)</span>}
                      </div>
                      {agruparLotes(rows).map(g=>{
                        const first = g[0];
                        const ap = g.filter(r=>r.status==='aprovado').length;
                        const rj = g.filter(r=>r.status==='rejeitado').length;
                        const rejMotivo = (g.find(r=>r.status==='rejeitado' && r.observacao)||{}).observacao;
                        const decidido = g.find(r=>r.revisado_em);
                        return (
                          <div key={first.lote_id || first.id} style={{padding:16,background:'var(--surface)',borderRadius:16,border:'1px solid var(--line-soft)'}}>
                            <div style={{display:'flex',justifyContent:'space-between',gap:10,flexWrap:'wrap',alignItems:'flex-start'}}>
                              <div style={{minWidth:0}}>
                                <div style={{fontSize:11,color:'var(--muted)'}}>
                                  Pedido em {new Date(first.criado_em).toLocaleString('pt-BR')}
                                  {first.email_empresa ? ` · ${first.email_empresa}` : ''}
                                  {first.telefone_empresa ? ` · ${first.telefone_empresa}` : ''}
                                </div>
                              </div>
                              <div style={{display:'flex',gap:6,flexShrink:0,alignItems:'center'}}>
                                {ap>0 && <span style={{background:'var(--leaf-soft)',color:'var(--leaf)',fontSize:10,padding:'2px 8px',borderRadius:8}}>{ap} aprovado(s)</span>}
                                {rj>0 && <span style={{background:'var(--accent-soft)',color:'var(--accent-2)',fontSize:10,padding:'2px 8px',borderRadius:8}}>{rj} rejeitado(s)</span>}
                                {first.lote_id && <button className="btn ghost btn-sm" onClick={()=>abrirPedido(first.lote_id)}>Ver pedido</button>}
                              </div>
                            </div>
                            <div style={{marginTop:10,borderTop:'1px dashed var(--line-soft)',paddingTop:8,display:'grid',gap:4}}>
                              {g.map(r=>(
                                <div key={r.id} style={{display:'flex',justifyContent:'space-between',gap:10,fontSize:12}}>
                                  <span>{r.nome} <span style={{color:'var(--muted)'}}>· {r.tipo==='novo'?'Novo cadastro':'Embarque'}</span></span>
                                  <span style={{color:r.status==='aprovado'?'var(--leaf)':'var(--accent-2)',fontSize:11}}>{r.status}</span>
                                </div>
                              ))}
                            </div>
                            <div style={{fontSize:11,color:'var(--muted)',marginTop:10,background:'var(--cream-2)',borderRadius:10,padding:'8px 10px'}}>
                              Decidido em {decidido ? new Date(decidido.revisado_em).toLocaleString('pt-BR') : '—'}
                              {decidido && decidido.revisado_por_nome ? ` por ${decidido.revisado_por_nome}` : ''}
                              {rejMotivo ? ` · Motivo: ${rejMotivo}` : ''}
                            </div>
                          </div>
                        );
                      })}
                    </div>
                  );
                });
              })()}
            </div>
          )}
        </div>
      </div>

      {pedido && (
        <div onClick={()=>setPedido(null)} style={{position:'fixed',inset:0,zIndex:9050,background:'rgba(31,42,46,0.5)',display:'flex',alignItems:'flex-start',justifyContent:'center',padding:'6vh 16px',overflowY:'auto'}}>
          <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:680,background:'var(--cream)',borderRadius:22,boxShadow:'var(--shadow-lg)',overflow:'hidden',marginBottom:40}}>
            <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',padding:'16px 20px',borderBottom:'1px solid var(--line-soft)'}}>
              <div>
                <h3 className="t-h3" style={{fontFamily:'var(--fs)',fontSize:20,fontWeight:400}}>Pedido completo</h3>
                <div style={{fontSize:12,color:'var(--muted)'}}>Todas as pessoas do pedido, inclusive as já decididas</div>
              </div>
              <button className="btn ghost btn-sm" onClick={()=>setPedido(null)}><Icon n="x" size={14}/></button>
            </div>
            {pedido.loading ? <div className="empty" style={{padding:40,textAlign:'center'}}>carregando…</div> : (
              <div>
                {renderLoteHeader(pedido.rows, false)}
                {pedido.rows.map((p,idx)=>renderPessoa(p, idx, p.status==='pendente'))}
                <div style={{padding:'10px 16px',fontSize:11,color:'var(--muted)',borderTop:'1px solid var(--line-soft)',background:'var(--cream-2)'}}>
                  {pedido.rows.filter(r=>r.status==='pendente').length} pendente(s) · {pedido.rows.filter(r=>r.status==='aprovado').length} aprovada(s) · {pedido.rows.filter(r=>r.status==='rejeitado').length} rejeitada(s)
                </div>
              </div>
            )}
          </div>
        </div>
      )}

      {detalhe && (
        <div onClick={()=>setDetalhe(null)} style={{position:'fixed',inset:0,zIndex:9100,background:'rgba(31,42,46,0.5)',display:'flex',alignItems:'center',justifyContent:'center',padding:16}}>
          <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:480,background:'var(--cream)',borderRadius:22,padding:24,boxShadow:'var(--shadow-lg)'}}>
            <div style={{display:'flex',alignItems:'center',gap:14,marginBottom:16}}>
              {detalhe.foto_url ? (
                <img src={detalhe.foto_url} style={{width:64,height:64,borderRadius:'50%',objectFit:'cover'}} onError={e=>e.target.style.display='none'}/>
              ) : (
                <div style={{width:64,height:64,borderRadius:'50%',background:'var(--accent-soft)',display:'grid',placeItems:'center',fontSize:18,fontWeight:600,color:'var(--accent-2)'}}>
                  {detalhe.nome ? detalhe.nome.split(' ').slice(0,2).map(x=>x[0]).join('').toUpperCase() : '?'}
                </div>
              )}
              <div>
                <h3 className="t-h3" style={{fontFamily:'var(--fs)',fontSize:20}}>{detalhe.nome}</h3>
                <div style={{fontSize:12,color:'var(--muted)'}}>{detalhe.funcao} · {detalhe.empresa}</div>
              </div>
            </div>
            <div style={{display:'grid',gap:10,fontSize:13}}>
              {detalhe.origem==='autocadastro' && <div><span style={{background:'var(--sky-soft)',color:'var(--sky)',fontSize:10,padding:'3px 9px',borderRadius:8}}>🙋 Auto-cadastro de tripulante</span></div>}
              {detalhe.cpf && <div><strong>CPF:</strong> {detalhe.cpf}</div>}
              {detalhe.passaporte && <div><strong>Passaporte:</strong> {detalhe.passaporte}</div>}
              {detalhe.genero && <div><strong>Gênero:</strong> {detalhe.genero==='M'?'Masculino':detalhe.genero==='F'?'Feminino':'Outro'}</div>}
              {detalhe.email && <div><strong>E-mail:</strong> {detalhe.email}</div>}
              {detalhe.telefone && <div><strong>Telefone:</strong> {detalhe.telefone}</div>}
              {detalhe.data_nascimento && <div><strong>Nascimento:</strong> {fmtD(detalhe.data_nascimento)}</div>}
              {detalhe.nacionalidade && <div><strong>Nacionalidade:</strong> {detalhe.nacionalidade}</div>}
              {detalhe.em_nome && <div><strong>Contato de emergência:</strong> {detalhe.em_nome}{detalhe.em_telefone ? ` · ${detalhe.em_telefone}` : ''}</div>}
              {detalhe.email_empresa && <div><strong>E-mail da empresa:</strong> {detalhe.email_empresa}</div>}
              {detalhe.telefone_empresa && <div><strong>Telefone da empresa:</strong> {detalhe.telefone_empresa}</div>}
              {detalhe.data_prevista_embarque && <div><strong>Previsão de embarque:</strong> {fmtD(detalhe.data_prevista_embarque)}</div>}
              {(() => {
                const x = detalhe.dados_extra || {};
                const linhas = [
                  x.tipo_sanguineo && `🩸 Tipo sanguíneo: ${x.tipo_sanguineo}`,
                  (x.epi_macacao || x.epi_calcado) && `🦺 EPI: macacão ${x.epi_macacao||'—'} · calçado ${x.epi_calcado||'—'}`,
                  (x.cidade_nascimento || x.estado_nascimento) && `📍 Naturalidade: ${[x.cidade_nascimento, x.estado_nascimento].filter(Boolean).join(' / ')}`,
                  (x.end_logradouro || x.end_cidade) && `🏠 Endereço: ${[x.end_logradouro, x.end_numero ? `nº ${x.end_numero}` : null, x.end_complemento, x.end_cidade, x.end_estado, x.end_pais, x.end_cep].filter(Boolean).join(', ')}`,
                  (x.em_end || x.em_cidade) && `🚨 End. do contato: ${[x.em_end, x.em_cidade, x.em_pais].filter(Boolean).join(', ')}`,
                  x.em_lingua && `🗣 Língua do contato: ${x.em_lingua}`,
                ].filter(Boolean);
                if (!linhas.length) return null;
                return <div style={{background:'var(--cream-2)',padding:12,borderRadius:10,display:'grid',gap:5,fontSize:12.5}}>{linhas.map((l,i)=><div key={i}>{l}</div>)}</div>;
              })()}
              {detalhe.observacao && <div style={{background:'var(--cream-2)',padding:10,borderRadius:10,marginTop:6}}><strong>Observação:</strong> {detalhe.observacao}</div>}
            </div>
            <div style={{display:'flex',gap:10,marginTop:20,alignItems:'center',flexWrap:'wrap'}}>
              <button className="btn" style={{flex:1}} onClick={()=>setDetalhe(null)}>Fechar</button>
              {detalhe.status==='pendente' && (
                <>
                  <button className="btn" onClick={()=>abrirEdicao(detalhe)}>Editar</button>
                  {roleSelect(detalhe)}
                  <button className="btn btn-primary" style={{flex:1}} onClick={()=>{ aprovar(detalhe); setDetalhe(null); }}>Aprovar</button>
                </>
              )}
            </div>
          </div>
        </div>
      )}

      {editando && (
        <div onClick={()=>setEditando(null)} style={{position:'fixed',inset:0,zIndex:9200,background:'rgba(31,42,46,0.55)',display:'flex',alignItems:'flex-start',justifyContent:'center',padding:'5vh 16px',overflowY:'auto'}}>
          <div onClick={e=>e.stopPropagation()} style={{width:'100%',maxWidth:560,background:'var(--cream)',borderRadius:22,padding:24,boxShadow:'var(--shadow-lg)',marginBottom:40}}>
            <h3 className="t-h3" style={{fontFamily:'var(--fs)',fontSize:20,fontWeight:400}}>Editar dados</h3>
            <div style={{fontSize:12,color:'var(--muted)',marginTop:2}}>{editando.nome} · as correções valem antes da aprovação</div>
            <div style={{display:'grid',gridTemplateColumns:'1fr 1fr',gap:12,marginTop:16}}>
              {campoEdit('nome','Nome completo',{full:true})}
              {campoEdit('email','E-mail')}
              {campoEdit('telefone','Telefone')}
              {campoEdit('cpf','CPF')}
              {campoEdit('passaporte','Passaporte')}
              {campoEdit('empresa','Empresa')}
              {campoEdit('funcao','Função')}
              {campoEdit('turno','Turno')}
              {campoEdit('genero','Gênero',{select:[{v:'',l:'—'},{v:'M',l:'Masculino'},{v:'F',l:'Feminino'},{v:'O',l:'Outro'}]})}
              {campoEdit('data_nascimento','Data de nascimento',{type:'date'})}
              {campoEdit('nacionalidade','Nacionalidade')}
              {campoEdit('data_prevista_embarque','Previsão de embarque',{type:'date'})}
            </div>
            <div style={{display:'flex',gap:10,marginTop:20}}>
              <button className="btn" style={{flex:1}} onClick={()=>setEditando(null)}>Cancelar</button>
              <button className="btn btn-primary" style={{flex:1}} disabled={salvandoEdicao || !String(editForm.nome || '').trim()} onClick={salvarEdicao}>{salvandoEdicao?'Salvando…':'Salvar alterações'}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};


Object.assign(window, {Pessoas, Musica, Avisos, Manifesto, Importar, Conquistas, ExportDropdown, CadastroExternoModal});
