// Campaign Detail — hero screen, internal + client view. Fully wired to PulseStore.
const { useState: useStateCD, useMemo: useMemoCD, useEffect: useEffectCD } = React;

const cdMediaSrc = (url) => {
  if (!url) return '';
  if (typeof window === 'undefined' || window.location.protocol === 'file:') return url;
  const apiStatus = window.PulseStore?.getApiStatus ? window.PulseStore.getApiStatus() : { enabled: false, baseUrl: '' };
  const canUseLocalProxy = ['127.0.0.1', 'localhost'].includes(window.location.hostname);
  if (!apiStatus.enabled && !canUseLocalProxy) return url;
  try {
    const parsed = new URL(url, window.location.href);
    if (!['http:', 'https:'].includes(parsed.protocol)) return url;
    const baseUrl = ((apiStatus.enabled && apiStatus.baseUrl) || window.location.origin || '').replace(/\/$/, '');
    return `${baseUrl}/api/media-proxy?url=${encodeURIComponent(parsed.toString())}`;
  } catch (err) {
    return url;
  }
};

const CampaignDetail = ({ readOnly = false, clientMode = false, campaignId = null }) => {
  const t = useT();
  const isMobile = useIsMobile();
  const resolvedCampaignId = campaignId || (window.getQueryParam ? window.getQueryParam('id', '') : '') || 'c1';
  const requestedTab = window.getQueryParam ? window.getQueryParam('tab', '') : '';
  const initialTab = ['overview', 'influencers', 'posts', 'bookings', 'comments'].includes(requestedTab) ? requestedTab : 'overview';
  const [tab, setTab] = useStateCD(clientMode && initialTab === 'bookings' ? 'overview' : initialTab);
  const [filter, setFilter] = useStateCD('all');
  const [editingBooking, setEditingBooking] = useStateCD(null);
  const [showShare, setShowShare] = useStateCD(false);
  const [, force] = React.useReducer(x => x + 1, 0);
  useEffectCD(() => {
    const h = () => force();
    window.addEventListener('pulse-data-change', h);
    window.addEventListener('pulse-lang-change', h);
    return () => { window.removeEventListener('pulse-data-change', h); window.removeEventListener('pulse-lang-change', h); };
  }, []);

  const campaignRecord = window.PulseStore.getCampaign(resolvedCampaignId);
  const c = campaignRecord || {
    id: resolvedCampaignId,
    name: '',
    brand: '',
    brandColor: 'var(--brand-500)',
    status: 'draft',
    startDate: '',
    endDate: '',
    daysLeft: 0,
    influencerCount: 0,
    posts: 0,
    budget: 0,
    actualViews: 0,
    committedViews: 0,
    actualEngagement: 0,
    committedEngagement: 0,
    reach: 0,
    kpiPct: 0,
    timelinePct: 0,
    stalePosts: 0,
    staleAfterHours: 6,
    platforms: [],
  };
  const bookings = window.PulseStore.getBookings(resolvedCampaignId);
  const daily = window.PulseStore.getDaily(resolvedCampaignId);
  const posts = window.PulseStore.getPosts({ campaignId: resolvedCampaignId });

  const normalizedPlatform = (value) => {
    const key = String(value || '').toLowerCase();
    return ['tiktok', 'instagram', 'facebook'].includes(key) ? key : 'tiktok';
  };
  const platformName = (platform) => platform === 'tiktok' ? 'TikTok' : platform === 'instagram' ? 'Instagram' : 'Facebook';
  const platformColor = (platform) => platform === 'tiktok' ? '#EC2024' : platform === 'instagram' ? '#111111' : '#6E6E6E';
  const bookingHasPlatform = (booking, platform) => {
    const rows = booking.posts || posts.filter(p => p.bookingId === booking.id);
    return rows.some(p => normalizedPlatform(p.platform) === platform) || normalizedPlatform(booking.platform) === platform;
  };

  const viewsPct = window.PulseStore.pct(c.actualViews, c.committedViews);
  const engPct   = window.PulseStore.pct(c.actualEngagement, c.committedEngagement);
  const chartDaily = daily.length > 1 ? daily : [
    { label: (c.startDate || 'Start').slice(5).replace('-', '/') || 'Start', views: 0, engagement: 0 },
    { label: (c.endDate || 'Now').slice(5).replace('-', '/') || 'Now', views: c.actualViews || 0, engagement: c.actualEngagement || 0 },
  ];

  // Filter bookings for list
  const filteredBookings = useMemoCD(() => {
    let list = bookings;
    if (filter === 'under') list = list.filter(b => b.kpiPct < 80);
    else if (filter === 'over') list = list.filter(b => b.kpiPct >= 100);
    else if (filter !== 'all') list = list.filter(b => bookingHasPlatform(b, filter));
    return list;
  }, [filter, bookings.length, posts.length, JSON.stringify(bookings.map(b => [b.id, b.platform, b.kpiPct, (b.posts || []).map(p => p.platform).join('|')]))]);

  // Tab count
  const tabs = [
    { key: 'overview',    label: t('cd.tab_overview') },
    { key: 'influencers', label: t('cd.tab_influencers', { n: bookings.length }) },
    { key: 'posts',       label: t('cd.tab_posts', { n: posts.length }) },
    { key: 'bookings',    label: t('bk.title') },
    { key: 'comments',    label: t('cd.tab_comments') },
  ];

  // Client-mode: skip "bookings" tab so internal fees stay hidden.
  const visibleTabs = clientMode ? tabs.filter(x => x.key !== 'bookings') : tabs;

  // Platforms for donut — actuals come from real post metrics first.
  const platformAgg = useMemoCD(() => {
    const map = {};
    const add = (platform, views) => {
      const key = normalizedPlatform(platform);
      if (!map[key]) map[key] = { key, name: platformName(key), views: 0, color: platformColor(key) };
      map[key].views += views || 0;
    };
    const usablePosts = posts.filter(p => p.url && !['missing_url', 'not_found', 'private_or_removed', 'error'].includes(p.pullStatus));
    if (usablePosts.length) usablePosts.forEach(p => add(p.platform, p.views));
    else bookings.forEach(b => add(b.platform, b.actualViews));
    const total = Object.values(map).reduce((s, p) => s + p.views, 0) || 1;
    return Object.values(map).map(p => ({ ...p, share: Math.round(p.views / total * 100) }));
  }, [JSON.stringify(posts.map(p => [p.id, p.platform, p.views, p.pullStatus, p.url])), JSON.stringify(bookings.map(b => [b.platform, b.actualViews]))]);

  if (!campaignRecord) return <div style={{ padding: 40 }}>{t('misc.empty')}</div>;

  return (
    <div style={{ padding: isMobile ? 16 : '24px 32px 64px', maxWidth: 1400, margin: '0 auto' }}>
      {/* Campaign header */}
      <div style={cdStyles.header}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10, flexWrap: 'wrap' }}>
            <span className="chip" style={{ background: c.brandColor, color: 'white', borderColor: c.brandColor }}>
              <span className="dot"/> {c.brand}
            </span>
            {c.status === 'live' && (
              <span className="chip" style={{ background: 'var(--bg-inverse)', color: 'var(--text-inverse)', borderColor: 'var(--bg-inverse)' }}>
                <span style={{ width: 6, height: 6, borderRadius: '50%', background: 'var(--brand-500)', animation: 'pulse 2s infinite' }}/>
                {t('camp.live')}
              </span>
            )}
            <span style={{ fontSize: 12, color: 'var(--ink-500)' }}>{c.startDate} → {c.endDate} · {t('camp.days_left', { n: c.daysLeft })}</span>
            {(c.platforms || []).map(platform => (
              <span key={platform} className="chip" style={{ gap: 6 }}>
                <PlatformBadge platform={platform} size={16}/>
                {platformName(normalizedPlatform(platform))}
              </span>
            ))}
          </div>
          <h1 style={cdStyles.title}>{c.name}</h1>
          <div style={{ display: 'flex', gap: 20, marginTop: 10, fontSize: 13, color: 'var(--ink-500)', flexWrap: 'wrap' }}>
            <span><span style={{ color: 'var(--ink-900)', fontWeight: 600 }}>{c.influencerCount}</span> influencers</span>
            <span><span style={{ color: 'var(--ink-900)', fontWeight: 600 }}>{c.posts}</span> {t('cd.tab_posts', { n: c.posts }).split('·')[0].trim().toLowerCase()}</span>
            {!clientMode && <span><span style={{ color: 'var(--ink-900)', fontWeight: 600 }}>${c.budget.toLocaleString()}</span> {t('camp.budget')}</span>}
          </div>
          <TimelineBar pct={c.timelinePct} kpiPct={c.kpiPct}/>
        </div>
        {!clientMode && !isMobile && (
          <div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
            <button className="btn" onClick={() => setShowShare(true)}><Icon name="share" size={14}/> {t('action.share_client')}</button>
            <button className="btn" onClick={() => window.print()}><Icon name="download" size={14}/> {t('action.export_pdf')}</button>
            {!readOnly && <a className="btn primary" href={window.withQuery ? window.withQuery('Admin.html') : 'Admin.html'}><Icon name="settings" size={14}/> {t('action.manage')}</a>}
          </div>
        )}
        {clientMode && (
          <div style={{ display: 'flex', gap: 8, flexShrink: 0 }}>
            <button className="btn" onClick={() => window.print()}><Icon name="download" size={14}/> {t('action.download_report')}</button>
          </div>
        )}
      </div>

      {c.stalePosts > 0 && !clientMode && (
        <div className="panel" style={{ padding: 14, marginBottom: 18, borderLeft: '3px solid var(--brand-500)', display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }}>
          <Icon name="refresh" size={16} style={{ color: 'var(--brand-500)' }}/>
          <div style={{ flex: 1, fontSize: 13, color: 'var(--text-secondary)' }}>
            {t('sync.stale_warning', { n: c.stalePosts, h: c.staleAfterHours })}
          </div>
          <a className="btn ghost" href={window.withQuery ? window.withQuery('Admin.html') : 'Admin.html'} style={{ fontSize: 12 }}>{t('admin.ingestion_title')}</a>
        </div>
      )}

      {/* KPI summary strip — v1 uses public post/video metrics only. */}
      <div style={{ ...cdStyles.kpiGrid, gridTemplateColumns: `repeat(auto-fit, minmax(${isMobile ? '140px' : '180px'}, 1fr))` }}>
        <KpiCard icon="eye"        label={t('cd.kpi_views')}      value={window.PulseStore.fmt(c.actualViews)} sub={t('cd.of_committed', { pct: viewsPct, total: window.PulseStore.fmt(c.committedViews) })} pct={viewsPct} spark={chartDaily.map(d => d.views)} tint="brand"/>
        <KpiCard icon="heart"      label={t('cd.kpi_eng')}        value={window.PulseStore.fmt(c.actualEngagement)} sub={t('cd.of_committed', { pct: engPct, total: window.PulseStore.fmt(c.committedEngagement) })} pct={engPct} spark={chartDaily.map(d => d.engagement)} tint="ink"/>
        <KpiCard icon="users-group" label={t('cd.kpi_reach')}      value={window.PulseStore.fmt(c.reach)} sub={t('cd.unique_reach')} delta="+12.4%" tint="ink"/>
        <KpiCard icon="target"     label={t('cd.kpi_completion')} value={`${c.kpiPct}%`} sub={t('cd.forecast', { n: Math.min(130, c.kpiPct + 15) })} pct={c.kpiPct} delta="+4.2%" tint="ink"/>
      </div>

      {/* Tabs */}
      <div style={{ ...cdStyles.tabs, overflowX: isMobile ? 'visible' : 'auto', flexWrap: isMobile ? 'wrap' : 'nowrap', maxWidth: '100%' }}>
        {visibleTabs.map(tb => (
          <button key={tb.key} onClick={() => setTab(tb.key)} style={{
            ...cdStyles.tab,
            ...(isMobile ? { flex: '1 1 calc(50% - 4px)', textAlign: 'center', padding: '10px 8px' } : {}),
            ...(tab === tb.key ? cdStyles.tabActive : {}),
          }}>{tb.label}</button>
        ))}
        {!isMobile && (
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
            <button className="btn" style={{ fontSize: 12 }}><Icon name="calendar" size={12}/> {t('cd.last_30')}</button>
            <button className="btn" style={{ fontSize: 12 }}><Icon name="filter" size={12}/> {t('cd.filters')}</button>
          </div>
        )}
      </div>

      {tab === 'overview' && (
        <>
          <div style={cdStyles.mainGrid}>
            <div className="panel" style={cdStyles.chartCard}>
              <div style={cdStyles.cardHeader}>
                <div>
                  <div style={cdStyles.cardTitle}>{t('cd.daily_growth')}</div>
                  <div style={cdStyles.cardSub}>{t('cd.daily_growth_sub')}</div>
                </div>
              </div>
              <AreaChart data={chartDaily} height={260}/>
            </div>

            <div className="panel" style={cdStyles.donutCard}>
              <div style={cdStyles.cardHeader}>
                <div>
                  <div style={cdStyles.cardTitle}>{t('cd.platform_split')}</div>
                  <div style={cdStyles.cardSub}>{t('cd.platform_split_sub')}</div>
                </div>
              </div>
              <div style={{ display: 'grid', placeItems: 'center', padding: '8px 0 16px' }}>
                <DonutChart
                  data={platformAgg.map(p => ({ value: p.views, color: p.color }))}
                  size={isMobile ? 160 : 180} thickness={26}
                  centerValue={window.PulseStore.fmt(platformAgg.reduce((s, p) => s + p.views, 0))}
                  centerLabel={t('camp.views')}
                />
              </div>
              <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
                {platformAgg.map(p => (
                  <div key={p.key} style={cdStyles.legend}>
                    <PlatformBadge platform={p.key} size={20}/>
                    <span style={{ flex: 1, fontSize: 13, color: 'var(--ink-700)' }}>{p.name}</span>
                    <span style={{ fontSize: 12, color: 'var(--ink-500)' }}>{window.PulseStore.fmt(p.views)}</span>
                    <span style={{ fontSize: 12, fontWeight: 700, minWidth: 32, textAlign: 'right' }}>{p.share}%</span>
                  </div>
                ))}
              </div>
            </div>
          </div>

          {/* KPI Committed vs Actual */}
          <div className="panel" style={{ marginTop: 16, padding: 0 }}>
            <div style={{ ...cdStyles.cardHeader, padding: '16px 20px', borderBottom: '1px solid var(--border)' }}>
              <div>
                <div style={cdStyles.cardTitle}>{t('cd.kpi_vs_actual')}</div>
                <div style={cdStyles.cardSub}>{t('cd.per_inf')}</div>
              </div>
              <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>
                {['all','under','over','tiktok','instagram','facebook'].map(f => (
                  <button key={f} onClick={() => setFilter(f)} style={{
                    ...cdStyles.filterPill,
                    ...(filter === f ? cdStyles.filterPillActive : {}),
                  }}>{f === 'all' ? t('cd.filter_all') : f === 'under' ? t('cd.filter_under') : f === 'over' ? t('cd.filter_over') : f[0].toUpperCase()+f.slice(1)}</button>
                ))}
              </div>
            </div>
            <div style={{ maxHeight: 500, overflowY: 'auto' }}>
              {filteredBookings.length ? filteredBookings.map(b => (
                <BookingRow key={b.id} bk={b} clientMode={clientMode}/>
              )) : (
                <div style={{ padding: '28px 20px', textAlign: 'center', color: 'var(--text-muted)', fontSize: 13 }}>
                  {t('cd.filter_empty')}
                </div>
              )}
            </div>
          </div>

          {!clientMode && (
            <div style={{ marginTop: 16 }}>
              <AlertsPanel campaignId={resolvedCampaignId}/>
            </div>
          )}
        </>
      )}

      {tab === 'influencers' && (
        <div style={{ display: 'grid', gridTemplateColumns: `repeat(auto-fill, minmax(${isMobile ? '240px' : '280px'}, 1fr))`, gap: 14, marginTop: 4 }}>
          {bookings.map(b => <InfluencerCard key={b.id} bk={b}/>)}
        </div>
      )}

      {tab === 'posts' && (
        <PostsTab posts={posts} bookings={bookings}/>
      )}

      {tab === 'bookings' && !clientMode && (
        <BookingsTab campaign={c} bookings={bookings} onEdit={setEditingBooking}/>
      )}

      {tab === 'comments' && (
        <div className="panel" style={{ padding: '48px 24px', textAlign: 'center', color: 'var(--ink-500)' }}>
          <Icon name="sparkle" size={24} style={{ color: 'var(--ink-400)', margin: '0 auto 8px', display: 'block' }}/>
          <div style={{ fontWeight: 600, color: 'var(--ink-700)', marginBottom: 4, textTransform: 'uppercase', letterSpacing: '0.04em' }}>{t('misc.soon')}</div>
        </div>
      )}

      {editingBooking && <EditBookingModal booking={editingBooking} campaign={c} onClose={() => setEditingBooking(null)}/>}
      {showShare && <ShareCampaignModal campaign={c} onClose={() => setShowShare(false)}/>}
    </div>
  );
};

