// Client-facing dashboard tabs.
// Depends on window primitives from components.jsx + recharts global.

const { useState: cuState, useMemo: cuMemo } = React;
const RC = window.Recharts;

const BRAND_RED = '#BE2E2B';
const RICH_BLACK = '#1A1919';
const DARK_GREEN = '#315A49';
const DARK_BLUE = '#29254E';
const DARK_GRAY = '#5B5C5B';
const SOFT_RULE = 'rgba(26,25,25,0.12)';
const FAINT_RULE = 'rgba(26,25,25,0.06)';

// ── Shared chart styling ─────────────────────────────────
// Custom tooltip card. Hard-edged (RCG brand), white, soft shadow, tabular
// numerals so values line up vertically when multiple series are visible.
function ChartTooltip({ active, payload, label, valueFormatter }) {
  if (!active || !payload || !payload.length) return null;
  return (
    <div style={{
      background: '#fff',
      border: '1px solid ' + SOFT_RULE,
      boxShadow: '0 8px 28px rgba(26,25,25,0.10)',
      padding: '10px 14px',
      fontFamily: 'var(--sans)',
      fontSize: 12,
      minWidth: 160,
    }}>
      <div style={{
        fontSize: 10,
        letterSpacing: '0.10em',
        textTransform: 'uppercase',
        color: 'var(--ink-stone)',
        marginBottom: 6,
        fontWeight: 600,
      }}>{label}</div>
      {payload.map((row, i) => (
        <div key={i} style={{
          display: 'flex', alignItems: 'center', justifyContent: 'space-between',
          gap: 14, padding: '3px 0',
        }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, color: 'var(--rich-black)' }}>
            <span style={{
              display: 'inline-block', width: 8, height: 8,
              background: row.color || row.fill || RICH_BLACK,
              borderRadius: row.dataKey === 'leads' ? 999 : 0,
            }} />
            {row.name}
          </span>
          <span style={{ fontVariantNumeric: 'tabular-nums', fontWeight: 500 }}>
            {valueFormatter ? valueFormatter(row.value, row.dataKey, row) : row.value}
          </span>
        </div>
      ))}
    </div>
  );
}

// Reusable axis defaults so every chart looks like family.
const axisProps = {
  stroke: SOFT_RULE,
  tickLine: false,
  axisLine: false,
  tick: { fontSize: 11, fill: 'var(--ink-stone)', fontFamily: 'var(--sans)' },
};
const legendProps = {
  verticalAlign: 'top',
  align: 'left',
  iconType: 'circle',
  iconSize: 7,
  wrapperStyle: {
    paddingBottom: 14,
    fontSize: 10.5,
    letterSpacing: '0.10em',
    textTransform: 'uppercase',
    color: 'var(--ink-stone)',
    fontWeight: 600,
  },
};

// Overlay shown centered on a chart when its data is empty / all zero.
// Renders an editorial "no data" message instead of an empty axes grid.
function ChartEmptyOverlay({ message = 'No data in this period' }) {
  return (
    <div style={{
      position: 'absolute', inset: 0,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      pointerEvents: 'none',
    }}>
      <div style={{
        fontFamily: 'var(--sans)', fontSize: 11,
        letterSpacing: '0.14em', textTransform: 'uppercase',
        color: 'var(--ink-stone)', fontWeight: 600,
        padding: '8px 14px', background: 'rgba(255,255,255,0.9)',
        border: '1px solid var(--soft-rule)',
      }}>{message}</div>
    </div>
  );
}

// True when a Recharts series dataset has no rows OR every numeric value is
// zero. Lets us swap in <ChartEmptyOverlay> over the empty axes grid.
function isChartEmpty(data, keys) {
  if (!Array.isArray(data) || data.length === 0) return true;
  for (const row of data) {
    for (const k of keys) {
      const n = Number(row?.[k]);
      if (Number.isFinite(n) && n !== 0) return false;
    }
  }
  return true;
}

// Shared animation timing so every chart enters with the same feel.
const CHART_ANIMATION_MS = 650;

// Render a single chart series given a chart-type kind ("line", "bar",
// "area"). The ChartControls component switches between these by setting
// `kind`. The same dataKey + props pipe through so swapping render type
// doesn't require duplicating series config.
function renderSeries({ kind, yAxisId, dataKey, name, stroke, fill, stackId }) {
  const common = {
    yAxisId, dataKey, name,
    animationDuration: CHART_ANIMATION_MS,
  };
  if (kind === 'bar') {
    return (
      <RC.Bar key={dataKey + ':bar'} {...common} stackId={stackId} fill={fill || stroke} radius={[2, 2, 0, 0]} barSize={18} />
    );
  }
  if (kind === 'area') {
    return (
      <RC.Area key={dataKey + ':area'} {...common} type="monotone"
        stroke={stroke} strokeWidth={2} fill={fill || stroke}
        dot={{ r: 2.5, fill: '#fff', stroke, strokeWidth: 1.5 }}
        activeDot={{ r: 5, fill: stroke, stroke: '#fff', strokeWidth: 2 }}
      />
    );
  }
  // default 'line'
  return (
    <RC.Line key={dataKey + ':line'} {...common} type="monotone"
      stroke={stroke} strokeWidth={2}
      dot={{ r: 3, fill: '#fff', stroke, strokeWidth: 1.6 }}
      activeDot={{ r: 5, fill: stroke, stroke: '#fff', strokeWidth: 2 }}
    />
  );
}

