// ═══ GENESIS BUILD dbexplorer-f1 · 2026-07-12 ═══
// Tidal — Shell (sidebar + topbar) + global state, role-aware

const NAV_ALL = [
  {section:'sec_abordo', items:[
    {id:'home', icon:'home', label:'nav_home', roles:['*']},
    {id:'agenda', icon:'calendar', label:'nav_agenda', roles:['*'], count:e=>{const today=(new Date()).toISOString().split('T')[0]; return (e.events||[]).filter(x=>(x.data||'')>=today).length;}},
    {id:'avisos', icon:'bell', label:'nav_avisos', roles:['*'], count:e=>(e.announcements||[]).filter(a=>!a.expired).length},
    {id:'solicitacoes', icon:'tools', label:'nav_solicitacoes', roles:['*']},
  ]},
  {section:'sec_conviv', items:[
    {id:'cinema', icon:'film', label:'nav_cinema', roles:['*']},
    {id:'musica', icon:'music', label:'nav_musica', roles:['*']},
    {id:'saude', icon:'health', label:'nav_saude', roles:['*']},
    {id:'conquistas', icon:'star', label:'nav_conquistas', roles:['*']},
    {id:'habilidades', icon:'fork', label:'nav_habilidades', roles:['*']},
  ]},
  {section:'sec_oper', items:[
    {id:'bridge', icon:'anchor', label:'nav_bridge', roles:['admin','staff']},
    {id:'logbook', icon:'list', label:'nav_logbook', roles:['admin','staff']},
    {id:'gangway', icon:'anchor', label:'nav_gangway', roles:['admin','staff'], count:e=>e.abordo_count||0},
    {id:'cabines', icon:'grid', label:'nav_cabines', roles:['admin','staff']},
    {id:'manifesto', icon:'list', label:'nav_manifesto', roles:['admin','staff'], count:e=>e.abordo_count||0},
    {id:'pessoas', icon:'users', label:'nav_pessoas', roles:['admin','staff']},
    {id:'solicitacoes_op', icon:'flag', label:'nav_solicitacoes_op', roles:['admin','staff']},
    {id:'coach', icon:'health', label:'nav_coach', roles:['admin','staff']},
    {id:'inventario', icon:'list', label:'Inventário', roles:['admin','staff']},
    {id:'dbexplorer', icon:'grid', label:'DB Explorer', roles:['admin']},
    {id:'config',   icon:'flag',   label:'nav_config',   roles:['*']},
  ]},
];

const visibleNav = (role, isStaff, permissoes) => {
  const canManage = role === 'admin' || isStaff === true;
  const chaves = permissoes || [];
  const temChave = (id) => chaves.some(k => k.startsWith(id + '.'));
  return NAV_ALL.map(sec => ({
    ...sec,
    items: sec.items.filter(it => {
      if (it.roles.includes('*')) return true;
      if (it.roles.includes('admin') && it.roles.length === 1) return role === 'admin';
      if (it.roles.includes('admin') || it.roles.includes('staff')) return canManage || temChave(it.id);
      return it.roles.includes(role) || temChave(it.id);
    })
  })).filter(sec => sec.items.length);
};

const Sidebar = ({page, setPage, state, lang, role, isStaff, currentUser, onLogout}) => (
  <aside style={{
    width:240, background:'var(--cream)', padding:'22px 16px',
    borderRight:'1px solid var(--line-soft)',
    display:'flex', flexDirection:'column', position:'sticky', top:0, height:'100vh',
    overflowY:'auto'
  }}>
    <div style={{padding:'4px 8px 18px',display:'flex',alignItems:'center',gap:10}}>
      <div style={{width:30,height:30,borderRadius:'50%',background:'var(--accent)',display:'grid',placeItems:'center',color:'var(--cream)',fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18}}>g</div>
      <div>
        <div style={{fontFamily:'var(--fs)',fontStyle:'italic',fontSize:18,color:'var(--ink)',lineHeight:1}}>Genesis I</div>
        <div style={{fontFamily:'var(--fm)',fontSize:9,letterSpacing:'0.18em',color:'var(--muted)',marginTop:2}}>MPSV</div>
      </div>
    </div>

    <nav style={{flex:1,display:'flex',flexDirection:'column',gap:18}}>
      {visibleNav(role, isStaff, state?.currentUser?.permissoes).map(sec => (
        <div key={sec.section}>
          <div style={{fontFamily:'var(--fs)',fontStyle:'italic',fontSize:14,color:'var(--muted)',padding:'14px 8px 6px'}}>{t(sec.section, lang)}</div>
          {sec.items.map(item => {
            const active = page === item.id;
            const count = item.count ? item.count(state) : null;
            return (
              <div key={item.id}
                onClick={() => setPage(item.id)}
                style={{
                  display:'flex',alignItems:'center',gap:11,padding:'8px 12px',
                  borderRadius:10,cursor:'pointer',
                  color: active ? 'var(--accent)' : 'var(--ink-2)',
                  background: active ? 'var(--accent-soft)' : 'transparent',
                  fontSize:13.5, fontWeight: active ? 500 : 400,
                  marginBottom:1, transition:'all .15s'
                }}
                onMouseEnter={e=>{if(!active) e.currentTarget.style.background='var(--cream-3)'}}
                onMouseLeave={e=>{if(!active) e.currentTarget.style.background='transparent'}}
              >
                <Icon n={item.icon} size={15}/>
                <span style={{flex:1}}>{t(item.label, lang)}</span>
                {count != null && count > 0 && (
                  <span style={{
                    fontFamily:'var(--fm)',fontSize:10.5,
                    padding:'2px 7px',borderRadius:8,
                    background: active ? 'var(--accent)' : 'var(--cream-3)',
                    color: active ? 'var(--cream)' : 'var(--muted)'
                  }}>{count > 99 ? '99+' : count}</span>
                )}
              </div>
            );
          })}
        </div>
      ))}
    </nav>

    <div style={{padding:'14px 8px',borderTop:'1px solid var(--line-soft)',display:'flex',alignItems:'center',gap:10}}>
      {currentUser?.foto_url ? (
        <img src={currentUser.foto_url} style={{width:30,height:30,borderRadius:'50%',objectFit:'cover',flexShrink:0}} onError={e=>e.target.style.display='none'}/>
      ) : (
        <div style={{width:30,height:30,borderRadius:'50%',background:'var(--accent)',color:'var(--cream)',display:'grid',placeItems:'center',fontFamily:'var(--fs)',fontSize:13,flexShrink:0}}>
          {(currentUser?.initials || currentUser?.nome?.charAt(0) || '?')}
        </div>
      )}
      <div style={{flex:1,minWidth:0}}>
        <div style={{fontSize:12.5,color:'var(--ink)',whiteSpace:'nowrap',overflow:'hidden',textOverflow:'ellipsis'}}>
          {currentUser?.nome || '—'}
        </div>
        <div style={{fontSize:10,color:'var(--muted)',fontFamily:'var(--fm)',textTransform:'capitalize'}}>{role}</div>
      </div>
      <button onClick={onLogout} title="Sair"
        style={{background:'none',border:'none',cursor:'pointer',color:'var(--muted)',padding:'4px',borderRadius:8,display:'grid',placeItems:'center',flexShrink:0,transition:'color .15s'}}
        onMouseEnter={e=>e.currentTarget.style.color='var(--accent)'}
        onMouseLeave={e=>e.currentTarget.style.color='var(--muted)'}>
        <Icon n="log-out" size={15}/>
      </button>
    </div>
  </aside>
);

