=== JS diff (app.js only, should be sidebar fix + logo badge) ===
60a61,66
>   } else if (opts.body && typeof opts.body === "object" &&
>              !(opts.body instanceof FormData) && !(opts.body instanceof Blob) &&
>              !(opts.body instanceof URLSearchParams) && !(opts.body instanceof ArrayBuffer)) {
>     // plain object → JSON body (guards against accidental "[object Object]" sends)
>     headers["Content-Type"] = "application/json";
>     opts.body = JSON.stringify(opts.body);
71,72c77,89
<     const detail = data && (data.detail?.message || data.detail || data.error);
<     const err = new Error(typeof detail === "string" ? detail : (res.status + " " + res.statusText));
---
>     let detail = data && (data.detail?.message || data.detail || data.error);
>     // Pydantic 422: detail is [{loc:[...], msg:'...'}] — surface the msg
>     if (Array.isArray(detail)) {
>       detail = detail.map(e => {
>         let field = Array.isArray(e?.loc) ? e.loc.filter(x => typeof x === "string").join(".") : "";
>         field = field.replace(/^body\./, "");
>         let msg = e?.msg || String(e);
>         msg = msg.replace(/^Value error,\s*/i, "");
>         return (field ? field + ": " : "") + msg;
>       }).join("; ");
>     }
>     if (detail && typeof detail === "object") detail = JSON.stringify(detail);
>     const err = new Error(typeof detail === "string" && detail ? detail : (res.status + " " + res.statusText));
182a200,206
>   // The login page has its own full-page layout (no sidebar chrome) even
>   // for an already-authenticated session that happens to land here.
>   if (location.pathname.startsWith("/login")) {
>     if (window.pageBoot) window.pageBoot(me);
>     return me;
>   }
> 
185,186c209,210
<     el("span", { class: "brand-badge" }, "M"),
<     el("span", {}, "Bulk Mailer")));
---
>     el("span", { class: "brand-badge", html: '<img src="/static/img/logo-badge.png" alt="Ravan Mailer">' }),
>     el("span", { class: "brand-name" }, "Ravan Mailer")));
=== CSS diff line count ===
106
=== confirm no python files touched ===
Files /tmp/orig_check/bulkmailer/app/api/routers/attachments.py and /workspace/bulkmailer/app/api/routers/attachments.py differ
Files /tmp/orig_check/bulkmailer/app/api/routers/auth.py and /workspace/bulkmailer/app/api/routers/auth.py differ
Files /tmp/orig_check/bulkmailer/app/api/routers/campaigns.py and /workspace/bulkmailer/app/api/routers/campaigns.py differ
Files /tmp/orig_check/bulkmailer/app/api/routers/logs.py and /workspace/bulkmailer/app/api/routers/logs.py differ
Files /tmp/orig_check/bulkmailer/app/api/routers/phones.py and /workspace/bulkmailer/app/api/routers/phones.py differ
Files /tmp/orig_check/bulkmailer/app/api/routers/subjects.py and /workspace/bulkmailer/app/api/routers/subjects.py differ
Files /tmp/orig_check/bulkmailer/app/api/routers/templates.py and /workspace/bulkmailer/app/api/routers/templates.py differ
Files /tmp/orig_check/bulkmailer/app/main.py and /workspace/bulkmailer/app/main.py differ
Files /tmp/orig_check/bulkmailer/app/services/gmail_service.py and /workspace/bulkmailer/app/services/gmail_service.py differ
Files /tmp/orig_check/bulkmailer/app/services/message_builder.py and /workspace/bulkmailer/app/services/message_builder.py differ
Files /tmp/orig_check/bulkmailer/app/tasks/send_tasks.py and /workspace/bulkmailer/app/tasks/send_tasks.py differ
