Ops event page overhaul (hub lib/events.js + email.js, ops EventsPage.vue + api):
- Create/edit/delete events (persisted config shadowing the seeded SEED_CONTENT);
labels factored into shared LABELS, content editable bilingual (EN falls back to FR).
- Capacity cap by headcount: public RSVP form closes with a "booking is full" message
when reached (server-enforced); existing registrants can still update.
- Email attachments (image/PDF, <=4MB, <=6), tagged per language (fr/en/both) so each
customer gets files in their account language; optional Gemini auto-detect on images.
email.sendEmail gains a generic attachments[] param.
- Additive main audience list (persisted per event): build one deduped list by appending
batches from filters, manual picks, or CSV; per-source breakdown, remove/clear.
- Filters: name-contains (customer_name like) and city (Service Location.city, resolved
accent/case-insensitively via PG with REST fallback), intersected with other criteria.
- Manual picker + fuzzy search reuse app-wide /collab/customer-search (pg_trgm), so
"repa" finds "Réparation …".
- nginx /hub/ proxy: client_max_body_size 8m for base64 attachment uploads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>