// Pulse — shared layout: Sidebar, Topbar, MobileNav, ThemeToggle, LanguageToggle
const { useState, useEffect, useRef } = React;

// ===== i18n hook — re-render on language change =====
const useT = () => {
  const [, force] = React.useReducer(x => x + 1, 0);
  useEffect(() => {
    const h = () => force();
    window.addEventListener('pulse-lang-change', h);
    window.addEventListener('pulse-data-change', h);
    return () => {
      window.removeEventListener('pulse-lang-change', h);
      window.removeEventListener('pulse-data-change', h);
    };
  }, []);
  return window.PulseI18n ? window.PulseI18n.t : ((k) => k);
};
const useLang = () => {
  const [lang, setLang] = useState(window.PulseI18n ? window.PulseI18n.getLang() : 'vi');
  useEffect(() => {
    const h = () => setLang(window.PulseI18n.getLang());
    window.addEventListener('pulse-lang-change', h);
    return () => window.removeEventListener('pulse-lang-change', h);
  }, []);
  return lang;
};

// ===== Viewport hook for responsive layout =====
const useIsMobile = (bp = 900) => {
  const [isMobile, setIsMobile] = useState(typeof window !== 'undefined' ? window.innerWidth < bp : false);
  useEffect(() => {
    const onR = () => setIsMobile(window.innerWidth < bp);
    window.addEventListener('resize', onR);
    return () => window.removeEventListener('resize', onR);
  }, [bp]);
  return isMobile;
};

const getQueryParam = (key, defaultValue = '') => {
  try {
    const params = new URLSearchParams(window.location.search);
    return params.get(key) || defaultValue;
  } catch (e) { return defaultValue; }
};
const withQuery = (href, params = {}) => {
  let runtime = {};
  try {
    const current = new URLSearchParams(window.location.search);
    if (current.get('api') && params.api === undefined) runtime.api = current.get('api');
  } catch (e) {}
  const qs = Object.entries({ ...params, ...runtime })
    .filter(([, v]) => v !== undefined && v !== null && v !== '')
    .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
    .join('&');
  return qs ? `${href}?${qs}` : href;
};

// ===== Navigation config =====
const NAV = [
  { key: 'overview',    labelKey: 'nav.overview',    icon: 'grid',      href: 'index.html' },
  { key: 'campaigns',   labelKey: 'nav.campaigns',   icon: 'megaphone', href: 'Campaign List.html' },
  { key: 'influencers', labelKey: 'nav.influencers', icon: 'users',     href: 'Influencer Detail.html' },
  { key: 'alerts',      labelKey: 'nav.alerts',      icon: 'bell',      href: 'Alerts.html' },
  { key: 'compare',     labelKey: 'nav.compare',     icon: 'users-group', href: 'Compare.html' },
];
const NAV_BOTTOM = [
  { key: 'admin',    labelKey: 'nav.admin',    icon: 'settings',    href: 'Admin.html' },
  { key: 'help',     labelKey: 'nav.help',     icon: 'help-circle', href: 'Guide.html' },
];