// ─── Mobile bottom-nav ───
const BottomNav = ({page, setPage, state, lang, role, openMenu}) => {
  // primary 4 items + menu
  const primary = [
    {id:'home', icon:'home', label:'nav_home'},
    {id:'agenda', icon:'calendar', label:'nav_agenda'},
    {id:'avisos', icon:'bell', label:'nav_avisos'},
    {id:'solicitacoes', icon:'tools', label:'nav_solicitacoes'},
  ];
  return (
    <nav style={{
      position:'fixed', bottom:0, left:0, right:0, zIndex:50,
      background:'var(--surface)', borderTop:'1px solid var(--line-soft)',
      padding:'8px max(env(safe-area-inset-left),12px) max(env(safe-area-inset-bottom),8px) max(env(safe-area-inset-right),12px)',
      display:'flex', justifyContent:'space-around', boxShadow:'0 -8px 24px -16px rgba(0,0,0,0.12)'
    }}>
      {primary.map(it => {
        const active = page === it.id;
        return (
          <button key={it.id} onClick={()=>setPage(it.id)} style={{
            flex:1, display:'flex', flexDirection:'column', alignItems:'center', gap:3,
            padding:'6px 4px', background:'transparent', border:'none', cursor:'pointer',
            color: active ? 'var(--accent)':'var(--muted)', borderRadius:12
          }}>
            <Icon n={it.icon} size={20}/>
            <span style={{fontSize:9.5, fontFamily:'var(--f)'}}>{t(it.label, lang)}</span>
          </button>
        );
      })}
      <button onClick={openMenu} style={{
        flex:1, display:'flex', flexDirection:'column', alignItems:'center', gap:3,
        padding:'6px 4px', background:'transparent', border:'none', cursor:'pointer',
        color:'var(--muted)', borderRadius:12
      }}>
        <Icon n="list" size={20}/>
        <span style={{fontSize:9.5, fontFamily:'var(--f)'}}>{lang==='pt'?'mais':'more'}</span>
      </button>
    </nav>
  );
};

const MobileMenu = ({open, onClose, page, setPage, state, lang, role, isStaff, currentUser, onLogout}) => {
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position:'fixed', inset:0, zIndex:150, background:'rgba(0,0,0,0.45)',
      display:'flex', alignItems:'flex-end',
      animation:'fadeIn .2s'
    }}>
      <div onClick={e=>e.stopPropagation()} style={{
        background:'var(--surface)', width:'100%',
        maxHeight:'82vh', borderRadius:'24px 24px 0 0',
        padding:'10px 0 max(env(safe-area-inset-bottom),20px)',
        animation:'slideUp .3s', overflowY:'auto'
      }}>
        <div style={{width:42,height:4,background:'var(--line)',borderRadius:2,margin:'8px auto 18px'}}/>
        {visibleNav(role, isStaff, state?.currentUser?.permissoes).map(sec => (
          <div key={sec.section} style={{padding:'8px 8px'}}>
            <div style={{padding:'10px 18px 6px',fontFamily:'var(--fs)',fontStyle:'italic',color:'var(--muted)'}}>{t(sec.section, lang)}</div>
            {sec.items.map(it => (
              <div key={it.id} onClick={()=>{setPage(it.id);onClose();}} style={{
                display:'flex',alignItems:'center',gap:14,padding:'14px 22px',
                color: page===it.id ? 'var(--accent)':'var(--ink)',
                background: page===it.id ? 'var(--accent-soft)':'transparent',
                fontSize:15
              }}>
                <Icon n={it.icon} size={18}/>
                <span>{t(it.label, lang)}</span>
              </div>
            ))}
          </div>
        ))}
        {/* Botão de logout */}
        <div style={{padding:'8px 8px',borderTop:'1px solid var(--line-soft)',marginTop:8}}>
          <div onClick={()=>{onClose();onLogout();}} style={{
            display:'flex',alignItems:'center',gap:14,padding:'14px 22px',
            color:'var(--muted)',fontSize:15,cursor:'pointer',borderRadius:12
          }}>
            <Icon n="log-out" size={18}/>
            <span>Sair</span>
          </div>
        </div>
      </div>
    </div>
  );
};

