# Paid vs Org: target line, star markers, per-channel averages

## 1. PipelineAnalyticsService::paidVsOrg() — replace the return block

Add a paid target (the trailing average lifted by a configurable stretch factor) and
per-channel averages split out of `by_source`:

        $paidAvg = $avg('paid_leads');
        $orgAvg = $avg('org_leads');
        $target = round($paidAvg * (float) config('pipeline-analytics.paid_target_multiplier', 1.54), 1);

        $channel = fn (string $needle) => round(
            $this->leads($location)
                ->filter(fn ($l) => ($t = $this->ts($l, 'created')) && $t->gte(CarbonImmutable::now()->subMonths($months)))
                ->filter(fn ($l) => str_contains(strtolower($l['LeadSource'] ?? ''), $needle))
                ->count() / $n, 1);

        return [
            'paid_average' => $paidAvg,
            'organic_average' => $orgAvg,
            'google_average' => $channel('google'),
            'facebook_average' => $channel('facebook'),
            'ratio' => $orgAvg > 0 ? round($paidAvg / $orgAvg, 2) : null,
            'paid_target' => $target,
            'by_source' => $bySource->sortDesc()->all(),
            'buckets' => $trend['buckets'],
            'paid' => $trend['series']['paid_leads'],
            'org' => $trend['series']['org_leads'],
            // Months where paid acquisition beat the target — starred in the chart.
            'above_target' => collect($trend['series']['paid_leads'])
                ->map(fn ($v, $i) => $v >= $target ? $trend['buckets'][$i] : null)
                ->filter()->values()->all(),
        ];

## 2. config/pipeline-analytics.php — add

    'paid_target_multiplier' => 1.54,   // trailing paid average × this = target

## 3. pipeline-analytics.tsx — the Paid vs Org view

Replace the stats strip with:

    {view === 'paid_vs_org' && (
        <div className="mb-3 flex flex-wrap gap-4 text-xs">
            <span className="text-destructive">Paid average: <strong>{paidVsOrg.paid_average}</strong></span>
            <span className="text-emerald-600">Organic average: <strong>{paidVsOrg.organic_average}</strong></span>
            <span className="text-amber-600">Google average: <strong>{paidVsOrg.google_average}</strong></span>
            <span className="text-blue-600">Facebook average: <strong>{paidVsOrg.facebook_average}</strong></span>
            <span className="text-muted-foreground">Paid : Org ratio: <strong>{paidVsOrg.ratio}</strong></span>
            <span className="text-primary">Paid target: <strong>{paidVsOrg.paid_target}</strong></span>
        </div>
    )}

and inside <LineChart> when the paid-vs-org view is active:

    {view === 'paid_vs_org' && (
        <>
            <ReferenceLine y={paidVsOrg.paid_target} stroke="#C99A2E" strokeWidth={2}
                label={{ value: 'target', position: 'right', fontSize: 10 }} />
            <ReferenceLine y={paidVsOrg.paid_average} stroke="#DC2626" strokeDasharray="4 4" />
            <ReferenceLine y={paidVsOrg.organic_average} stroke="#10B981" strokeDasharray="4 4" />
        </>
    )}

Star the above-target points by giving the paid Line a custom dot:

    <Line dataKey="paid" stroke="#DC2626" strokeWidth={2}
        dot={(props) => {
            const hit = paidVsOrg.above_target.includes(props.payload.bucket);
            return hit
                ? <text x={props.cx} y={props.cy + 5} textAnchor="middle" fontSize={16} fill="#C99A2E">★</text>
                : <circle cx={props.cx} cy={props.cy} r={3} fill="#DC2626" />;
        }} />

`ReferenceLine` is already imported in that file.