// ===== Timeline bar — visualize alert milestones =====
const TimelineBar = ({ pct, kpiPct }) => {
  const t = useT();
  const isMobile = useIsMobile();
  const milestones = [
    { m: 50, label: '50%' },
    { m: 80, label: '80%' },
    { m: 95, label: '95%' },
  ];
  const state = isMobile ? `${pct}%` : `${pct}% ${t('cd.delivered', { n: '' }).split('%')[1]?.trim() || 'timeline'}`;
  return (
    <div style={{ marginTop: 14, maxWidth: 520 }}>
      <div style={{ position: 'relative', height: 8, background: 'var(--ink-100)', borderRadius: 4, overflow: 'hidden' }}>
        <div style={{ position: 'absolute', left: 0, top: 0, bottom: 0, width: `${pct}%`, background: 'var(--ink-900)' }}/>
        {milestones.map(ms => (
          <div key={ms.m} style={{
            position: 'absolute', left: `${ms.m}%`, top: -2, bottom: -2,
            width: 2,
            background: pct >= ms.m && kpiPct < ms.m ? 'var(--brand-500)' : 'var(--border-strong)',
          }}/>
        ))}
      </div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, marginTop: 6, minHeight: 18 }}>
        <div style={{ display: 'flex', gap: isMobile ? 10 : 12, flexWrap: 'wrap' }}>
          {milestones.map(ms => (
            <span key={ms.m} style={{
              fontFamily: 'var(--font-mono)', fontSize: 9, fontWeight: 700, letterSpacing: isMobile ? '0.02em' : '0.1em',
              color: pct >= ms.m && kpiPct < ms.m ? 'var(--brand-500)' : 'var(--text-muted)',
            }}>{ms.label}</span>
          ))}
        </div>
        <div style={{ fontFamily: 'var(--font-mono)', fontSize: 9, fontWeight: 700, letterSpacing: isMobile ? '0.02em' : '0.08em', color: 'var(--text-muted)', whiteSpace: 'nowrap' }}>
          {state}
        </div>
      </div>
    </div>
  );
};