// Expose to other tab files so all charts look like family.
window.RcgChartTooltip = ChartTooltip;
window.rcgAxisProps = axisProps;
window.rcgLegendProps = legendProps;
window.RcgChartEmptyOverlay = ChartEmptyOverlay;
window.isChartEmpty = isChartEmpty;
window.RCG_CHART_ANIM_MS = CHART_ANIMATION_MS;
window.RCG_FAINT_RULE = FAINT_RULE;

// Extract a readable hostname out of an entity's GA4 hostname filter, for
// display in card subtitles like "Live from GA4 — filtered to rottmancreative.com".
function getHostnameLabel(client) {
  const f = client?.supermetrics?.ga4?.hostnameFilter || '';
  // "Hostname =@ rottmancreative.com" → "rottmancreative.com"
  const match = f.match(/=@\s*(.+)$/);
  return match ? match[1].trim() : 'rottmancreative.com';
}

// ── OVERVIEW ──────────────────────────────────────────────
function OverviewTab({ client, onConnect }) {
  const [range, setRange] = cuState('30D');
  // Per-chart controls state — date range, metrics, chart type — owned here.
  const [chartRange, setChartRange] = cuState('30D');
  const [chartMetrics, setChartMetrics] = cuState(['leads', 'spend']);
  const [chartType, setChartType] = cuState('combo');

  const rangeMap = { '7D': -2, '30D': -4, '90D': -12, 'YTD': -12, '12M': -12 };
  const cut = rangeMap[range];
  const sliced = (arr) => arr.slice(cut);

  // The chart honors its own range knob (independent of the KPI row).
  const chartCut = typeof chartRange === 'string' ? (rangeMap[chartRange] || -12) : -12;
  const chart = client.chartData.slice(chartCut);

  // Live data for RCG only. These hooks always fire but only return values
  // when client.isSelf AND the matching Supermetrics IDs are configured.
  // Until both conditions are met, each falls back to the mock numbers.
  //
  // The date range from the section-header RangeToggle (`range`) is passed
  // to each hook so changing it re-queries Supermetrics with the new window.
  const liveSessions = useGa4Total(client, 'Sessions', { dateRange: range });
  const liveAdSpend = useLinkedinAdsTotal(client, 'Cost', { dateRange: range });
  const liveEmail = useActiveCampaignTotals(client, ['campaigns__sends', 'campaigns__unique_opens'], { dateRange: range });
  const liveEmailSent = liveEmail.values.campaigns__sends;
  const liveEmailOpens = liveEmail.values.campaigns__unique_opens;

  const mockSessions = sliced(client.sessions).reduce((a, b) => a + b, 0);
  const mockAdSpend = sliced(client.adSpend).reduce((a, b) => a + b, 0);

  const sessionsValue = liveSessions.value != null ? liveSessions.value : mockSessions;
  const adSpendValue = liveAdSpend.value != null ? liveAdSpend.value : mockAdSpend;
  // For Open Rate: divide opens by sends. Both must be live AND sends > 0.
  const emailRateIsLive = liveEmailSent != null && liveEmailOpens != null && liveEmailSent > 0;
  const emailOpenRateValue = emailRateIsLive
    ? liveEmailOpens / liveEmailSent
    : client.kpis.emailOpen;

  return (
    <div className="tab-content" style={{ display: 'flex', flexDirection: 'column', gap: 28 }}>
      <TabHeader
        title={<>Overview<br /><span style={{ color: 'var(--brand-red)', fontStyle: 'italic', fontSize: '0.7em', textTransform: 'none' }}>{client.name}</span></>}
        desc="The state of the work. Numbers that matter, plain English on what changed, what to do next."
        right={<RangeToggle value={range} onChange={setRange} options={['7D', '30D', '90D', '12M']} />}
      />

      {/* KPI row */}
      <div className="kpi-grid">
        <KpiCell
          label={<span>Sessions {client.isSelf && <LivePill live={liveSessions.value != null} loading={liveSessions.loading} error={liveSessions.error} />}</span>}
          value={fmtNum(sessionsValue, { compact: true })}
          delta={client.sessionDelta}
          sparkData={sliced(client.sessions)}
        />
        <KpiCell label="Leads" value={fmtNum(sliced(client.leads).reduce((a, b) => a + b, 0))}
                 delta={client.leadDelta} sparkData={sliced(client.leads)} />
        <KpiCell
          label={<span>Ad Spend {client.isSelf && <LivePill live={liveAdSpend.value != null} loading={liveAdSpend.loading} error={liveAdSpend.error} />}</span>}
          value={fmtMoney(adSpendValue, { compact: true })}
          delta={client.spendDelta}
          sparkData={sliced(client.adSpend)}
        />
        <KpiCell
          label={<span>Email Open Rate {client.isSelf && <LivePill live={emailRateIsLive} loading={liveEmail.loading} error={liveEmail.error} />}</span>}
          value={fmtPct(emailOpenRateValue)}
          delta={-14.7}
          sparkData={[34, 33, 32, 30, 31, 29, 28, 29, 28, 27, 29, 29]}
        />
        <KpiCell label="Pipeline Value" value={fmtMoney(client.kpis.pipelineValue, { compact: true })}
                 delta={4.8} sparkData={[3200, 3400, 3500, 3380, 3700, 3920, 4100, 4180, 4220, 4320, 4380, 4462]} />
      </div>

      {/* Leads vs Ad Spend — with per-chart controls */}
      <Card
        title="Leads vs. Ad Spend"
        sub={`${chart.length} weeks. Adjust the controls to change what's shown.`}
        actions={
          <ChartControls
            dateRange={chartRange}
            onDateRangeChange={setChartRange}
            metrics={chartMetrics}
            onMetricsChange={setChartMetrics}
            availableMetrics={[
              { id: 'leads', label: 'Leads' },
              { id: 'spend', label: 'Ad Spend' },
              { id: 'sessions', label: 'Sessions' },
            ]}
            chartType={chartType}
            onChartTypeChange={setChartType}
            availableChartTypes={['combo', 'line', 'bar', 'area']}
          />
        }
        pad="lg"
      >
        <div style={{ width: '100%', height: 300, position: 'relative' }}>
          <RC.ResponsiveContainer>
            <RC.ComposedChart data={chart} margin={{ top: 10, right: 18, left: 0, bottom: 0 }}>
              <defs>
                <linearGradient id="spendGrad" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0%" stopColor={RICH_BLACK} stopOpacity={0.22} />
                  <stop offset="100%" stopColor={RICH_BLACK} stopOpacity={0.06} />
                </linearGradient>
                <linearGradient id="leadsArea" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0%" stopColor={BRAND_RED} stopOpacity={0.20} />
                  <stop offset="100%" stopColor={BRAND_RED} stopOpacity={0} />
                </linearGradient>
                <linearGradient id="sessionsArea" x1="0" y1="0" x2="0" y2="1">
                  <stop offset="0%" stopColor={DARK_GREEN} stopOpacity={0.18} />
                  <stop offset="100%" stopColor={DARK_GREEN} stopOpacity={0} />
                </linearGradient>
              </defs>
              <RC.CartesianGrid stroke={FAINT_RULE} vertical={false} strokeDasharray="2 4" />
              <RC.XAxis dataKey="week" {...axisProps} interval="preserveStartEnd" />
              <RC.YAxis yAxisId="leads" {...axisProps} tickFormatter={(v) => fmtNum(v)} width={44} />
              <RC.YAxis yAxisId="spend" orientation="right" {...axisProps}
                tickFormatter={(v) => '$' + (v / 1000).toFixed(0) + 'K'} width={44} />
              <RC.Tooltip
                cursor={{ fill: 'rgba(26,25,25,0.035)' }}
                content={(props) => (
                  <ChartTooltip
                    {...props}
                    valueFormatter={(v, key) => key === 'spend' ? fmtMoney(v) : fmtNum(v)}
                  />
                )}
              />
              <RC.Legend {...legendProps} />
              {chartMetrics.includes('spend') && renderSeries({
                kind: chartType === 'combo' ? 'bar' : chartType,
                yAxisId: 'spend', dataKey: 'spend', name: 'Ad Spend',
                stroke: RICH_BLACK, fill: 'url(#spendGrad)',
              })}
              {chartMetrics.includes('leads') && renderSeries({
                kind: chartType === 'combo' ? 'line' : chartType,
                yAxisId: 'leads', dataKey: 'leads', name: 'Leads',
                stroke: BRAND_RED, fill: 'url(#leadsArea)',
              })}
              {chartMetrics.includes('sessions') && renderSeries({
                kind: chartType === 'combo' ? 'line' : chartType,
                yAxisId: 'leads', dataKey: 'sessions', name: 'Sessions',
                stroke: DARK_GREEN, fill: 'url(#sessionsArea)',
              })}
            </RC.ComposedChart>
          </RC.ResponsiveContainer>
          {isChartEmpty(chart, ['leads', 'spend']) && <ChartEmptyOverlay />}
        </div>
      </Card>

      {/* This Month at a Glance */}
      <Card
        title="This month at a glance"
        sub="Editorial summary. Three lines, no fluff."
        actions={<Pill size="sm" variant="outline" onClick={() => onConnect && onConnect()}>Add note</Pill>}
        pad="lg"
      >
        <ul style={{ margin: 0, padding: 0, listStyle: 'none' }}>
          {client.monthAtAGlance.map((line, i) => (
            <li key={i} style={{
              padding: '14px 0 14px 22px',
              borderBottom: i < client.monthAtAGlance.length - 1 ? '1px solid var(--border)' : 'none',
              position: 'relative',
              fontSize: 14.5,
              color: 'var(--rich-black)',
              lineHeight: 1.55,
            }}>
              <span style={{
                position: 'absolute', left: 0, top: 24, width: 10, height: 1, background: BRAND_RED,
              }} />
              {line}
            </li>
          ))}
        </ul>
      </Card>
    </div>
  );
}
window.OverviewTab = OverviewTab;