// ===== Icons (Feather-style) =====
const Icon = ({ name, size = 16, stroke = 1.75, style }) => {
  const common = { width: size, height: size, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: stroke, strokeLinecap: 'round', strokeLinejoin: 'round', style };
  const paths = {
    grid:         <><rect x="3" y="3" width="7" height="7" rx="1.5"/><rect x="14" y="3" width="7" height="7" rx="1.5"/><rect x="3" y="14" width="7" height="7" rx="1.5"/><rect x="14" y="14" width="7" height="7" rx="1.5"/></>,
    megaphone:    <><path d="M3 11v2a1 1 0 0 0 1 1h2l5 4V6L6 10H4a1 1 0 0 0-1 1Z"/><path d="M15 8a5 5 0 0 1 0 8"/><path d="M18 5a9 9 0 0 1 0 14"/></>,
    users:        <><circle cx="9" cy="8" r="3.5"/><path d="M3 20c0-3.3 2.7-6 6-6s6 2.7 6 6"/><circle cx="17" cy="9" r="2.5"/><path d="M15 20c0-2.5 1.8-4.5 4-5"/></>,
    bell:         <><path d="M6 8a6 6 0 1 1 12 0c0 6 3 7 3 7H3s3-1 3-7Z"/><path d="M10 21a2 2 0 0 0 4 0"/></>,
    'file-text':  <><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/><path d="M9 13h6"/><path d="M9 17h4"/></>,
    settings:     <><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 0 1-4 0v-.1a1.7 1.7 0 0 0-1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 0 1 0-4h.1a1.7 1.7 0 0 0 1.5-1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3h0a1.7 1.7 0 0 0 1-1.5V3a2 2 0 0 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8v0a1.7 1.7 0 0 0 1.5 1H21a2 2 0 0 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1Z"/></>,
    'help-circle':<><circle cx="12" cy="12" r="9"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 3-3 3"/><path d="M12 17h.01"/></>,
    search:       <><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></>,
    plus:         <><path d="M12 5v14M5 12h14"/></>,
    'chevron-down': <><path d="m6 9 6 6 6-6"/></>,
    'chevron-right':<><path d="m9 6 6 6-6 6"/></>,
    'chevron-left': <><path d="m15 6-6 6 6 6"/></>,
    'chevron-up':   <><path d="m18 15-6-6-6 6"/></>,
    'arrow-right':<><path d="M5 12h14M13 5l7 7-7 7"/></>,
    'arrow-up':   <><path d="M12 19V5M5 12l7-7 7 7"/></>,
    'arrow-down': <><path d="M12 5v14M5 12l7 7 7-7"/></>,
    'arrow-left': <><path d="M19 12H5M12 19l-7-7 7-7"/></>,
    external:     <><path d="M15 3h6v6"/><path d="M10 14 21 3"/><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></>,
    eye:          <><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12Z"/><circle cx="12" cy="12" r="3"/></>,
    'eye-off':    <><path d="M10 3.3a9.8 9.8 0 0 1 2 .2c6.5 0 10 7 10 7a13 13 0 0 1-2 2.7M6.6 6.6A13.6 13.6 0 0 0 2 11.5S5.5 19 12 19a9.8 9.8 0 0 0 5.4-1.6"/><path d="m1 1 22 22"/><path d="M14.1 14.1a3 3 0 0 1-4.2-4.2"/></>,
    heart:        <><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.7l-1-1.1a5.5 5.5 0 0 0-7.8 7.8l8.8 8.8 8.8-8.8a5.5 5.5 0 0 0 0-7.8Z"/></>,
    message:      <><path d="M21 11.5a8.4 8.4 0 0 1-9 8.3 8.5 8.5 0 0 1-3.8-.8L3 21l2-5.2A8.4 8.4 0 1 1 21 11.5Z"/></>,
    share:        <><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 13.5 6.8 4M15.4 6.5l-6.8 4"/></>,
    'trending-up':  <><path d="M22 7 13.5 15.5l-5-5L2 17"/><path d="M16 7h6v6"/></>,
    'trending-down':<><path d="M22 17 13.5 8.5l-5 5L2 7"/><path d="M16 17h6v-6"/></>,
    alert:        <><path d="M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z"/><path d="M12 9v4M12 17h.01"/></>,
    check:        <><path d="m5 12 5 5 9-11"/></>,
    download:     <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M7 10l5 5 5-5"/><path d="M12 15V3"/></>,
    filter:       <><path d="M22 3H2l8 9.5V19l4 2v-8.5Z"/></>,
    calendar:     <><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></>,
    target:       <><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1.5"/></>,
    zap:          <><path d="M13 2 3 14h9l-1 8 10-12h-9l1-8Z"/></>,
    more:         <><circle cx="5" cy="12" r="1.5"/><circle cx="12" cy="12" r="1.5"/><circle cx="19" cy="12" r="1.5"/></>,
    link:         <><path d="M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 1 0-7-7l-1.8 1.7"/><path d="M14 11a5 5 0 0 0-7.5-.5l-3 3a5 5 0 0 0 7 7l1.8-1.7"/></>,
    x:            <><path d="M18 6 6 18M6 6l12 12"/></>,
    sun:          <><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></>,
    moon:         <><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8Z"/></>,
    asterisk:     <><path d="M12 2v20M4.2 6 19.8 18M4.2 18 19.8 6"/></>,
    copy:         <><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></>,
    'users-group':<><circle cx="9" cy="8" r="3"/><path d="M3 20c0-3.3 2.7-6 6-6s6 2.7 6 6"/><circle cx="17" cy="8" r="3"/><path d="M13 20c0-3.3 2.7-6 6-6"/></>,
    sparkle:      <><path d="M12 3v3M12 18v3M3 12h3M18 12h3M5.6 5.6l2.1 2.1M16.3 16.3l2.1 2.1M5.6 18.4l2.1-2.1M16.3 7.7l2.1-2.1"/></>,
    globe:        <><circle cx="12" cy="12" r="9"/><path d="M3 12h18"/><path d="M12 3a13 13 0 0 1 0 18 13 13 0 0 1 0-18Z"/></>,
    menu:         <><path d="M3 6h18M3 12h18M3 18h18"/></>,
    edit:         <><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 1 1 3 3L7 19l-4 1 1-4Z"/></>,
    trash:        <><path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M10 11v6M14 11v6"/></>,
    save:         <><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2Z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></>,
    database:     <><ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v6c0 1.7 4 3 9 3s9-1.3 9-3V5"/><path d="M3 11v6c0 1.7 4 3 9 3s9-1.3 9-3v-6"/></>,
    upload:       <><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="M17 8l-5-5-5 5"/><path d="M12 3v12"/></>,
    lock:         <><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></>,
    refresh:      <><path d="M1 4v6h6"/><path d="M23 20v-6h-6"/><path d="M20.5 9a9 9 0 0 0-15.6-3L1 10"/><path d="M3.5 15a9 9 0 0 0 15.6 3L23 14"/></>,
    shield:       <><path d="M12 2 4 5v6c0 5 3.5 9.5 8 11 4.5-1.5 8-6 8-11V5l-8-3Z"/></>,
  };
  return <svg {...common}>{paths[name] || paths.grid}</svg>;
};