const TINT_MAP = {
  brand: { bg: 'var(--brand-100)', fg: 'var(--brand-600)' },
  ink:   { bg: 'var(--ink-100)',   fg: 'var(--text-primary)' },
};

const KpiCard = ({ icon, label, value, sub, pct, delta, spark, tint = 'brand' }) => {
  const tt = TINT_MAP[tint] || TINT_MAP.brand;
  const isMobile = useIsMobile();
  return (
    <div className="panel" style={cdStyles.kpiCard}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 14 }}>
        <div style={{ width: 32, height: 32, borderRadius: 4, background: tt.bg, color: tt.fg, display: 'grid', placeItems: 'center' }}>
          <Icon name={icon} size={16}/>
        </div>
        {delta && (
          <span style={{ fontSize: 11, fontWeight: 700, color: 'var(--ink-900)', display: 'flex', alignItems: 'center', gap: 2 }}>
            <Icon name="arrow-up" size={10}/> {delta}
          </span>
        )}
      </div>
      <div style={{ fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.08em', fontWeight: 700, marginBottom: 6 }}>{label}</div>
      <div className="num" style={{ fontSize: 28, fontWeight: 600, letterSpacing: 0, lineHeight: 1 }}>{value}</div>
      {spark && (
        <div style={{ marginTop: 10, marginBottom: -4 }}>
          <Sparkline data={spark} color={tt.fg} width={isMobile ? 140 : 180} height={28}/>
        </div>
      )}
      <div style={{ fontSize: 11, color: 'var(--ink-500)', marginTop: 8 }}>{sub}</div>
      {pct !== undefined && (
        <div style={{ marginTop: 10 }}>
          <ProgressBar pct={pct} height={4}/>
        </div>
      )}
    </div>
  );
};