const PageTitles = {
  home:        {t1:'page_home_t1',  t2:'page_home_t2',  sub:'page_home_sub'},
  agenda:      {t1:'page_agenda_t1',t2:'page_agenda_t2',sub:'page_agenda_sub'},
  avisos:      {t1:'page_avisos_t1',t2:'page_avisos_t2',sub:'page_avisos_sub'},
  solicitacoes:{t1:'page_solic_t1', t2:'page_solic_t2', sub:'page_solic_sub'},
  cinema:      {t1:'page_cinema_t1',t2:'page_cinema_t2',sub:'page_cinema_sub'},
  musica:      {t1:'page_musica_t1',t2:'page_musica_t2',sub:'page_musica_sub'},
  saude:       {t1:'page_saude_t1', t2:'page_saude_t2', sub:'page_saude_sub'},
  conquistas:  {t1:'page_conq_t1',  t2:'page_conq_t2',  sub:'page_conq_sub'},
  habilidades: {t1:'page_habil_t1', t2:'page_habil_t2', sub:'page_habil_sub'},
  manifesto:   {t1:'page_manif_t1', t2:'page_manif_t2', sub:'page_manif_sub'},
  gangway:     {t1:'page_gang_t1',  t2:'page_gang_t2',  sub:'page_gang_sub'},
  cabines:     {t1:'page_cab_t1',   t2:'page_cab_t2',   sub:'page_cab_sub'},
  pessoas:     {t1:'page_pessoas_t1',t2:'page_pessoas_t2',sub:'page_pessoas_sub'},
  importar:    {t1:'page_imp_t1',   t2:'page_imp_t2',   sub:'page_imp_sub'},
  config:      {t1:'page_cfg_t1',   t2:'page_cfg_t2',   sub:'page_cfg_sub'},
  solicitacoes_op:{t1:'page_solop_t1',t2:'page_solop_t2',sub:'page_solop_sub'},
};

const Topbar = ({page, lang, setLang, theme, setTheme, role, setRole, onCmdK, isMobile, openMenu, currentUser}) => {
  const cfg = PageTitles[page] || PageTitles.home;

  // Saudação baseada no horário
  const hour = new Date().getHours();
  const greeting = hour < 12 ? (lang==='pt'?'Bom dia,':'Good morning,')
                 : hour < 18 ? (lang==='pt'?'Boa tarde,':'Good afternoon,')
                 :              (lang==='pt'?'Boa noite,':'Good evening,');

  const primeiroNome = (currentUser?.nome || '').split(' ')[0] || '—';

  // Resolve título — substitui t1 pela saudação na home
  const t1 = page === 'home' ? greeting : t(cfg.t1, lang);
  const t2 = page === 'home' ? `${primeiroNome}.` : t(cfg.t2, lang);

  return (
    <div style={{
      padding: isMobile ? '14px 18px 4px' : '32px var(--pad) 6px',
      borderBottom: '1px solid var(--line-soft)'
    }}>
      <div style={{display:'flex',alignItems:'flex-start',justifyContent:'space-between',gap:16,flexWrap:'wrap'}}>
        <div style={{minWidth:0,flex:1}}>
          <div className="t-display" style={{fontSize: isMobile? 32 : undefined}}>
            {t1} <em>{t2}</em>
          </div>
          <div style={{
            fontFamily:'var(--fs)',fontStyle:'italic',color:'var(--muted)',
            marginTop:6,fontSize: isMobile ? 14 : 16
          }}>{t(cfg.sub, lang)}</div>
        </div>

        <div style={{display:'flex',alignItems:'center',gap:8,flexWrap:'wrap'}}>
          {!isMobile && (
            <button onClick={onCmdK} className="btn ghost" style={{padding:'7px 14px'}}>
              <Icon n="search" size={14}/>
              <span style={{fontSize:12.5}}>{t('search', lang)}</span>
              <span style={{fontFamily:'var(--fm)',fontSize:10,color:'var(--muted-2)',padding:'2px 6px',borderRadius:6,background:'var(--cream-3)',marginLeft:6}}>⌘K</span>
            </button>
          )}
          <button className="btn icon ghost" onClick={()=>setTheme(theme==='dark'?'light':'dark')} title={theme==='dark'?'Light':'Dark'}>
            <Icon n={theme==='dark'?'sun':'moon'} size={15}/>
          </button>
          <button className="btn ghost sm" onClick={()=>setLang(lang==='pt'?'en':'pt')} title="Language">
            <span style={{fontFamily:'var(--fm)',fontSize:11,letterSpacing:'0.08em'}}>{lang.toUpperCase()}</span>
          </button>
        </div>
      </div>
    </div>
  );
};

// Escalas para descrever o tempo no banner (bilíngue pt/en)
const _wxBeaufort = (kt) => {
  if (kt == null || isNaN(kt)) return null;
  const T = [[1,0,'Calmaria','Calm'],[3,1,'Aragem','Light air'],[6,2,'Brisa leve','Light breeze'],[10,3,'Brisa fraca','Gentle breeze'],[16,4,'Brisa moderada','Moderate breeze'],[21,5,'Brisa forte','Fresh breeze'],[27,6,'Vento fresco','Strong breeze'],[33,7,'Vento forte','Near gale'],[40,8,'Ventania','Gale'],[47,9,'Ventania forte','Strong gale'],[55,10,'Tempestade','Storm'],[63,11,'Tempestade violenta','Violent storm']];
  for (const [max,forca,pt,en] of T) if (kt <= max) return {forca,pt,en};
  return {forca:12,pt:'Furacão',en:'Hurricane'};
};
const _wxDouglas = (m) => {
  if (m == null || isNaN(m)) return null;
  const T = [[0,0,'Calmo','Calm'],[0.10,1,'Calmo','Rippled'],[0.50,2,'Chão','Smooth'],[1.25,3,'Pequeno','Slight'],[2.50,4,'Moderado','Moderate'],[4,5,'Grosso','Rough'],[6,6,'Muito grosso','Very rough'],[9,7,'Alto','High'],[14,8,'Muito alto','Very high']];
  for (const [max,grau,pt,en] of T) if (m <= max) return {grau,pt,en};
  return {grau:9,pt:'Excepcional',en:'Phenomenal'};
};

