93ac0c240f
Grid detail (G6): replace the single row.notes blob with a NoteTimeline fed by a new investor-level read, GET /api/communications?source_row_id=<grid row id> (filter maps the grid row -> fundraising_investors.source_row_id -> fundraising_contacts.contact_id -> communications, soft-delete-respecting; cancelled-flag fetch + commsReload after a log). Note-logging now uses the shared LogCommunicationSheet, retiring the bespoke noteForm select sheet and the dead .fs-note-log style. New MobileQuickLog: a shell mobile-top-bar pencil reachable from every tab — two-step sheet (pick investor via search + recent-first pool -> inline log form) writing through the one-row /api/fundraising/log-communication path. source_row_id and contact_id are kept mutually exclusive in handle_list_communications so a future caller passing both can't get the empty intersection. Guarded by test_grid_comm_timeline.py (cross-contact aggregation, investor isolation, soft-delete); 39/39 backend green.