componentsfix(statistics): follow-up time is silence, and a meeting row admits it is hoverable Follow-up time counted a REPLY as a follow-up. It walked the outbound emails alone and measured every gap between them, so "you write, they answer next morning, you answer back within the minute" scored as a ~20h follow-up made entirely of THEIR reply latency. On a real 30-day window 25 of 29 gaps were that shape, holding the median at 21h52m while the true chase-into-silence median was 41 minutes — the metric moved with how fast prospects wrote back, not with anything the rep did. Now a gap counts only between TWO OUTBOUND EMAILS IN A ROW: the window carries both directions and an inbound reply between two sends breaks the pair. Same rule in all four places that computed it — the shared helper, the cadence samples the reasoning layer reads, the per-conversation column and the per-rep team view — so no surface disagrees with another. A meeting between two emails leaves the pair intact; meeting→email is its own metric. The number gets honest and much smaller (13m median on the same window), and its sample gets thin, which is the metric telling the truth rather than padding itself. The rail's Statistics tile: "21h 52m" wrapped inside the shared fixed-width number column and read as two figures, so the duration column now sizes to its content, and demo mode swaps in fixed values instead of showing real figures on a shared screen. The meetings widget lost its hover fill because `hover:bg-accent/50` IS `--surface-raised` in dark mode — the exact colour of the card behind it. It is a sunken well now. The row also had `w-full` fighting its own `-mx-2`: a negative margin cannot widen a box pinned at 100%, so the row shifted 8px left instead of bleeding both ways, leaving 4px of fill on one side and 20px on the other. Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 31, 2026, 12:25 AMhooksfeat: support multiple CRM opportunities per conversation Allows an unlimited number of CRM deals to be linked to a single conversation (previously one per provider was enforced). The agent sees all linked opps with their name and stage, and can target a specific deal when updating fields. - linkExternalCrmEventToConversation: append new entries instead of overwriting; account-match placeholders still merge correctly - ExternalCrmIntegrationMetadata: add dealName/dealStage fields, refreshed on every sync event - unlinkExternalCrm: now per-deal (provider + optional dealId) instead of nuking all entries - updateExternalCrmWorkflow / upsertWorkingMemory: accept targetDealId to push fields to a specific linked opp - updateConversationFieldsTool: accepts targetDealId, threaded through syncFieldsToExternalCrmAtomic - moveCrmDeal: fix instanceUrl lookup when multiple opps from same provider - Agent context: each <linked_crm_deal> block now includes Deal Name and Deal Stage for stage-aware routing in AOP instructions - Frontend: CrmAgentBadge renders one badge per linked opp with individual unlink buttons; CrmDealLinkDialog shows already-linked deals as disabled; ConversationOverviewCard collects customObjectRecords from all entries Co-Authored-By: Claude Sonnet 4.6 (1M context) <<email>>Jun 7, 2026, 4:46 PMutilsfeat: statistics pageFeb 9, 2026, 9:35 PMindex.tsfeat: statistics pageFeb 9, 2026, 9:35 PM