// ===== Theme toggle =====
const ThemeToggle = ({ compact }) => {
  const [theme, setTheme] = useState(typeof window !== 'undefined' ? (window.getTheme ? window.getTheme() : 'light') : 'light');
  useEffect(() => {
    const onChange = (e) => setTheme(e.detail.mode);
    window.addEventListener('pulse-theme-change', onChange);
    return () => window.removeEventListener('pulse-theme-change', onChange);
  }, []);
  return (
    <button className="btn ghost" style={{ padding: 8 }} onClick={() => window.toggleTheme && window.toggleTheme()} title={theme === 'dark' ? 'Light' : 'Dark'} aria-label="Theme toggle">
      <Icon name={theme === 'dark' ? 'sun' : 'moon'} size={16}/>
    </button>
  );
};

// ===== Language toggle =====
const LanguageToggle = () => {
  const lang = useLang();
  const toggle = () => window.PulseI18n && window.PulseI18n.toggleLang();
  return (
    <button
      className="btn ghost"
      onClick={toggle}
      title="Switch language"
      aria-label="Language"
      style={{
        padding: '6px 10px',
        fontFamily: 'var(--font-mono)',
        fontSize: 11,
        fontWeight: 700,
        letterSpacing: '0.1em',
        textTransform: 'uppercase',
        border: '1px solid var(--border-strong)',
        display: 'inline-flex', alignItems: 'center', gap: 6,
      }}
    >
      <Icon name="globe" size={13}/>
      <span style={{ color: lang === 'vi' ? 'var(--brand-500)' : 'var(--text-muted)' }}>VN</span>
      <span style={{ color: 'var(--text-muted)' }}>/</span>
      <span style={{ color: lang === 'en' ? 'var(--brand-500)' : 'var(--text-muted)' }}>EN</span>
    </button>
  );
};

