﻿// Sidebar + Topbar

function useCurrentUser() {
  const [info, setInfo] = React.useState({ nome: '', initials: '…', empresa: '', color: '#1F93FF', avatarUrl: '' });

  React.useEffect(() => {
    function load() {
      const u = window.HOROS_USER;
      if (!u) return;

      const colors = ['#1F93FF','#007dff','#10b981','#f97316','#ec4899','#8b5cf6','#14b8a6'];
      const color = colors[u.id.charCodeAt(0) % colors.length];

      const sb = window.HOROS_SB;
      if (!sb) return;
      sb.from('profiles').select('nome,sobrenome,empresa,avatar_url').eq('id', u.id).single()
        .then(({ data }) => {
          const meta = u.user_metadata || {};
          const nome = data?.nome || meta.nome || '';
          const sob  = data?.sobrenome || meta.sobrenome || '';
          setInfo({
            nome: `${nome} ${sob}`.trim() || u.email,
            initials: nome && sob ? (nome[0] + sob[0]).toUpperCase() : (nome[0] || u.email[0] || '?').toUpperCase(),
            empresa: data?.empresa || meta.empresa || '',
            color,
            avatarUrl: data?.avatar_url || '',
          });
        });
    }

    if (window.HOROS_USER) {
      load();
    } else {
      const t = setTimeout(load, 400);
      return () => clearTimeout(t);
    }
  }, []);

  return info;
}

// Celular (≤ 760px): mesma quebra do horos-mobile.css
const _MOBILE_MQ = '(max-width: 760px)';
function useHorosMobile() {
  const [m, setM] = React.useState(() => !!(window.matchMedia && window.matchMedia(_MOBILE_MQ).matches));
  React.useEffect(() => {
    if (!window.matchMedia) return;
    const mq = window.matchMedia(_MOBILE_MQ);
    const on = () => setM(mq.matches);
    mq.addEventListener ? mq.addEventListener('change', on) : mq.addListener(on);
    return () => { mq.removeEventListener ? mq.removeEventListener('change', on) : mq.removeListener(on); };
  }, []);
  return m;
}
window.useHorosMobile = useHorosMobile;