const BookingRow = ({ bk, clientMode }) => {
  const t = useT();
  const hasLimitedMetrics = (bk.posts || []).some(post => post.metricsScope === 'profile_list_limited');
  const influencerHref = window.withQuery
    ? window.withQuery('Influencer Detail.html', { id: bk.influencerId, campaignId: bk.campaignId })
    : `Influencer Detail.html?id=${encodeURIComponent(bk.influencerId)}&campaignId=${encodeURIComponent(bk.campaignId)}`;
  return (
    <div style={cdStyles.infRow}>
      <img src={cdMediaSrc(bk.avatar)} alt="" style={cdStyles.avatar}/>
      <div style={{ minWidth: 160, flex: '1 1 160px' }}>
        <div style={{ fontWeight: 600, fontSize: 13, display: 'flex', alignItems: 'center', gap: 6 }}>
          <a href={influencerHref} style={{ color: 'inherit', textDecoration: 'none' }} title={`Open ${bk.name}`}>
            {bk.name}
          </a>
          <span style={cdStyles.tierPill}>{t('tier.' + bk.tier)}</span>
        </div>
        <div style={{ fontSize: 11, color: 'var(--ink-500)' }}>{bk.handle} · {window.PulseStore.fmt(bk.followers)} followers</div>
        {hasLimitedMetrics && (
          <div style={{ marginTop: 4, fontSize: 10, color: 'var(--brand-600)', fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.04em' }}>
            Profile-source metrics · views not verified
          </div>
        )}
      </div>
      <PlatformBadge platform={bk.platform} size={24}/>
      <div style={{ flex: 2, minWidth: 160, maxWidth: 320 }}>
        <ProgressBar pct={bk.kpiPct} committed={bk.committedViews} actual={bk.actualViews} label={t('camp.views')}/>
      </div>
      <div style={cdStyles.metric}>
        <div className="num" style={cdStyles.metricValue}>{window.PulseStore.fmt(bk.likes)}</div>
        <div style={cdStyles.metricLabel}>Likes</div>
      </div>
      <div style={cdStyles.metric}>
        <div className="num" style={cdStyles.metricValue}>{bk.engagementRate}%</div>
        <div style={cdStyles.metricLabel}>ER</div>
      </div>
      <KpiRing pct={bk.kpiPct} size={42} thickness={5}/>
    </div>
  );
};

const InfluencerCard = ({ bk }) => {
  const t = useT();
  const influencerHref = window.withQuery
    ? window.withQuery('Influencer Detail.html', { id: bk.influencerId, campaignId: bk.campaignId })
    : `Influencer Detail.html?id=${encodeURIComponent(bk.influencerId)}&campaignId=${encodeURIComponent(bk.campaignId)}`;
  // 1990 palette — mono thumbs.
  const thumbs = [
    { bg: '#EC2024', fg: '#FFFFFF' },
    { bg: '#111111', fg: '#EC2024' },
    { bg: '#1A1A1A', fg: '#FFFFFF' },
    { bg: 'var(--bg-panel)', fg: 'var(--brand-500)', border: 'var(--border-strong)' },
  ];
  const tv = thumbs[(bk.thumbHue || 0) % thumbs.length];
  return (
    <div className="panel" style={cdStyles.infCard}>
      <div style={{ position: 'relative', aspectRatio: '16 / 10', background: tv.bg, color: tv.fg, overflow: 'hidden', borderBottom: tv.border ? `1px solid ${tv.border}` : 'none' }}>
        <div style={{ position: 'absolute', left: 16, top: 14, fontFamily: 'var(--font-display)', fontSize: 72, lineHeight: 0.8, letterSpacing: 0, textTransform: 'uppercase', opacity: 0.85 }}>
          {bk.name.charAt(bk.name.indexOf(' ') + 1) || bk.name.charAt(0)}
        </div>
        <div style={{ position: 'absolute', right: 14, top: 12, fontFamily: 'var(--font-display)', fontSize: 28, lineHeight: 1, opacity: 0.7 }}>✱</div>
        <div style={{ position: 'absolute', top: 10, left: 10 }}>
          <PlatformBadge platform={bk.platform} size={24}/>
        </div>
        <div style={{ position: 'absolute', bottom: 10, right: 10 }}>
          <KpiRing pct={bk.kpiPct} size={48} thickness={5}/>
        </div>
        <div style={{ position: 'absolute', bottom: 10, left: 16, color: tv.fg, fontFamily: 'var(--font-mono)', letterSpacing: '0.1em', textTransform: 'uppercase' }}>
          <div style={{ fontSize: 9, fontWeight: 700, opacity: 0.9 }}>Posted {bk.postedAt}</div>
        </div>
      </div>
      <div style={{ padding: 14 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
          <img src={cdMediaSrc(bk.avatar)} alt="" style={{ width: 36, height: 36, borderRadius: 6 }}/>
          <div style={{ flex: 1, minWidth: 0 }}>
            <a href={influencerHref} style={{ display: 'block', color: 'inherit', textDecoration: 'none', fontWeight: 600, fontSize: 13, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={`Open ${bk.name}`}>
              {bk.name}
            </a>
            <div style={{ fontSize: 11, color: 'var(--ink-500)' }}>{bk.handle} · {window.PulseStore.fmt(bk.followers)}</div>
          </div>
        </div>
        <div style={{ marginBottom: 12 }}>
          <ProgressBar pct={bk.kpiPct} committed={bk.committedViews} actual={bk.actualViews} label={t('camp.views')}/>
        </div>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 6, padding: '10px 0 0', borderTop: '1px solid var(--border)' }}>
          {[
            { icon: 'eye',     v: window.PulseStore.fmt(bk.actualViews) },
            { icon: 'heart',   v: window.PulseStore.fmt(bk.likes) },
            { icon: 'message', v: window.PulseStore.fmt(bk.comments) },
            { icon: 'share',   v: window.PulseStore.fmt(bk.shares) },
          ].map((m, i) => (
            <div key={i} style={{ textAlign: 'center' }}>
              <Icon name={m.icon} size={12} style={{ color: 'var(--ink-400)', marginBottom: 2 }}/>
              <div className="num" style={{ fontSize: 13, fontWeight: 600 }}>{m.v}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

const AlertsPanel = ({ campaignId }) => {
  const t = useT();
  const alerts = window.PulseStore.getAlerts()
    .filter(a => a.status === 'open' && (!campaignId || a.campaignId === campaignId))
    .slice(0, 4);
  const alertsHref = window.withQuery
    ? window.withQuery('Alerts.html', campaignId ? { campaignId } : {})
    : `Alerts.html${campaignId ? `?campaignId=${encodeURIComponent(campaignId)}` : ''}`;
  return (
    <div className="panel" style={{ padding: 0 }}>
      <div style={{ ...cdStyles.cardHeader, padding: '14px 18px', borderBottom: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ width: 28, height: 28, borderRadius: 4, background: 'var(--brand-100)', color: 'var(--brand-600)', display: 'grid', placeItems: 'center' }}>
            <Icon name="alert" size={14}/>
          </div>
          <div>
            <div style={cdStyles.cardTitle}>{t('cd.alerts_title')}</div>
            <div style={cdStyles.cardSub}>{t('cd.alerts_sub', { n: alerts.length })}</div>
          </div>
        </div>
        <a className="btn ghost" style={{ fontSize: 12 }} href={alertsHref}>{t('action.view_all')} <Icon name="arrow-right" size={12}/></a>
      </div>
      <div>
        {alerts.length === 0 && (
          <div style={{ padding: 28, textAlign: 'center', color: 'var(--ink-500)', fontSize: 13 }}>{t('al.no_alerts')}</div>
        )}
        {alerts.map(a => (
          <div key={a.id} style={cdStyles.alertRow}>
            {a.influencer ? (
              <img src={cdMediaSrc(a.influencer.avatar)} alt="" style={{ width: 32, height: 32, borderRadius: 8 }}/>
            ) : (
              <div style={{ width: 32, height: 32, borderRadius: 4, background: a.brandColor || 'var(--ink-900)', display: 'grid', placeItems: 'center', color: '#FFF', fontFamily: 'var(--font-display)', fontSize: 13 }}>{(a.brand || a.campaignName || '?').charAt(0)}</div>
            )}
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 600 }}>{a.influencer?.name || a.campaignName}</div>
              <div style={{ fontSize: 11, color: 'var(--ink-500)' }}>
                {a.type === 'timeline' ? t('al.msg.timeline_' + a.milestone, { pct: a.kpiPct }) : a.type === 'stale_data' ? t('sync.stale_alert', { n: a.staleCount }) : t('al.msg.timeline_80', { pct: a.kpiPct })}
              </div>
            </div>
            <span className="chip brand" style={{ fontSize: 11 }}>{a.kpiPct}%</span>
          </div>
        ))}
      </div>
    </div>
  );
};

const TopPostsPanel = ({ bookings, posts = [] }) => {
  const t = useT();
  const topPosts = [...posts].sort((a, b) => (b.views || 0) - (a.views || 0)).slice(0, 4);
  const topBookings = [...bookings].sort((a, b) => b.actualViews - a.actualViews).slice(0, 4);
  const top = topPosts.length ? topPosts : topBookings;
  const bookingById = Object.fromEntries(bookings.map(b => [b.id, b]));
  return (
    <div className="panel" style={{ padding: 0 }}>
      <div style={{ ...cdStyles.cardHeader, padding: '14px 18px', borderBottom: '1px solid var(--border)' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <div style={{ width: 28, height: 28, borderRadius: 4, background: 'var(--bg-inverse)', color: 'var(--text-inverse)', display: 'grid', placeItems: 'center' }}>
            <Icon name="trending-up" size={14}/>
          </div>
          <div>
            <div style={cdStyles.cardTitle}>{t('cd.top_title')}</div>
            <div style={cdStyles.cardSub}>{t('cd.top_sub')}</div>
          </div>
        </div>
      </div>
      <div>
        {top.map((item, i) => {
          const bk = item.bookingId ? bookingById[item.bookingId] : item;
          return (
          <div key={item.id} style={cdStyles.alertRow}>
            <div style={{ width: 22, height: 22, borderRadius: 4, background: i === 0 ? 'var(--brand-500)' : 'var(--ink-100)', color: i === 0 ? '#FFF' : 'var(--ink-700)', display: 'grid', placeItems: 'center', fontFamily: 'var(--font-display)', fontSize: 11, fontWeight: 700 }}>
              {i+1}
            </div>
            <img src={cdMediaSrc(bk?.avatar)} alt="" style={{ width: 32, height: 32, borderRadius: 8 }}/>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: 13, fontWeight: 600 }}>{bk?.name || item.title}</div>
              <div style={{ fontSize: 11, color: 'var(--ink-500)' }}>{window.PulseStore.fmt(item.views || item.actualViews)} {t('camp.views')} · {item.lastPulledAt ? `Pulled ${item.lastPulledAt.slice(0,10)}` : `${bk?.engagementRate || 0}% ER`}</div>
            </div>
            <KpiRing pct={bk?.kpiPct || 0} size={36} thickness={4}/>
          </div>
        );})}
      </div>
    </div>
  );
};

const postStatusCopy = (p) => {
  if (!p.url || p.pullStatus === 'missing_url') return { label: 'Missing URL', tone: 'var(--brand-500)' };
  if (p.pullStatus === 'pending') return { label: 'Pending pull', tone: 'var(--brand-500)' };
  if (p.pullStatus === 'rate_limited') return { label: 'Rate limited', tone: 'var(--brand-500)' };
  if (['not_found', 'private_or_removed', 'error'].includes(p.pullStatus)) return { label: p.pullStatus.replace(/_/g, ' '), tone: 'var(--brand-500)' };
  const staleAfterHours = +(window.PulseStore?.getSyncStatus()?.staleAfterHours || 6);
  const pulledAt = p.lastPulledAt ? new Date(p.lastPulledAt).getTime() : NaN;
  if (!Number.isFinite(pulledAt) || (Date.now() - pulledAt) > staleAfterHours * 60 * 60 * 1000) return { label: 'Stale', tone: 'var(--brand-500)' };
  return { label: 'Pulled', tone: 'var(--ink-900)' };
};

const postSafeDate = (value) => {
  if (typeof value === 'string' && value.length >= 16) return value.slice(0,16).replace('T',' ');
  const d = value ? new Date(value) : null;
  return d && !Number.isNaN(d.getTime()) ? d.toISOString().slice(0,16).replace('T',' ') : '—';
};

const postPlatformName = (platform) => {
  const key = String(platform || '').toLowerCase();
  if (key === 'tiktok') return 'TikTok';
  if (key === 'instagram') return 'Instagram';
  if (key === 'facebook') return 'Facebook';
  return 'Social';
};

const inferPostPlatformFromUrl = (url = '') => {
  const value = String(url || '').toLowerCase();
  if (value.includes('tiktok.com')) return 'tiktok';
  if (value.includes('instagram.com')) return 'instagram';
  if (value.includes('facebook.com') || value.includes('fb.watch')) return 'facebook';
  return '';
};

const postUrlDomain = (url = '') => {
  try {
    return new URL(url).hostname.replace(/^www\./, '');
  } catch (err) {
    return '';
  }
};

const PostsTab = ({ posts, bookings }) => {
  const t = useT();
  const bookingById = Object.fromEntries(bookings.map(b => [b.id, b]));
  if (!posts.length) return (
    <div className="panel" style={{ padding: 40, textAlign: 'center', color: 'var(--text-muted)' }}>{t('misc.empty')}</div>
  );
  const postCards = posts.map(p => {
    const bk = bookingById[p.bookingId];
    const eng = (p.likes || 0) + (p.comments || 0) + (p.shares || 0);
    const status = postStatusCopy(p);
    const urlPlatform = inferPostPlatformFromUrl(p.url);
    const hasPlatformMismatch = urlPlatform && String(p.platform || '').toLowerCase() !== urlPlatform;
    const domain = postUrlDomain(p.url);
    return { p, bk, eng, status, domain, hasPlatformMismatch };
  });
  return (
    <div className="panel posts-tab-panel">
      <div className="posts-tab-tablewrap">
      <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 900 }}>
        <thead>
          <tr style={{ background: 'var(--ink-50)', borderBottom: '1px solid var(--border)' }}>
            <th style={bkStyles.th}>{t('bk.influencer')}</th>
            <th style={bkStyles.th}>{t('bk.platform')}</th>
            <th style={bkStyles.th}>URL</th>
            <th style={bkStyles.th}>Status</th>
            <th style={{ ...bkStyles.th, textAlign: 'right' }}>{t('camp.views')}</th>
            <th style={{ ...bkStyles.th, textAlign: 'right' }}>Eng.</th>
            <th style={bkStyles.th}>{t('sync.last_pull')}</th>
          </tr>
        </thead>
        <tbody>
          {postCards.map(({ p, bk, eng, status, domain, hasPlatformMismatch }) => {
            return (
              <tr key={p.id} style={{ borderBottom: '1px solid var(--border)' }}>
                <td style={bkStyles.td}>{bk?.name || p.influencerId}</td>
                <td style={bkStyles.td}>
                  <div style={{ display: 'flex', flexDirection: 'column', gap: 4, minWidth: 112 }}>
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, whiteSpace: 'nowrap' }}>
                      <PlatformBadge platform={p.platform} size={22}/>
                      <span style={{ fontWeight: 800 }}>{postPlatformName(p.platform)}</span>
                    </span>
                    {domain && <span style={{ fontSize: 10, color: 'var(--text-muted)', fontFamily: 'var(--font-mono)' }}>{domain}</span>}
                    {hasPlatformMismatch && <span className="chip" style={{ width: 'fit-content', fontSize: 9, color: 'var(--brand-500)', borderColor: 'rgba(236,32,36,0.28)', background: 'rgba(236,32,36,0.08)' }}>Check platform</span>}
                  </div>
                </td>
                <td style={{ ...bkStyles.td, maxWidth: 320 }}>{p.url ? <a href={p.url} target="_blank" rel="noreferrer" style={{ color: 'var(--brand-500)', display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.platformPostId || p.url}</a> : <span style={{ color: 'var(--ink-400)' }}>Missing URL</span>}</td>
                <td style={bkStyles.td}><span className="chip" style={{ fontSize: 10, color: status.tone }}>{status.label}</span></td>
                <td style={{ ...bkStyles.td, textAlign: 'right' }} className="num">{window.PulseStore.fmt(p.views)}</td>
                <td style={{ ...bkStyles.td, textAlign: 'right' }} className="num">{window.PulseStore.fmt(eng)}</td>
                <td style={bkStyles.td}><span style={{ fontFamily: 'var(--font-mono)', fontSize: 11 }}>{postSafeDate(p.lastPulledAt)}</span></td>
              </tr>
            );
          })}
        </tbody>
      </table>
      </div>
      <div className="posts-tab-cardgrid">
        {postCards.map(({ p, bk, eng, status, domain, hasPlatformMismatch }) => (
          <article className="post-mobile-card" key={`mobile-${p.id}`}>
            <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 12, marginBottom: 8 }}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontWeight: 800, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{bk?.name || p.influencerId}</div>
                {domain && <div style={{ fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--text-muted)', overflowWrap: 'anywhere' }}>{domain}</div>}
              </div>
              <span className="chip" style={{ flex: '0 0 auto', fontSize: 10, color: status.tone }}>{status.label}</span>
            </div>
            <div className="post-row">
              <div className="post-label">{t('bk.platform')}</div>
              <div className="post-value" style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 8 }}>
                <PlatformBadge platform={p.platform} size={22}/>
                <span>{postPlatformName(p.platform)}</span>
              </div>
            </div>
            <div className="post-row">
              <div className="post-label">URL</div>
              <div className="post-value">{p.url ? <a href={p.url} target="_blank" rel="noreferrer" style={{ color: 'var(--brand-500)' }}>{p.platformPostId || p.url}</a> : <span style={{ color: 'var(--ink-400)' }}>Missing URL</span>}</div>
            </div>
            {hasPlatformMismatch && (
              <div className="post-row">
                <div className="post-label">Check</div>
                <div className="post-value" style={{ color: 'var(--brand-500)' }}>Platform mismatch</div>
              </div>
            )}
            <div className="post-row"><div className="post-label">{t('camp.views')}</div><div className="post-value num">{window.PulseStore.fmt(p.views)}</div></div>
            <div className="post-row"><div className="post-label">Eng.</div><div className="post-value num">{window.PulseStore.fmt(eng)}</div></div>
            <div className="post-row"><div className="post-label">{t('sync.last_pull')}</div><div className="post-value" style={{ fontFamily: 'var(--font-mono)', fontSize: 11 }}>{postSafeDate(p.lastPulledAt)}</div></div>
          </article>
        ))}
      </div>
    </div>
  );
};

// ===== Bookings management tab (internal only) =====
const BookingsTab = ({ campaign, bookings, onEdit }) => {
  const t = useT();
  const [showAdd, setShowAdd] = useStateCD(false);

  const totalFee = bookings.reduce((s, b) => s + (b.fee || 0), 0);

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 16, flexWrap: 'wrap', gap: 12 }}>
        <div style={{ fontSize: 13, color: 'var(--ink-500)' }}>
          <span>{bookings.length} bookings</span>
          <span style={{ margin: '0 10px', color: 'var(--border-strong)' }}>·</span>
          <span>Total fee: <strong style={{ color: 'var(--ink-900)' }}>${totalFee.toLocaleString()}</strong></span>
        </div>
        <button className="btn brand" onClick={() => setShowAdd(true)}><Icon name="plus" size={13}/> {t('bk.add')}</button>
      </div>

      <div className="panel" style={{ padding: 0, overflow: 'auto' }}>
        <table style={{ width: '100%', borderCollapse: 'collapse', minWidth: 760 }}>
          <thead>
            <tr style={{ background: 'var(--ink-50)', borderBottom: '1px solid var(--border)' }}>
              <th style={bkStyles.th}>{t('bk.influencer')}</th>
              <th style={bkStyles.th}>{t('bk.platform')}</th>
              <th style={{ ...bkStyles.th, textAlign: 'right' }}>{t('bk.fee')}</th>
              <th style={{ ...bkStyles.th, textAlign: 'right' }}>{t('camp.views')}</th>
              <th style={{ ...bkStyles.th, width: 160 }}>KPI</th>
              <th style={bkStyles.th}></th>
            </tr>
          </thead>
          <tbody>
            {bookings.map(b => <BookingTableRow key={b.id} bk={b} onEdit={onEdit}/>)}
          </tbody>
        </table>
      </div>

      {showAdd && <AddBookingModal campaign={campaign} onClose={() => setShowAdd(false)}/>}
    </div>
  );
};