const AuthStatus = () => {
  const [auth, setAuth] = useState(window.PulseAuth ? { ...window.PulseAuth.state } : { active: false });
  const [open, setOpen] = useState(false);
  const [email, setEmail] = useState('');
  const [sent, setSent] = useState(false);
  const [error, setError] = useState('');
  const [sending, setSending] = useState(false);
  useEffect(() => {
    if (!window.PulseAuth?.onChange) return undefined;
    return window.PulseAuth.onChange(setAuth);
  }, []);
  if (!auth.active) return null;
  const busy = auth.loading || !auth.ready;
  const label = auth.user?.email ? auth.user.email.split('@')[0] : (busy ? 'AUTH' : 'Sign in');
  const submit = async (event) => {
    event.preventDefault();
    setError('');
    setSending(true);
    try {
      await window.PulseAuth.requestEmailLink(email);
      setSent(true);
    } catch (err) {
      setError(err.message || 'Could not send sign-in link');
    } finally {
      setSending(false);
    }
  };
  const action = () => {
    if (auth.user?.email) window.PulseAuth.signOut().catch(err => alert(err.message));
    else setOpen(true);
  };
  return (
    <>
      <button className="btn ghost" onClick={action} title={auth.user?.email || auth.error || 'Sign in'} style={{ padding: '6px 10px', gap: 6, maxWidth: 150 }}>
        <Icon name={auth.user?.email ? 'shield' : 'lock'} size={13}/>
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: 11, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.06em' }}>{label}</span>
      </button>
      {open && !auth.user?.email && (
        <div role="dialog" aria-modal="true" aria-label="Sign in" style={{
          position: 'fixed',
          inset: 0,
          zIndex: 9999,
          background: 'rgba(17, 24, 39, 0.38)',
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          padding: 20,
        }}>
          <form onSubmit={submit} style={{
            width: 'min(420px, 100%)',
            background: 'var(--surface)',
            color: 'var(--text)',
            border: '1px solid var(--border)',
            borderRadius: 8,
            boxShadow: 'var(--shadow-lg)',
            padding: 20,
          }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, marginBottom: 14 }}>
              <div>
                <div style={{ fontSize: 18, fontWeight: 800 }}>Sign in</div>
                <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 4 }}>Use your email to receive a secure link.</div>
              </div>
              <button type="button" className="btn ghost" onClick={() => setOpen(false)} aria-label="Close" style={{ padding: 8 }}>
                <Icon name="x" size={14}/>
              </button>
            </div>
            <input
              value={email}
              onChange={event => setEmail(event.target.value)}
              type="email"
              placeholder="you@example.com"
              autoComplete="email"
              style={{ width: '100%', height: 40, border: '1px solid var(--border)', borderRadius: 6, background: 'var(--surface-muted)', color: 'var(--text)', padding: '0 10px' }}
            />
            {error && <div style={{ color: 'var(--danger)', fontSize: 12, marginTop: 10 }}>{error}</div>}
            {sent && <div style={{ color: 'var(--success)', fontSize: 12, marginTop: 10 }}>Check your inbox for the sign-in link.</div>}
            <button className="btn primary" type="submit" disabled={sending || !email} style={{ width: '100%', justifyContent: 'center', marginTop: 14 }}>
              {sending ? 'Sending...' : 'Send link'}
            </button>
          </form>
        </div>
      )}
    </>
  );
};

