// ═══ GENESIS BUILD rbac-fase-a · 2026-07-10 ═══
// Tidal — Cabines: vagas (berths) com gênero, turno, compatibilidade e export PDF.
// Sistema de papel-chave: cada CARD é uma VAGA (não uma cabine inteira).
// Algoritmo de compatibilidade: vaga mostra avatares dos colegas de cabine + sinais
//  - mesmo turno = ✓ rest (não acordam um ao outro)
//  - turno oposto = ⚠ (acorda) ou ✓ hot-bunk (depende da política — aqui a UI alerta)
//  - misto de gênero numa cabine de >1 berço com gênero declarado = ✗ bloqueio

const Cabines = ({state, setState}) => {
  const tt = useT();
  const lang = tt.lang;
  const toast = useToast();
  const isMobile = useMobile();
  const [dragging, setDragging] = useState(null);
  const [dropTarget, setDropTarget] = useState(null);
  const [genderAlert, setGenderAlert] = useState(null);  // {nome, vaga, labels} — modal de confirmação
  const [overUnassign, setOverUnassign] = useState(false);
  const [history, setHistory] = useState([]);
  // ─── Persistência leve dos filtros e preferências de visualização ───
  const loadCabinesUI = () => {
    try { return JSON.parse(localStorage.getItem('genesis_cabines_ui') || '{}'); }
    catch { return {}; }
  };
  const savedUI = loadCabinesUI();
  const [search, setSearch] = useState('');
  const [filterDeck, setFilterDeck] = useState(savedUI.filterDeck || 'all');
  const [filterTurno, setFilterTurno] = useState(savedUI.filterTurno || 'all');
  const [filterEmpresa, setFilterEmpresa] = useState(savedUI.filterEmpresa || 'all');
  const [filterGenero, setFilterGenero] = useState(savedUI.filterGenero || 'all');   // all | M | F
  const [filterStatus, setFilterStatus] = useState(savedUI.filterStatus || 'all');   // all | ocupada | livre
  const [lazyView, setLazyView] = useState(savedUI.lazyView === true);
  const [planView, setPlanView] = useState(savedUI.planView === 'corte' ? 'corte' : 'planta'); // 'planta' | 'corte'
  const [swapAsk, setSwapAsk] = useState(null);      // {userId, vaga} — troca em 1 passo
  const [histBerth, setHistBerth] = useState(null);  // id da vaga com histórico aberto
  const [resolvendo, setResolvendo] = useState(false);
  const [acoesOpen, setAcoesOpen] = useState(false);
  const [filtroOpen, setFiltroOpen] = useState(false);
  const [hoveredUser, setHoveredUser] = useState(null);
  const [managing, setManaging] = useState(false);
  const [filterRole, setFilterRole] = useState(savedUI.filterRole || 'all');
  // ─── Planejamento (simulações de arrumação) ───
  const [mode, setMode] = useState('live');          // 'live' | 'plan'
  const [plans, setPlans] = useState([]);
  const [currentPlanId, setCurrentPlanId] = useState(null);
  const [planName, setPlanName] = useState('');
  const [planAssign, setPlanAssign] = useState({});  // { berthId: userId }
  const [planPool, setPlanPool] = useState(() => new Set());  // userIds importados pro plano ("a acomodar")
  const [importOpen, setImportOpen] = useState(false);
  const [planDirty, setPlanDirty] = useState(false);
  const [savingPlan, setSavingPlan] = useState(false);
  const [applyOpen, setApplyOpen] = useState(false);
  const [modeChoiceOpen, setModeChoiceOpen] = useState(false);
  const [loadOpen, setLoadOpen] = useState(false);
  // micro-autorização: admin/staff (legado) OU a chave cabines.gerir do perfil de permissões
  const isAdmin = state.currentUser?.role === 'admin' || state.currentUser?.is_staff === true || state.role === 'admin'
    || (state.currentUser?.permissoes || []).includes('cabines.gerir');

  // ─── Dados reais da API ───
  const [cabins, setCabins] = useState([]);
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);

  const token = () => localStorage.getItem('genesis_token');

  const [intents, setIntents] = useState([]);
  const intentByBerth = useMemo(() => { const m = {}; (intents||[]).forEach(it => { m[it.berth_id] = it; }); return m; }, [intents]);
  const empresas = useMemo(() => [...new Set((users||[]).map(u=>u.empresa).filter(Boolean))].sort(), [users]);

  const fetchData = async () => {
    setLoading(true);
    try {
      const [cabRes, usersRes] = await Promise.all([
        fetch('/api/cabins', { headers: { 'Authorization': `Bearer ${token()}` } }),
        fetch('/api/users',  { headers: { 'Authorization': `Bearer ${token()}` } }),
      ]);
      if (cabRes.ok)   setCabins(await cabRes.json());
      if (usersRes.ok) setUsers(await usersRes.json());
      // intenções do lazy embark (pro flip dos cards na aba ao vivo)
      if (isAdmin) { try { const ir = await fetch('/api/cabins/plans/intents', { headers: { 'Authorization': `Bearer ${token()}` } }); if (ir.ok) setIntents(await ir.json()); } catch(e){} }
    } catch(e) { console.error(e); }
    finally { setLoading(false); }
  };

  useEffect(() => { fetchData(); }, []);

  // ─── Salva filtros e preferências de visualização no aparelho ───
  useEffect(() => {
    try {
      localStorage.setItem('genesis_cabines_ui', JSON.stringify({
        filterDeck, filterTurno, filterEmpresa, filterGenero, filterStatus, filterRole, lazyView, planView
      }));
    } catch {}
  }, [filterDeck, filterTurno, filterEmpresa, filterGenero, filterStatus, filterRole, lazyView, planView]);

  // ─── Atalhos de teclado no módulo Cabines ───
  useEffect(() => {
    const onKey = (e) => {
      const inInput = e.target && (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable);
      // Esc limpa o drag ou fecha o filtro
      if (e.key === 'Escape') {
        if (dragging) { e.preventDefault(); setDragging(null); setDropTarget(null); return; }
        if (filtroOpen) { e.preventDefault(); setFiltroOpen(false); return; }
      }
      // Ctrl/Cmd + Z desfaz a última atribuição, exceto em campos de texto
      if (!inInput && (e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'z' && !e.shiftKey) {
        e.preventDefault();
        if (history.length) undoLast();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [dragging, filtroOpen, history]);

  // Mapa do estado AO VIVO (berthId -> userId), independente do modo
  const liveAssign = useMemo(() => {
    const m = {};
    cabins.forEach(c => (c.berths_detail||[]).forEach(b => { if (b.user_id) m[b.berth_id] = parseInt(b.user_id); }));
    return m;
  }, [cabins]);

  // Adapta o formato da API para o formato esperado pelo componente.
  // Em modo "plan", a ocupação de cada vaga vem do rascunho (planAssign).
  const adaptedCabins = useMemo(() => cabins.map(c => ({
    num: c.number,
    deck: parseInt(c.deck),
    tipo: c.type,
    berths: c.berths_detail.map(b => ({
      id: b.berth_id,
      user: mode==='plan'
        ? (planAssign[b.berth_id] ?? null)
        : (b.user_id ? parseInt(b.user_id) : null),
    }))
  })), [cabins, mode, planAssign]);

  const usersById = useMemo(() => {
    const map = {};
    users.forEach(u => {
      const entry = {
        ...u,
        status: u.status_bordo,
        cab: u.cabine_atual,
        genero: u.genero,
        turno: u.turno,
      };
      map[u.id] = entry;
      map[String(u.id)] = entry; // garante lookup por string ou número
    });
    return map;
  }, [users]);

  const cabinByBerth = useMemo(() => {
    const map = {};
    adaptedCabins.forEach(c => c.berths.forEach(b => map[b.id] = c));
    return map;
  }, [adaptedCabins]);

  const placedInPlan = useMemo(() => new Set(Object.values(planAssign)), [planAssign]);
  const unassigned = mode==='plan'
    ? users.filter(u => planPool.has(u.id) && !placedInPlan.has(u.id))
    : users.filter(u => u.status_bordo === 'a_bordo' && !u.cabine_atual && u.role !== 'guest' && u.role !== 'admin');

  // Build flat list of all vagas (berths) with deck/cabin context
  const vagas = useMemo(() => {
    const arr = [];
    adaptedCabins.forEach(cab => {
      cab.berths.forEach(b => {
        arr.push({
          ...b,
          deck: cab.deck,
          cabinNum: cab.num,
          cabinType: cab.tipo,
          cabinBerths: cab.berths,
        });
      });
    });
    return arr;
  }, [adaptedCabins]);

  // Filtered set
  const filteredVagas = useMemo(() => vagas.filter(v => {
    if (filterDeck !== 'all' && String(v.deck) !== filterDeck) return false;
    const occupant = v.user ? usersById[v.user] : null;
    if (filterTurno !== 'all') {
      if (occupant && occupant.turno !== filterTurno) return false;
    }
    if (filterEmpresa !== 'all') { if (!occupant || occupant.empresa !== filterEmpresa) return false; }
    if (filterGenero !== 'all') { if (!occupant || occupant.genero !== filterGenero) return false; }
    if (filterStatus === 'ocupada' && !occupant) return false;
    if (filterStatus === 'livre' && occupant) return false;
    return true;
  }), [vagas, filterDeck, filterTurno, filterEmpresa, filterGenero, filterStatus, usersById]);
  const filtroAtivo = filterDeck!=='all' || filterTurno!=='all' || filterEmpresa!=='all' || filterGenero!=='all' || filterStatus!=='all';

  // Group filtered vagas by deck
  const decks = useMemo(() => {
    const grouped = {};
    filteredVagas.forEach(v => {
      const k = String(v.deck);
      (grouped[k] = grouped[k] || []).push(v);
    });
    return Object.keys(grouped).sort((a,b)=>b-a).map(d => ({deck:d, vagas:grouped[d]}));
  }, [filteredVagas]);

  // Agrupa vagas por camarote dentro de cada deck (layout visual por cabine)
  const cabinsByDeck = useMemo(() => {
    const byDeck = {};
    filteredVagas.forEach(v => {
      byDeck[v.deck] = byDeck[v.deck] || {};
      const c = byDeck[v.deck][v.cabinNum] || { num: v.cabinNum, deck: v.deck, tipo: v.cabinType, berths: [] };
      c.berths.push(v);
      byDeck[v.deck][v.cabinNum] = c;
    });
    return Object.entries(byDeck).sort((a,b)=>+b[0]-+a[0]).map(([deck, map]) => ({
      deck,
      cabins: Object.values(map).sort((a,b) => String(a.num).localeCompare(String(b.num), undefined, { numeric: true }))
    }));
  }, [filteredVagas]);

  // Stats
  const totalBerths = vagas.length;
  const occupied = vagas.filter(v => v.user).length;
  const femaleVagas = vagas.filter(v => v.user && usersById[v.user]?.genero === 'F').length;

  // ─── Compatibility check for a target berth ───
  // Em camarotes de 4 (ex.: 704 A1/A2/B1/B2) cada letra é um quarto separado.
  // roomOf extrai o quarto a partir do id da vaga ("704-A1"->"A", "604-1"->"*", "913"->"*").
  const roomOf = (berthId) => {
    const slot = String(berthId).split('-').slice(1).join('-');
    const m = slot.match(/^([A-Za-z])/);
    return m ? m[1].toUpperCase() : '*';
  };

  // Conflitos do PLANO: camarotes/quartos com gênero misto (aviso suave, não bloqueia)
  const planConflitos = useMemo(() => {
    if (mode !== 'plan') return [];
    const quartos = {};
    Object.entries(planAssign).forEach(([berthId, userId]) => {
      const cabNum = String(berthId).split('-')[0];
      const room = roomOf(berthId);
      const key = `${cabNum}|${room}`;
      (quartos[key] = quartos[key] || { cabNum, room, ocupantes: [] }).ocupantes.push(userId);
    });
    const conflitos = [];
    Object.values(quartos).forEach(q => {
      if (q.ocupantes.length < 2) return;
      const pessoas = q.ocupantes.map(id => usersById[id]).filter(Boolean);
      const generos = [...new Set(pessoas.map(u => u.genero).filter(Boolean))];
      if (generos.length > 1) conflitos.push({ cabNum: q.cabNum, room: q.room, nomes: pessoas.map(u => ({ nome: u.nome, genero: u.genero })) });
    });
    return conflitos;
  }, [mode, planAssign, usersById]);


  const compatCheck = (userId, berthId) => {
    if (!userId || !berthId) return {ok:true, signals:[]};
    const u = usersById[userId];
    const cab = cabinByBerth[berthId];
    if (!u || !cab) return {ok:true, signals:[]};
    // Só considera colegas do MESMO QUARTO (A1↔A2, B1↔B2 — não cruza A com B)
    const tRoom = roomOf(berthId);
    const others = cab.berths
      .filter(b => b.id !== berthId && b.user && roomOf(b.id) === tRoom)
      .map(b => usersById[b.user]).filter(Boolean);
    const signals = [];

    if (others.length) {
      // Gênero diferente no quarto → AVISO (não bloqueia mais)
      const otherGenders = [...new Set(others.map(o => o.genero).filter(Boolean))];
      if (u.genero && otherGenders.length && !otherGenders.includes(u.genero)) {
        signals.push({type:'warn', icon:'alert', label: lang==='pt'?'gênero diferente no quarto':'different gender in room'});
      }
      // Turno: mesmo turno → AVISO (dormem no mesmo horário); turnos opostos → ideal
      const sameShift = others.filter(o => o.turno && o.turno === u.turno).length;
      const oppShift  = others.filter(o => o.turno && u.turno && o.turno !== u.turno).length;
      if (sameShift) signals.push({type:'warn', icon:'bell', label: lang==='pt'?`${sameShift} colega no mesmo turno`:`${sameShift} same-shift`});
      if (oppShift)  signals.push({type:'good', icon:'check', label: lang==='pt'?`${oppShift} em turno oposto`:`${oppShift} opposite-shift`});
    }

    // Nunca bloqueia — apenas sinaliza
    return {ok:true, signals, others};
  };

  const removeFromCabin = async (userId) => {
    try {
      const res = await fetch(`/api/cabins/assign/${userId}`, {
        method: 'DELETE',
        headers: { 'Authorization': `Bearer ${token()}` }
      });
      if (res.ok) {
        await fetchData();
        toast.push({icon:'check', title: lang==='pt'?'Removido da cabine':'Removed from cabin'});
      } else {
        const d = await res.json();
        toast.push({icon:'x', title: d.error || 'Erro ao remover'});
      }
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };

  const assign = async (userId, berthId) => {
    const check = compatCheck(userId, berthId);
    try {
      const res = await fetch('/api/cabins/assign', {
        method: 'POST',
        headers: { 'Content-Type':'application/json', 'Authorization': `Bearer ${token()}` },
        body: JSON.stringify({ user_id: userId, berth_id: berthId })
      });
      const data = await res.json();
      if (res.ok) {
        setHistory(h => [...h, {userId, berthId}]);
        await fetchData();
        const u = usersById[userId];
        const warns = check.signals.filter(s => s.type === 'warn');
        const genderWarn = check.signals.find(s => s.icon === 'alert');
        if (genderWarn) {
          setGenderAlert({ nome: u?.nome || '', vaga: berthId, labels: warns.map(s=>s.label) });
        } else {
          toast.push({
            icon: warns.length ? 'bell' : 'check',
            title: warns.length
              ? (lang==='pt'?`⚠ ${u?.nome} na vaga ${berthId} — confira`:`⚠ ${u?.nome} in ${berthId} — review`)
              : `${u?.nome} ${lang==='pt'?'na vaga':'in berth'} ${berthId}`,
            body: check.signals.length ? check.signals.map(s=>s.label).join(' · ') : (lang==='pt'?'compatibilidade ok':'compat ok'),
            duration: warns.length ? 8000 : 6000,
          });
        }
      } else {
        toast.push({icon:'x', title: data.error || 'Erro ao atribuir vaga'});
      }
    } catch(e) {
      toast.push({icon:'x', title:'Erro de conexão'});
    } finally {
      setDragging(null); setDropTarget(null);
    }
  };

  const undo = async (userId, berthId) => {
    try {
      await fetch(`/api/cabins/assign/${userId}`, {
        method: 'DELETE',
        headers: { 'Authorization': `Bearer ${token()}` }
      });
      await fetchData();
    } catch(e) { console.error(e); }
  };

  const undoLast = () => {
    if (!history.length) return;
    const last = history[history.length-1];
    undo(last.userId, last.berthId);
    setHistory(h => h.slice(0,-1));
    toast.push({icon:'check', title: lang==='pt'?'Desfeito':'Undone', duration:2200});
  };

  // ─────────── Planejamento ───────────
  const fetchPlans = async () => {
    try {
      const r = await fetch('/api/cabins/plans', { headers:{ Authorization:`Bearer ${token()}` } });
      if (r.ok) setPlans(await r.json());
    } catch(e) { console.error(e); }
  };
  useEffect(() => { if (isAdmin) fetchPlans(); }, [isAdmin]);

  const planSet = (userId, berthId) => {
    const check = compatCheck(userId, berthId);
    setPlanAssign(prev => {
      const next = {};
      for (const [bid, uid] of Object.entries(prev)) {
        if (uid === userId) continue;            // tira a pessoa da vaga antiga
        if (bid === String(berthId)) continue;   // libera a vaga de destino
        next[bid] = uid;
      }
      next[berthId] = userId;
      return next;
    });
    setPlanDirty(true); setDragging(null); setDropTarget(null);
    const warns = check.signals.filter(s => s.type === 'warn');
    const genderWarn = check.signals.find(s => s.icon === 'alert');
    const u = usersById[userId];
    if (genderWarn) {
      setGenderAlert({ nome: u?.nome || '', vaga: berthId, labels: warns.map(s=>s.label) });
    } else if (warns.length) {
      toast.push({icon:'bell', title: lang==='pt'?`⚠ ${u?.nome}: ${warns.map(s=>s.label).join(' · ')}`:`⚠ ${u?.nome}: ${warns.map(s=>s.label).join(' · ')}`, duration:7000});
    }
  };
  const planUnset = (userId) => {
    setPlanAssign(prev => {
      const next = {};
      for (const [bid, uid] of Object.entries(prev)) if (uid !== userId) next[bid] = uid;
      return next;
    });
    setPlanDirty(true); setDragging(null); setDropTarget(null);
  };

  // Ações de drag respeitam o modo
  const resolverAgora = async () => {
    setResolvendo(true);
    try {
      const r = await fetch('/api/cabins/plans/resolve', { method:'POST', headers:{ Authorization:`Bearer ${token()}` } });
      const d = await r.json();
      if (r.ok) {
        if (d.resolvidas.length) toast.push({ icon:'check', title: lang==='pt' ? `${d.resolvidas.length} troca(s) resolvida(s)` : `${d.resolvidas.length} move(s) resolved`, body: d.resolvidas.map(x=>`${x.nome} → ${x.berth_id}`).join(' · '), duration: 8000 });
        else toast.push({ icon:'bell', title: lang==='pt' ? 'Nada pronto ainda' : 'Nothing ready yet', body: lang==='pt' ? 'as condições das pendências ainda não bateram' : 'pending conditions not met yet' });
        await fetchData();
      } else toast.push({ icon:'x', title: d.error || 'Erro' });
    } catch(e) { toast.push({ icon:'x', title:'Erro de conexão' }); }
    setResolvendo(false);
  };
  const desarmarLazy = async () => {
    try {
      const r = await fetch('/api/cabins/plans/disarm', { method:'POST', headers:{ Authorization:`Bearer ${token()}` } });
      if (r.ok) { toast.push({ icon:'check', title: lang==='pt'?'Embarque reativo desarmado':'Reactive embark disarmed' }); await fetchData(); }
    } catch(e) { toast.push({ icon:'x', title:'Erro de conexão' }); }
  };
  const berthDe = (userId) => Object.keys(liveAssign).find(b => String(liveAssign[b]) === String(userId)) || null;
  const swapLive = async (userId, vaga, trocar) => {
    const occId = vaga.user;
    const antiga = berthDe(userId);
    try {
      await fetch(`/api/cabins/assign/${occId}`, { method:'DELETE', headers:{ Authorization:`Bearer ${token()}` } });
      const r1 = await fetch('/api/cabins/assign', { method:'POST', headers:{ 'Content-Type':'application/json', Authorization:`Bearer ${token()}` }, body: JSON.stringify({ user_id: userId, berth_id: vaga.id }) });
      if (!r1.ok) throw new Error('assign');
      if (trocar && antiga) await fetch('/api/cabins/assign', { method:'POST', headers:{ 'Content-Type':'application/json', Authorization:`Bearer ${token()}` }, body: JSON.stringify({ user_id: occId, berth_id: antiga }) });
      const uIn = usersById[userId], uOut = usersById[occId];
      toast.push({ icon:'check', title: trocar && antiga
        ? (lang==='pt' ? `🔀 ${uIn?.nome} ↔ ${uOut?.nome}` : `🔀 ${uIn?.nome} ↔ ${uOut?.nome}`)
        : (lang==='pt' ? `${uIn?.nome} na ${vaga.id} · ${uOut?.nome} sem vaga` : `${uIn?.nome} in ${vaga.id} · ${uOut?.nome} unassigned`) });
      await fetchData();
    } catch(e) { toast.push({ icon:'x', title: lang==='pt'?'Não consegui completar a troca':'Swap failed' }); await fetchData(); }
    setDragging(null); setDropTarget(null);
  };
  const swapPlan = (userId, vaga, trocar) => {
    const occId = vaga.user;
    setPlanAssign(prev => {
      const antiga = Object.keys(prev).find(b => String(prev[b]) === String(userId)) || null;
      const next = { ...prev };
      if (antiga) delete next[antiga];
      delete next[vaga.id];
      next[vaga.id] = userId;
      if (trocar && antiga) next[antiga] = occId; // sem antiga → occ volta pro grupo do plano
      return next;
    });
    setPlanDirty(true);
    setDragging(null); setDropTarget(null);
  };
  const doAssign = (userId, berthId) => mode==='plan' ? planSet(userId, berthId) : assign(userId, berthId);
  const doRemove = (userId)          => mode==='plan' ? planUnset(userId)       : removeFromCabin(userId);

  const enterPlan = () => {
    setMode('plan'); // o plano começa em branco; importe pessoas ou o estado atual
  };
  const importarEstadoAtual = () => {
    setPlanAssign({...liveAssign});
    setPlanPool(new Set(Object.values(liveAssign)));
    setPlanDirty(true);
    toast.push({icon:'check', title: lang==='pt'?'Estado atual do barco importado':'Current state imported'});
  };
  const importarPessoas = (ids) => {
    setPlanPool(prev => { const s = new Set(prev); ids.forEach(id => s.add(id)); return s; });
    setImportOpen(false); setPlanDirty(true);
    toast.push({icon:'check', title: lang==='pt'?`${ids.length} pessoa(s) importada(s)`:`${ids.length} person(s) imported`});
  };
  const removerDoPlano = (id) => {
    setPlanPool(prev => { const s = new Set(prev); s.delete(id); return s; });
    planUnset(id); setPlanDirty(true);
  };
  const loadPlan = async (id) => {
    if (!id) { setPlanAssign({}); setPlanPool(new Set()); setCurrentPlanId(null); setPlanName(''); setPlanDirty(false); return; }
    try {
      const r = await fetch(`/api/cabins/plans/${id}`, { headers:{ Authorization:`Bearer ${token()}` } });
      if (!r.ok) { toast.push({icon:'x', title:'Erro ao abrir plano'}); return; }
      const p = await r.json();
      const map = {}; const pool = new Set();
      (p.dados||[]).forEach(it => { if (it.user_id != null) { pool.add(parseInt(it.user_id)); if (it.berth_id) map[it.berth_id] = parseInt(it.user_id); } });
      setPlanAssign(map); setPlanPool(pool); setCurrentPlanId(p.id); setPlanName(p.nome); setPlanDirty(false);
      toast.push({icon:'check', title: `${lang==='pt'?'Plano aberto':'Plan opened'}: ${p.nome}`});
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };
  const newPlan = () => {
    setPlanAssign({}); setPlanPool(new Set()); setCurrentPlanId(null); setPlanName(''); setPlanDirty(false);
    toast.push({icon:'check', title: lang==='pt'?'Novo plano em branco':'New blank plan', duration:2600});
  };
  const armarLazy = async () => {
    const items = Object.entries(planAssign).map(([berth_id, user_id]) => ({ berth_id, user_id }));
    if (!items.length) { toast.push({icon:'x', title: lang==='pt'?'O plano está vazio':'The plan is empty'}); return; }
    try {
      const r = await fetch('/api/cabins/plans/arm', { method:'POST', headers:{ 'Content-Type':'application/json', Authorization:`Bearer ${token()}` }, body: JSON.stringify({ plano_id: currentPlanId, plano_nome: planName || null, items }) });
      const d = await r.json();
      if (r.ok) {
        toast.push({icon:'check', title: lang==='pt'?'Embarque reativo armado':'Reactive embark armed', body: lang==='pt'?`${d.resolvidas} já resolvida(s) · ${d.pendentes} aguardando`:`${d.resolvidas} resolved · ${d.pendentes} waiting`});
        setModeChoiceOpen(false); setMode('live'); fetchData();
      } else toast.push({icon:'x', title: d.error || (lang==='pt'?'Erro ao armar':'Error arming')});
    } catch(e) { toast.push({icon:'x', title: lang==='pt'?'Erro de conexão':'Connection error'}); }
  };

  const savePlan = async () => {
    if (!planName.trim()) { toast.push({icon:'x', title: lang==='pt'?'Dê um nome ao plano':'Name the plan first'}); return; }
    setSavingPlan(true);
    const dados = [
      ...Object.entries(planAssign).map(([berth_id, user_id]) => ({ berth_id, user_id })),
      ...[...planPool].filter(id => !placedInPlan.has(id)).map(user_id => ({ berth_id: null, user_id }))
    ];
    try {
      const url = currentPlanId ? `/api/cabins/plans/${currentPlanId}` : '/api/cabins/plans';
      const r = await fetch(url, { method: currentPlanId ? 'PUT' : 'POST',
        headers:{ 'Content-Type':'application/json', Authorization:`Bearer ${token()}` },
        body: JSON.stringify({ nome: planName.trim(), dados }) });
      if (r.status === 404) { toast.push({icon:'x', title: lang==='pt'?'Rota de planos não encontrada (404)':'Plans route not found (404)', body: lang==='pt'?'reinicie o container com o other.js novo':'restart the container with the new other.js'}); return; }
      let p = {};
      try { p = await r.json(); } catch(_) { toast.push({icon:'x', title: lang==='pt'?'Resposta inválida do servidor':'Invalid server response', body: lang==='pt'?'o backend pode não ter sido reiniciado':'backend may not be restarted'}); return; }
      if (r.ok) { setCurrentPlanId(p.id); setPlanDirty(false); fetchPlans(); toast.push({icon:'check', title: lang==='pt'?'Plano salvo':'Plan saved'}); }
      else toast.push({icon:'x', title: p.error || (lang==='pt'?'Erro ao salvar':'Error saving')});
    } catch(e) { toast.push({icon:'x', title: lang==='pt'?'Sem resposta do servidor':'No server response', body: lang==='pt'?'backend offline ou não reiniciado':'backend offline or not restarted'}); }
    finally { setSavingPlan(false); }
  };
  const deletePlan = async () => {
    if (!currentPlanId) { setPlanAssign({}); setPlanPool(new Set()); setPlanName(''); setPlanDirty(false); return; }
    if (!window.confirm(lang==='pt'?'Excluir este plano de cabines?':'Delete this cabin plan?')) return;
    try {
      await fetch(`/api/cabins/plans/${currentPlanId}`, { method:'DELETE', headers:{ Authorization:`Bearer ${token()}` } });
      setPlanAssign({}); setPlanPool(new Set()); setCurrentPlanId(null); setPlanName(''); setPlanDirty(false); fetchPlans();
      toast.push({icon:'check', title: lang==='pt'?'Plano excluído':'Plan deleted'});
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };
  const deletePlanById = async (id) => {
    if (!window.confirm(lang==='pt'?'Excluir este plano?':'Delete this plan?')) return;
    try {
      await fetch(`/api/cabins/plans/${id}`, { method:'DELETE', headers:{ Authorization:`Bearer ${token()}` } });
      if (id===currentPlanId) { setCurrentPlanId(null); setPlanName(''); }
      fetchPlans();
      toast.push({icon:'check', title: lang==='pt'?'Plano excluído':'Plan deleted'});
    } catch(e) { toast.push({icon:'x', title:'Erro de conexão'}); }
  };

  // ─── PDF export of berth manifest ───
  const exportPDF = () => {
    const w = window.open('', '_blank', 'width=900,height=1100');
    const today = new Date().toISOString().split('T')[0];
    const sections = [...decks].sort((a,b)=>+b.deck-+a.deck).map(d => {
      const rows = d.vagas.map(v => {
        const u = v.user ? usersById[v.user] : null;
        const turno = u?.turno ? TURNOS[u.turno]?.label : '—';
        return `<tr>
          <td><strong>${v.id}</strong></td>
          <td>${v.cabinType}</td>
          <td>${u ? u.nome : '<em style="color:#948B7E">vaga livre</em>'}</td>
          <td>${u?.empresa||''}</td>
          <td>${u?.genero==='F' ? '♀' : u?.genero==='M' ? '♂' : ''}</td>
          <td>${turno}</td>
        </tr>`;
      }).join('');
      return `<h2 style="font-family:Fraunces,Georgia,serif;font-size:22px;font-weight:400;margin-top:28px;margin-bottom:8px;letter-spacing:-0.4px">Deck <em>${d.deck}</em></h2>
      <table>
        <thead><tr><th>Vaga</th><th>Tipo</th><th>Ocupante</th><th>Empresa</th><th>G</th><th>Turno</th></tr></thead>
        <tbody>${rows}</tbody>
      </table>`;
    }).join('');
    w.document.write(`<!doctype html><html><head><meta charset="utf-8"><title>Mapa de vagas · ${today}</title>
<style>
  @page{margin:18mm}
  body{font-family:'Inter',Arial,sans-serif;color:#1F2A2E;font-size:11px}
  h1{font-family:'Fraunces',Georgia,serif;font-weight:400;font-size:36px;letter-spacing:-0.7px;margin:0 0 4px}
  h1 em{color:#D9694B;font-style:italic}
  .meta{font-family:'IBM Plex Mono',monospace;font-size:10px;letter-spacing:1.5px;text-transform:uppercase;color:#7A8388;margin-bottom:18px}
  .stats{display:flex;gap:24px;margin:18px 0;padding:12px 18px;background:#F5EFE3;border-radius:10px;font-size:11px}
  .stats b{font-family:'Fraunces',Georgia,serif;font-size:20px;font-weight:500;display:block}
  table{width:100%;border-collapse:collapse;font-size:11px;margin-top:6px}
  th{text-align:left;font-family:'IBM Plex Mono',monospace;font-size:9px;letter-spacing:1.2px;text-transform:uppercase;color:#7A8388;padding:8px;border-bottom:1px solid #1F2A2E}
  td{padding:7px 8px;border-bottom:1px solid #EAE3D6}
  .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>Mapa de <em>vagas</em></h1>
<div class="meta">${today} · Genesis · documento operacional</div>
<div class="stats">
  <div><b>${occupied}/${totalBerths}</b>vagas ocupadas</div>
  <div><b>${totalBerths-occupied}</b>vagas livres</div>
  <div><b>${femaleVagas}</b>femininas</div>
  <div><b>${unassigned.length}</b>aguardando acomodação</div>
</div>
${sections}
<div class="footer">documento sensível · uso interno · operação Genesis</div>
</body></html>`);
    w.document.close();
    setTimeout(()=>w.print(), 300);
  };

  // Available decks for filter
  const allDecks = useMemo(() => [...new Set(vagas.map(v=>String(v.deck)))].sort((a,b)=>+b-+a), [vagas]);

  // ─── Ship plan: build deck-by-deck layout for the schematic ───
  const shipPlan = useMemo(() => {
    const byDeck = {};
    adaptedCabins.forEach(c => {
      (byDeck[c.deck] = byDeck[c.deck] || []).push(c);
    });
    const decks = [9,8,7,6].map(d => ({
      deck: d,
      label: ({9:'Residencial',8:'Tripulação',7:'Misto',6:'Operações'}[d]),
      label_en: ({9:'Residential',8:'Crew',7:'Mixed',6:'Operations'}[d]),
      cabins: byDeck[d] || null,
      zones: null,
    }));
    return decks;
  }, [adaptedCabins, lang]);

  return (
    <main style={{padding: isMobile ? '16px 16px 240px' : '30px 36px 280px', flex:1}}>
      {/* Cabeçalho de página */}
      <div style={{marginBottom:22}}>
        <div className="t-eyebrow">{lang==='pt'?'acomodação a bordo':'accommodation on board'}</div>
        <h1 style={{fontFamily:'var(--fs)',fontSize:isMobile?30:38,letterSpacing:'-0.8px',lineHeight:1.05,marginTop:6}}>
          {lang==='pt'?<>Cab<em style={{fontStyle:'italic',color:'var(--accent)'}}>ines.</em></>:<>Cab<em style={{fontStyle:'italic',color:'var(--accent)'}}>ins.</em></>}
        </h1>
      </div>

      {/* Toggle Ao vivo / Planejamento */}
      <div style={{display:'flex',alignItems:'center',gap:12,marginBottom:16,flexWrap:'wrap'}}>
        <div style={{display:'inline-flex',background:'var(--cream-2)',borderRadius:12,padding:4,gap:4}}>
          {[{id:'live',pt:'Ao vivo',en:'Live'},{id:'plan',pt:'Planejamento',en:'Planning'}].map(t=>(
            <button key={t.id}
              onClick={()=> t.id==='plan' ? enterPlan() : setMode('live')}
              style={{padding:'8px 16px',borderRadius:9,border:'none',cursor:'pointer',fontFamily:'var(--fm)',fontSize:12.5,letterSpacing:0.3,
                background: mode===t.id ? 'var(--surface)' : 'transparent',
                color: mode===t.id ? 'var(--ink)' : 'var(--muted)',
                boxShadow: mode===t.id ? 'var(--shadow-sm)' : 'none', transition:'all .15s'}}>
              {lang==='pt'?t.pt:t.en}
            </button>
          ))}
        </div>
        {mode==='plan' && (
          <span style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--accent-2)',background:'rgba(217,105,75,0.08)',border:'1px solid var(--accent)',borderRadius:20,padding:'4px 12px'}}>
            🗺️ {lang==='pt'?'PLANO — não afeta o estado real do barco':'PLAN — does not affect the live state'}
          </span>
        )}
        <div style={{marginLeft:'auto',display:'flex',alignItems:'center',gap:8}}>
          {mode==='live' && (
            <button className={'btn-icone' + (lazyView ? '' : ' neutro')} onClick={()=>setLazyView(v=>!v)} title={lang==='pt'?'programados pelo embarque reativo':'scheduled by the reactive embark'}>
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke={lazyView?'#fff':'var(--ink)'} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 12a9 9 0 0 1 15-6.7L21 8"/><path d="M21 3v5h-5"/><path d="M21 12a9 9 0 0 1-15 6.7L3 16"/><path d="M3 21v-5h5"/></svg>
            </button>
          )}
          <div style={{position:'relative'}}>
            <button className="btn-icone" onClick={()=>setAcoesOpen(v=>!v)} title={lang==='pt'?'ações (exportar, gerenciar)':'actions (export, manage)'} style={{fontSize:18,letterSpacing:1}}>⋯</button>
            {acoesOpen && (
              <div style={{position:'absolute',top:'100%',right:0,marginTop:4,zIndex:50,background:'var(--surface)',border:'1px solid var(--line)',borderRadius:10,boxShadow:'var(--shadow-md)',overflow:'hidden',minWidth:190}}>
                <div onClick={()=>{setAcoesOpen(false);exportPDF();}} style={{padding:'11px 14px',fontFamily:'var(--fm)',fontSize:12.5,cursor:'pointer',color:'var(--ink)'}} onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'} onMouseLeave={e=>e.currentTarget.style.background='transparent'}>📄 {lang==='pt'?'Exportar PDF':'Export PDF'}</div>
                {isAdmin && <div onClick={()=>{setAcoesOpen(false);setManaging(true);}} style={{padding:'11px 14px',fontFamily:'var(--fm)',fontSize:12.5,cursor:'pointer',color:'var(--ink)',borderTop:'1px solid var(--line-soft)'}} onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'} onMouseLeave={e=>e.currentTarget.style.background='transparent'}>⚙ {lang==='pt'?'Gerenciar cabines':'Manage cabins'}</div>}
              </div>
            )}
          </div>
        </div>
      </div>

      {/* Painel de resumo */}
      <div style={{display:'grid', gridTemplateColumns:'repeat(auto-fit, minmax(110px, 1fr))', gap:10, marginBottom:18}}>
        {[
          {n:`${occupied}/${totalBerths}`, l:lang==='pt'?'ocupadas':'occupied', c:'var(--accent)'},
          {n:totalBerths - occupied, l:lang==='pt'?'livres':'free', c:'var(--teal)'},
          {n:femaleVagas, l:lang==='pt'?'femininas':'female', c:'var(--rose)'},
          {n:unassigned.length, l:lang==='pt'?'a acomodar':'to assign', c:'var(--sun)'},
          ...(mode==='plan' && planConflitos.length > 0 ? [{n:planConflitos.length, l:lang==='pt'?'conflitos':'conflicts', c:'var(--accent-2)'}] : [])
        ].map((s,i) => (
          <div key={i} style={{background:'var(--surface)',border:'1px solid var(--line-soft)',borderTop:`3px solid ${s.c}`,borderRadius:14,padding:'10px 14px',minWidth:90,boxShadow:'var(--shadow-sm)'}}>
            <div style={{fontFamily:'var(--fs)',fontSize:20,fontWeight:500,lineHeight:1,color:'var(--ink)'}}>{s.n}</div>
            <div className="t-eyebrow" style={{marginTop:3,fontSize:9,color:'var(--muted)'}}>{s.l}</div>
          </div>
        ))}
      </div>

      {mode==='live' && isAdmin && (
        <LazyBanner intents={intents} lang={lang} lazyView={lazyView} resolvendo={resolvendo}
          onResolver={resolverAgora} onDesarmar={desarmarLazy} onVerCards={()=>setLazyView(v=>!v)}/>
      )}

      {/* Barra de gestão de planos */}
      {mode==='plan' && (
        <div style={{display:'flex',alignItems:'center',gap:10,marginBottom:18,flexWrap:'wrap',padding:'12px 16px',background:'var(--surface)',border:'1px solid var(--line-soft)',borderLeft:'3px solid var(--accent)',borderRadius:14,boxShadow:'var(--shadow-sm)'}}>
          <input className="input" placeholder={lang==='pt'?'nome do plano…':'plan name…'} value={planName} onChange={e=>{setPlanName(e.target.value);setPlanDirty(true);}} style={{minWidth:180,flex:'1 1 200px',boxSizing:'border-box'}}/>
          <button className="btn-icone" onClick={newPlan} title={lang==='pt'?'novo plano em branco':'new blank plan'}><Icon n="plus" size={16}/></button>
          <button className="btn-icone" onClick={()=>setImportOpen(true)} title={lang==='pt'?'importar pessoas do cadastro':'import people from the registry'}>
            <svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round"><circle cx="9" cy="8" r="3.4"/><path d="M2.8 20c0-3.3 2.9-5.2 6.2-5.2s6.2 1.9 6.2 5.2"/><path d="M18 8v6M15 11h6"/></svg>
          </button>
          <button className="btn-icone" onClick={importarEstadoAtual} title={lang==='pt'?'importar a arrumação atual do barco':'import the current arrangement'}>⚓</button>
          <button className="btn-icone" onClick={()=>{ fetchPlans(); setLoadOpen(true); }} title={lang==='pt'?'carregar plano salvo':'load a saved plan'}>
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="2.1" strokeLinecap="round" strokeLinejoin="round"><path d="M3 7a2 2 0 0 1 2-2h4l2 2.4h8A2 2 0 0 1 21 9.4V17a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z"/></svg>
            {plans.length > 0 && <span className="badge">{plans.length}</span>}
          </button>
          {currentPlanId && (
            <button className="btn-icone neutro" onClick={deletePlan} title={lang==='pt'?'excluir este plano':'delete this plan'}>
              <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="var(--rose)" strokeWidth="2.2" strokeLinecap="round"><path d="M4 7h16M9 7V5h6v2M7 7l1 13h8l1-13M10 11v6M14 11v6"/></svg>
            </button>
          )}
          <button className="btn-tidal" onClick={savePlan} disabled={savingPlan}>{savingPlan?(lang==='pt'?'Salvando…':'Saving…'):(lang==='pt'?'Salvar':'Save')}{planDirty?' •':''}</button>
          <div style={{flex:1,minWidth:12}}/>
          <button className="btn-tidal" onClick={()=>{ if(!Object.keys(planAssign).length){ toast.push({icon:'x', title: lang==='pt'?'O plano está vazio':'The plan is empty'}); return; } setModeChoiceOpen(true); }}>
            <Icon n="upload" size={14}/>{lang==='pt'?'Aplicar':'Apply'}
          </button>
        </div>
      )}

      {mode==='plan' && planConflitos.length > 0 && (
        <div style={{marginBottom:18,padding:'14px 18px',background:'rgba(177,78,92,0.06)',border:'1px solid var(--rose)',borderRadius:14}}>
          <div style={{display:'flex',alignItems:'center',gap:8,marginBottom:8}}>
            <span style={{fontSize:15}}>⚠️</span>
            <span style={{fontFamily:'var(--fs)',fontSize:16,color:'var(--rose)'}}>
              {lang==='pt'
                ? `${planConflitos.length} camarote${planConflitos.length>1?'s':''} com gênero misto`
                : `${planConflitos.length} cabin${planConflitos.length>1?'s':''} with mixed gender`}
            </span>
            <span style={{marginLeft:'auto',fontFamily:'var(--fm)',fontSize:10.5,color:'var(--muted)'}}>{lang==='pt'?'aviso — dá pra salvar mesmo assim':'warning — you can still save'}</span>
          </div>
          <div style={{display:'grid',gap:5}}>
            {planConflitos.map((c,i) => (
              <div key={i} style={{fontFamily:'var(--fm)',fontSize:12,color:'var(--ink)',display:'flex',flexWrap:'wrap',gap:6,alignItems:'baseline'}}>
                <span style={{fontWeight:600,color:'var(--rose)'}}>Cab {c.cabNum}{c.room!=='*'?` · ${lang==='pt'?'ala':'wing'} ${c.room}`:''}</span>
                <span style={{color:'var(--muted)'}}>{c.nomes.map(n=>`${n.nome} (${n.genero})`).join('  ·  ')}</span>
              </div>
            ))}
          </div>
        </div>
      )}

      {loading ? (
        // Skeleton loaders
        <div>
          <div style={{height:120,borderRadius:20,marginBottom:24,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 style={{display:'grid',gridTemplateColumns:isMobile?'1fr':'300px 1fr',gap:24}}>
            <div style={{height:300,borderRadius:24,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 style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(186px, 1fr))',gap:12}}>
              {[1,2,3,4,5,6].map(i=>(
                <div key={i} style={{height:110,borderRadius:16,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>
        </div>
      ) : (
      <>
      {/* Toggle entre corte longitudinal e planta baixa */}
      <div style={{display:'flex',justifyContent:'flex-end',marginBottom:10}}>
        <div style={{display:'inline-flex',background:'var(--cream-2)',borderRadius:12,padding:4,gap:4}}>
          {[{id:'planta',pt:'🗺️ Planta baixa',en:'🗺️ Floor plan'},{id:'corte',pt:'🚢 Corte',en:'🚢 Section'}].map(t=>(
            <button key={t.id}
              onClick={()=>setPlanView(t.id)}
              style={{padding:'7px 14px',borderRadius:9,border:'none',cursor:'pointer',fontFamily:'var(--fm)',fontSize:12,letterSpacing:0.3,
                background: planView===t.id ? 'var(--surface)' : 'transparent',
                color: planView===t.id ? 'var(--ink)' : 'var(--muted)',
                boxShadow: planView===t.id ? 'var(--shadow-sm)' : 'none', transition:'all .15s'}}>
              {lang==='pt'?t.pt:t.en}
            </button>
          ))}
        </div>
      </div>

      {/* Mapa do navio — planta baixa (padrão) ou corte longitudinal */}
      {planView === 'corte' ? (
        <ShipPlan plan={shipPlan} usersById={usersById} filterDeck={filterDeck} setFilterDeck={setFilterDeck} onOpenFilter={()=>setFiltroOpen(true)} filtroAtivo={filtroAtivo} lang={lang}/>
      ) : (
        <DeckPlan vagas={vagas} usersById={usersById} filterDeck={filterDeck} setFilterDeck={setFilterDeck} onOpenFilter={()=>setFiltroOpen(true)} filtroAtivo={filtroAtivo} lang={lang}/>
      )}



      <div style={{display:'grid',gridTemplateColumns: isMobile ? '1fr' : '300px 1fr',gap: isMobile ? 14 : 24}}>
        {/* Unassigned column */}
        <aside
          onDragOver={e=>{ const inPlace = dragging && (mode==='plan' ? placedInPlan.has(dragging) : usersById[dragging]?.cabine_atual); if(inPlace){ e.preventDefault(); if(!overUnassign) setOverUnassign(true); } }}
          onDragLeave={e=>{ if(!e.currentTarget.contains(e.relatedTarget)) setOverUnassign(false); }}
          onDrop={e=>{ e.preventDefault(); const uid=dragging; const inPlace = uid && (mode==='plan' ? placedInPlan.has(uid) : usersById[uid]?.cabine_atual); setOverUnassign(false); setDragging(null); setDropTarget(null); if(inPlace) doRemove(uid); }}
          style={{
          background: overUnassign ? 'rgba(217,105,75,0.07)' : 'var(--surface)',
          border: `1px solid ${overUnassign ? 'var(--accent)' : 'var(--line-soft)'}`,
          borderTop: `3px solid ${overUnassign ? 'var(--accent)' : 'var(--accent)'}`,
          borderRadius:24,padding:22,
          height:'fit-content',position:'sticky',top:18, maxHeight:'calc(100vh - 40px)', overflowY:'auto',
          boxShadow:'var(--shadow-md)', transition:'background .15s, border-color .15s'
        }}>
          <div style={{display:'flex',alignItems:'baseline',justifyContent:'space-between',marginBottom:14}}>
            <div style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.3px'}}>
              {lang==='pt'?'A':'To'} <em style={{fontStyle:'italic',color:'var(--accent)'}}>{lang==='pt'?'acomodar':'assign'}</em>
            </div>
            <Pill tone="coral">{unassigned.length}</Pill>
          </div>
          {dragging && (mode==='plan' ? placedInPlan.has(dragging) : usersById[dragging]?.cabine_atual) && (
            <div style={{fontSize:11.5,fontFamily:'var(--fm)',color:'var(--accent-2)',background:'rgba(217,105,75,0.08)',border:'1px dashed var(--accent)',borderRadius:10,padding:'8px 10px',marginBottom:10,textAlign:'center'}}>
              {lang==='pt'?'↩ solte aqui para tirar da cabine':'↩ drop here to unassign'}
            </div>
          )}
          <input className="input" placeholder={lang==='pt'?'buscar nome, ID ou empresa…':'search name, ID or company…'} value={search} onChange={e=>setSearch(e.target.value)} style={{marginBottom:8}}/>

          {/* Filtro rápido por função */}
          <div style={{display:'flex',gap:6,flexWrap:'wrap',marginBottom:10}}>
            {[
              {id:'all', pt:'Todos', en:'All'},
              {id:'crew', pt:'Tripulação', en:'Crew'},
              {id:'ops', pt:'Operações', en:'Ops'},
              {id:'catering', pt:'Catering', en:'Catering'},
              {id:'terceirizada', pt:'Terceirizada', en:'Contracted'}
            ].map(r => (
              <button key={r.id}
                onClick={()=>setFilterRole(r.id)}
                style={{
                  padding:'5px 10px', borderRadius:20, border:'1px solid var(--line-soft)',
                  background: filterRole===r.id ? 'var(--accent)' : 'var(--cream-2)',
                  color: filterRole===r.id ? '#fff' : 'var(--muted)',
                  fontFamily:'var(--fm)', fontSize:11, fontWeight:500, cursor:'pointer',
                  transition:'all .15s'
                }}>
                {lang==='pt'?r.pt:r.en}
              </button>
            ))}
          </div>

          {['crew','ops','catering','terceirizada'].filter(role => filterRole==='all' || role===filterRole).map(role => {
            const q = search.toLowerCase();
            const list = unassigned.filter(u => u.role === role &&
              (!search || u.nome.toLowerCase().includes(q) || u.genesis_id.toLowerCase().includes(q) || (u.empresa||'').toLowerCase().includes(q)));
            if (!list.length) return null;
            const labels = lang==='pt'
              ? {admin:'Administração', crew:'Tripulação', ops:'Operações', catering:'Catering', terceirizada:'Terceirizada'}
              : {admin:'Admin', crew:'Crew', ops:'Operations'};
            return (
              <div key={role}>
                <div style={{fontFamily:'var(--fs)',fontStyle:'italic',fontSize:14,color:'var(--muted)',margin:'14px 0 4px',display:'flex',justifyContent:'space-between'}}>
                  <span>{labels[role]}</span>
                  <span style={{fontFamily:'var(--fm)',fontSize:10,color:'var(--muted-2)',fontStyle:'normal'}}>{list.length}</span>
                </div>
                {list.map(u => {
                  const turno = u.turno && TURNOS[u.turno];
                  return (
                    <div key={u.id}
                      draggable
                      onDragStart={e=>{setDragging(u.id);e.dataTransfer.effectAllowed='move'}}
                      onDragEnd={()=>{setDragging(null);setDropTarget(null)}}
                      onClick={()=>setDragging(prev => prev===u.id ? null : u.id)}
                      onMouseEnter={()=>setHoveredUser(u.id)}
                      onMouseLeave={()=>setHoveredUser(null)}
                      title={lang==='pt'?'Arraste para uma vaga':'Drag to a berth'}
                      style={{
                        display:'flex',alignItems:'center',gap:10,padding:'9px 10px',borderRadius:14,
                        cursor: dragging===u.id ? 'grabbing' : 'grab',
                        transition:'all .15s', margin:'0 -6px',
                        userSelect:'none', WebkitUserSelect:'none', MozUserSelect:'none',
                        outline: dragging===u.id ? '2px solid var(--accent)' : 'none',
                        background: dragging===u.id ? 'var(--cream-2)' : (hoveredUser===u.id ? 'var(--cream-2)' : 'transparent')
                      }}>
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="var(--muted-2)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{flexShrink:0, opacity:0.7}}>
                        <line x1="8" y1="6" x2="21" y2="6"/>
                        <line x1="8" y1="12" x2="21" y2="12"/>
                        <line x1="8" y1="18" x2="21" y2="18"/>
                        <line x1="3" y1="6" x2="3.01" y2="6"/>
                        <line x1="3" y1="12" x2="3.01" y2="12"/>
                        <line x1="3" y1="18" x2="3.01" y2="18"/>
                      </svg>
                      <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',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)',letterSpacing:0.2,marginTop:1,display:'flex',alignItems:'center',gap:6}}>
                          {turno && (
                            <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
                              <span style={{width:6,height:6,borderRadius:'50%',background:turno.color}}/>
                              {turno.short}
                            </span>
                          )}
                          {u.empresa}
                        </div>
                      </div>
                      {mode==='plan' && <button onClick={(e)=>{e.stopPropagation(); removerDoPlano(u.id);}} title={lang==='pt'?'remover do plano':'remove from plan'} style={{flexShrink:0,border:'none',background:'transparent',cursor:'pointer',color:'var(--muted)',fontSize:16,lineHeight:1,padding:'2px 4px'}}>×</button>}
                    </div>
                  );
                })}
              </div>
            );
          })}
          {!unassigned.length && (
            <div className="empty" style={{padding:'24px 12px',fontSize:15}}>
              {lang==='pt'?'todos acomodados ✓':'all assigned ✓'}
            </div>
          )}
        </aside>

        {/* Decks of vagas */}
        <div style={{display:'flex',flexDirection:'column',gap:30}}>
          {cabinsByDeck.map(({deck, cabins}) => {
            const deckVagas = cabins.flatMap(c => c.berths);
            const occ = deckVagas.filter(v => v.user).length;
            const tot = deckVagas.length;
            const fem = deckVagas.filter(v => v.user && usersById[v.user]?.genero === 'F').length;
            const labels = lang==='pt'
              ? {9:'residencial',8:'tripulação',7:'misto',6:'operações',5:'operações'}
              : {9:'residential',8:'crew',7:'mixed',6:'operations',5:'operations'};
            return (
              <div key={deck} id={'deck-sec-'+deck}>
                <div style={{display:'flex',alignItems:'baseline',gap:14,marginBottom:14,flexWrap:'wrap'}}>
                  <div style={{fontFamily:'var(--fs)',fontSize:30,letterSpacing:'-0.6px',fontWeight:400}}>
                    Deck {deck} <em style={{fontStyle:'italic',color:'var(--accent)',fontSize:18,fontWeight:400}}>{labels[deck]}</em>
                  </div>
                  {fem > 0 && <Pill tone="rose">♀ {fem}</Pill>}
                  <div style={{marginLeft:'auto',display:'flex',alignItems:'center',gap:10,fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)'}}>
                    <div style={{width:140,height:6,background:'var(--cream-2)',borderRadius:3,overflow:'hidden'}}>
                      <div style={{width:`${occ/tot*100}%`,height:'100%',background:'var(--teal)',borderRadius:3,transition:'width .4s'}}/>
                    </div>
                    {occ} / {tot}
                  </div>
                </div>
                <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(220px, 1fr))',gap:14}}>
                  {cabins.map(cabin => (
                    <CabinCard key={cabin.num} cabin={cabin} usersById={usersById}
                      dragging={dragging} dropTarget={dropTarget} setDropTarget={setDropTarget}
                      onDrop={(berthId)=>{if(dragging) { doAssign(dragging, berthId); setDragging(null); }}}
                      onDropOcupada={isAdmin ? ((vg)=>{ if (dragging && dragging!==vg.user) setSwapAsk({ userId: dragging, vaga: vg }); }) : null}
                      onTap={isAdmin ? ((vg)=>{
                        if (dragging && !vg.user) { doAssign(dragging, vg.id); setDragging(null); }
                        else if (dragging && vg.user && dragging!==vg.user) setSwapAsk({ userId: dragging, vaga: vg });
                        else if (vg.user) setDragging(prev => prev===vg.user ? null : vg.user);
                      }) : null}
                      onHist={isAdmin && mode==='live' ? ((bid)=>setHistBerth(bid)) : null}
                      onDragUser={(uid)=>setDragging(uid)}
                      onRemove={isAdmin ? doRemove : null}
                      compatCheck={compatCheck}
                      intentByBerth={mode==='live' ? intentByBerth : null}
                      lazyView={lazyView}
                      lang={lang}
                      isMobile={isMobile}
                    />
                  ))}
                </div>
              </div>
            );
          })}
          {!cabinsByDeck.length && (
            <div className="empty" style={{padding:80,fontSize:18}}>
              {lang==='pt'?'nenhuma cabine atende ao filtro.':'no cabin matches the filter.'}
            </div>
          )}
        </div>
      </div>

      {dragging && usersById[dragging] && (
        <div style={{position:'fixed',bottom:18,left:'50%',transform:'translateX(-50%)',zIndex:8150,display:'flex',alignItems:'center',gap:10,background:'var(--ink)',color:'var(--cream)',borderRadius:999,padding:'9px 10px 9px 18px',boxShadow:'var(--shadow-lg)',fontFamily:'var(--fm)',fontSize:12}}>
          {lang==='pt'?'movendo':'moving'} <b>{usersById[dragging].nome}</b> — {lang==='pt'?'toque na vaga de destino':'tap the target berth'}
          <button onClick={()=>{setDragging(null);setDropTarget(null);}} style={{border:'none',borderRadius:999,padding:'5px 12px',cursor:'pointer',background:'var(--cream)',color:'var(--ink)',fontFamily:'var(--fm)',fontSize:11}}>{lang==='pt'?'cancelar':'cancel'}</button>
        </div>
      )}
      {swapAsk && (() => {
        const uIn = usersById[swapAsk.userId];
        const uOut = usersById[swapAsk.vaga.user];
        const antiga = mode==='plan'
          ? (Object.keys(planAssign).find(b => String(planAssign[b]) === String(swapAsk.userId)) || null)
          : berthDe(swapAsk.userId);
        return (
          <SwapModal
            nomeEntra={uIn ? uIn.nome : '?'} nomeSai={uOut ? uOut.nome : '?'}
            vaga={swapAsk.vaga.id} vagaAntiga={antiga} modoPlan={mode==='plan'} lang={lang}
            onTrocar={()=>{ mode==='plan' ? swapPlan(swapAsk.userId, swapAsk.vaga, true) : swapLive(swapAsk.userId, swapAsk.vaga, true); setSwapAsk(null); }}
            onSair={()=>{ mode==='plan' ? swapPlan(swapAsk.userId, swapAsk.vaga, false) : swapLive(swapAsk.userId, swapAsk.vaga, false); setSwapAsk(null); }}
            onClose={()=>{ setSwapAsk(null); setDragging(null); setDropTarget(null); }}
          />
        );
      })()}
      {histBerth && <BerthHistModal berthId={histBerth} token={token} lang={lang} onClose={()=>setHistBerth(null)}/>}
      {managing && (
        <CabinManager state={state} setState={setState} lang={lang} toast={toast}
          onClose={()=>setManaging(false)} usersById={usersById}
          cabins={cabins} fetchData={fetchData}/>
      )}

      {importOpen && (
        <ImportPeopleModal
          users={users}
          jaNoPlano={planPool}
          onImport={importarPessoas}
          onClose={()=>setImportOpen(false)}
          lang={lang} isMobile={isMobile}
        />
      )}
      {filtroOpen && (
        <CabinFiltroModal
          filterDeck={filterDeck} setFilterDeck={setFilterDeck}
          filterTurno={filterTurno} setFilterTurno={setFilterTurno}
          filterEmpresa={filterEmpresa} setFilterEmpresa={setFilterEmpresa}
          filterGenero={filterGenero} setFilterGenero={setFilterGenero}
          filterStatus={filterStatus} setFilterStatus={setFilterStatus}
          allDecks={allDecks} empresas={empresas} lang={lang}
          onLimpar={()=>{ setFilterDeck('all'); setFilterTurno('all'); setFilterEmpresa('all'); setFilterGenero('all'); setFilterStatus('all'); setFilterRole('all'); }}
          onClose={()=>setFiltroOpen(false)}
        />
      )}
      {modeChoiceOpen && (
        <PlanModeModal
          temLazy={(intents||[]).length > 0}
          lang={lang}
          onClose={()=>setModeChoiceOpen(false)}
          onAgora={()=>{ setModeChoiceOpen(false); setApplyOpen(true); }}
          onReativo={armarLazy}
        />
      )}
      {applyOpen && (
        <ApplyPlanModal
          planAssign={planAssign} usersById={usersById} users={users} planPool={planPool}
          token={token} toast={toast} lang={lang} isMobile={isMobile}
          onClose={()=>setApplyOpen(false)}
          onApplied={()=>{ setApplyOpen(false); setMode('live'); fetchData(); }}
        />
      )}

      {loadOpen && (
        <LoadPlanModal
          plans={plans} currentPlanId={currentPlanId} lang={lang} isMobile={isMobile}
          onLoad={(id)=>{ loadPlan(id); setLoadOpen(false); }}
          onDelete={deletePlanById}
          onClose={()=>setLoadOpen(false)}
        />
      )}

      {genderAlert && (
        <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 18px'}}
          onClick={()=>setGenderAlert(null)}>
          <div onClick={e=>e.stopPropagation()} style={{
            width:'100%',maxWidth:420,background:'var(--surface)',borderRadius:22,padding:'28px 26px',
            boxShadow:'0 24px 60px -16px rgba(31,42,46,.45)',textAlign:'center',animation:'slideIn .25s both'
          }}>
            <div style={{fontSize:40,lineHeight:1,marginBottom:12}}>⚠️</div>
            <h3 style={{fontFamily:'var(--fs)',fontSize:22,letterSpacing:'-0.4px',marginBottom:10}}>
              {lang==='pt'?'Gêneros diferentes no camarote':'Different genders in room'}
            </h3>
            <p style={{fontFamily:'var(--fm)',fontSize:13.5,color:'var(--muted)',lineHeight:1.55,marginBottom:6}}>
              {lang==='pt'
                ? <>Você está alocando <b style={{color:'var(--ink)'}}>{genderAlert.nome}</b> na vaga <b style={{color:'var(--ink)'}}>{genderAlert.vaga}</b>, que já tem ocupante de gênero diferente no mesmo quarto.</>
                : <>You are assigning <b style={{color:'var(--ink)'}}>{genderAlert.nome}</b> to berth <b style={{color:'var(--ink)'}}>{genderAlert.vaga}</b>, which already has an occupant of a different gender in the same room.</>}
            </p>
            <p style={{fontFamily:'var(--fm)',fontSize:12,color:'var(--muted-2)',marginBottom:22}}>
              {lang==='pt'?'A alocação foi mantida. Confirme que você está ciente.':'The assignment was kept. Please confirm you are aware.'}
            </p>
            <button onClick={()=>setGenderAlert(null)} style={{
              width:'100%',padding:'13px',borderRadius:13,border:'none',cursor:'pointer',
              background:'var(--accent)',color:'#fff',fontFamily:'var(--fm)',fontSize:14,fontWeight:600
            }}>
              OK, estou ciente
            </button>
          </div>
        </div>
      )}
      </>
      )}
    </main>
  );
};

Object.assign(window, {Cabines});
