# Report polish (items 8–11) — apply

    cp -R report-polish/app/*       ./app/
    cp -R report-polish/resources/* ./resources/
    cp -R report-polish/tests/*     ./tests/

## 1. Routes — inside the auth group:
    Route::get('/portfolio/freshness', [\App\Http\Controllers\DataRefreshController::class, 'status'])->name('portfolio.freshness');
    Route::post('/portfolio/refresh', [\App\Http\Controllers\DataRefreshController::class, 'refresh'])->name('portfolio.refresh');

## 2. PortfolioController — share freshness + the renewal breakdown

In overview(), add to the Inertia payload:

    'freshness' => app(\App\Analytics\Services\DataFreshnessService::class)->summary(),
    'expiringBreakdown' => app(\App\Analytics\Services\ProjectionService::class)
        ->retentionSummary(null, $window)['counts'],

## 3. overview.tsx
    import { ReportHeader } from '@/components/portfolio/ReportHeader';
    import { ExpiringTooltip } from '@/components/portfolio/ExpiringTooltip';

    Render <ReportHeader freshness={freshness} /> just under <PortfolioTabs />.
    Replace the company-totals "{totals.expiring_soon} exp ≤ {win}d" with:
        <ExpiringTooltip count={totals.expiring_soon} days={win} breakdown={expiringBreakdown} />

## 4. Paid vs Org chart — see docs/paid-vs-org-patch.md (items 10 & 11)

## 5. Test + build
    php artisan test --filter=ReportPolish
    node node_modules/vite/bin/vite.js build

## What this adds
- 8: "Updated X ago" with a per-source breakdown on click, and a Refresh button that
  re-syncs every connected account. Stale sources (>24h) are flagged amber.
- 9: hovering "N exp ≤90d" reveals the renewal split (1st/2nd/3rd/long-term).
- 10: paid-target reference line with ★ markers on months that beat it.
- 11: Google and Facebook averages alongside paid/organic.
