sms:+17024964214).
Two channels:
/api/ab-event, which writes to D1. /api/ab-stats reads aggregated counts. That's what this page shows.ab_cta_assignment (params: ab_variant, ab_page) and ab_cta_click (params: ab_variant, ab_action, ab_page). Filter by these in GA4 Explore for sophisticated analysis.Compare ctr_overall (total clicks ÷ assignments) between the two variants. Higher is better. The variant with more call_click + sms_click + schedule_click per assignment is the winner. Ignore noise until each variant has at least 200 assignments — anything less is too small for confident conclusions.
Rule of thumb at small N: if absolute CTR difference is <3 percentage points and you have under 500 assignments per variant, call it inconclusive. If absolute difference is >5 percentage points and you have 200+ assignments per variant, you have directional signal. For real significance testing, paste both assignment and total_clicks counts into a two-proportion z-test (any online calculator works).
Either (a) one variant is clearly winning at 500+ assignments per variant and you're ready to ship the winner site-wide, or (b) results are flat at 1,000+ assignments per variant — in which case the copy variants don't matter and you should test something more substantial.
When a winner is clear, edit /ab-cta.js — change the getOrAssignVariant function to always return the winning variant. Or remove the script entirely and hard-code the winning copy into the hero CTA template across the 14 pages. Then deploy.
Wipe the D1 table and let it re-seed:
npx wrangler d1 execute nmlp-db --remote --command "DELETE FROM ab_events;"