const BookingTableRow = ({ bk, onEdit }) => {
  return (
    <tr style={{ borderBottom: '1px solid var(--border)' }}>
      <td style={bkStyles.td}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <img src={cdMediaSrc(bk.avatar)} alt="" style={{ width: 32, height: 32, borderRadius: 6 }}/>
          <div>
            <div style={{ fontSize: 13, fontWeight: 600 }}>{bk.name}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-500)' }}>{bk.handle}</div>
          </div>
        </div>
      </td>
      <td style={bkStyles.td}><PlatformBadge platform={bk.platform} size={22}/></td>
      <td style={{ ...bkStyles.td, textAlign: 'right' }}>
        <div className="num" style={{ fontWeight: 600 }}>${bk.fee.toLocaleString()}</div>
      </td>
      <td style={{ ...bkStyles.td, textAlign: 'right' }}>
        <div className="num" style={{ fontWeight: 600 }}>{window.PulseStore.fmt(bk.actualViews)}</div>
        <div style={{ fontSize: 10, color: 'var(--ink-500)' }}>/ {window.PulseStore.fmt(bk.committedViews)}</div>
      </td>
      <td style={bkStyles.td}><ProgressBar pct={bk.kpiPct} color="var(--brand-500)" height={4}/></td>
      <td style={bkStyles.td}>
        <button className="btn ghost" style={{ padding: 6 }} onClick={() => onEdit(bk)}><Icon name="edit" size={13}/></button>
      </td>
    </tr>
  );
};