// Seções do menu por id (antes era pela posição — um item novo empurrava os outros de seção)
const NAV_MAIN = new Set(['dashboard', 'crm', 'clients', 'properties', 'messages', 'ai']);
// Redes sociais e aba DRE do Financeiro: por enquanto só para o Daniel e o Cruz (pedido do usuário 2026-11-08). Mesma lista em api/social-metrics.js
const NAV_OPS = new Set(['automations', 'broadcast', 'distribution', 'calendar', 'reports', 'social', 'finance', 'integrations']);
function Sidebar({ active, onNav }) {
  const isMobile = useHorosMobile();
  const solo = window.useHorosSolo ? window.useHorosSolo() : false;
  const dist = window.useHorosDist ? window.useHorosDist() : false;
  // Telas por ramo (horosSegHas em horos-segments.js): Imóveis só no imobiliário
  const segment = window.useHorosSegment ? window.useHorosSegment() : 'imobiliario';
  const user = useCurrentUser();
  const [crmBadge, setCrmBadge] = React.useState('');
  const [msgsBadge, setMsgsBadge] = React.useState(() => (window.HorosNotif && window.HorosNotif.waUnread) || 0);
  const [waConnected, setWaConnected] = React.useState(() => localStorage.getItem('horos-wa-connected') === '1');
  const [collapsed, setCollapsed] = React.useState(() => localStorage.getItem('horos-sidebar-collapsed') === '1');

  React.useEffect(() => {
    document.body.classList.toggle('sidebar-collapsed', collapsed);
    localStorage.setItem('horos-sidebar-collapsed', collapsed ? '1' : '');
    window.dispatchEvent(new CustomEvent('horos:sidebar-changed', { detail: collapsed }));
  }, [collapsed]);

  React.useEffect(() => {
    const handler = () => setCollapsed(c => !c);
    window.addEventListener('horos:sidebar-toggle', handler);
    return () => window.removeEventListener('horos:sidebar-toggle', handler);
  }, []);

  // Celular: a barra lateral vira um menu que desliza da esquerda (body.mnav-open)
  React.useEffect(() => {
    const toggle = (e) => {
      const open = e && e.detail != null ? !!e.detail : !document.body.classList.contains('mnav-open');
      document.body.classList.toggle('mnav-open', open);
    };
    const onKey = (e) => { if (e.key === 'Escape') document.body.classList.remove('mnav-open'); };
    window.addEventListener('horos:mnav', toggle);
    document.addEventListener('keydown', onKey);
    return () => { window.removeEventListener('horos:mnav', toggle); document.removeEventListener('keydown', onKey); document.body.classList.remove('mnav-open'); };
  }, []);

  React.useEffect(() => {
    const onWa = (e) => setMsgsBadge(e.detail || 0);
    window.addEventListener('horos:wa-unread', onWa);
    const onStorage = (e) => {
      if (e.key === 'horos-wa-connected') setWaConnected(e.newValue === '1');
    };
    window.addEventListener('storage', onStorage);
    return () => {
      window.removeEventListener('horos:wa-unread', onWa);
      window.removeEventListener('storage', onStorage);
    };
  }, []);

  React.useEffect(() => {
    const update = () => {
      const leads = window.LEADS || [];
      const active = leads.filter(l => !ARCHIVED_COLS.has(l.col) && l.col !== 'fechado').length;
      setCrmBadge(active > 0 ? String(active) : '');
    };
    update();
    const t = setTimeout(update, 800);
    window.addEventListener('horos:lead-created', update);
    window.addEventListener('horos:lead-updated', update);
    window.addEventListener('horos:leads-refreshed', update);
    return () => {
      clearTimeout(t);
      window.removeEventListener('horos:lead-created', update);
      window.removeEventListener('horos:lead-updated', update);
      window.removeEventListener('horos:leads-refreshed', update);
    };
  }, []);

  // Distribuição de leads: só para quem gerencia a fila
  const distNav = !solo && ['gerente', 'super', 'diretor', 'ceo'].includes(window.HOROS_WORKSPACE?.role)
    ? [{ id: 'distribution', label: 'Distribuição', icon: 'distribute', href: 'distribution.html' }] : [];
  // Plano Distribuição: só receber e distribuir leads
  const distItems = [
    { id: 'leads', label: 'Leads recebidos', icon: 'funnel', href: 'leads.html' },
    ...distNav,
    { id: 'calendar', label: 'Agenda', icon: 'cal', href: 'calendar.html' },
    { id: 'reports', label: 'Relatórios', icon: 'chart', href: 'reports.html' },
    { id: 'workspace', label: 'Equipe', icon: 'team', href: 'team.html' },
    { id: 'integrations', label: 'Integrações', icon: 'link', href: 'integrations.html' },
  ];
  const items = dist ? [...distItems] : [
    { id: 'dashboard', label: 'Dashboard', icon: 'home', href: 'dashboard.html' },
    { id: 'crm', label: 'CRM', icon: 'crm', badge: crmBadge, href: 'crm.html' },
    { id: 'clients', label: 'Clientes', icon: 'clients', href: 'clients.html' },
    ...((window.horosSegHas ? window.horosSegHas('properties', segment) : segment === 'imobiliario') ? [{ id: 'properties', label: 'Imóveis', icon: 'building', href: 'properties.html' }] : []),
    { id: 'messages', label: 'Mensagens', icon: 'msgs', href: 'messages.html', badge: waConnected && msgsBadge > 0 ? String(msgsBadge > 99 ? '99+' : msgsBadge) : '' },
    { id: 'ai', label: 'IA', icon: 'ai', mascot: true, href: 'ai.html' },
    { id: 'automations', label: 'Automações', icon: 'flow', href: 'automations.html' },
    { id: 'broadcast', label: 'Disparos', icon: 'send', href: 'broadcast.html' },
    ...distNav,
    { id: 'calendar', label: 'Agenda', icon: 'cal', href: 'calendar.html' },
    { id: 'reports', label: 'Relatórios', icon: 'chart', href: 'reports.html' },
    { id: 'social', label: 'Redes sociais', icon: 'social', href: 'social.html' },
    { id: 'finance', label: 'Financeiro', icon: 'wallet', href: 'finance.html' },
    { id: 'integrations', label: 'Integrações', icon: 'link', href: 'integrations.html' },
    ...(solo ? [] : [{ id: 'workspace', label: 'Equipe', icon: 'team', href: 'team.html' }]),
  ];
  items.push(
    // Ajuda interna (support.html, com menu e topo do app); a pública é help.html. page= leva a tela de onde veio para o e-mail
    { id: 'help', label: 'Ajuda e suporte', icon: 'help',
      href: /\/support(\.html)?$/.test(location.pathname) ? 'support.html' : `support.html?page=${encodeURIComponent(location.pathname.replace(/^\//, '') + location.search)}` },
    { id: 'logout', label: 'Sair', icon: 'logout', onClick: async () => {
      if (window.HOROS_SB) await window.HOROS_SB.auth.signOut();
      // Limpa todos os dados do usuário do localStorage — evita vazamento entre contas
      const keep = new Set(['horos-theme', 'horos-sidebar-collapsed', 'horos-emoji-recents']);
      for (let i = localStorage.length - 1; i >= 0; i--) {
        const k = localStorage.key(i);
        if (k && (k.startsWith('horos-') || k.startsWith('horos:')) && !keep.has(k)) {
          localStorage.removeItem(k);
        }
      }
      // Leads guardados no aparelho (IndexedDB) e dados da aba também saem — computador compartilhado
      try { sessionStorage.clear(); } catch {}
      try { indexedDB.deleteDatabase('horos-cache'); } catch {}
      window.location.href = 'login.html';
    }},
  );

  const NavItem = ({ it }) => {
    const cls = `nav-item ${active === it.id ? 'active' : ''}`;
    const inner = (
      <>
        {it.mascot ? <span className="nav-mascot"><horos-mascot size="20" tone="mono" track="off" aria-hidden="true" /></span> : <Icon name={it.icon} />}
        <span>{it.label}</span>
        {it.badge && <span className="badge">{it.badge}</span>}
      </>
    );
    const closeM = () => document.body.classList.remove('mnav-open');
    if (it.href) {
      return <a className={cls} href={it.href} onClick={closeM}>{inner}</a>;
    }
    return <div className={cls} onClick={() => { closeM(); (it.onClick || (() => onNav && onNav(it.id)))(); }}>{inner}</div>;
  };

  return (
    <>
    <div className="mnav-scrim" aria-hidden="true" onClick={() => document.body.classList.remove('mnav-open')} />
    <aside className={`sidebar${collapsed && !isMobile ? ' collapsed' : ''}`}>
      <button type="button" className="mnav-close" aria-label="Fechar menu" onClick={() => document.body.classList.remove('mnav-open')}>
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12"/></svg>
      </button>
      <div className="brand">
        {collapsed && !isMobile
          ? <img src="favicon.svg" alt="Horos" style={{ display: 'block', height: '60px', width: 'auto' }} />
          : <HorosMark height={72} />}
      </div>

      <div className="nav-section">
        <div className="nav-label" style={{display:'flex',alignItems:'center',justifyContent:'space-between'}}>
          <span>{(window.HOROS_WORKSPACE && window.HOROS_WORKSPACE.workspace_name) || 'Workspace'}</span>
          {/* Plano individual: só a pessoa, sem cargo; o cargo volta quando vira Team/Enterprise (equipe) */}
          {window.HOROS_WORKSPACE && !solo && <span style={{fontSize:'10px',fontWeight:500,opacity:0.6,textTransform:'uppercase',letterSpacing:'0.04em'}}>{window.HOROS_WORKSPACE.role}</span>}
        </div>
        {(dist ? items.slice(0, distItems.length) : items.filter(it => NAV_MAIN.has(it.id))).map(it => <NavItem key={it.id} it={it} />)}
      </div>

      {!dist && (
      <div className="nav-section">
        <div className="nav-label">Operação</div>
        {items.filter(it => NAV_OPS.has(it.id)).map(it => <NavItem key={it.id} it={it} />)}
      </div>
      )}

      <div className="nav-section">
        <div className="nav-label">Sistema</div>
        {(dist ? items.slice(distItems.length) : items.filter(it => !NAV_MAIN.has(it.id) && !NAV_OPS.has(it.id))).map(it => <NavItem key={it.id} it={it} />)}
      </div>

      <div className="sidebar-foot">
        <a className="user-card" href="profile.html" style={{ textDecoration: 'none' }}>
          <div className="avatar online" style={user.avatarUrl ? { background: 'none' } : { background: `linear-gradient(135deg, ${user.color}, ${user.color}99)` }}>
            {user.avatarUrl
              ? <img src={user.avatarUrl} alt="" style={{ width:'100%', height:'100%', objectFit:'cover', borderRadius:'50%', display:'block' }} />
              : user.initials}
          </div>
          <div className="user-meta">
            <span className="user-name">{user.nome || '…'}</span>
            <span className="user-plan">
              {(() => {
                // Plano real da imobiliária (get_my_workspace); sem plano pago, sem selo
                const w = window.HOROS_WORKSPACE || {};
                const live = ['trialing', 'active', 'past_due', 'unpaid', 'incomplete'].includes(w.plan_status);
                const name = live && { pro: 'PRO', max: 'MAX', team: 'TEAM', enterprise: 'ENTERPRISE', dist_10: 'DISTRIBUIÇÃO', dist_30: 'DISTRIBUIÇÃO', dist_unl: 'DISTRIBUIÇÃO' }[w.plan];
                return name ? <span className="plan-pill">{name}</span> : null;
              })()} Online
            </span>
          </div>
        </a>
      </div>

    </aside>
    <MobileTabBar active={active} msgsBadge={waConnected && msgsBadge > 0 ? (msgsBadge > 99 ? '99+' : String(msgsBadge)) : ''} />
    </>
  );
}