// ===== Sidebar (desktop) =====
const Sidebar = ({ active = 'overview' }) => {
  const t = useT();
  const isMobile = useIsMobile();
  const campaigns = window.PulseStore ? window.PulseStore.getCampaigns() : [];
  const alertsOpen = window.PulseStore ? window.PulseStore.getAlerts().filter(a => a.status === 'open').length : 0;

  if (isMobile) return null; // On mobile, sidebar is replaced by bottom nav

  return (
    <aside style={sbStyles.root}>
      <div style={sbStyles.brand}>
        <div style={sbStyles.logo}>
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" strokeWidth="2.6" strokeLinecap="round">
            <path d="M12 2v20M4.2 6 19.8 18M4.2 18 19.8 6"/>
          </svg>
        </div>
        <div>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 15, letterSpacing: '0.02em', textTransform: 'uppercase' }}>{t('brand.name')}</div>
          <div style={{ fontSize: 10, color: 'var(--text-muted)', letterSpacing: '0.12em', textTransform: 'uppercase', fontWeight: 600, marginTop: 2 }}>{t('brand.tagline')}</div>
        </div>
      </div>

      <div style={sbStyles.newBtnWrap}>
        <a href={withQuery('Campaign List.html')} className="btn brand" style={{ width: '100%', justifyContent: 'center', padding: '10px 14px', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
          <Icon name="plus" size={14}/> {t('nav.new_campaign')}
        </a>
      </div>

      <nav style={sbStyles.nav}>
        <div style={sbStyles.navLabel}>{t('nav.workspace')}</div>
        {NAV.map(item => {
          const badge = item.key === 'alerts' && alertsOpen > 0 ? alertsOpen : null;
          return (
            <a key={item.key} href={withQuery(item.href)} style={{
              ...sbStyles.navItem,
              ...(active === item.key ? sbStyles.navItemActive : {}),
            }}>
              <Icon name={item.icon} size={16}/>
              <span style={{ flex: 1 }}>{t(item.labelKey)}</span>
              {badge && <span style={sbStyles.badge}>{badge}</span>}
            </a>
          );
        })}

        {campaigns.length > 0 && (
          <>
            <div style={{ ...sbStyles.navLabel, marginTop: 20 }}>{t('nav.recent')}</div>
            {campaigns.slice(0, 3).map(c => (
              <a key={c.id} href={withQuery('Campaign Detail.html', { id: c.id })} style={sbStyles.campaignItem}>
                <span style={{ width: 6, height: 6, borderRadius: '50%', background: c.brandColor, flexShrink: 0 }}/>
                <span style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', fontSize: 13 }}>{c.name}</span>
                {c.status === 'live' && <span style={sbStyles.live}>● LIVE</span>}
              </a>
            ))}
          </>
        )}
      </nav>

      <div style={sbStyles.bottom}>
        {NAV_BOTTOM.map(item => (
          <a key={item.key} href={withQuery(item.href)} style={{
            ...sbStyles.navItem,
            ...(active === item.key ? sbStyles.navItemActive : {}),
          }}>
            <Icon name={item.icon} size={16}/>
            <span style={{ flex: 1 }}>{t(item.labelKey)}</span>
          </a>
        ))}
        <div style={sbStyles.userRow}>
          <img src="https://i.pravatar.cc/64?img=68" style={{ width: 28, height: 28, borderRadius: 8 }} alt=""/>
          <div style={{ flex: 1, overflow: 'hidden' }}>
            <div style={{ fontSize: 13, fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>Maya Trần</div>
            <div style={{ fontSize: 11, color: 'var(--ink-500)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>maya@1990.agency</div>
          </div>
          <Icon name="chevron-down" size={14} style={{ color: 'var(--ink-400)' }}/>
        </div>
      </div>
    </aside>
  );
};

// ===== Topbar =====
const Topbar = ({ crumbs = [], right }) => {
  const t = useT();
  const isMobile = useIsMobile();
  const [query, setQuery] = useState('');
  const [searchOpen, setSearchOpen] = useState(false);
  const searchRef = useRef(null);
  const inputRef = useRef(null);
  const searchResults = React.useMemo(() => {
    if (!window.PulseStore) return [];
    const q = query.trim().toLowerCase();
    const campaigns = window.PulseStore.getCampaigns().map(c => ({
      type: 'campaign',
      title: c.name,
      meta: `${c.brand || ''} · ${c.status || ''}`,
      href: withQuery('Campaign Detail.html', { id: c.id }),
      haystack: `${c.name} ${c.brand || ''} ${c.status || ''}`.toLowerCase(),
    }));
    const bookings = window.PulseStore.getBookings();
    const influencers = window.PulseStore.getInfluencers().map(i => {
      const bk = bookings.find(b => b.influencerId === i.id);
      return {
        type: 'influencer',
        title: i.name,
        meta: `${i.handle || ''} · ${i.platform || ''}`,
        href: withQuery('Influencer Detail.html', { id: i.id, campaignId: bk?.campaignId || '' }),
        haystack: `${i.name} ${i.handle || ''} ${i.platform || ''}`.toLowerCase(),
      };
    });
    const all = [...campaigns, ...influencers];
    return (q ? all.filter(x => x.haystack.includes(q)) : all.slice(0, 6)).slice(0, 8);
  }, [query]);

  useEffect(() => {
    const onKey = (e) => {
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        setSearchOpen(true);
        setTimeout(() => inputRef.current?.focus(), 0);
      }
      if (e.key === 'Escape') setSearchOpen(false);
    };
    const onClick = (e) => {
      if (searchRef.current && !searchRef.current.contains(e.target)) setSearchOpen(false);
    };
    window.addEventListener('keydown', onKey);
    window.addEventListener('mousedown', onClick);
    return () => {
      window.removeEventListener('keydown', onKey);
      window.removeEventListener('mousedown', onClick);
    };
  }, []);

  const goToSearchResult = (result) => {
    if (!result) return;
    window.location.href = result.href;
  };

  return (
    <header style={{ ...tbStyles.root, padding: isMobile ? '0 14px' : '0 24px', gap: isMobile ? 10 : 20 }}>
      {isMobile && (
        <div style={tbStyles.mobileBrand}>
          <div style={sbStyles.logo}>
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#FFFFFF" strokeWidth="2.6" strokeLinecap="round">
              <path d="M12 2v20M4.2 6 19.8 18M4.2 18 19.8 6"/>
            </svg>
          </div>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 13, letterSpacing: '0.02em', textTransform: 'uppercase' }}>PULSE</div>
        </div>
      )}
      {!isMobile && (
        <div style={tbStyles.crumbs}>
          {crumbs.map((c, i) => (
            <React.Fragment key={i}>
              {i > 0 && <Icon name="chevron-right" size={13} style={{ color: 'var(--text-muted)' }}/>}
              <a style={{
                color: i === crumbs.length - 1 ? 'var(--text-primary)' : 'var(--text-muted)',
                fontWeight: i === crumbs.length - 1 ? 700 : 500,
                fontSize: 11,
                textTransform: 'uppercase',
                letterSpacing: '0.08em',
              }} href={c.href || '#'}>{c.label}</a>
            </React.Fragment>
          ))}
        </div>
      )}
      {!isMobile && (
        <div style={{ ...tbStyles.search, borderColor: searchOpen ? 'var(--brand-500)' : 'transparent' }} ref={searchRef}>
          <Icon name="search" size={14} style={{ color: 'var(--text-muted)' }}/>
          <input
            ref={inputRef}
            placeholder={t('action.search')}
            style={tbStyles.searchInput}
            value={query}
            onFocus={() => setSearchOpen(true)}
            onChange={e => { setQuery(e.target.value); setSearchOpen(true); }}
            onKeyDown={e => {
              if (e.key === 'Enter') goToSearchResult(searchResults[0]);
            }}
          />
          <span style={tbStyles.kbd}>⌘ K</span>
          {searchOpen && (
            <div style={tbStyles.searchMenu}>
              {searchResults.length ? searchResults.map((r, i) => (
                <a key={`${r.type}-${r.href}`} href={r.href} style={tbStyles.searchItem}>
                  <div style={{ width: 26, height: 26, borderRadius: 6, background: i === 0 ? 'var(--brand-500)' : 'var(--ink-100)', color: i === 0 ? '#FFFFFF' : 'var(--text-secondary)', display: 'grid', placeItems: 'center', flexShrink: 0 }}>
                    <Icon name={r.type === 'campaign' ? 'megaphone' : 'users'} size={13}/>
                  </div>
                  <div style={{ minWidth: 0, flex: 1 }}>
                    <div style={{ fontSize: 12, fontWeight: 800, color: 'var(--text-primary)', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{r.title}</div>
                    <div style={{ fontSize: 10, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.08em', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{t(r.type === 'campaign' ? 'search.campaign' : 'search.influencer')} · {r.meta}</div>
                  </div>
                  <Icon name="arrow-right" size={12} style={{ color: 'var(--text-muted)', flexShrink: 0 }}/>
                </a>
              )) : (
                <div style={tbStyles.searchEmpty}>{query ? t('search.empty') : t('search.hint')}</div>
              )}
            </div>
          )}
        </div>
      )}
      <div style={tbStyles.right}>
        {right}
        <AuthStatus/>
        <LanguageToggle/>
        <ThemeToggle/>
        {!isMobile && (
          <a href={withQuery('Alerts.html')} className="btn ghost" style={{ padding: 8 }} aria-label="Alerts">
            <Icon name="bell" size={16}/>
          </a>
        )}
      </div>
    </header>
  );
};

// ===== Mobile bottom nav =====
const MobileNav = ({ active }) => {
  const t = useT();
  const isMobile = useIsMobile();
  const [moreOpen, setMoreOpen] = useState(false);
  const alertsOpen = window.PulseStore ? window.PulseStore.getAlerts().filter(a => a.status === 'open').length : 0;
  if (!isMobile) return null;

  const primary = [
    { key: 'overview',    labelKey: 'nav.overview',    icon: 'grid',        href: 'index.html' },
    { key: 'campaigns',   labelKey: 'nav.campaigns',   icon: 'megaphone',   href: 'Campaign List.html' },
    { key: 'influencers', labelKey: 'nav.influencers', icon: 'users',       href: 'Influencer Detail.html' },
    { key: 'alerts',      labelKey: 'nav.alerts',      icon: 'bell',        href: 'Alerts.html', badge: alertsOpen },
  ];
  const more = [
    { key: 'compare', labelKey: 'nav.compare',  icon: 'users-group', href: 'Compare.html' },
    { key: 'admin',   labelKey: 'nav.admin',    icon: 'settings',    href: 'Admin.html' },
    { key: 'help',    labelKey: 'nav.help',     icon: 'help-circle', href: 'Guide.html' },
  ];

  return (
    <>
      <nav style={mnStyles.bar}>
        {primary.map(i => (
          <a key={i.key} href={withQuery(i.href)} style={{
            ...mnStyles.tab,
            ...(active === i.key ? mnStyles.tabActive : {}),
          }}>
            <div style={{ position: 'relative' }}>
              <Icon name={i.icon} size={20} stroke={active === i.key ? 2.2 : 1.8}/>
              {i.badge ? <span style={mnStyles.tabBadge}>{i.badge}</span> : null}
            </div>
            <span style={mnStyles.tabLabel}>{t(i.labelKey)}</span>
          </a>
        ))}
        <button onClick={() => setMoreOpen(o => !o)} style={{
          ...mnStyles.tab,
          ...(moreOpen ? mnStyles.tabActive : {}),
          background: 'none', border: 'none',
        }}>
          <Icon name="more" size={20} stroke={1.8}/>
          <span style={mnStyles.tabLabel}>{t('nav.more')}</span>
        </button>
      </nav>
      {moreOpen && (
        <div style={mnStyles.sheetBackdrop} onClick={() => setMoreOpen(false)}>
          <div style={mnStyles.sheet} onClick={e => e.stopPropagation()}>
            <div style={mnStyles.sheetHandle}/>
            <div style={mnStyles.sheetTitle}>{t('nav.more')}</div>
            {more.map(i => (
              <a key={i.key} href={withQuery(i.href)} style={mnStyles.sheetItem}>
                <Icon name={i.icon} size={18}/>
                <span>{t(i.labelKey)}</span>
                <div style={{ flex: 1 }}/>
                <Icon name="chevron-right" size={14} style={{ color: 'var(--text-muted)' }}/>
              </a>
            ))}
            <div style={{ display: 'flex', gap: 8, padding: '14px 8px 4px', flexWrap: 'wrap' }}>
              <LanguageToggle/>
              <ThemeToggle/>
            </div>
          </div>
        </div>
      )}
    </>
  );
};

// ===== Styles =====
const sbStyles = {
  root: { width: 240, minWidth: 240, borderRight: '1px solid var(--border)', background: 'var(--bg-panel)', display: 'flex', flexDirection: 'column', height: '100vh', position: 'sticky', top: 0 },
  brand: { padding: '18px 18px 14px', display: 'flex', gap: 10, alignItems: 'center' },
  logo: { width: 34, height: 34, borderRadius: 6, background: '#EC2024', display: 'grid', placeItems: 'center', flexShrink: 0 },
  newBtnWrap: { padding: '0 14px 14px' },
  nav: { padding: '0 10px', display: 'flex', flexDirection: 'column', gap: 2, flex: 1, overflowY: 'auto' },
  navLabel: { fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.12em', color: 'var(--text-muted)', padding: '12px 8px 6px', fontWeight: 700 },
  navItem: { display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', borderRadius: 6, color: 'var(--text-secondary)', fontSize: 12, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.04em' },
  navItemActive: { background: 'var(--ink-100)', color: 'var(--text-primary)', borderLeft: '2px solid var(--brand-500)', paddingLeft: 8 },
  badge: { fontSize: 10, fontWeight: 700, background: 'var(--brand-500)', color: 'white', padding: '2px 6px', borderRadius: 3 },
  campaignItem: { display: 'flex', alignItems: 'center', gap: 10, padding: '6px 10px', borderRadius: 6, color: 'var(--text-secondary)' },
  live: { fontSize: 9, color: 'var(--brand-500)', fontWeight: 800, letterSpacing: '0.08em' },
  bottom: { borderTop: '1px solid var(--border)', padding: 10, display: 'flex', flexDirection: 'column', gap: 2 },
  userRow: { display: 'flex', alignItems: 'center', gap: 10, padding: 8, borderRadius: 10, marginTop: 6 },
};
const tbStyles = {
  root: { height: 56, borderBottom: '1px solid var(--border)', background: 'var(--bg-panel)', display: 'flex', alignItems: 'center', gap: 20, position: 'sticky', top: 0, zIndex: 10 },
  mobileBrand: { display: 'flex', alignItems: 'center', gap: 8, flex: '0 0 auto' },
  crumbs: { display: 'flex', alignItems: 'center', gap: 8, flex: '0 0 auto' },
  search: { flex: 1, display: 'flex', alignItems: 'center', gap: 8, background: 'var(--ink-100)', padding: '6px 10px', borderRadius: 6, maxWidth: 420, border: '1px solid transparent', position: 'relative' },
  searchInput: { border: 'none', background: 'transparent', outline: 'none', flex: 1, fontSize: 13, color: 'var(--text-primary)', fontFamily: 'inherit' },
  kbd: { fontSize: 10, color: 'var(--text-muted)', background: 'var(--bg-panel)', padding: '2px 6px', borderRadius: 3, border: '1px solid var(--border)' },
  searchMenu: { position: 'absolute', left: 0, right: 0, top: 44, zIndex: 80, background: 'var(--bg-panel)', border: '1px solid var(--border)', boxShadow: 'var(--sh-lg)', borderRadius: 8, padding: 6, display: 'grid', gap: 4 },
  searchItem: { display: 'flex', alignItems: 'center', gap: 10, padding: 8, borderRadius: 6, color: 'inherit', minWidth: 0 },
  searchEmpty: { padding: 12, fontSize: 12, color: 'var(--text-muted)', textAlign: 'center' },
  right: { display: 'flex', alignItems: 'center', gap: 8, marginLeft: 'auto' },
};
const mnStyles = {
  bar: { position: 'fixed', bottom: 0, left: 0, right: 0, zIndex: 30, background: 'var(--bg-panel)', borderTop: '1px solid var(--border)', display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', paddingBottom: 'env(safe-area-inset-bottom, 0)' },
  tab: { display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 3, padding: '10px 4px', color: 'var(--text-muted)', fontSize: 9, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.06em' },
  tabActive: { color: 'var(--brand-500)' },
  tabLabel: { fontSize: 9, fontWeight: 700, letterSpacing: '0.06em' },
  tabBadge: { position: 'absolute', top: -4, right: -8, background: 'var(--brand-500)', color: '#FFFFFF', fontSize: 9, fontWeight: 800, padding: '1px 5px', borderRadius: 999, minWidth: 14, textAlign: 'center' },
  sheetBackdrop: { position: 'fixed', inset: 0, zIndex: 40, background: 'rgba(0,0,0,0.45)', display: 'flex', alignItems: 'flex-end' },
  sheet: { width: '100%', background: 'var(--bg-panel)', borderTopLeftRadius: 16, borderTopRightRadius: 16, padding: '12px 16px 24px', borderTop: '3px solid var(--brand-500)' },
  sheetHandle: { width: 36, height: 4, background: 'var(--border-strong)', borderRadius: 99, margin: '0 auto 12px' },
  sheetTitle: { fontFamily: 'var(--font-display)', fontSize: 13, textTransform: 'uppercase', letterSpacing: '0.1em', marginBottom: 8, color: 'var(--text-muted)' },
  sheetItem: { display: 'flex', alignItems: 'center', gap: 14, padding: '14px 8px', fontSize: 14, fontWeight: 600, color: 'var(--text-primary)', textTransform: 'uppercase', letterSpacing: '0.04em', borderBottom: '1px solid var(--border)' },
};

// ===== Shared form primitives (used by campaign-list, campaign-detail, admin) =====
const Field = ({ label, children }) => (
  <label style={{ display: 'block', marginBottom: 12 }}>
    <div style={{ fontSize: 10, fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.1em', color: 'var(--text-muted)', marginBottom: 6 }}>{label}</div>
    {children}
  </label>
);

const modalStyles = {
  backdrop: { position: 'fixed', inset: 0, zIndex: 50, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 16 },
  panel: { width: '100%', maxWidth: 720, maxHeight: '90vh', overflowY: 'auto', background: 'var(--bg-panel)', borderTop: '4px solid var(--brand-500)', boxShadow: 'var(--sh-lg)' },
  header: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '16px 20px', borderBottom: '1px solid var(--border)' },
  title: { fontFamily: 'var(--font-display)', fontSize: 18, textTransform: 'uppercase', letterSpacing: '0.02em' },
  body: { padding: 20 },
  footer: { padding: 16, borderTop: '1px solid var(--border)', display: 'flex', justifyContent: 'flex-end', gap: 8 },
  input: { width: '100%', padding: '9px 12px', fontSize: 13, border: '1px solid var(--border-strong)', background: 'var(--bg)', color: 'var(--text-primary)', fontFamily: 'inherit', borderRadius: 4 },
};

Object.assign(window, { Icon, Sidebar, Topbar, MobileNav, ThemeToggle, LanguageToggle, NAV, NAV_BOTTOM, useT, useLang, useIsMobile, getQueryParam, withQuery, Field, modalStyles });