// ── PAID MEDIA ────────────────────────────────────────────
function PaidTab({ client }) {
  const [range, setRange] = cuState('30D');
  // Per-chart state for "Spend by channel".
  const [chartRange, setChartRange] = cuState('30D');
  const [visibleChannels, setVisibleChannels] = cuState(['google', 'meta', 'linkedin']);
  const [channelChartType, setChannelChartType] = cuState('bar');
  const chartCutMap = { '7D': -2, '30D': -4, '90D': -12, '12M': -12, 'YTD': -12 };
  const chartCutPaid = chartCutMap[typeof chartRange === 'string' ? chartRange : '30D'] || -12;

  const totalSpend = client.totalSpend;
  const totalConv = client.totalConv;
  const cpc = 2.84;
  const ctr = 0.034;

  // Live ad spend via Supermetrics LinkedIn Ads (RCG only — clients stay on
  // mock until their own connectors are configured). All four queries share
  // the same date range from the section-header RangeToggle.
  const liveSpend = useLinkedinAdsTotal(client, 'Cost', { dateRange: range });
  const liveConv = useLinkedinAdsTotal(client, 'Conversions', { dateRange: range });
  const liveClicks = useLinkedinAdsTotal(client, 'Clicks', { dateRange: range });
  const liveImpressions = useLinkedinAdsTotal(client, 'Impressions', { dateRange: range });

  const totalSpendValue = liveSpend.value != null ? liveSpend.value : totalSpend;
  const totalConvValue = liveConv.value != null ? liveConv.value : totalConv;
  const liveCpc = (liveSpend.value && liveClicks.value) ? liveSpend.value / liveClicks.value : null;
  const liveCtr = (liveClicks.value && liveImpressions.value) ? liveClicks.value / liveImpressions.value : null;
  const cpcValue = liveCpc != null ? liveCpc : cpc;
  const ctrValue = liveCtr != null ? liveCtr : ctr;

  return (
    <div className="tab-content" style={{ display: 'flex', flexDirection: 'column', gap: 28 }}>
      <TabHeader title="Paid Media" desc="Spend across Google, Meta, LinkedIn. Top campaigns and conversion economics."
        right={<RangeToggle value={range} onChange={setRange} options={['7D', '30D', '90D', '12M']} />} />

      <div className="kpi-grid cols-4">
        <KpiCell
          label={<span>Total Spend {client.isSelf && <LivePill live={liveSpend.value != null} loading={liveSpend.loading} error={liveSpend.error} />}</span>}
          value={fmtMoney(totalSpendValue, { compact: true })}
          delta={client.spendDelta}
        />
        <KpiCell
          label={<span>Avg. CPC {client.isSelf && <LivePill live={liveCpc != null} loading={liveSpend.loading || liveClicks.loading} error={liveSpend.error || liveClicks.error} />}</span>}
          value={'$' + cpcValue.toFixed(2)}
          delta={-3.4}
        />
        <KpiCell
          label={<span>CTR {client.isSelf && <LivePill live={liveCtr != null} loading={liveClicks.loading || liveImpressions.loading} error={liveClicks.error || liveImpressions.error} />}</span>}
          value={fmtPct(ctrValue)}
          delta={5.1}
        />
        <KpiCell
          label={<span>Conversions {client.isSelf && <LivePill live={liveConv.value != null} loading={liveConv.loading} error={liveConv.error} />}</span>}
          value={fmtNum(totalConvValue)}
          delta={12.4}
        />
      </div>

      <Card
        title="Spend by channel"
        sub={client.isSelf ? "Live LinkedIn Ads weekly spend." : "Stacked weekly view across active channels."}
        actions={
          <ChartControls
            dateRange={chartRange}
            onDateRangeChange={setChartRange}
            metrics={visibleChannels}
            onMetricsChange={setVisibleChannels}
            availableMetrics={[
              { id: 'google', label: 'Google Ads' },
              { id: 'meta', label: 'Meta Ads' },
              { id: 'linkedin', label: 'LinkedIn Ads' },
            ]}
            chartType={channelChartType}
            onChartTypeChange={setChannelChartType}
            availableChartTypes={['bar', 'line', 'area']}
          />
        }
        pad="lg"
      >
        <div style={{ width: '100%', height: 320, position: 'relative' }}>
          <RC.ResponsiveContainer>
            <RC.ComposedChart data={client.channelData.slice(chartCutPaid)} margin={{ top: 10, right: 18, left: 0, bottom: 0 }}>
              <RC.CartesianGrid stroke={FAINT_RULE} vertical={false} strokeDasharray="2 4" />
              <RC.XAxis dataKey="week" {...axisProps} interval="preserveStartEnd" />
              <RC.YAxis {...axisProps} tickFormatter={(v) => '$' + (v / 1000).toFixed(0) + 'K'} width={48} />
              <RC.Tooltip
                cursor={{ fill: 'rgba(26,25,25,0.035)' }}
                content={(props) => <ChartTooltip {...props} valueFormatter={(v) => fmtMoney(v)} />}
              />
              <RC.Legend {...legendProps} />
              {visibleChannels.includes('google') && renderSeries({
                kind: channelChartType, yAxisId: undefined, dataKey: 'google', name: 'Google Ads',
                stroke: RICH_BLACK, fill: RICH_BLACK, stackId: channelChartType === 'bar' ? 'a' : undefined,
              })}
              {visibleChannels.includes('meta') && renderSeries({
                kind: channelChartType, yAxisId: undefined, dataKey: 'meta', name: 'Meta Ads',
                stroke: BRAND_RED, fill: BRAND_RED, stackId: channelChartType === 'bar' ? 'a' : undefined,
              })}
              {visibleChannels.includes('linkedin') && renderSeries({
                kind: channelChartType, yAxisId: undefined, dataKey: 'linkedin', name: 'LinkedIn Ads',
                stroke: DARK_GRAY, fill: DARK_GRAY, stackId: channelChartType === 'bar' ? 'a' : undefined,
              })}
            </RC.ComposedChart>
          </RC.ResponsiveContainer>
          {isChartEmpty(client.channelData, ['google', 'meta', 'linkedin']) && <ChartEmptyOverlay message="No paid spend in this period" />}
        </div>
      </Card>

      <Card title="Top campaigns" sub="Ranked by spend, last 30 days" pad="lg">
        <table className="tbl">
          <thead>
            <tr>
              <th>Channel</th>
              <th>Campaign</th>
              <th className="num-head">Spend</th>
              <th className="num-head">Conversions</th>
              <th className="num-head">CPA</th>
              <th>Status</th>
            </tr>
          </thead>
          <tbody>
            {client.campaigns.map((c, i) => (
              <tr key={i}>
                <td className="muted">{c.channel}</td>
                <td className="strong">{c.name}</td>
                <td className="num">{fmtMoney(c.spend)}</td>
                <td className="num">{c.conv}</td>
                <td className="num">{fmtMoney(c.cpa)}</td>
                <td><StatusChip status={c.status} /></td>
              </tr>
            ))}
          </tbody>
        </table>
      </Card>
    </div>
  );
}
window.PaidTab = PaidTab;