// ── MobileTabBar ───────────────────────────────────────────────────────────────
// Só no celular (CSS): atalhos fixos embaixo da tela + "Menu" abre a barra lateral inteira
function MobileTabBar({ active, msgsBadge }) {
  const dist = window.useHorosDist ? window.useHorosDist() : false;
  const tabs = dist ? [
    { id: 'leads',        label: 'Leads',       icon: 'funnel', href: 'leads.html' },
    { id: 'calendar',     label: 'Agenda',      icon: 'cal',    href: 'calendar.html' },
    { id: 'reports',      label: 'Relatórios',  icon: 'chart',  href: 'reports.html' },
    { id: 'workspace',    label: 'Equipe',      icon: 'team',   href: 'team.html' },
  ] : [
    { id: 'dashboard', label: 'Início',    icon: 'home', href: 'dashboard.html' },
    { id: 'crm',       label: 'CRM',       icon: 'crm',  href: 'crm.html' },
    { id: 'messages',  label: 'Mensagens', icon: 'msgs', href: 'messages.html', badge: msgsBadge },
    { id: 'calendar',  label: 'Agenda',    icon: 'cal',  href: 'calendar.html' },
  ];
  return (
    <nav className="mtab" aria-label="Navegação principal">
      {tabs.map(t => (
        <a key={t.id} href={t.href} className={`mtab-item${active === t.id ? ' active' : ''}`} aria-current={active === t.id ? 'page' : undefined}>
          <span className="mtab-ico"><Icon name={t.icon} size={21} />{t.badge ? <span className="mtab-badge">{t.badge}</span> : null}</span>
          <span className="mtab-lbl">{t.label}</span>
        </a>
      ))}
      <button type="button" className="mtab-item" onClick={() => window.dispatchEvent(new CustomEvent('horos:mnav', { detail: true }))}>
        <span className="mtab-ico"><svg width="21" height="21" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round"><path d="M4 7h16M4 12h16M4 17h16"/></svg></span>
        <span className="mtab-lbl">Menu</span>
      </button>
    </nav>
  );
}

// ── GlobalToast ────────────────────────────────────────────────────────────────
// Aviso que aparece quando chega uma notificação (canto superior direito).
// Some sozinho em 5 s — passar o mouse pausa (a barrinha embaixo mostra o tempo); entra e sai deslizando.
const _TOAST_KICKER = { messages: 'Mensagem', leads: 'Lead', agenda: 'Agenda', system: 'Horos' };
function GlobalToast({ notif, onClose }) {
  const DURATION = 5000;
  const [leaving, setLeaving] = React.useState(false);
  const [paused, setPaused] = React.useState(false);
  const leftRef = React.useRef(DURATION);
  const startRef = React.useRef(Date.now());
  const close = React.useCallback(() => { setLeaving(true); setTimeout(onClose, 220); }, [onClose]);
  React.useEffect(() => {
    if (paused || leaving) return;
    startRef.current = Date.now();
    const t = setTimeout(close, leftRef.current);
    return () => { clearTimeout(t); leftRef.current = Math.max(0, leftRef.current - (Date.now() - startRef.current)); };
  }, [paused, leaving, close]);
  const kicker = _TOAST_KICKER[notif.module] || 'Notificação';
  return (
    <div className={`hn-toast hn-mod-${notif.module || 'default'}${leaving ? ' leaving' : ''}${paused ? ' paused' : ''}`}
      role="status" aria-live="polite"
      onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}
      onClick={() => { if (notif.url) window.location.href = notif.url; close(); }}>
      <div className="hn-toast-ico"><_NotifIcon module={notif.module} size={18} /></div>
      <div className="hn-toast-body">
        <div className="hn-toast-top"><span className="hn-toast-kicker">{kicker}</span><span className="hn-toast-when">agora</span></div>
        <div className="hn-toast-title">{notif.title}</div>
        {notif.body && <div className="hn-toast-msg">{notif.body}</div>}
      </div>
      <button className="hn-toast-close" aria-label="Fechar" onClick={e => { e.stopPropagation(); close(); }}><Icon name="close" size={14} /></button>
      <span className="hn-toast-timer" style={{ animationDuration: DURATION + 'ms' }} aria-hidden="true" />
    </div>
  );
}

// ── Notification module icons ───────────────────────────────────────────────────
function _NotifIcon({ module, size }) {
  const s = size || 20;
  if (module === 'messages') return (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="#25D366">
      <path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z"/>
    </svg>
  );
  if (module === 'system') return (
    <img src="/favicon.svg" width={s} height={s} alt="" style={{ display: 'block' }} />
  );
  if (module === 'agenda') return (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
      <rect x="3" y="4" width="18" height="18" rx="2"/>
      <path d="M16 2v4M8 2v4M3 10h18"/>
    </svg>
  );
  return (
    <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
      <circle cx="12" cy="8" r="3.5"/>
      <path d="M5 20c0-3.5 3-6 7-6s7 2.5 7 6"/>
    </svg>
  );
}