const Toggle = ({ checked, onChange }) => (
  <button onClick={onChange} role="switch" aria-checked={checked} style={{
    width: 36, height: 20, borderRadius: 999,
    background: checked ? 'var(--brand-500)' : 'var(--ink-200)',
    position: 'relative', padding: 0, transition: 'background 0.15s',
    border: 'none',
  }}>
    <span style={{
      position: 'absolute', top: 2, left: checked ? 18 : 2,
      width: 16, height: 16, borderRadius: '50%', background: '#FFFFFF',
      transition: 'left 0.15s', boxShadow: '0 1px 2px rgba(0,0,0,0.25)',
    }}/>
  </button>
);

const AddBookingModal = ({ campaign, onClose }) => {
  const t = useT();
  const influencers = window.PulseStore.getInfluencers();
  const booked = window.PulseStore.getBookings(campaign.id).map(b => b.influencerId);
  const available = influencers.filter(i => !booked.includes(i.id));
  const [form, setForm] = useStateCD({
    influencerId: available[0]?.id || '',
    fee: 2000,
    committedViews: 500000,
    postsPlanned: 1,
  });
  const submit = () => {
    if (!form.influencerId) return;
    window.PulseStore.createBooking({ ...form, campaignId: campaign.id, trackConversions: false, conversions: null });
    onClose();
  };
  return (
    <div style={modalStyles.backdrop} onClick={onClose}>
      <div style={modalStyles.panel} onClick={e => e.stopPropagation()}>
        <div style={modalStyles.header}>
          <div style={modalStyles.title}>{t('bk.add')}</div>
          <button className="btn ghost" onClick={onClose}><Icon name="x" size={16}/></button>
        </div>
        <div style={modalStyles.body}>
          <Field label={t('bk.influencer')}>
            <select style={modalStyles.input} value={form.influencerId} onChange={e => setForm({ ...form, influencerId: e.target.value })}>
              {available.map(i => <option key={i.id} value={i.id}>{i.name} · {i.handle}</option>)}
            </select>
          </Field>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <Field label={t('bk.fee')}>
              <input type="number" style={modalStyles.input} value={form.fee} onChange={e => setForm({ ...form, fee: +e.target.value })}/>
            </Field>
            <Field label={t('bk.committed_views')}>
              <input type="number" style={modalStyles.input} value={form.committedViews} onChange={e => setForm({ ...form, committedViews: +e.target.value })}/>
            </Field>
          </div>
          <Field label={t('bk.posts')}>
            <input type="number" style={modalStyles.input} value={form.postsPlanned} onChange={e => setForm({ ...form, postsPlanned: +e.target.value })} min={1} max={10}/>
          </Field>
        </div>
        <div style={modalStyles.footer}>
          <button className="btn" onClick={onClose}>{t('action.cancel')}</button>
          <button className="btn brand" onClick={submit}>{t('action.save')}</button>
        </div>
      </div>
    </div>
  );
};

const EditBookingModal = ({ booking, campaign, onClose }) => {
  const t = useT();
  const [form, setForm] = useStateCD({
    ...booking,
    contractDetails: {
      bookingDate: booking.contractDetails?.bookingDate || booking.createdAt || '',
      exclusivity: booking.contractDetails?.exclusivity || 'No competitor for 30d',
      usageRights: booking.contractDetails?.usageRights || '90 days paid repurposing',
      paymentStatus: booking.contractDetails?.paymentStatus || '50% paid',
      clientVisibleFields: {
        bookingDate: true,
        fee: false,
        postsCommitted: true,
        viewsCommitted: true,
        engagementRate: true,
        exclusivity: false,
        usageRights: false,
        paymentStatus: false,
        ...(booking.contractDetails?.clientVisibleFields || {}),
      },
    },
    deliverables: booking.deliverables || [],
  });
  const [postRows, setPostRows] = useStateCD((booking.posts || []).map(p => ({ ...p })));
  const [deletedPostIds, setDeletedPostIds] = useStateCD([]);
  const addPostRow = () => setPostRows(rows => [...rows, makeDraftPost(booking, rows.length)]);
  const updatePostRow = (idx, patch) => setPostRows(rows => rows.map((p, i) => i === idx ? { ...p, ...patch } : p));
  const removePostRow = (idx) => {
    const row = postRows[idx];
    if (row?.id && !String(row.id).startsWith('draft_')) setDeletedPostIds(ids => [...ids, row.id]);
    setPostRows(rows => rows.filter((_, i) => i !== idx));
  };
  const save = () => {
    deletedPostIds.forEach(id => window.PulseStore.deletePost(id));
    postRows.forEach(row => {
      const payload = {
        ...row,
        bookingId: booking.id,
        campaignId: booking.campaignId,
        influencerId: booking.influencerId,
        platform: row.platform || booking.platform,
      };
      const hasContent = payload.url || payload.platformPostId || payload.title || payload.caption || payload.thumbnailUrl;
      if (!hasContent) return;
      if (payload.id && !String(payload.id).startsWith('draft_')) window.PulseStore.updatePost(payload.id, payload);
      else window.PulseStore.createPost(payload);
    });
    window.PulseStore.updateBooking(booking.id, {
      fee: +form.fee,
      committedViews: +form.committedViews,
      actualViews: +form.actualViews,
      postsLive: +form.postsLive,
      postsPlanned: +form.postsPlanned || postRows.length || 0,
      contractDetails: form.contractDetails,
      deliverables: form.deliverables,
    });
    onClose();
  };
  const del = () => {
    if (confirm('Delete booking for ' + booking.name + '?')) {
      window.PulseStore.deleteBooking(booking.id);
      onClose();
    }
  };
  return (
    <div style={modalStyles.backdrop} onClick={onClose}>
      <div style={modalStyles.panel} onClick={e => e.stopPropagation()}>
        <div style={modalStyles.header}>
          <div style={modalStyles.title}>Edit booking · {booking.name}</div>
          <button className="btn ghost" onClick={onClose}><Icon name="x" size={16}/></button>
        </div>
        <div style={modalStyles.body}>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
            <Field label={t('bk.fee')}><input type="number" style={modalStyles.input} value={form.fee} onChange={e => setForm({ ...form, fee: e.target.value })}/></Field>
            <Field label={t('bk.committed_views')}><input type="number" style={modalStyles.input} value={form.committedViews} onChange={e => setForm({ ...form, committedViews: e.target.value })}/></Field>
            <Field label="Actual views"><input type="number" style={modalStyles.input} value={form.actualViews} onChange={e => setForm({ ...form, actualViews: e.target.value })}/></Field>
            <Field label="Posts planned"><input type="number" style={modalStyles.input} value={form.postsPlanned || 0} onChange={e => setForm({ ...form, postsPlanned: e.target.value })}/></Field>
          </div>
          <ContractAdminEditor form={form} setForm={setForm}/>
          <DeliverablesAdminEditor form={form} setForm={setForm}/>
          <PostUrlEditor rows={postRows} onAdd={addPostRow} onUpdate={updatePostRow} onRemove={removePostRow}/>
        </div>
        <div style={{ ...modalStyles.footer, justifyContent: 'space-between' }}>
          <button className="btn" onClick={del} style={{ color: 'var(--brand-500)', borderColor: 'var(--brand-500)' }}><Icon name="trash" size={13}/> {t('action.delete')}</button>
          <div style={{ display: 'flex', gap: 8 }}>
            <button className="btn" onClick={onClose}>{t('action.cancel')}</button>
            <button className="btn brand" onClick={save}>{t('action.save')}</button>
          </div>
        </div>
      </div>
    </div>
  );
};

const contractFieldLabels = {
  bookingDate: 'Booking date',
  fee: 'Fee',
  postsCommitted: 'Posts committed',
  viewsCommitted: 'Views committed',
  engagementRate: 'Min engagement rate',
  exclusivity: 'Exclusivity',
  usageRights: 'Usage rights',
  paymentStatus: 'Payment status',
};