// ── EMAIL ─────────────────────────────────────────────────
function EmailTab({ client }) {
  const [range, setRange] = cuState('30D');
  // Live ActiveCampaign for RCG. ACT field names use DWH-style double
  // underscores. Single round-trip pulls every funnel value at once.
  const liveAc = useActiveCampaignTotals(client, [
    'campaigns__sends',
    'campaigns__total_opens',
    'campaigns__unique_opens',
    'campaigns__total_link_clicks',
    'campaigns__unique_link_clicks',
    'campaigns__unsubscribes',
    'campaigns__hard_bounces',
    'campaigns__soft_bounces',
  ], { dateRange: range });
  const acReady = client.isSelf && liveAc.values.campaigns__sends != null;
  const acLoading = client.isSelf && liveAc.loading;
  const acError = client.isSelf && liveAc.error;

  // Use live values where available, fall back to mock for the rest.
  // For the funnel + rates we use UNIQUE opens/clicks (one per recipient) —
  // total_opens can exceed sends (people re-open), which breaks both the
  // funnel narrative and percentage math.
  const mock = client.emailFunnel;
  const sentVal      = acReady ? liveAc.values.campaigns__sends             : mock.sent;
  const uniqueOpens  = acReady ? liveAc.values.campaigns__unique_opens      : mock.opened;
  const uniqueClicks = acReady ? liveAc.values.campaigns__unique_link_clicks: mock.clicked;
  const unsubVal     = acReady ? liveAc.values.campaigns__unsubscribes      : 0;
  const hardBounce   = acReady ? liveAc.values.campaigns__hard_bounces      : 0;
  const softBounce   = acReady ? liveAc.values.campaigns__soft_bounces      : 0;
  const deliveredVal = acReady ? Math.max(0, sentVal - hardBounce - softBounce) : mock.delivered;
  // ACT doesn't track "converted" without a CRM tie-in. On live data show
  // 0 (= "we don't know" — better than leaking the mock 412).
  const convertedVal = acReady ? 0 : mock.converted;

  const openRate  = deliveredVal > 0 ? uniqueOpens  / deliveredVal : 0;
  const clickRate = deliveredVal > 0 ? uniqueClicks / deliveredVal : 0;
  const unsubRate = sentVal > 0 ? unsubVal / sentVal : 0;

  const max = sentVal || 1;
  const funnel = [
    { stage: 'Sent',      val: sentVal,      red: false },
    { stage: 'Delivered', val: deliveredVal, red: false },
    { stage: 'Opened',    val: uniqueOpens,  red: false },
    { stage: 'Clicked',   val: uniqueClicks, red: true  },
    { stage: 'Converted', val: convertedVal, red: true  },
  ];

  return (
    <div className="tab-content" style={{ display: 'flex', flexDirection: 'column', gap: 28 }}>
      <TabHeader title="Email" desc="Drip and broadcast performance. Open and click are softening; the funnel says why."
        right={<RangeToggle value={range} onChange={setRange} options={['7D', '30D', '90D', '12M']} />} />

      <div className="kpi-grid cols-4">
        <KpiCell
          label={<span>Sent {client.isSelf && <LivePill live={acReady} loading={acLoading} error={acError} />}</span>}
          value={fmtNum(sentVal, { compact: true })}
          delta={8.4}
        />
        <KpiCell
          label={<span>Open Rate {client.isSelf && <LivePill live={acReady} loading={acLoading} error={acError} />}</span>}
          value={fmtPct(openRate)}
          delta={-4.2}
        />
        <KpiCell
          label={<span>Click Rate {client.isSelf && <LivePill live={acReady} loading={acLoading} error={acError} />}</span>}
          value={fmtPct(clickRate)}
          delta={-1.8}
        />
        <KpiCell
          label={<span>Unsubscribe {client.isSelf && <LivePill live={acReady} loading={acLoading} error={acError} />}</span>}
          value={fmtPct(unsubRate || 0.0042, { digits: 2 })}
          delta={0.4}
        />
      </div>

      <div className="row-12">
        <Card title="Conversion funnel" sub="Sent → Converted, last 30 days" pad="lg">
          {funnel.map((row, i) => {
            const pct = (row.val / max) * 100;
            const dropFromPrev = i > 0 ? (1 - row.val / funnel[i - 1].val) * 100 : 0;
            return (
              <div className={`funnel-row ${row.red ? 'red' : ''}`} key={row.stage}>
                <div className="h">
                  <span className="stage">{row.stage}</span>
                  <span className="val tnum">{fmtNum(row.val)}</span>
                  <span className="pct">{i > 0 ? `−${dropFromPrev.toFixed(1)}% step` : '100% of send'}</span>
                </div>
                <div className="bar">
                  <div className="fill" style={{ width: pct + '%' }} />
                </div>
              </div>
            );
          })}
        </Card>

        <Card title="Top sequences" pad="lg">
          <div className="kv-list">
            {client.emailSequences.map((s, i) => (
              <div className="row" key={i} style={{ display: 'flex', flexDirection: 'column', gap: 4, padding: '12px 0', borderBottom: i < client.emailSequences.length - 1 ? '1px solid var(--border)' : 'none' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', gap: 12 }}>
                  <span className="name strong">{s.name}</span>
                  <span style={{ color: s.trend === 'up' ? DARK_GREEN : s.trend === 'down' ? BRAND_RED : 'var(--ink-stone)', fontSize: 11 }}>
                    <Icon name={s.trend === 'up' ? 'ArrowUpRight' : s.trend === 'down' ? 'ArrowDownRight' : 'ArrowRight'} size={12} />
                  </span>
                </div>
                <div style={{ display: 'flex', gap: 18, fontSize: 11.5, color: 'var(--ink-stone)' }}>
                  <span>{fmtNum(s.sent)} sent</span>
                  <span>{fmtPct(s.openRate)} open</span>
                  <span>{fmtPct(s.clickRate)} click</span>
                </div>
              </div>
            ))}
          </div>
        </Card>
      </div>
    </div>
  );
}
window.EmailTab = EmailTab;

// ── WEBSITE / SEO ─────────────────────────────────────────
function WebSeoTab({ client }) {
  const [range, setRange] = cuState('30D');
  // Live GA4 KPIs (RCG only). Sessions + Conversions are single-field totals.
  const liveSessions = useGa4Total(client, 'Sessions', { dateRange: range });
  const liveConv = useGa4Total(client, 'Conversions', { dateRange: range });
  // Live Top Pages table — GA4 with Pagepath + Sessions + Bouncerate.
  const livePages = useGa4Rows(client, ['Pagepath'], ['Sessions', 'Bouncerate'], { limit: 10, dateRange: range });
  const hasLivePages = client.isSelf && livePages.rows && livePages.rows.length > 0;

  const sessionsValue = liveSessions.value != null ? liveSessions.value : client.totalSessions;
  const convValue = liveConv.value != null ? liveConv.value : client.totalConv;
  // Top page = first row of the live result, or the mock top page.
  const topPageLabel = hasLivePages ? livePages.rows[0][0] : '/solutions';

  return (
    <div className="tab-content" style={{ display: 'flex', flexDirection: 'column', gap: 28 }}>
      <TabHeader title="Website & SEO" desc="Organic traffic, top pages, search position. Plus a quick read on CMS health."
        right={<RangeToggle value={range} onChange={setRange} options={['7D', '30D', '90D', '12M']} />} />

      <div className="kpi-grid cols-4">
        <KpiCell
          label={<span>Sessions {client.isSelf && <LivePill live={liveSessions.value != null} loading={liveSessions.loading} error={liveSessions.error} />}</span>}
          value={fmtNum(sessionsValue, { compact: true })}
          delta={client.sessionDelta}
        />
        <KpiCell
          label={<span>Top Page {client.isSelf && <LivePill live={hasLivePages} loading={livePages.loading} error={livePages.error} />}</span>}
          value={topPageLabel}
        />
        <KpiCell label="Avg. Position" value="5.8" delta={-12.4} />
        <KpiCell
          label={<span>Conversions {client.isSelf && <LivePill live={liveConv.value != null} loading={liveConv.loading} error={liveConv.error} />}</span>}
          value={fmtNum(convValue)}
          delta={12.4}
        />
      </div>

      <div className="row-2">
        <Card
          title={<span>Top pages {client.isSelf && <LivePill live={hasLivePages} loading={livePages.loading} error={livePages.error} />}</span>}
          sub={client.isSelf ? `Live from Google Analytics 4 — filtered to ${getHostnameLabel(client)}` : null}
          pad="lg"
        >
          <table className="tbl">
            <thead>
              <tr>
                <th>Path</th>
                <th className="num-head">Sessions</th>
                <th className="num-head">Bounce</th>
              </tr>
            </thead>
            <tbody>
              {(hasLivePages
                ? livePages.rows.map((r, i) => ({
                    path: r[0],
                    sessions: Number(r[1]) || 0,
                    bounce: Number(r[2]) || 0,
                  }))
                : client.topPages
              ).map((p, i) => (
                <tr key={i}>
                  <td className="strong" style={{ maxWidth: 320, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{p.path}</td>
                  <td className="num">{fmtNum(p.sessions)}</td>
                  <td className="num">{fmtPct(p.bounce)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </Card>

        <Card
          title="Top queries"
          sub={client.isSelf ? "Connect Google Search Console in Supermetrics to populate this card with live data." : null}
          pad="lg"
        >
          <table className="tbl">
            <thead>
              <tr>
                <th>Query</th>
                <th className="num-head">Impr.</th>
                <th className="num-head">Clicks</th>
                <th className="num-head">Pos.</th>
              </tr>
            </thead>
            <tbody>
              {client.topQueries.map((q, i) => (
                <tr key={i}>
                  <td className="strong" style={{ maxWidth: 240, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{q.q}</td>
                  <td className="num">{fmtNum(q.imp)}</td>
                  <td className="num">{fmtNum(q.clicks)}</td>
                  <td className="num">{q.pos.toFixed(1)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </Card>
      </div>

      <Card title="Magnolia CMS health" sub="Publishing cadence, form capture, and structural integrity." pad="lg">
        <div className="row-3">
          <div style={{ padding: '16px 0' }}>
            <div className="kpi-label">Pages Published</div>
            <div className="kpi-value tnum" style={{ marginTop: 6 }}>{client.cmsHealth.published}</div>
            <div style={{ fontSize: 11.5, color: 'var(--ink-stone)', marginTop: 6 }}>This month. Up from 5 last month.</div>
          </div>
          <div style={{ padding: '16px 0', borderLeft: '1px solid var(--soft-rule)', paddingLeft: 24 }}>
            <div className="kpi-label">Form Submissions</div>
            <div className="kpi-value tnum" style={{ marginTop: 6 }}>{client.cmsHealth.formSubs}</div>
            <div style={{ fontSize: 11.5, color: 'var(--ink-stone)', marginTop: 6 }}>Across 4 lead capture forms.</div>
          </div>
          <div style={{ padding: '16px 0', borderLeft: '1px solid var(--soft-rule)', paddingLeft: 24 }}>
            <div className="kpi-label">Broken Links</div>
            <div className="kpi-value tnum" style={{ marginTop: 6, color: client.cmsHealth.brokenLinks > 0 ? BRAND_RED : 'inherit' }}>
              {client.cmsHealth.brokenLinks}
            </div>
            <div style={{ fontSize: 11.5, color: 'var(--ink-stone)', marginTop: 6 }}>Detected in last crawl. Worth a fix.</div>
          </div>
        </div>
      </Card>
    </div>
  );
}
window.WebSeoTab = WebSeoTab;

// ── CRM ───────────────────────────────────────────────────
function CrmTab({ client }) {
  const PIE_COLORS = [RICH_BLACK, BRAND_RED, DARK_GREEN, DARK_BLUE, DARK_GRAY, '#B4B5B4'];
  const max = client.pipelineStages[0].count;

  return (
    <div className="tab-content" style={{ display: 'flex', flexDirection: 'column', gap: 28 }}>
      <TabHeader title="CRM Pipeline" desc="From inbound lead to closed-won. Where deals sit, who owns them, where they came from." />

      <div className="row-12">
        <Card title="Pipeline funnel" sub="By stage count, last 90 days" pad="lg">
          {client.pipelineStages.map((s, i) => {
            const pct = (s.count / max) * 100;
            return (
              <div className={`funnel-row ${i === client.pipelineStages.length - 1 ? 'red' : ''}`} key={s.stage}>
                <div className="h">
                  <span className="stage">{s.stage}</span>
                  <span className="val tnum">{fmtNum(s.count)}</span>
                  <span className="pct">{fmtMoney(s.value, { compact: true })}</span>
                </div>
                <div className="bar"><div className="fill" style={{ width: pct + '%' }} /></div>
              </div>
            );
          })}
        </Card>

        <Card title="Lead source mix" sub="Last 30 days" pad="lg">
          <div style={{ width: '100%', height: 200 }}>
            <RC.ResponsiveContainer>
              <RC.PieChart>
                <RC.Pie
                  data={client.leadSources} dataKey="value" nameKey="name"
                  innerRadius={48} outerRadius={78} paddingAngle={1}
                >
                  {client.leadSources.map((_, i) => (
                    <RC.Cell key={i} fill={PIE_COLORS[i % PIE_COLORS.length]} stroke="var(--white)" strokeWidth={2} />
                  ))}
                </RC.Pie>
                <RC.Tooltip formatter={(v) => v + '%'} />
              </RC.PieChart>
            </RC.ResponsiveContainer>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6px 18px', marginTop: 8, fontSize: 11.5 }}>
            {client.leadSources.map((s, i) => (
              <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ width: 8, height: 8, background: PIE_COLORS[i % PIE_COLORS.length], borderRadius: 999 }} />
                <span style={{ flex: 1, color: 'var(--rich-black)' }}>{s.name}</span>
                <span className="txt-muted tnum">{s.value}%</span>
              </div>
            ))}
          </div>
        </Card>
      </div>

      <Card title="Recent deals" pad="lg">
        <table className="tbl">
          <thead>
            <tr>
              <th>Company</th>
              <th>Stage</th>
              <th className="num-head">Value</th>
              <th>Owner</th>
              <th>Source</th>
            </tr>
          </thead>
          <tbody>
            {client.recentDeals.map((d, i) => (
              <tr key={i}>
                <td className="strong">{d.company}</td>
                <td><span className="chip">{d.stage}</span></td>
                <td className="num">{fmtMoney(d.value)}</td>
                <td>{d.owner}</td>
                <td className="muted">{d.source}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </Card>
    </div>
  );
}
window.CrmTab = CrmTab;

// ── MONTHLY REPORT ────────────────────────────────────────
function MonthlyReportTab({ client, pushToast }) {
  const allKpis = [
    { label: 'Sessions', value: fmtNum(client.totalSessions, { compact: true }), prev: '142K', delta: client.sessionDelta },
    { label: 'Leads', value: fmtNum(client.totalLeads), prev: '2,140', delta: client.leadDelta },
    { label: 'Ad spend', value: fmtMoney(client.totalSpend, { compact: true }), prev: '$178K', delta: client.spendDelta },
    { label: 'Cost per lead', value: '$71', prev: '$87', delta: -18.4 },
    { label: 'Email open rate', value: '29%', prev: '34%', delta: -14.7 },
    { label: 'Email click rate', value: '6.1%', prev: '7.9%', delta: -22.8 },
    { label: 'Pipeline value', value: fmtMoney(client.kpis.pipelineValue, { compact: true }), prev: '$4.25M', delta: 4.8 },
    { label: 'Won deals', value: '8', prev: '6', delta: 33.3 },
  ];

  return (
    <div className="tab-content">
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 18, gap: 16 }}>
        <p className="meta">Read-only client export</p>
        <Pill variant="filled" onClick={() => { console.log('PDF download'); pushToast('Report download started.', 'success'); }}>
          <Icon name="ArrowDownToLine" size={12} style={{ marginRight: 6 }} /> Download PDF
        </Pill>
      </div>

      <article className="report-card">
        <div style={{ paddingBottom: 24, borderBottom: '1px solid var(--soft-rule)', marginBottom: 32 }}>
          <p className="kicker">Monthly performance report</p>
          <h2 style={{
            fontFamily: 'var(--serif)', fontWeight: 400, fontSize: 38, lineHeight: 0.95,
            textTransform: 'uppercase', letterSpacing: '-0.025em', margin: '12px 0 6px',
          }}>
            {client.name} <span style={{ color: BRAND_RED, fontStyle: 'italic', textTransform: 'none' }}>May 2026</span>
          </h2>
          <p style={{ fontSize: 12.5, color: 'var(--ink-stone)', margin: 0 }}>
            Prepared by Rottman Creative · Account lead: {client.accountLead}
          </p>
        </div>

        <section className="report-section">
          <h3>Executive Summary</h3>
          <p style={{ fontSize: 14.5, lineHeight: 1.65, color: 'var(--rich-black)', maxWidth: '60ch' }}>
            Paid acquisition continued to scale efficiently in May. Cost per lead dropped from $87 to $71 while
            ad spend grew {Math.round(client.spendDelta)}% month over month, putting the program ahead of plan.
            Email engagement is the one soft spot. Open rate slid five points and the click rate is following.
            The pipeline added $214K and {client.industry === 'Manufacturing' ? 'two' : 'three'} deals are
            now sitting in proposal stage longer than 14 days.
          </p>
        </section>

        <section className="report-section">
          <h3>Wins</h3>
          <ul>
            <li><strong>CPL down 18%</strong>. Tightening creative and pausing the cold Meta audience moved CPL from $87 to $71.</li>
            <li><strong>LinkedIn conversions up 64%</strong>. The whitepaper sequence is now the highest-converting paid asset.</li>
            <li><strong>Organic sessions up 22%</strong>. The buyer-guide post is ranking on page one for three target queries.</li>
          </ul>
        </section>

        <section className="report-section">
          <h3>Challenges</h3>
          <ul>
            <li><strong>Email engagement down across the board</strong>. Open rate from 34% to 29%. Likely list fatigue plus iOS Mail Privacy noise.</li>
            <li><strong>Two stalled deals in proposal stage</strong>. Apex Industrial and Meridian Logistics. Both quiet for 14+ days.</li>
          </ul>
        </section>

        <section className="report-section">
          <h3>Recommendations</h3>
          <ul>
            <li>Run a 30-day list cleanse and segment the inactive cohort into a re-engagement sequence.</li>
            <li>Move a third of cold Meta budget into LinkedIn ABM for tier-one accounts.</li>
            <li>Get the proposal-stage deals on the calendar this week. We will draft talking points.</li>
          </ul>
        </section>

        <section className="report-section">
          <h3>Detailed Metrics</h3>
          <table className="tbl" style={{ marginTop: 8 }}>
            <thead>
              <tr>
                <th>Metric</th>
                <th className="num-head">This month</th>
                <th className="num-head">Last month</th>
                <th className="num-head">Change</th>
              </tr>
            </thead>
            <tbody>
              {allKpis.map((k, i) => (
                <tr key={i}>
                  <td className="strong">{k.label}</td>
                  <td className="num tnum">{k.value}</td>
                  <td className="num tnum muted">{k.prev}</td>
                  <td className="num"><Delta value={k.delta} /></td>
                </tr>
              ))}
            </tbody>
          </table>
        </section>
      </article>
    </div>
  );
}
window.MonthlyReportTab = MonthlyReportTab;

Object.assign(window, { OverviewTab, PaidTab, EmailTab, WebSeoTab, CrmTab, MonthlyReportTab });
