    |     return await dependant.call(**values)
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/workspace/bulkmailer/app/api/routers/campaigns.py", line 338, in preview_campaign
    |     res = engine.substitute(tpl.body_html, rec, g,
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |   File "/workspace/bulkmailer/app/services/variable_engine.py", line 142, in substitute
    |     pool = dict(recipient)
    |            ^^^^^^^^^^^^^^^
    | TypeError: 'Recipient' object is not iterable
--
    return await dependant.call(**values)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/workspace/bulkmailer/app/api/routers/campaigns.py", line 338, in preview_campaign
    res = engine.substitute(tpl.body_html, rec, g,
===
201:async def materialize_campaign(db: AsyncSession, campaign: Campaign, recipient_rows: Sequence[Recipient],
201:async def materialize_campaign(db: AsyncSession, campaign: Campaign, recipient_rows: Sequence[Recipient],
202-                               preview: bool = False, preview_size: int = 0) -> dict:
203-    """Create CampaignRecipient rows with generated values & assignments.
204-
205-    This is the core deterministic plan: which subject/template/phone/account each recipient gets,
206-    plus generated invoice numbers (unique per campaign), amounts, shipping.
207-    """
208-    subjects, templates, phones, accounts = await get_campaign_entities(db, campaign)
209-
210-    var_cfg = dict(campaign.variable_config or {})
211-    engine = VariableEngine(var_cfg)
212-
213-    subj_sel = SubjectSelector(subjects, campaign.subject_rotation)
