    def test_preview_renders_real_messages(self, api, db):
        f = _seed_campaign_fixture(db, user_id_of(api), n_recipients=3)
        cid = api.post("/campaigns/create", headers=H(api),
                       json=_cIn(f, name="Prev")).json()["id"]
        r = api.post(f"/campaigns/{cid}/preview?sample_size=3", headers=H(api))
        assert r.status_code == 200, r.text
        d = r.json()
        assert d["sample_size"] == 3
        msgs = d["messages"]
        assert len(msgs) == 3
        m0 = msgs[0]
        assert "Camp1" in m0["body_html"]          # substituted first name
        assert "%FirstName%" not in m0["body_html"]  # no raw tokens remain
        assert "camp-acc" in m0["from_account"]     # real account email
        assert m0["subject"]                        # subject rendered
        # invoice numbers unique per recipient
        invs = [m["raw_variables"]["invoice_number"] for m in msgs]
        assert len(set(invs)) == len(invs)
        assert all(m["amount"] is not None for m in msgs)
        # preview must NOT create campaign_recipients (nothing persisted)
        from app.models import CampaignRecipient
        assert db.query(CampaignRecipient).count() == 0

===
262:@router.post("/clear-all")
263:async def clear_all_campaigns(db: AsyncSession = Depends(get_db),
@router.post("/clear-all")
async def clear_all_campaigns(db: AsyncSession = Depends(get_db),
                              user: User = Depends(get_current_user),
                              _: None = Depends(require_csrf)):
    """Delete ALL campaigns (not running ones). Requires explicit UI confirmation."""
    rows = (await db.execute(select(Campaign).where(Campaign.user_id == user.id))).scalars().all()
    deleted, skipped = 0, 0
    for c in rows:
        if c.status == "running":
            skipped += 1
            continue
        await db.delete(c)
        deleted += 1
    await db.commit()
    await log_audit(db, user.id, "campaign.clear_all", "campaign", None,
                    {"deleted": deleted, "skipped": skipped})
    return {"deleted": deleted, "skipped": skipped}


# ---------------------------------------------------------------------------
# Preview: materialize the campaign plan and render REAL messages (no sending)
# ---------------------------------------------------------------------------

@router.post("/{cid}/preview")
async def preview_campaign(cid: int, sample_size: int = Query(0),
                           db: AsyncSession = Depends(get_db),