const BridgeBanner = ({state, lang}) => {
  const [st, setSt] = useState(null);
  const pt = lang === 'pt';
  const me = state.currentUser || {};
  const canManage = me.role === 'admin' || me.is_staff === true;
  const token = () => localStorage.getItem('genesis_token');
  const load = () => fetch('/api/bridge/status', {headers:{'Authorization':`Bearer ${token()}`}}).then(r=>r.ok?r.json():null).then(setSt).catch(()=>{});
  useEffect(() => { load(); const iv = setInterval(load, 120000); return () => clearInterval(iv); }, []);
  const reg = st && st.reg;
  const local = (st && st.local) || (pt ? 'Bacia de Santos' : 'Santos Basin');
  const editarLocal = async () => {
    const v = window.prompt(pt ? 'Local / posição:' : 'Location / position:', local);
    if (v === null) return;
    await fetch('/api/bridge/local', {method:'PUT', headers:{'Authorization':`Bearer ${token()}`,'Content-Type':'application/json'}, body:JSON.stringify({local:v})}).catch(()=>{});
    load();
  };
  const heading = (reg && reg.heading != null) ? `${String(Math.round(reg.heading)).padStart(3,'0')}°` : '047°';
  const speed = (reg && reg.speed != null) ? reg.speed : '11.2';
  const nm = (o) => (o && (pt ? o.pt : o.en)) || '';
  const bf = reg ? _wxBeaufort(reg.vento_int) : null;
  const dg = reg ? _wxDouglas(reg.swell_int) : null;
  let weather;
  if (reg && (reg.vento_int != null || reg.swell_int != null)) {
    const p = [];
    if (reg.vento_int != null && bf) p.push(`${pt?'vento':'wind'} ${reg.vento_int}kt · ${nm(bf).toLowerCase()}`);
    if (reg.swell_int != null && dg) p.push(`${pt?'mar':'sea'} ${reg.swell_int}m · ${nm(dg).toLowerCase()}`);
    weather = p.join('  ·  ');
  } else weather = pt ? 'mar calmo · vento leve' : 'calm sea · light wind';
  return (
    <div style={{
      background:'var(--nav-bg)', color:'var(--nav-fg)',
      padding:'10px 18px', display:'flex', alignItems:'center', gap:18,
      fontSize:12, fontFamily:'var(--fm)', letterSpacing:'0.04em',
      flexWrap:'wrap'
    }}>
      <div style={{display:'flex',alignItems:'center',gap:8}}>
        <span style={{width:7,height:7,borderRadius:'50%',background:'#7BB67E',animation:'pulse 2s infinite'}}/>
        <span style={{opacity:0.7}}>{t('bridge', lang).toUpperCase()}</span>
      </div>
      <span style={{opacity:0.55}}>·</span>
      <span>{heading} • {speed}kn</span>
      <span style={{opacity:0.55}}>·</span>
      <span onClick={canManage ? editarLocal : undefined} title={canManage ? (pt?'clique pra editar o local':'click to edit location') : undefined} style={{cursor:canManage?'pointer':'default', textDecoration:canManage?'underline':'none', textDecorationStyle:'dotted', textUnderlineOffset:3}}>{local}</span>
      <span style={{opacity:0.55}}>·</span>
      <span>{weather}</span>
      {reg && reg.hora != null && <span style={{opacity:0.4,marginLeft:'auto'}}>{String(reg.hora).padStart(2,'0')}:00</span>}
    </div>
  );
};

