    if data.template_sequence_mode not in VALID_ENUMS["template_sequence_mode"]:
        errors.append("template_sequence_mode must be repeat|stop|random")
    if data.phone_selection not in VALID_ENUMS["phone_selection"]:
        errors.append("phone_selection must be round_robin|random|fixed")

    if data.recipient_list_id:
        rl = await db.get(RecipientList, data.recipient_list_id)
        if not rl or rl.user_id != user.id:
            errors.append("Recipient list not found")
        else:
            rc = (await db.execute(select(func.count()).select_from(Recipient)
                                   .where(Recipient.list_id == rl.id,
                                          Recipient.is_valid == True))).scalar() or 0  # noqa: E712
            if rc == 0:
                errors.append("Recipient list is empty")
            if rc > cfg.MAX_RECIPIENTS_PER_CAMPAIGN:
                errors.append(f"Recipient list has {rc} entries; max {cfg.MAX_RECIPIENTS_PER_CAMPAIGN}")

    for sid in data.subject_set_ids or []:
        s = await db.get(SubjectLine, sid)
        if not s or s.user_id != user.id:
            errors.append(f"Subject line {sid} not found")
    if not (data.subject_set_ids or []):
        errors.append("Select at least one subject line")

    for tid in data.template_ids or []:
        t = await db.get(EmailTemplate, tid)
        if not t or t.user_id != user.id:
            errors.append(f"Template {tid} not found")
    if not (data.template_ids or []):
        errors.append("Select at least one HTML template")

    if not data.account_ids:
        errors.append("Select at least one Gmail account")
    elif len(data.account_ids) > cfg.MAX_GMAIL_ACCOUNTS_PER_CAMPAIGN:
        errors.append(f"Max {cfg.MAX_GMAIL_ACCOUNTS_PER_CAMPAIGN} accounts per campaign")
    else:
        for aid in data.account_ids:
            acc = await db.get(GmailAccount, aid)
            if not acc or acc.user_id != user.id:
                errors.append(f"Gmail account {aid} not found")
            elif acc.status != "authorized" or not acc.is_active:
                errors.append(f"Account {acc.email} is not authorized/active")

    if data.attach_mode == "pdf":
        if not (data.attach_pdf or data.attach_pdf_template_id or data.attachment_id):
            errors.append("PDF attachment mode requires a template or attachment to convert")
    if data.attach_mode in ("file", "image") and not data.attachment_id:
        errors.append("File/image attachment mode requires selecting an attachment")

    if len(data.rotation_weights or {}) > 0 and data.rotation_strategy != "weighted":