// ── NotificationCenter ─────────────────────────────────────────────────────────
function NotificationCenter({ onClose }) {
  const [items,      setItems]      = React.useState([]);
  const [loading,    setLoading]    = React.useState(true);
  const [filter,     setFilter]     = React.useState('all');
  const [page,       setPage]       = React.useState(0);
  const [hasMore,    setHasMore]    = React.useState(false);
  const [detailItem, setDetailItem] = React.useState(null);
  const LIMIT = 25;
  const now = Date.now();
  const NC_CLEARED_KEY = 'horos-notif-cleared-at';

  const load = async (p, mod) => {
    setLoading(true);
    // Só as não vistas: depois de abrir uma notificação ela some do sino (pedido do usuário 2026-10-11)
    const data = await (window.HorosNotif && window.HorosNotif.getNotifications({ page: p, limit: LIMIT, module: mod || undefined, unread: true })) || [];
    const clearedAt = localStorage.getItem(NC_CLEARED_KEY);
    const visible = clearedAt
      ? data.filter(x => new Date(x.created_at) > new Date(clearedAt))
      : data;
    if (p === 0) setItems(visible);
    else setItems(prev => [...prev, ...visible]);
    setHasMore(data.length === LIMIT);
    setPage(p);
    setLoading(false);
  };

  React.useEffect(() => { load(0, filter === 'all' ? null : filter); }, [filter]);

  React.useEffect(() => {
    const onN = e => {
      if (!e.detail || e.detail.module === 'messages') return; // mensagens são efêmeras
      setItems(prev => [e.detail, ...prev]);
    };
    window.addEventListener('horos:notification', onN);
    return () => window.removeEventListener('horos:notification', onN);
  }, []);

  const handleClick = async (item) => {
    // Visualizou → marca como lida e tira da lista na hora
    setItems(prev => prev.filter(x => x.id !== item.id));
    if (!item.is_read && window.HorosNotif) window.HorosNotif.markAsRead(item.id).catch(() => {});
    if (item.module === 'messages' || item.module === 'system') {
      setDetailItem(item);
      return;
    }
    onClose();
    if (item.module === 'leads') {
      const leadId = item.payload?.lead_id;
      if (leadId && window.horosOpenLeadSheet) window.horosOpenLeadSheet(leadId);
      else window.location.href = leadId ? `client.html?id=${leadId}` : (item.url || 'crm.html');
      return;
    }
    if (item.module === 'agenda') {
      const eventId = item.payload?.event_id || item.payload?.eventId;
      if (eventId) sessionStorage.setItem('horos:highlight-event', eventId);
      window.location.href = item.url || 'calendar.html';
      return;
    }
    if (item.url) window.location.href = item.url;
  };

  const handleMarkAll = async () => {
    if (window.HorosNotif) await window.HorosNotif.markAllAsRead();
    localStorage.setItem(NC_CLEARED_KEY, new Date().toISOString());
    setItems([]);
  };

  const fmtTs = ts => {
    const diff = Math.floor((now - new Date(ts).getTime()) / 1000);
    if (diff < 60)    return 'agora';
    if (diff < 3600)  return `há ${Math.floor(diff / 60)}min`;
    if (diff < 86400) return new Date(ts).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' });
    if (diff < 172800) return 'ontem';
    return new Date(ts).toLocaleDateString('pt-BR');
  };

  const today   = items.filter(x => now - new Date(x.created_at).getTime() < 86400000);
  const earlier = items.filter(x => now - new Date(x.created_at).getTime() >= 86400000);

  const Section = ({ title, list }) => {
    if (!list.length) return null;
    return (
      <>
        <div className="hn-nc-section">{title}</div>
        {list.map(item => (
          <div key={item.id} className={`hn-nc-item${item.is_read ? ' read' : ''}`} onClick={() => handleClick(item)}>
            <div className={`hn-nc-icon hn-nc-icon-${item.module || 'default'}`}>
              <_NotifIcon module={item.module} />
            </div>
            <div className="hn-nc-body">
              <div className="hn-nc-title">{item.title}</div>
              {item.body && <div className="hn-nc-msg">{item.body}</div>}
              <div className="hn-nc-time">{fmtTs(item.created_at)}</div>
            </div>
            {!item.is_read && <div className="hn-nc-dot" />}
          </div>
        ))}
      </>
    );
  };

  const FILTERS = [
    { id: 'all',    label: 'Todas' },
    { id: 'leads',  label: 'Leads' },
    { id: 'agenda', label: 'Agenda' },
    { id: 'system', label: 'Sistema' },
  ];

  return (
    <div className="hn-nc">
      <div className="hn-nc-header">
        <span className="hn-nc-heading">Notificações</span>
        <button className="hn-nc-mark-all" onClick={handleMarkAll}>Marcar todas como lidas</button>
      </div>
      <div className="hn-nc-filters">
        {FILTERS.map(f => (
          <button key={f.id} className={`hn-nc-filter-btn${filter === f.id ? ' active' : ''}`} onClick={() => setFilter(f.id)}>
            {f.label}
          </button>
        ))}
      </div>
      <div className="hn-nc-list">
        {loading && page === 0
          ? <SkelList rows={5} avatar={34} pad="12px 16px" label="Carregando notificações" />
          : items.length === 0
            ? <div className="hn-nc-empty">Nenhuma notificação nova</div>
            : <>
                <Section title="Hoje"       list={today}   />
                <Section title="Anteriores" list={earlier} />
                {hasMore && (
                  <button className="hn-nc-load-more"
                          onClick={() => load(page + 1, filter === 'all' ? null : filter)}>
                    Carregar mais
                  </button>
                )}
              </>
        }
      </div>

      {detailItem && (
        <div className="hn-nc-detail-overlay" onClick={() => setDetailItem(null)}>
          <div className="hn-nc-detail" onClick={e => e.stopPropagation()}>
            <div className="hn-nc-detail-head">
              <div className={`hn-nc-icon hn-nc-icon-${detailItem.module || 'default'}`} style={{ flexShrink: 0 }}>
                <_NotifIcon module={detailItem.module} size={22} />
              </div>
              <span className="hn-nc-detail-title">{detailItem.title}</span>
              <button className="hn-nc-detail-close" onClick={() => setDetailItem(null)}><Icon name="close" size={14} /></button>
            </div>
            {detailItem.body && <p className="hn-nc-detail-body">{detailItem.body}</p>}
            {detailItem.url && (
              <button className="hn-nc-detail-link" onClick={() => { setDetailItem(null); onClose(); window.location.href = detailItem.url; }}>
                Abrir →
              </button>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

// ── AIModal — chat flutuante ───────────────────────────────────────────────────
function AIModal({ onClose, initialPrompt }) {
  const [thread,  setThread]  = React.useState([]);
  const [history, setHistory] = React.useState([]);
  const [draft,   setDraft]   = React.useState('');
  const [loading, setLoading] = React.useState(false);
  const endRef      = React.useRef(null);
  const inputRef    = React.useRef(null);
  const modalRef    = React.useRef(null);
  const autoSentRef = React.useRef(false);

  React.useEffect(() => {
    if (initialPrompt && !autoSentRef.current) {
      autoSentRef.current = true;
      const t = setTimeout(() => handleSend(initialPrompt), 280);
      return () => clearTimeout(t);
    } else {
      inputRef.current?.focus();
    }
  }, []);
  React.useEffect(() => { endRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [thread.length, loading]);

  React.useEffect(() => {
    const handler = e => { if (modalRef.current && !modalRef.current.contains(e.target)) onClose(); };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, []);

  function fmtNow() {
    const d = new Date();
    return `${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
  }

  async function handleSend(text) {
    const msg = (text || draft).trim();
    if (!msg || loading) return;
    setDraft('');
    setThread(prev => [...prev, { from: 'user', text: msg, time: fmtNow() }]);
    setLoading(true);
    try {
      const token = (window.HOROS_SESSION || {}).access_token;
      const res = await fetch('/api/intelligence/broker-chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) },
        body: JSON.stringify({ message: msg, history }),
      });
      const data = await res.json();
      const aiText = data.ok && data.text ? data.text : 'Desculpe, ocorreu um erro. Tente novamente.';
      setThread(prev => [...prev, { from: 'ai', text: aiText, refs: data.refs || [], time: fmtNow() }]);
      setHistory(prev => [...prev, { role: 'user', content: msg }, { role: 'assistant', content: aiText }]);
    } catch {
      setThread(prev => [...prev, { from: 'ai', text: 'Não consegui conectar. Verifique sua conexão.', time: fmtNow() }]);
    } finally {
      setLoading(false);
      setTimeout(() => inputRef.current?.focus(), 50);
    }
  }

  const handleKey = e => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); } };

  const QUICK = ['Leads quentes hoje', 'Resumo do funil', 'Quem contatar agora?'];

  return (
    <div className="ai-modal" ref={modalRef}>
      {/* Header */}
      <div className="ai-modal-head">
        <div className="ai-modal-head-left">
          <horos-mascot size="26" state={loading ? 'thinking' : 'idle'} aria-hidden="true" />
          <span className="ai-modal-title">Horos AI</span>
        </div>
        <div className="ai-modal-head-right">
          <a href="ai.html" className="ai-modal-expand" title="Abrir página completa">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
              <path d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/>
            </svg>
          </a>
          <button className="icon-btn" onClick={onClose} title="Fechar">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <path d="M18 6 6 18M6 6l12 12"/>
            </svg>
          </button>
        </div>
      </div>

      {/* Body */}
      <div className="ai-modal-body">
        {thread.length === 0 && !loading && (
          <div className="ai-modal-empty">
            <horos-mascot size="64" aria-hidden="true" />
            <p className="ai-modal-empty-title">Como posso ajudar?</p>
            <p className="ai-modal-empty-sub">Pergunte sobre seus leads, funil ou prioridades.</p>
            <div className="ai-modal-quicks">
              {QUICK.map((q, i) => (
                <button key={i} className="ai-modal-quick" onClick={() => handleSend(q)}>{q}</button>
              ))}
            </div>
          </div>
        )}
        {thread.map((m, i) => (
          <div key={i} className={`ai-modal-msg ai-modal-msg--${m.from}`}>
            {m.from === 'user'
              ? <div className="ai-modal-bubble">{m.text}</div>
              : <div className="ai-modal-ai">{window.HorosRichText ? <HorosRichText text={m.text} refs={m.refs} /> : m.text}</div>
            }
            <span className="ai-modal-time">{m.time}</span>
          </div>
        ))}
        {loading && (
          <div className="ai-modal-msg ai-modal-msg--ai">
            <div className="ai-modal-ai">
              <span className="ai-modal-dots"><span/><span/><span/></span>
            </div>
          </div>
        )}
        <div ref={endRef} />
      </div>

      {/* Input */}
      <div className="ai-modal-foot">
        <input ref={inputRef} className="ai-modal-input" value={draft}
               onChange={e => setDraft(e.target.value)} onKeyDown={handleKey}
               placeholder="Pergunte qualquer coisa…" disabled={loading} />
        <button className="ai-modal-send" onClick={() => handleSend()} disabled={!draft.trim() || loading}>
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M22 2 11 13M22 2 15 22l-4-9-9-4 20-7Z"/>
          </svg>
        </button>
      </div>
    </div>
  );
}

// ── Primeiros passos (tutorial de 5 passos) ─────────────────────────────────────
// Recriado 2026-10-05 (pedido do usuário): sem etapa de workspace, no tema claro/escuro (CSS .tour-* em styles.css).
// Cada passo se marca sozinho quando a pessoa faz a ação de verdade.
const _tourMonth = () => { const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`; };
const _TOUR_STEPS = [
  {
    id: 'lead', icon: 'userPlus',
    label: 'Cadastre seu primeiro lead',
    desc: 'Coloque um cliente no funil. Pode ser à mão ou importando uma planilha.',
    actionLabel: 'Novo lead',
    check: async () => {
      const sb = window.HOROS_SB;
      if (!sb) return false;
      const { count } = await sb.from('leads').select('id', { count: 'exact', head: true }).eq('archived', false);
      return (count || 0) > 0;
    },
    run: () => {
      if (window.NewLeadModal) window.dispatchEvent(new CustomEvent('horos:new-lead'));
      else window.location.href = 'crm.html';
    },
  },
  {
    id: 'whatsapp', icon: 'whats',
    label: 'Conecte seu WhatsApp',
    desc: 'Escaneie o QR code com o celular e converse com os clientes pelo Horos.',
    actionLabel: 'Conectar',
    check: async () => {
      const sb = window.HOROS_SB, uid = window.HOROS_SESSION?.user?.id;
      if (!sb || !uid) return false;
      const { data } = await sb.from('wa_instances').select('status').eq('user_id', uid).eq('status', 'connected').limit(1);
      return !!(data && data.length);
    },
    run: () => { window.location.href = 'integrations.html'; },
  },
  {
    id: 'message', icon: 'send',
    label: 'Mande sua primeira mensagem',
    desc: 'Abra uma conversa e responda um cliente direto daqui.',
    actionLabel: 'Abrir Mensagens',
    check: async () => {
      if (localStorage.getItem('horos-tour-sent-msg')) return true;
      // outro aparelho/navegador limpo: confere no banco se já mandou alguma mensagem
      const sb = window.HOROS_SB;
      if (!sb) return false;
      const { data } = await sb.from('messages').select('id').eq('direction', 'out').limit(1);
      if (data && data.length) { try { localStorage.setItem('horos-tour-sent-msg', '1'); } catch {} return true; }
      return false;
    },
    run: () => { window.location.href = 'messages.html'; },
  },
  {
    id: 'goal', icon: 'target',
    label: 'Defina sua meta do mês',
    get desc() { const g = window.horosSeg ? window.horosSeg() : null; return g ? `Diga ${g.fem ? 'quantas' : 'quantos'} ${g.unit[1]} quer ${g.verb}. O Horos mostra quantos leads você precisa prospectar.` : 'Diga quantos imóveis quer vender. O Horos mostra quantos leads você precisa prospectar.'; },
    actionLabel: 'Definir meta',
    check: async () => {
      const sb = window.HOROS_SB, uid = window.HOROS_SESSION?.user?.id;
      if (!sb || !uid) return false;
      const { data } = await sb.from('profiles').select('monthly_goal').eq('id', uid).maybeSingle();
      const g = data && data.monthly_goal;
      return !!(g && g.month === _tourMonth() && Number(g.count) > 0);
    },
    run: () => {
      const el = document.querySelector('.mv-goal');
      if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }); el.classList.add('tour-flash'); setTimeout(() => el.classList.remove('tour-flash'), 1800); }
      else window.location.href = 'dashboard.html';
    },
  },
  {
    id: 'ia', icon: 'sparkles',
    label: 'Converse com a IA do Horos',
    desc: 'Pergunte quais leads atender primeiro ou peça uma mensagem pronta.',
    actionLabel: 'Abrir IA',
    check: async () => !!localStorage.getItem('horos-tour-visited-ia'),
    run: () => { window.location.href = 'ai.html'; },
  },
];

// Qualquer mensagem enviada pelo Horos (Mensagens, Horos 360, respostas rápidas…) conta como o passo 3 na hora
(function () {
  if (window.__horosTourFetch || !window.fetch) return;
  window.__horosTourFetch = true;
  const orig = window.fetch.bind(window);
  window.fetch = function (input, init) {
    const p = orig(input, init);
    try {
      const url = typeof input === 'string' ? input : (input && input.url) || '';
      if (url.indexOf('/api/whatsapp-send') >= 0 && !localStorage.getItem('horos-tour-sent-msg')) {
        p.then(r => { if (r && r.ok) { try { localStorage.setItem('horos-tour-sent-msg', '1'); } catch {} window.dispatchEvent(new CustomEvent('horos:tour-check', { detail: { step: 'message' } })); } }, () => {});
      }
    } catch {}
    return p;
  };
})();

const _TK = { seen: 'horos-tour2-seen', dismissed: 'horos-tour2-dismissed', state: 'horos-tour2-state' };
const _tourGet = (k) => { try { return localStorage.getItem(k); } catch { return null; } };
const _tourSet = (k, v) => { try { v == null ? localStorage.removeItem(k) : localStorage.setItem(k, v); } catch {} };
// Guia concluído/fechado fica gravado na conta (user_metadata.horos_tour_done) — vale em qualquer aparelho/navegador
const _tourDoneInAccount = () => !!(window.HOROS_SESSION?.user?.user_metadata?.horos_tour_done);
// "Já viu" e os passos feitos também ficam na conta: o Sair apaga o localStorage (horos-*) e, sem isso,
// o guia abria sozinho de novo a cada login, com os passos zerados
const _tourMeta = () => window.HOROS_SESSION?.user?.user_metadata || {};
const _tourSaveMeta = (data) => {
  try {
    const u = window.HOROS_SESSION?.user;
    if (u) u.user_metadata = { ...(u.user_metadata || {}), ...data };   // vale na hora, sem esperar o servidor
    window.HOROS_SB?.auth?.updateUser({ data }).catch(() => {});
  } catch {}
};
const _tourSaveDone = () => {
  _tourSet(_TK.dismissed, '1');
  if (_tourDoneInAccount()) return;
  _tourSaveMeta({ horos_tour_done: true });
};

function TourRing({ value, total, size = 52, stroke = 3 }) {
  const r = (size - stroke) / 2, c = 2 * Math.PI * r;
  return (
    <svg className="tour-ring" width={size} height={size} viewBox={`0 0 ${size} ${size}`} aria-hidden="true">
      <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="currentColor" strokeOpacity=".28" strokeWidth={stroke} />
      <circle cx={size / 2} cy={size / 2} r={r} fill="none" stroke="currentColor" strokeWidth={stroke} strokeLinecap="round"
        strokeDasharray={c} strokeDashoffset={c * (1 - value / total)} transform={`rotate(-90 ${size / 2} ${size / 2})`} />
    </svg>
  );
}

// Feedback semanal: só para quem entrou pelo link promocional (RPC feedback_due); 1x a cada 7 dias.
const FBW_FACES = [
  { v: 1, label: 'Péssimo', mouth: 'M16 16s-1.5-2-4-2-4 2-4 2', extra: <><path d="M9 13v2" stroke="#3b82f6" /><path d="M15 13v2" stroke="#3b82f6" /></> },
  { v: 2, label: 'Ruim', mouth: 'M16 16s-1.5-2-4-2-4 2-4 2' },
  { v: 3, label: 'Ok', mouth: 'M8 15h8' },
  { v: 4, label: 'Bom', mouth: 'M8 13s1.5 2 4 2 4-2 4-2' },
  { v: 5, label: 'Incrível', mouth: 'M7 12.5s1.5 3.5 5 3.5 5-3.5 5-3.5', extra: <><path d="M9 9l.5 1.5 1.5.5-1.5.5-.5 1.5-.5-1.5-1.5-.5 1.5-.5z" fill="#f97316" stroke="none" /><path d="M15 9l.5 1.5 1.5.5-1.5.5-.5 1.5-.5-1.5-1.5-.5 1.5-.5z" fill="#f97316" stroke="none" /></> },
];
function WeeklyFeedback() {
  const [show, setShow] = React.useState(false);
  const [rating, setRating] = React.useState(0);
  const [text, setText] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [done, setDone] = React.useState(false);
  const snoozeKey = () => 'horos-fbw-snooze:' + ((window.HOROS_USER && window.HOROS_USER.id) || '');
  React.useEffect(() => {
    if (window.self !== window.top) return;
    let dead = false;
    const t = setTimeout(async () => {
      try {
        if (Number(localStorage.getItem(snoozeKey()) || 0) > Date.now()) return;
        const sb = window.HOROS_SB; if (!sb) return;
        const { data } = await sb.rpc('feedback_due');
        if (!dead && data === true) setShow(true);
      } catch (e) {}
    }, 5000);
    return () => { dead = true; clearTimeout(t); };
  }, []);
  if (!show) return null;
  const later = () => {
    try { localStorage.setItem(snoozeKey(), String(Date.now() + 864e5)); } catch (e) {}
    setShow(false);
  };
  const send = async () => {
    setBusy(true);
    const { data, error } = await window.HOROS_SB.rpc('submit_weekly_feedback', { p_rating: rating, p_message: text.trim() });
    setBusy(false);
    if (error || data !== true) { window.dispatchEvent(new CustomEvent('horos:toast', { detail: { module: 'system', title: 'Feedback não enviado', body: 'Tente de novo em instantes.' } })); return; }
    setDone(true); setTimeout(() => setShow(false), 2200);
  };
  return (
    <div className="fbw" role="dialog" aria-label="Feedback semanal">
      {done ? <div className="fbw-done">Obrigado pelo feedback!</div> : <>
        <button className="fbw-x" onClick={later} title="Agora não">×</button>
        <h3 className="fbw-title">Como foi sua semana no Horos?</h3>
        <p className="fbw-sub">Você é um dos primeiros usuários. Sua opinião muda o produto.</p>
        <div className="fbw-faces">
          {FBW_FACES.map(f => (
            <button key={f.v} className={'fbw-face' + (rating === f.v ? ' on' : '')} onClick={() => setRating(f.v)} title={f.label}>
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <circle cx="12" cy="12" r="10" /><path d={f.mouth} />
                {f.v !== 5 && <><path d="M9 9h.01" /><path d="M15 9h.01" /></>}
                {f.extra}
              </svg>
              {f.label}
            </button>
          ))}
        </div>
        <textarea className="fbw-text" maxLength={2000} placeholder="O que está bom, o que atrapalha, o que falta?" value={text} onChange={e => setText(e.target.value)} />
        <div className="fbw-foot">
          <button className="fbw-later" onClick={later}>Agora não</button>
          <button className="fbw-send" disabled={!rating || busy} onClick={send}>{busy ? 'Enviando…' : 'Enviar'}</button>
        </div>
      </>}
    </div>
  );
}

function TourWidget() {
  const N = _TOUR_STEPS.length;
  // Último estado conhecido (localStorage): ao abrir outra tela, o que foi concluído lá também é comemorado aqui
  const loadStored = () => {
    try { const a = JSON.parse(_tourGet(_TK.state) || 'null'); return Array.isArray(a) && a.length === N ? a.map(Boolean) : null; } catch { return null; }
  };
  const [dismissed, setDismissed] = React.useState(() => !!_tourGet(_TK.dismissed) || _tourDoneInAccount());
  // Não persiste entre páginas: só o 1º aparecimento automático abre sozinho — navegar para outra tela
  // sem clicar em "Minimizar" não deve deixar o painel voltando a abrir sempre (pedido do usuário 2026-10-12)
  const [open, setOpen] = React.useState(false);
  const prevRef = React.useRef(undefined);
  if (prevRef.current === undefined) prevRef.current = loadStored();
  const [checks, setChecks] = React.useState(() => prevRef.current || _TOUR_STEPS.map(() => false));
  const [loaded, setLoaded] = React.useState(() => !!prevRef.current);
  const [expanded, setExpanded] = React.useState(null);
  const [justDone, setJustDone] = React.useState([]);   // ids concluídos agora (aviso + animação)
  const doneTimer = React.useRef(null);
  const mascotRef = React.useRef(null);
  // passo concluído → mascote comemora rapidinho
  React.useEffect(() => { if (justDone.length && mascotRef.current && mascotRef.current.react) mascotRef.current.react('excited', 1500); }, [justDone]);

  // Junta o resultado novo com o que já estava feito (passo feito não "desfaz" por falha de rede) e comemora o que é novo
  const apply = React.useCallback((res) => {
    const prev = prevRef.current;
    const merged = res.map((v, k) => !!v || !!(prev && prev[k]));
    if (prev) {
      const fresh = _TOUR_STEPS.filter((s, k) => merged[k] && !prev[k]).map(s => s.id);
      if (fresh.length) {
        setJustDone(fresh); setExpanded(null);
        clearTimeout(doneTimer.current);
        doneTimer.current = setTimeout(() => setJustDone([]), 3600);
      }
    }
    prevRef.current = merged;
    _tourSet(_TK.state, JSON.stringify(merged));
    const acc = _tourMeta().horos_tour_steps;
    if (window.HOROS_SESSION?.user && merged.some(Boolean) && JSON.stringify(acc) !== JSON.stringify(merged)) _tourSaveMeta({ horos_tour_steps: merged });
    setChecks(merged);
    setLoaded(true);
    return merged;
  }, []);

  const runChecks = React.useCallback(async () => {
    if (!window.HOROS_SB || !window.HOROS_SESSION?.user?.id) return null;   // sessão ainda carregando: não conta como "não feito"
    // passos já feitos guardados na conta (outro navegador ou depois de Sair) contam como feitos
    const acc = _tourMeta().horos_tour_steps;
    if (Array.isArray(acc) && acc.length === N && acc.some(Boolean)) {
      const cur = prevRef.current || _TOUR_STEPS.map(() => false);
      prevRef.current = cur.map((v, k) => v || !!acc[k]);
    }
    const res = await Promise.all(_TOUR_STEPS.map(s => s.check().catch(() => false)));
    return apply(res);
  }, [apply]);

  // Passo confirmado pela própria tela (ex.: WhatsApp conectou) — marca sem esperar o banco
  const markDone = React.useCallback((id) => {
    const k = _TOUR_STEPS.findIndex(s => s.id === id);
    const cur = prevRef.current || _TOUR_STEPS.map(() => false);
    if (k < 0 || cur[k]) return;
    if (!prevRef.current) prevRef.current = cur;
    apply(cur.map((v, j) => j === k ? true : v));
  }, [apply]);

  React.useEffect(() => {
    if (dismissed) return;
    let alive = true, t = null, pend = null, tries = 0, ch = null, sb = null;
    const re = () => { clearTimeout(pend); pend = setTimeout(runChecks, 250); };
    const onEvt = (e) => { const id = e && e.detail && e.detail.step; if (id) markDone(id); re(); };
    const STEP_OF = { 'horos:lead-created': 'lead', 'horos:wa-connected': 'whatsapp', 'horos:goal-saved': 'goal' };
    const onNamed = (e) => { if (STEP_OF[e.type]) markDone(STEP_OF[e.type]); re(); };
    const evs = ['horos:lead-created', 'horos:lead-updated', 'horos:wa-connected', 'horos:goal-saved'];
    const onVis = () => { if (document.visibilityState === 'visible') re(); };
    const onStorage = (e) => { if (!e.key || e.key.startsWith('horos-tour')) re(); };
    evs.forEach(e => window.addEventListener(e, onNamed));
    window.addEventListener('horos:tour-check', onEvt);
    window.addEventListener('storage', onStorage);
    document.addEventListener('visibilitychange', onVis);
    const iv = setInterval(() => { if (document.visibilityState === 'visible' && prevRef.current && !prevRef.current.every(Boolean)) runChecks(); }, 15000);

    const boot = async () => {
      const res = await runChecks();
      if (!alive) return;
      if (!res) { if (tries++ < 50) t = setTimeout(boot, 300); return; }
      // já concluiu ou fechou o guia nesta conta (em qualquer aparelho): não aparece mais
      if (_tourDoneInAccount()) { _tourSet(_TK.dismissed, '1'); setDismissed(true); return; }
      sb = window.HOROS_SB;
      const uid = window.HOROS_SESSION?.user?.id;
      if (sb && uid && sb.channel && !res.every(Boolean)) {
        try {
          ch = sb.channel(`tour-${uid}-${Date.now()}`)
            .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages', filter: `user_id=eq.${uid}` }, (p) => { if (p.new && p.new.direction === 'out') re(); })
            .subscribe();
        } catch { ch = null; }
      }
      if (res.every(Boolean)) { _tourSaveDone(); setDismissed(true); return; }   // já fez tudo: nem aparece
      if (_tourGet(_TK.seen) || _tourMeta().horos_tour_seen) { _tourSet(_TK.seen, '1'); return; }
      _tourSet(_TK.seen, '1');
      _tourSaveMeta({ horos_tour_seen: true });
      // no celular não abre sozinho (cobriria a tela toda) — fica o botão
      if (window.matchMedia && window.matchMedia(_MOBILE_MQ).matches) return;
      t = setTimeout(() => setOpen(true), 1200);
    };
    boot();
    return () => {
      alive = false;
      clearTimeout(t); clearTimeout(pend); clearInterval(iv);
      evs.forEach(e => window.removeEventListener(e, onNamed));
      window.removeEventListener('horos:tour-check', onEvt);
      window.removeEventListener('storage', onStorage);
      document.removeEventListener('visibilitychange', onVis);
      if (ch) try { sb.removeChannel(ch); } catch {}
    };
  }, [dismissed, runChecks, markDone]);

  React.useEffect(() => {
    if (!open) return;
    const onKey = e => { if (e.key === 'Escape' && !document.querySelector('.modal-overlay, [role="dialog"][aria-modal="true"]')) setOpen(false); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [open]);

  // Concluiu o último passo agora: comemora uns segundos e o guia some de vez (antes o botão ficava para sempre)
  const allDoneNow = loaded && checks.every(Boolean);
  React.useEffect(() => {
    if (dismissed || !allDoneNow) return;
    const tm = setTimeout(() => { _tourSaveDone(); setDismissed(true); }, open ? 6000 : 4000);
    return () => clearTimeout(tm);
  }, [dismissed, allDoneNow, open]);

  if (dismissed) return null;
  const total = _TOUR_STEPS.length;
  const doneCount = checks.filter(Boolean).length;
  const allDone = loaded && doneCount === total;
  const nextIdx = checks.findIndex(v => !v);
  const openIdx = expanded != null && !checks[expanded] ? expanded : nextIdx;
  const close = () => { _tourSaveDone(); setDismissed(true); };

  if (!open) {
    return (
      <>
      {justDone.length > 0 && (
        <button className="tour-bubble" onClick={() => setOpen(true)} role="status">
          <span className="tour-bubble-ck"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.4" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6 9 17l-5-5" /></svg></span>
          <span><b>{_TOUR_STEPS.find(s => s.id === justDone[0])?.label}{justDone.length > 1 ? ` +${justDone.length - 1}` : ''}</b><small>{allDone ? 'Todos os passos concluídos!' : `${doneCount} de ${total} concluídos`}</small></span>
        </button>
      )}
      <button className={`tour-fab${allDone ? ' done' : ''}${justDone.length ? ' pop' : ''}`} onClick={() => setOpen(true)}
        title={allDone ? 'Tudo pronto' : `Primeiros passos · ${doneCount} de ${total}`} aria-label="Abrir primeiros passos">
        <TourRing value={doneCount} total={total} />
        <horos-mascot size="30" tone="mono" mood={allDone ? 'happy' : 'normal'} aria-hidden="true" />
        {loaded && !allDone && <span className="tour-fab-badge">{total - doneCount}</span>}
      </button>
      </>
    );
  }

  return (
    <section className={`tour-panel${allDone ? ' is-done' : ''}`} role="dialog" aria-label="Primeiros passos">
      <header className="tour-head">
        <div className="tour-mascot">
          <horos-mascot ref={mascotRef} size="52" mood={allDone ? 'happy' : 'normal'} state={allDone ? 'done' : 'idle'} aria-hidden="true" />
        </div>
        <div className="tour-head-txt">
          <div className="tour-title">{allDone ? 'Tudo pronto!' : 'Primeiros passos'}</div>
          <div className="tour-sub">{allDone ? 'Seu Horos está configurado. Boas vendas!' : `${doneCount} de ${total} concluídos · uns 5 minutos`}</div>
        </div>
        <div className="tour-head-btns">
          <button className="tour-icon-btn" onClick={() => setOpen(false)} title="Minimizar" aria-label="Minimizar">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M5 12h14" /></svg>
          </button>
          <button className="tour-icon-btn" onClick={close} title="Fechar guia" aria-label="Fechar guia">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round"><path d="M18 6 6 18M6 6l12 12" /></svg>
          </button>
        </div>
      </header>

      <div className="tour-progress" aria-hidden="true">
        {_TOUR_STEPS.map((s, i) => <span key={s.id} className={checks[i] ? 'on' : (i === nextIdx ? 'next' : '')} />)}
      </div>

      <ol className="tour-steps">
        {_TOUR_STEPS.map((s, i) => {
          const ok = checks[i], isOpen = !ok && i === openIdx;
          return (
            <li key={s.id} className={`tour-step${ok ? ' ok' : ''}${isOpen ? ' open' : ''}${justDone.includes(s.id) ? ' pop' : ''}`} style={{ '--i': i }}>
              <button className="tour-step-row" onClick={() => !ok && setExpanded(i)} aria-expanded={isOpen} disabled={ok}>
                <span className="tour-check">
                  {ok
                    ? <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6 9 17l-5-5" /></svg>
                    : <span>{i + 1}</span>}
                </span>
                <span className="tour-step-ic"><Icon name={s.icon} size={16} /></span>
                <span className="tour-step-label">{s.label}</span>
                {ok && <span className="tour-step-done">Feito</span>}
              </button>
              {isOpen && (
                <div className="tour-step-body">
                  <p>{s.desc}</p>
                  <button className="tour-cta" onClick={() => { s.run(); if (window.innerWidth < 760) setOpen(false); }}>
                    {s.actionLabel}
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M5 12h14M13 6l6 6-6 6" /></svg>
                  </button>
                </div>
              )}
            </li>
          );
        })}
      </ol>

      <footer className="tour-foot">
        {allDone
          ? <button className="tour-cta wide" onClick={close}>Concluir</button>
          : <button className="tour-link" onClick={close}>Não mostrar mais</button>}
      </footer>
    </section>
  );
}

// ── Topbar ─────────────────────────────────────────────────────────────────────
const _PAGE_NAMES = {
  'dashboard': 'Dashboard',
  'crm': 'CRM',
  'clients': 'Clientes',
  'messages': 'Mensagens',
  'ai': 'IA',
  'automations': 'Automações',
  'calendar': 'Agenda',
  'reports': 'Relatórios',
  'analytics': 'Analytics',
  'integrations': 'Integrações',
  'team': 'Equipe',
  'profile': 'Configurações',
  'client': 'Editar Lead',
  'support': 'Ajuda e suporte',
  'leads': 'Leads recebidos',
  'distribution': 'Distribuição de leads',
  'properties': 'Imóveis',
  'finance': 'Financeiro',
  'social': 'Redes sociais',
  'broadcast': 'Disparos em massa',
};

function Topbar({ theme, onThemeChange }) {
  const pageName = _PAGE_NAMES[decodeURIComponent(window.location.pathname).split('/').pop().replace('.html','').toLowerCase()] || '';
  const [notifOpen,       setNotifOpen]       = React.useState(false);
  const [aiOpen,          setAiOpen]          = React.useState(false);
  const [aiPrompt,        setAiPrompt]        = React.useState('');
  const [notifCount,      setNotifCount]      = React.useState(() => (window.HorosNotif && window.HorosNotif.notifUnread) || 0);
  const [toasts,          setToasts]          = React.useState([]);
  const [sidebarCollapsed, setSidebarCollapsed] = React.useState(() => localStorage.getItem('horos-sidebar-collapsed') === '1');
  const notifRef = React.useRef(null);
  const dist = window.useHorosDist ? window.useHorosDist() : false;
  // Tema: sem botão no topo — muda em Configurações ou pelo sistema operacional (horos-ui.js)
  const themeCbRef = React.useRef(onThemeChange);
  themeCbRef.current = onThemeChange;
  React.useEffect(() => {
    const on = (e) => { if (e.detail && e.detail !== theme && themeCbRef.current) themeCbRef.current(e.detail); };
    window.addEventListener('horos:theme-change', on);
    return () => window.removeEventListener('horos:theme-change', on);
  }, [theme]);

  React.useEffect(() => {
    const onCount = e => setNotifCount(e.detail || 0);
    window.addEventListener('horos:notif-count', onCount);
    return () => window.removeEventListener('horos:notif-count', onCount);
  }, []);

  React.useEffect(() => {
    const handler = e => {
      setAiOpen(true);
      setNotifOpen(false);
      if (e.detail?.prompt) setAiPrompt(e.detail.prompt);
    };
    window.addEventListener('horos:open-ai', handler);
    return () => window.removeEventListener('horos:open-ai', handler);
  }, []);

  React.useEffect(() => {
    const onToast = e => {
      const t = { ...e.detail, _tid: e.detail.id || Date.now() + Math.random() };
      setToasts(prev => [...prev.slice(-3), t]);   // o próprio aviso some sozinho (GlobalToast)
    };
    window.addEventListener('horos:toast', onToast);
    return () => window.removeEventListener('horos:toast', onToast);
  }, []);

  React.useEffect(() => {
    if (!notifOpen) return;
    const handler = e => { if (notifRef.current && !notifRef.current.contains(e.target)) setNotifOpen(false); };
    document.addEventListener('mousedown', handler);
    return () => document.removeEventListener('mousedown', handler);
  }, [notifOpen]);

  React.useEffect(() => {
    const handler = e => setSidebarCollapsed(e.detail);
    window.addEventListener('horos:sidebar-changed', handler);
    return () => window.removeEventListener('horos:sidebar-changed', handler);
  }, []);

  return (
    <>
      <div className="hn-toast-stack">
        {toasts.map(t => (
          <GlobalToast key={t._tid} notif={t}
                       onClose={() => setToasts(prev => prev.filter(x => x._tid !== t._tid))} />
        ))}
      </div>

      {aiOpen && <AIModal onClose={() => { setAiOpen(false); setAiPrompt(''); }} initialPrompt={aiPrompt} />}
      {!dist && <TourWidget />}
      <WeeklyFeedback />

      <button
        className="sidebar-toggle"
        onClick={() => window.dispatchEvent(new CustomEvent('horos:sidebar-toggle'))}
        title={sidebarCollapsed ? 'Expandir menu' : 'Recolher menu'}>
        <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
          {sidebarCollapsed ? <path d="m9 18 6-6-6-6"/> : <path d="m15 18-6-6 6-6"/>}
        </svg>
      </button>

      <div className="topbar">
        <button type="button" className="topbar-burger" aria-label="Abrir menu"
                onClick={() => window.dispatchEvent(new CustomEvent('horos:mnav', { detail: true }))}>
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round"><path d="M4 7h16M4 12h16M4 17h16"/></svg>
        </button>
        {pageName === 'Mensagens' && <span className="topbar-title">{pageName}</span>}
        {pageName && pageName !== 'Mensagens' && <span className="topbar-title topbar-title-m">{pageName}</span>}
        <div className="topbar-actions">
          <div ref={notifRef} style={{ position: 'relative' }}>
            <button className="icon-btn" title="Notificações"
                    onClick={() => setNotifOpen(o => !o)}
                    style={{ position: 'relative' }}>
              <Icon name="bell" />
              {notifCount > 0 && (
                <span className="hn-bell-badge">{notifCount > 99 ? '99+' : notifCount}</span>
              )}
            </button>
            {notifOpen && <NotificationCenter onClose={() => setNotifOpen(false)} />}
          </div>

          {!dist && (
          <button className={`icon-btn ai hx-mascot-btn${aiOpen ? ' active' : ''}`} title="Horos AI"
                  onClick={() => { setAiOpen(o => !o); setNotifOpen(false); }}>
            <horos-mascot size="22" track="off" mood={aiOpen ? 'happy' : 'normal'} aria-hidden="true" />
          </button>
          )}
          <div style={{ width: 6 }} />
          <button className="btn-primary" onClick={() => {
            // Plano Distribuição: o modal de Novo Lead só existe na tela Leads recebidos
            if (dist && !window.NewLeadModal) { window.location.href = 'leads.html?novo=1'; return; }
            window.dispatchEvent(new CustomEvent('horos:new-lead'));
          }}>
            <Icon name="plus" size={14} stroke={2} />
            <span className="topbar-newlead-lbl">Novo Lead</span>
          </button>
        </div>
      </div>
    </>
  );
}

window.Sidebar = Sidebar;
window.Topbar = Topbar;