// ─── Cmd-K palette ───
const CmdK = ({open, onClose, setPage, role, isStaff, lang, permissoes}) => {
  const [q, setQ] = useState('');
  const [people, setPeople] = useState([]);
  const ref = useRef(null);
  const canManage = role === 'admin' || isStaff === true;

  const items = useMemo(() =>
    visibleNav(role, isStaff, permissoes).flatMap(s => s.items.map(i => ({id:i.id, label:t(i.label,lang), icon:i.icon, sec:t(s.section,lang)})))
  , [role, isStaff, lang]);

  // Carrega pessoas para busca (só quem pode gerir)
  useEffect(() => {
    if (open && canManage && people.length === 0) {
      const token = localStorage.getItem('genesis_token');
      fetch('/api/users', {headers:{'Authorization':`Bearer ${token}`}})
        .then(r => r.ok ? r.json() : [])
        .then(data => setPeople(data.filter(u => u.role !== 'admin')))
        .catch(() => {});
    }
  }, [open, canManage]);

  const filteredNav = q ? items.filter(i => i.label.toLowerCase().includes(q.toLowerCase())) : items;
  const filteredPeople = q && canManage
    ? people.filter(p =>
        (p.nome||'').toLowerCase().includes(q.toLowerCase()) ||
        (p.genesis_id||'').toLowerCase().includes(q.toLowerCase()) ||
        (p.empresa||'').toLowerCase().includes(q.toLowerCase())
      ).slice(0, 6)
    : [];

  useEffect(() => { if (open && ref.current) setTimeout(()=>ref.current.focus(),50) }, [open]);
  if (!open) return null;
  return (
    <div onClick={onClose} style={{
      position:'fixed', inset:0, zIndex:1000,
      background:'rgba(31,42,46,0.4)', backdropFilter:'blur(4px)',
      display:'flex', alignItems:'flex-start', justifyContent:'center',
      paddingTop:'12vh', animation:'fadeIn .15s'
    }}>
      <div onClick={e=>e.stopPropagation()} style={{
        background:'var(--surface)', borderRadius:18, width:'min(560px, 92vw)',
        boxShadow:'0 32px 80px -24px rgba(0,0,0,0.4)', overflow:'hidden',
        animation:'slideUp .25s'
      }}>
        <div style={{display:'flex',alignItems:'center',gap:12,padding:'18px 22px',borderBottom:'1px solid var(--line-soft)'}}>
          <Icon n="search" size={16}/>
          <input ref={ref} value={q} onChange={e=>setQ(e.target.value)}
            placeholder={lang==='pt'?'Buscar páginas ou pessoas…':'Search pages or people…'}
            style={{flex:1,border:'none',outline:'none',background:'transparent',fontSize:15,color:'var(--ink)',fontFamily:'var(--f)'}}/>
        </div>
        <div style={{padding:6,maxHeight:'50vh',overflowY:'auto'}}>
          {filteredNav.map(i => (
            <div key={i.id} onClick={()=>{setPage(i.id);onClose()}}
              style={{display:'flex',alignItems:'center',gap:12,padding:'10px 14px',borderRadius:12,cursor:'pointer'}}
              onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'}
              onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
              <Icon n={i.icon} size={15}/>
              <span style={{flex:1,fontSize:13.5}}>{i.label}</span>
              <span className="t-meta">{i.sec}</span>
            </div>
          ))}
          {filteredPeople.length > 0 && (
            <>
              <div style={{padding:'10px 14px 4px',fontFamily:'var(--fm)',fontSize:10,letterSpacing:1,textTransform:'uppercase',color:'var(--muted)'}}>
                {lang==='pt'?'Pessoas':'People'}
              </div>
              {filteredPeople.map(p => (
                <div key={p.id} onClick={()=>{setPage('pessoas');onClose()}}
                  style={{display:'flex',alignItems:'center',gap:12,padding:'10px 14px',borderRadius:12,cursor:'pointer'}}
                  onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'}
                  onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
                  <div style={{width:24,height:24,borderRadius:'50%',background:'var(--teal)',color:'white',display:'grid',placeItems:'center',fontSize:10,fontFamily:'var(--fm)',flexShrink:0}}>
                    {(p.initials || (p.nome||'?').slice(0,2)).toUpperCase()}
                  </div>
                  <span style={{flex:1,fontSize:13.5}}>{p.nome||p.genesis_id}</span>
                  <span className="t-meta">{p.empresa||p.genesis_id}</span>
                </div>
              ))}
            </>
          )}
          {filteredNav.length === 0 && filteredPeople.length === 0 && (
            <div style={{padding:'20px',textAlign:'center',fontSize:13,color:'var(--muted)',fontStyle:'italic',fontFamily:'var(--fs)'}}>
              nada encontrado para "{q}"
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// ─── TopNav — barra horizontal de navegação (estilo Cabin Management) ───
// ─── Central de notificações: o sino do portal ──────────────────────────────
const NotifSino = ({ setPage }) => {
  const [dados, setDados] = useState({ itens: [], nao_lidas: 0 });
  const [aberto, setAberto] = useState(false);
  const token = () => localStorage.getItem('genesis_token');

  const carregar = () => {
    fetch('/api/announcements/notifs', { headers: { 'Authorization': `Bearer ${token()}` } })
      .then(r => r.ok ? r.json() : null).then(d => { if (d) setDados(d); }).catch(() => {});
  };
  useEffect(() => { carregar(); const t = setInterval(carregar, 60000); return () => clearInterval(t); }, []);

  const tempoRel = (iso) => {
    const min = Math.floor((Date.now() - new Date(iso).getTime()) / 60000);
    if (min < 1) return 'agora';
    if (min < 60) return `${min}min`;
    const h = Math.floor(min / 60);
    if (h < 24) return `${h}h`;
    return `${Math.floor(h / 24)}d`;
  };
  const abrir = (n) => {
    fetch(`/api/announcements/notifs/${n.id}/lida`, { method: 'POST', headers: { 'Authorization': `Bearer ${token()}` } }).catch(() => {});
    setDados(d => ({ itens: d.itens.map(x => x.id === n.id ? { ...x, lida: true } : x), nao_lidas: Math.max(0, d.nao_lidas - 1) }));
    if (n.pagina && setPage) { setAberto(false); setPage(n.pagina); }
  };
  const todasLidas = () => {
    fetch('/api/announcements/notifs/lidas', { method: 'POST', headers: { 'Authorization': `Bearer ${token()}` } }).catch(() => {});
    setDados(d => ({ itens: d.itens.map(x => ({ ...x, lida: true })), nao_lidas: 0 }));
  };

  return (
    <div style={{position:'relative'}}>
      <button onClick={()=>setAberto(v=>!v)} title="notificações"
        style={{padding:8,borderRadius:9,border:'none',background:'rgba(255,255,255,0.06)',color:'rgba(255,255,255,0.8)',cursor:'pointer',position:'relative'}}>
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8a6 6 0 0 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.7 21a2 2 0 0 1-3.4 0"/></svg>
        {dados.nao_lidas > 0 && (
          <span style={{position:'absolute',top:2,right:2,minWidth:15,height:15,borderRadius:999,background:'var(--accent)',color:'#fff',fontFamily:'var(--fm)',fontSize:9,fontWeight:700,display:'grid',placeItems:'center',padding:'0 3px',boxSizing:'border-box'}}>
            {dados.nao_lidas > 9 ? '9+' : dados.nao_lidas}
          </span>
        )}
      </button>
      {aberto && (
        <>
          <div onClick={()=>setAberto(false)} style={{position:'fixed',inset:0,zIndex:8090}}/>
          <div style={{position:'absolute',top:'calc(100% + 8px)',right:0,zIndex:8100,width:330,maxWidth:'calc(100vw - 24px)',background:'var(--surface)',border:'1px solid var(--line)',borderRadius:14,boxShadow:'var(--shadow-lg)',overflow:'hidden'}}>
            <div style={{display:'flex',alignItems:'center',justifyContent:'space-between',padding:'11px 14px',borderBottom:'1px solid var(--line-soft)'}}>
              <span style={{fontFamily:'var(--fs)',fontSize:16,color:'var(--ink)'}}>Notificações</span>
              {dados.nao_lidas > 0 && <button onClick={todasLidas} style={{border:'none',background:'transparent',color:'var(--accent)',cursor:'pointer',fontFamily:'var(--fm)',fontSize:10.5}}>marcar todas como lidas</button>}
            </div>
            <div style={{maxHeight:380,overflowY:'auto'}}>
              {dados.itens.length === 0 ? (
                <div style={{padding:'28px 14px',textAlign:'center',color:'var(--muted)',fontFamily:'var(--fm)',fontSize:12}}>tudo em dia por aqui 🌊</div>
              ) : dados.itens.map(n => (
                <div key={n.id} onClick={()=>abrir(n)}
                  style={{padding:'11px 14px',borderBottom:'1px solid var(--line-soft)',cursor:'pointer',background: n.lida ? 'transparent' : 'var(--cream-2)',transition:'background .12s'}}
                  onMouseEnter={e=>e.currentTarget.style.background='var(--cream-3)'}
                  onMouseLeave={e=>e.currentTarget.style.background = n.lida ? 'transparent' : 'var(--cream-2)'}>
                  <div style={{display:'flex',alignItems:'baseline',gap:8}}>
                    {!n.lida && <span style={{width:7,height:7,borderRadius:'50%',background:'var(--accent)',flexShrink:0,alignSelf:'center'}}/>}
                    <span style={{fontFamily:'var(--f)',fontSize:13,fontWeight:n.lida?400:600,color:'var(--ink)',flex:1,minWidth:0,lineHeight:1.3}}>{n.titulo}</span>
                    <span style={{fontFamily:'var(--fm)',fontSize:10,color:'var(--muted-2)',whiteSpace:'nowrap'}}>{tempoRel(n.criado_em)}</span>
                  </div>
                  {n.corpo && <div style={{fontFamily:'var(--fm)',fontSize:11,color:'var(--muted)',marginTop:3,paddingLeft: n.lida ? 0 : 15,lineHeight:1.35}}>{n.corpo}</div>}
                </div>
              ))}
            </div>
          </div>
        </>
      )}
    </div>
  );
};

const TopNav = ({page, setPage, state, lang, setLang, theme, setTheme, role, isStaff, currentUser, onCmdK, onLogout, isMobile, openMenu}) => {
  const [userMenu, setUserMenu] = useState(false);
  const canManage = role === 'admin' || isStaff === true;

  // Itens principais da barra (achatados — sem seções)
  // 'modulos' é o hub de cards; 'home' é o dashboard
  const navItems = [
    {id:'home',    label: lang==='pt'?'Início':'Home',     icon:'home'},
    {id:'modulos', label: lang==='pt'?'Módulos':'Modules',  icon:'grid'},
    {id:'agenda',  label: lang==='pt'?'Agenda':'Schedule',  icon:'calendar'},
    {id:'avisos',  label: lang==='pt'?'Avisos':'Notices',   icon:'bell'},
    {id:'solicitacoes', label: lang==='pt'?'Solicitações':'Requests', icon:'tools'},
  ];

  const primeiroNome = (currentUser?.nome || '').split(' ')[0] || '—';
  const initials = (currentUser?.initials || (currentUser?.nome||'?').slice(0,2)).toUpperCase();

  if (isMobile) {
    // No mobile, a barra vira um cabeçalho compacto com botão de menu
    return (
      <div style={{
        position:'sticky',top:0,zIndex:100,
        background:'var(--nav-bg)',color:'var(--nav-fg)',
        padding:'12px 16px',display:'flex',alignItems:'center',justifyContent:'space-between',
        boxShadow:'var(--shadow-md)'
      }}>
        <div style={{display:'flex',alignItems:'center',gap:10}}>
          <div style={{width:32,height:32,borderRadius:9,background:'var(--accent)',display:'grid',placeItems:'center'}}>
            <span style={{fontFamily:'var(--fs)',fontWeight:600,fontSize:17,color:'#fff',lineHeight:1}}>G</span>
          </div>
          <div>
            <div style={{fontFamily:'var(--fs)',fontSize:16,fontWeight:600,lineHeight:1}}>Genesis I</div>
            <div style={{fontFamily:'var(--fm)',fontSize:9,opacity:0.6,letterSpacing:1}}>PORTAL</div>
          </div>
        </div>
        <button onClick={openMenu} className="btn icon" style={{background:'rgba(255,255,255,0.1)',border:'none',color:'var(--cream)'}}>
          <Icon n="menu" size={18}/>
        </button>
      </div>
    );
  }

  return (
    <div style={{
      position:'sticky',top:0,zIndex:100,
      background:'var(--nav-bg)',color:'var(--nav-fg)',
      borderBottom:'2px solid var(--accent)',
      boxShadow:'var(--shadow-md)'
    }}>
      <div style={{display:'flex',alignItems:'center',gap:8,padding:'0 24px',height:62}}>
        {/* Logo */}
        <div style={{display:'flex',alignItems:'center',gap:11,marginRight:18,cursor:'pointer'}} onClick={()=>setPage('home')}>
          <div style={{width:36,height:36,borderRadius:10,background:'var(--accent)',display:'grid',placeItems:'center'}}>
            <span style={{fontFamily:'var(--fs)',fontWeight:600,fontSize:19,color:'#fff',lineHeight:1}}>G</span>
          </div>
          <div>
            <div style={{fontFamily:'var(--fs)',fontSize:17,fontWeight:600,lineHeight:1,letterSpacing:'-0.3px'}}>MPSV Genesis I</div>
            <div style={{fontFamily:'var(--fm)',fontSize:9,opacity:0.55,letterSpacing:2,marginTop:2}}>PORTAL · OSM THOME</div>
          </div>
        </div>

        {/* Nav items */}
        <nav style={{display:'flex',alignItems:'center',gap:2,flex:1}}>
          {navItems.map(it => {
            const active = page === it.id || (it.id==='modulos' && MODULE_IDS.includes(page));
            return (
              <button key={it.id} onClick={()=>setPage(it.id)}
                style={{
                  display:'flex',alignItems:'center',gap:7,padding:'8px 14px',borderRadius:10,
                  border:'none',cursor:'pointer',fontFamily:'var(--f)',fontSize:13,fontWeight:500,
                  background: active ? 'rgba(217,105,75,0.18)' : 'transparent',
                  color: active ? '#fff' : 'rgba(255,255,255,0.7)',
                  borderBottom: active ? '2px solid var(--accent)' : '2px solid transparent',
                  transition:'all .15s'
                }}
                onMouseEnter={e=>{if(!active)e.currentTarget.style.background='rgba(255,255,255,0.07)'}}
                onMouseLeave={e=>{if(!active)e.currentTarget.style.background='transparent'}}>
                <Icon n={it.icon} size={15}/>{it.label}
              </button>
            );
          })}
        </nav>

        {/* Right controls */}
        <div style={{display:'flex',alignItems:'center',gap:6}}>
          <button onClick={onCmdK} style={{display:'flex',alignItems:'center',gap:8,padding:'7px 13px',borderRadius:9,border:'1px solid rgba(255,255,255,0.15)',background:'rgba(255,255,255,0.06)',color:'rgba(255,255,255,0.8)',cursor:'pointer',fontSize:12.5,fontFamily:'var(--f)'}}>
            <Icon n="search" size={13}/>
            <span style={{fontFamily:'var(--fm)',fontSize:10,opacity:0.6,padding:'1px 5px',borderRadius:5,background:'rgba(255,255,255,0.1)'}}>⌘K</span>
          </button>
          <NotifSino setPage={setPage}/>
          <button onClick={()=>setTheme(theme==='dark'?'light':'dark')} style={{padding:8,borderRadius:9,border:'none',background:'rgba(255,255,255,0.06)',color:'rgba(255,255,255,0.8)',cursor:'pointer'}}>
            <Icon n={theme==='dark'?'sun':'moon'} size={15}/>
          </button>
          <button onClick={()=>setLang(lang==='pt'?'en':'pt')} style={{padding:'8px 10px',borderRadius:9,border:'none',background:'rgba(255,255,255,0.06)',color:'rgba(255,255,255,0.8)',cursor:'pointer',fontFamily:'var(--fm)',fontSize:11}}>
            {lang.toUpperCase()}
          </button>

          {/* User menu */}
          <div style={{position:'relative',marginLeft:4}}>
            <button onClick={()=>setUserMenu(v=>!v)} style={{display:'flex',alignItems:'center',gap:8,padding:'5px 6px 5px 12px',borderRadius:20,border:'1px solid rgba(255,255,255,0.15)',background:'rgba(255,255,255,0.06)',color:'#fff',cursor:'pointer'}}>
              <span style={{fontSize:12.5,fontWeight:500}}>{primeiroNome}</span>
              <div style={{width:28,height:28,borderRadius:'50%',background:'var(--accent)',display:'grid',placeItems:'center',fontFamily:'var(--fm)',fontSize:10,fontWeight:600}}>{initials}</div>
            </button>
            {userMenu && (
              <>
                <div onClick={()=>setUserMenu(false)} style={{position:'fixed',inset:0,zIndex:198}}/>
                <div style={{position:'absolute',top:'calc(100% + 8px)',right:0,zIndex:199,background:'var(--surface)',borderRadius:14,boxShadow:'var(--shadow-lg)',border:'1px solid var(--line-soft)',minWidth:180,overflow:'hidden'}}>
                  <div style={{padding:'12px 16px',borderBottom:'1px solid var(--line-soft)'}}>
                    <div style={{fontSize:13,fontWeight:600,color:'var(--ink)'}}>{currentUser?.nome}</div>
                    <div style={{fontSize:11,color:'var(--muted)',fontFamily:'var(--fm)',marginTop:2}}>{currentUser?.genesis_id} · {role}</div>
                  </div>
                  <button onClick={()=>{setUserMenu(false);setPage('perfil');}} style={{width:'100%',textAlign:'left',padding:'11px 16px',border:'none',background:'transparent',color:'var(--ink)',cursor:'pointer',fontSize:13,fontFamily:'var(--f)',display:'flex',alignItems:'center',gap:8}}
                    onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'}
                    onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
                    <Icon n="users" size={14}/>{lang==='pt'?'Meu perfil':'My profile'}
                  </button>
                  <button onClick={()=>{setUserMenu(false);onLogout();}} style={{width:'100%',textAlign:'left',padding:'11px 16px',border:'none',background:'transparent',color:'var(--accent)',cursor:'pointer',fontSize:13,fontFamily:'var(--f)',display:'flex',alignItems:'center',gap:8}}
                    onMouseEnter={e=>e.currentTarget.style.background='var(--cream-2)'}
                    onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
                    <Icon n="logout" size={14}/>{lang==='pt'?'Sair':'Log out'}
                  </button>
                </div>
              </>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

// IDs que pertencem ao hub de Módulos (para destacar a aba ativa)
const MODULE_IDS = ['gangway','cabines','manifesto','pessoas','solicitacoes_op','coach','inventario','dbexplorer','config','cinema','musica','saude','conquistas','habilidades','permissoes'];

// ─── ModulesPage — hub de cards (estilo Cabin Management) ───
const ModulesPage = ({setPage, state, lang, role, isStaff}) => {
  const canManage = role === 'admin' || isStaff === true;
  const isAdmin = role === 'admin';

  // Catálogo de módulos com cor, ícone e permissão
  const MODULES = [
    // Operacional (gestão)
    {id:'bridge',   icon:'anchor', color:'#1F2A2E', pt:'Bridge',     en:'Bridge',     descPt:'Quadro de fainas, POs e NPT', descEn:'Tasks, POs & NPT', need:'manage', grupo:'op'},
    {id:'logbook',  icon:'list',   color:'#1F5755', pt:'Logbook',    en:'Logbook',    descPt:'Console de turno DP', descEn:'DP watch console', need:'manage', grupo:'op'},
    {id:'gangway',  icon:'anchor', color:'#1F5755', pt:'Gangway',    en:'Gangway',    descPt:'Embarque e desembarque', descEn:'Boarding control', need:'manage', grupo:'op'},
    {id:'cabines',  icon:'grid',   color:'#5C8DAE', pt:'Cabines',    en:'Cabins',     descPt:'Acomodação a bordo', descEn:'Accommodation', need:'manage', grupo:'op'},
    {id:'manifesto',icon:'list',   color:'#5C7E4F', pt:'Manifesto',  en:'Manifest',   descPt:'Quem está a bordo', descEn:"Who's on board", need:'manage', grupo:'op'},
    {id:'permissoes',icon:'lock',  color:'#B14E36', pt:'Permissões', en:'Permissions', descPt:'Perfis e autorizações por módulo', descEn:'Profiles & module grants', need:'manage', grupo:'op'},
    {id:'pessoas',  icon:'users',  color:'#D9694B', pt:'Pessoas',    en:'People',     descPt:'Tripulação e visitantes', descEn:'Crew & visitors', need:'manage', grupo:'op'},
    {id:'solicitacoes_op', icon:'flag', color:'#B14E5C', pt:'Triagem', en:'Triage',   descPt:'Painel de solicitações', descEn:'Requests board', need:'manage', grupo:'op'},
    {id:'coach',    icon:'health', color:'#C0728C', pt:'Coach',      en:'Coach',      descPt:'Treino, dieta e avaliações', descEn:'Training & diet', need:'manage', grupo:'op'},
    {id:'inventario', icon:'list', color:'#1F5755', pt:'Inventário', en:'Inventory', descPt:'Paióis, itens e contagens', descEn:'Storerooms & stock', need:'manage', grupo:'op'},
    {id:'dbexplorer', icon:'grid', color:'#1F2A2E', pt:'DB Explorer', en:'DB Explorer', descPt:'O banco como planilha (só leitura)', descEn:'Database as spreadsheet (read-only)', need:'admin', grupo:'op'},
    {id:'config',   icon:'flag',   color:'#6B6258', pt:'Configurações', en:'Settings', descPt:'Aparência e sistema', descEn:'Appearance & system', need:'all', grupo:'op'},
    // Convivência (todos)
    {id:'cinema',   icon:'film',   color:'#B14E36', pt:'Cinema',     en:'Cinema',     descPt:'Sessões e filmes', descEn:'Movie nights', need:'all', grupo:'conv'},
    {id:'musica',   icon:'music',  color:'#5C8DAE', pt:'Música',     en:'Music',      descPt:'Playlists do navio', descEn:'Ship playlists', need:'all', grupo:'conv'},
    {id:'saude',    icon:'health', color:'#5C7E4F', pt:'Saúde',      en:'Health',     descPt:'Bem-estar e exercício', descEn:'Wellness', need:'all', grupo:'conv'},
    {id:'conquistas',icon:'star',  color:'#C68A1F', pt:'Conquistas', en:'Achievements', descPt:'Medalhas da tripulação', descEn:'Crew badges', need:'all', grupo:'conv'},
    {id:'habilidades',icon:'fork', color:'#1F5755', pt:'Habilidades', en:'Skills',    descPt:'Talentos a bordo', descEn:'Talents aboard', need:'all', grupo:'conv'},
  ];

  const chavesHub = state?.currentUser?.permissoes || [];
  const canSee = (m) => m.need==='all' ? true : m.need==='admin' ? isAdmin : (canManage || chavesHub.some(k => k.startsWith(m.id + '.')));
  const visible = MODULES.filter(canSee);
  const op = visible.filter(m=>m.grupo==='op');
  const conv = visible.filter(m=>m.grupo==='conv');

  const renderGroup = (title, mods) => mods.length > 0 && (
    <div style={{marginBottom:32}}>
      <div className="t-eyebrow" style={{marginBottom:14}}>{title}</div>
      <div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill, minmax(220px, 1fr))',gap:16}}>
        {mods.map(m => (
          <button key={m.id} onClick={()=>setPage(m.id)}
            style={{
              textAlign:'left',background:'var(--surface)',border:'1px solid var(--line-soft)',
              borderRadius:18,padding:'20px',cursor:'pointer',position:'relative',overflow:'hidden',
              boxShadow:'var(--shadow-sm)',transition:'transform .16s, box-shadow .16s'
            }}
            onMouseEnter={e=>{e.currentTarget.style.transform='translateY(-3px)';e.currentTarget.style.boxShadow='var(--shadow-lg)'}}
            onMouseLeave={e=>{e.currentTarget.style.transform='none';e.currentTarget.style.boxShadow='var(--shadow-sm)'}}>
            <div style={{width:48,height:48,borderRadius:14,background:m.color,display:'grid',placeItems:'center',color:'#fff',marginBottom:14}}>
              <Icon n={m.icon} size={24}/>
            </div>
            <div style={{fontFamily:'var(--fs)',fontSize:19,fontWeight:600,letterSpacing:'-0.3px',color:'var(--ink)'}}>
              {lang==='pt'?m.pt:m.en}
            </div>
            <div style={{fontSize:12.5,color:'var(--muted)',marginTop:3}}>
              {lang==='pt'?m.descPt:m.descEn}
            </div>
            {m.id==='bridge' && state?.bridgeResumo && (state.bridgeResumo.minhas>0 || state.bridgeResumo.vencidas>0) && (
              <span style={{position:'absolute',top:14,right:14,display:'flex',gap:4,alignItems:'center'}}>
                {state.bridgeResumo.vencidas>0 && (
                  <span title={lang==='pt'?`${state.bridgeResumo.vencidas} faina(s) vencida(s) na ponte`:`${state.bridgeResumo.vencidas} overdue task(s) on the bridge`}
                    style={{fontFamily:'var(--fm)',fontSize:9.5,fontWeight:700,color:'#fff',background:'var(--rose)',borderRadius:999,padding:'2px 8px'}}>
                    ⏰ {state.bridgeResumo.vencidas}
                  </span>
                )}
                {state.bridgeResumo.minhas>0 && (
                  <span title={lang==='pt'?`${state.bridgeResumo.minhas} faina(s) com você`:`${state.bridgeResumo.minhas} task(s) assigned to you`}
                    style={{fontFamily:'var(--fm)',fontSize:9.5,fontWeight:700,color:'var(--accent)',background:'var(--accent-soft)',borderRadius:999,padding:'2px 8px'}}>
                    {state.bridgeResumo.minhas} {lang==='pt'?'suas':'yours'}
                  </span>
                )}
              </span>
            )}
          </button>
        ))}
      </div>
    </div>
  );

  return (
    <main style={{padding: '30px 36px 56px', flex:1}}>
      <div style={{marginBottom:28}}>
        <div className="t-eyebrow">{lang==='pt'?'todos os recursos':'all features'}</div>
        <h1 style={{fontFamily:'var(--fs)',fontSize:36,letterSpacing:'-0.8px',lineHeight:1.05,marginTop:6}}>
          Módu<em style={{fontStyle:'italic',color:'var(--accent)'}}>los.</em>
        </h1>
      </div>
      {renderGroup(lang==='pt'?'Operação':'Operations', op)}
      {renderGroup(lang==='pt'?'Convivência':'Community', conv)}
    </main>
  );
};

Object.assign(window, {Sidebar, BottomNav, MobileMenu, Topbar, TopNav, ModulesPage, MODULE_IDS, BridgeBanner, CmdK, NAV_ALL, visibleNav, PageTitles});