const ContractAdminEditor = ({ form, setForm }) => {
  const details = form.contractDetails || {};
  const visible = details.clientVisibleFields || {};
  const updateDetails = (patch) => setForm({ ...form, contractDetails: { ...details, ...patch } });
  const toggleVisible = (key) => updateDetails({ clientVisibleFields: { ...visible, [key]: !visible[key] } });
  return (
    <div style={{ marginTop: 18, paddingTop: 16, borderTop: '1px solid var(--border)' }}>
      <div style={{ fontSize: 12, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-primary)', marginBottom: 4 }}>Booking contract admin</div>
      <div style={{ fontSize: 11, color: 'var(--text-secondary)', marginBottom: 12 }}>Choose which contract fields are client-visible. Fee remains agency-only by default.</div>
      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
        <Field label="Booking date"><input type="date" style={modalStyles.input} value={details.bookingDate || ''} onChange={e => updateDetails({ bookingDate: e.target.value })}/></Field>
        <Field label="Payment status"><input style={modalStyles.input} value={details.paymentStatus || ''} onChange={e => updateDetails({ paymentStatus: e.target.value })}/></Field>
        <Field label="Exclusivity"><input style={modalStyles.input} value={details.exclusivity || ''} onChange={e => updateDetails({ exclusivity: e.target.value })}/></Field>
        <Field label="Usage rights"><input style={modalStyles.input} value={details.usageRights || ''} onChange={e => updateDetails({ usageRights: e.target.value })}/></Field>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(160px, 1fr))', gap: 8, marginTop: 8 }}>
        {Object.keys(contractFieldLabels).map(key => (
          <label key={key} style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 11, padding: 8, border: '1px solid var(--border)', background: visible[key] ? 'var(--brand-100)' : 'var(--bg-elevated)', borderRadius: 6 }}>
            <input type="checkbox" checked={!!visible[key]} onChange={() => toggleVisible(key)}/>
            <span>{contractFieldLabels[key]}</span>
          </label>
        ))}
      </div>
    </div>
  );
};

const defaultDeliverables = () => [
  { id: 'brief_signed', label: 'Brief signed', date: 'Mar 30', done: true, visibility: 'client' },
  { id: 'script_approved', label: 'Script approved', date: 'Apr 3', done: true, visibility: 'client' },
  { id: 'public_urls_added', label: 'Public post URLs added', date: '', done: false, visibility: 'client' },
  { id: 'metrics_pulled', label: 'ScrapeCreators metrics pulled', date: '', done: false, visibility: 'agency' },
  { id: 'posts_delivered', label: 'Committed posts delivered', date: '', done: false, visibility: 'client' },
  { id: 'final_signoff', label: 'Final report sign-off', date: 'Due Apr 30', done: false, visibility: 'agency' },
];

const DeliverablesAdminEditor = ({ form, setForm }) => {
  const rows = form.deliverables && form.deliverables.length ? form.deliverables : defaultDeliverables();
  const update = (idx, patch) => setForm({ ...form, deliverables: rows.map((d, i) => i === idx ? { ...d, ...patch } : d) });
  const add = () => setForm({ ...form, deliverables: [...rows, { id: `deliverable_${Date.now()}`, label: '', date: '', done: false, visibility: 'agency' }] });
  const remove = (idx) => setForm({ ...form, deliverables: rows.filter((_, i) => i !== idx) });
  return (
    <div style={{ marginTop: 18, paddingTop: 16, borderTop: '1px solid var(--border)' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 10, marginBottom: 10 }}>
        <div>
          <div style={{ fontSize: 12, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-primary)' }}>Deliverables checklist</div>
          <div style={{ fontSize: 11, color: 'var(--text-secondary)', marginTop: 3 }}>Shared progress between agency and brand. Use visibility to decide what client can see later.</div>
        </div>
        <button className="btn" type="button" onClick={add} style={{ padding: '7px 10px', fontSize: 11 }}><Icon name="plus" size={12}/> Add item</button>
      </div>
      <div style={{ display: 'grid', gap: 8 }}>
        {rows.map((row, idx) => (
          <div key={row.id || idx} style={{ display: 'grid', gridTemplateColumns: 'auto minmax(0, 1.4fr) minmax(100px, 0.7fr) minmax(120px, 0.7fr) auto', gap: 8, alignItems: 'center', border: '1px solid var(--border)', padding: 10, borderRadius: 6, background: 'var(--bg-elevated)' }}>
            <input type="checkbox" checked={!!row.done} onChange={e => update(idx, { done: e.target.checked })}/>
            <input style={modalStyles.input} value={row.label || ''} placeholder="Deliverable name" onChange={e => update(idx, { label: e.target.value })}/>
            <input style={modalStyles.input} value={row.date || ''} placeholder="Date/status" onChange={e => update(idx, { date: e.target.value })}/>
            <select style={modalStyles.input} value={row.visibility === 'client' ? 'client' : 'agency'} onChange={e => update(idx, { visibility: e.target.value })}>
              <option value="client">Client visible</option>
              <option value="agency">Agency only</option>
            </select>
            <button className="btn ghost" type="button" onClick={() => remove(idx)} style={{ padding: 8, color: 'var(--brand-500)' }}><Icon name="trash" size={13}/></button>
          </div>
        ))}
      </div>
    </div>
  );
};

const makeDraftPost = (booking, index = 0) => ({
  id: `draft_${Date.now()}_${index}`,
  bookingId: booking.id,
  campaignId: booking.campaignId,
  influencerId: booking.influencerId,
  platform: booking.platform || 'tiktok',
  url: '',
  platformPostId: '',
  title: '',
  caption: '',
  thumbnailUrl: '',
  pullStatus: 'pending',
  type: 'Video',
  views: 0,
  likes: 0,
  comments: 0,
  shares: 0,
  saves: 0,
  reach: 0,
});

const PostUrlEditor = ({ rows, onAdd, onUpdate, onRemove }) => {
  return (
    <div style={{ marginTop: 18, paddingTop: 16, borderTop: '1px solid var(--border)' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, marginBottom: 10, flexWrap: 'wrap' }}>
        <div>
          <div style={{ fontSize: 12, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-primary)' }}>Public post URLs</div>
          <div style={{ fontSize: 11, color: 'var(--text-secondary)', marginTop: 3 }}>n8n will send these URLs to ScrapeCreators and merge the returned metrics into PULSE.</div>
        </div>
        <button className="btn" type="button" onClick={onAdd} style={{ padding: '7px 10px', fontSize: 11 }}><Icon name="plus" size={12}/> Add post</button>
      </div>
      {rows.length === 0 ? (
        <div style={{ border: '1px dashed var(--border-strong)', padding: 14, fontSize: 12, color: 'var(--text-secondary)', background: 'var(--bg-elevated)' }}>
          No public URLs yet. Add one URL for each committed TikTok, Instagram, or Facebook post/video.
        </div>
      ) : (
        <div style={{ display: 'grid', gap: 10 }}>
          {rows.map((row, idx) => (
            <div key={row.id || idx} style={{ border: '1px solid var(--border)', background: 'var(--bg-elevated)', padding: 12, borderRadius: 6 }}>
              <div style={{ display: 'grid', gridTemplateColumns: 'minmax(92px, 120px) minmax(0, 1fr) auto', gap: 8, alignItems: 'center' }}>
                <select style={modalStyles.input} value={row.platform || 'tiktok'} onChange={e => onUpdate(idx, { platform: e.target.value })}>
                  <option value="tiktok">TikTok</option>
                  <option value="instagram">Instagram</option>
                  <option value="facebook">Facebook</option>
                </select>
                <input
                  style={{ ...modalStyles.input, minWidth: 0, fontFamily: 'var(--font-mono)', fontSize: 11 }}
                  value={row.url || ''}
                  placeholder="https://..."
                  onChange={e => onUpdate(idx, { url: e.target.value })}
                />
                <button className="btn ghost" type="button" onClick={() => onRemove(idx)} style={{ padding: 8, color: 'var(--brand-500)' }}><Icon name="trash" size={13}/></button>
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: 8, marginTop: 8 }}>
                <input
                  style={{ ...modalStyles.input, minWidth: 0 }}
                  value={row.caption || row.title || ''}
                  placeholder="Caption/title for UI display"
                  onChange={e => onUpdate(idx, { caption: e.target.value, title: e.target.value })}
                />
                <input
                  style={{ ...modalStyles.input, minWidth: 0, fontFamily: 'var(--font-mono)', fontSize: 11 }}
                  value={row.platformPostId || ''}
                  placeholder="Platform post ID / shortcode"
                  onChange={e => onUpdate(idx, { platformPostId: e.target.value })}
                />
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(120px, 160px)', gap: 8, marginTop: 8 }}>
                <div>
                  <div style={{ fontSize: 9, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-muted)', marginBottom: 5 }}>Thumbnail URL</div>
                  <input
                    style={{ ...modalStyles.input, minWidth: 0, fontFamily: 'var(--font-mono)', fontSize: 11 }}
                    value={row.thumbnailUrl || ''}
                    placeholder="Optional thumbnail URL"
                    onChange={e => onUpdate(idx, { thumbnailUrl: e.target.value })}
                  />
                  <div style={{ fontSize: 10, color: row.thumbnailUrl ? 'var(--text-secondary)' : 'var(--brand-500)', marginTop: 4 }}>
                    Thumbnail preview: {row.thumbnailUrl ? 'URL provided; backend proxy will try to load it' : 'provider did not return one yet; UI will use a safe placeholder'}
                  </div>
                </div>
                <div>
                  <div style={{ fontSize: 9, fontWeight: 800, textTransform: 'uppercase', letterSpacing: '0.08em', color: 'var(--text-muted)', marginBottom: 5 }}>Pull status</div>
                  <select style={modalStyles.input} value={row.pullStatus || 'pending'} onChange={e => onUpdate(idx, { pullStatus: e.target.value })}>
                    <option value="pending">Pending pull</option>
                    <option value="ok">Pulled</option>
                    <option value="missing_url">Missing post URL</option>
                    <option value="not_found">Post not found</option>
                    <option value="private_or_removed">Private/removed</option>
                    <option value="rate_limited">Rate limited</option>
                    <option value="error">Pull error</option>
                  </select>
                </div>
              </div>
            </div>
          ))}
        </div>
      )}
    </div>
  );
};

const ShareCampaignModal = ({ campaign, onClose }) => {
  const t = useT();
  const existing = window.PulseStore.getShareLinks(campaign.id)[0];
  const [link, setLink] = useStateCD(existing || null);
  const [form, setForm] = useStateCD({
    expiresAt: existing?.expiresAt || new Date(Date.now()+30*864e5).toISOString().slice(0,10),
    password: existing?.password || '',
  });
  const create = () => {
    const slug = campaign.brand.toLowerCase().replace(/[^a-z0-9]+/g, '-');
    const l = window.PulseStore.createShareLink({ campaignId: campaign.id, slug, expiresAt: form.expiresAt, password: form.password || null });
    setLink(l);
  };
  const update = () => {
    const l = window.PulseStore.updateShareLink(link.id, { expiresAt: form.expiresAt, password: form.password || null });
    setLink(l);
  };
  const url = link ? `${window.location.origin}${window.location.pathname.replace(/[^/]+$/, '')}Client%20View.html?token=${encodeURIComponent(link.token)}` : '';
  return (
    <div style={modalStyles.backdrop} onClick={onClose}>
      <div style={modalStyles.panel} onClick={e => e.stopPropagation()}>
        <div style={modalStyles.header}>
          <div style={modalStyles.title}>{t('action.share_client')}</div>
          <button className="btn ghost" onClick={onClose}><Icon name="x" size={16}/></button>
        </div>
        <div style={modalStyles.body}>
          <div style={{ fontSize: 13, color: 'var(--text-secondary)', marginBottom: 16, padding: 12, background: 'var(--bg-elevated)', borderLeft: '3px solid var(--brand-500)' }}>
            <Icon name="shield" size={14} style={{ marginRight: 6, verticalAlign: 'middle', color: 'var(--brand-500)' }}/>
            {t('cv.sensitive_hidden')}
          </div>
          {link ? (
            <>
              <Field label="Share URL">
                <div style={{ display: 'flex', gap: 8 }}>
                  <input readOnly style={{ ...modalStyles.input, flex: 1, fontFamily: 'var(--font-mono)', fontSize: 11 }} value={url}/>
                  <button className="btn" onClick={() => { if (navigator.clipboard) navigator.clipboard.writeText(url); }}>
                    <Icon name="copy" size={13}/>
                  </button>
                </div>
              </Field>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
                <Field label="Expires">
                  <input type="date" style={modalStyles.input} value={form.expiresAt} onChange={e => setForm({ ...form, expiresAt: e.target.value })}/>
                </Field>
                <Field label="Password (optional)">
                  <input type="text" style={modalStyles.input} value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} placeholder="Leave blank for no password"/>
                </Field>
              </div>
              <div style={{ fontSize: 11, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.08em', fontWeight: 700 }}>
                Viewed {link.viewCount} times · Last viewed {link.lastViewed || 'never'}
              </div>
            </>
          ) : (
            <>
              <Field label="Expires">
                <input type="date" style={modalStyles.input} value={form.expiresAt} onChange={e => setForm({ ...form, expiresAt: e.target.value })}/>
              </Field>
              <Field label="Password (optional)">
                <input type="text" style={modalStyles.input} value={form.password} onChange={e => setForm({ ...form, password: e.target.value })} placeholder="Leave blank for no password"/>
              </Field>
            </>
          )}
        </div>
        <div style={modalStyles.footer}>
          <button className="btn" onClick={onClose}>{t('action.close')}</button>
          {link
            ? <button className="btn brand" onClick={update}>{t('action.save')}</button>
            : <button className="btn brand" onClick={create}><Icon name="link" size={13}/> Create link</button>}
        </div>
      </div>
    </div>
  );
};

const cdStyles = {
  header: { display: 'flex', alignItems: 'flex-start', gap: 20, marginBottom: 24, flexWrap: 'wrap' },
  title: { fontFamily: 'var(--font-display)', fontSize: 'clamp(26px, 4.5vw, 40px)', fontWeight: 400, lineHeight: 1.05, margin: 0, letterSpacing: 0, textTransform: 'uppercase' },
  kpiGrid: { display: 'grid', gap: 12, marginBottom: 24 },
  kpiCard: { padding: 18 },
  tabs: { display: 'flex', alignItems: 'center', gap: 4, borderBottom: '1px solid var(--border)', marginBottom: 16 },
  tab: { padding: '10px 14px', fontSize: 13, color: 'var(--ink-500)', fontWeight: 600, borderBottomWidth: 2, borderBottomStyle: 'solid', borderBottomColor: 'transparent', marginBottom: -1, whiteSpace: 'nowrap', textTransform: 'uppercase', letterSpacing: '0.04em' },
  tabActive: { color: 'var(--ink-900)', borderBottomColor: 'var(--brand-500)' },
  mainGrid: { display: 'grid', gridTemplateColumns: 'minmax(0, 2fr) minmax(280px, 1fr)', gap: 16 },
  chartCard: { padding: 20 },
  donutCard: { padding: 20 },
  cardHeader: { display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: 14, gap: 10, flexWrap: 'wrap' },
  cardTitle: { fontSize: 14, fontWeight: 700, color: 'var(--ink-900)', textTransform: 'uppercase', letterSpacing: '0.02em' },
  cardSub: { fontSize: 12, color: 'var(--ink-500)', marginTop: 2 },
  legend: { display: 'flex', alignItems: 'center', gap: 10 },
  filterPill: { padding: '5px 10px', fontSize: 11, borderRadius: 999, background: 'var(--ink-100)', color: 'var(--ink-600)', fontWeight: 700, textTransform: 'uppercase', letterSpacing: '0.04em', border: 'none' },
  filterPillActive: { background: 'var(--brand-500)', color: 'white' },
  infRow: { display: 'flex', alignItems: 'center', gap: 14, padding: '14px 20px', borderBottom: '1px solid var(--border)', flexWrap: 'wrap' },
  avatar: { width: 40, height: 40, borderRadius: 6, objectFit: 'cover' },
  tierPill: { fontSize: 9, padding: '1px 5px', borderRadius: 2, textTransform: 'uppercase', fontWeight: 700, color: 'var(--ink-700)', letterSpacing: '0.06em', background: 'var(--ink-100)' },
  metric: { textAlign: 'right', minWidth: 60 },
  metricValue: { fontSize: 13, fontWeight: 600 },
  metricLabel: { fontSize: 9, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.08em', marginTop: 1, fontWeight: 700 },
  infCard: { overflow: 'hidden', transition: 'all 0.15s ease', cursor: 'pointer' },
  alertRow: { display: 'flex', alignItems: 'center', gap: 12, padding: '12px 18px', borderBottom: '1px solid var(--border)' },
};
const bkStyles = {
  th: { textAlign: 'left', padding: '12px 16px', fontSize: 10, color: 'var(--ink-500)', textTransform: 'uppercase', letterSpacing: '0.08em', fontWeight: 700 },
  td: { padding: '12px 16px', fontSize: 13, verticalAlign: 'middle' },
};

Object.assign(window, { CampaignDetail, BookingsTab, AddBookingModal, EditBookingModal, ShareCampaignModal, Toggle, TimelineBar, KpiCard, BookingRow, InfluencerCard, PostsTab });
