- Add startDate/endDate fields to PromoData type
- Extract isPromoDateActive helper, evaluated at runtime in the browser
- Remove date check from getFilteredPromos (build-time safe)
- Apply isPromoDateActive in PromoBanner and ExitIntentPopup
- Tag speakerDiarizationPro banner with Apr 14–28 2026 campaign dates
Add whitespace-nowrap to the CTA button so text like "Get it on MuseHub"
doesn't wrap at narrow viewports. Add px-4 to the banner container so
content doesn't touch the edges of the page.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Downloads and Social sections were accidentally nested inside the Links
list item, causing them to stack in a single column instead of appearing in
their own grid columns. Also improved responsive spacing: single column on
mobile, evenly spaced at md, and side-by-side at lg.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Netlify Edge Function sets persistent aud_ab_id cookie for cohort assignment
- Deterministic hash (djb2) maps user ID + experiment name to variant slot
- Experiment registry with weighted variant support (src/assets/data/experiments.ts)
- React hook useExperiment() for component-level variant rendering
- Matomo Custom Dimension 1 repurposed for experiment tracking
- Sample 'nav-logo' 50/50 experiment: control (logo+text) vs text-only
- Unit tests for hash distribution and variant assignment
- Add role="dialog", aria-modal, and aria-labelledby to the modal
- Add focus trap that cycles between dismiss and CTA buttons
- Auto-focus the first focusable element on open
- Give exit-intent popup id on title element for labelling
- Remove isAttentionOverlayLocked gating from conditions setup
- Exit-intent now preempts cookie consent (priority 100 > 10)
- Cookie consent yields and re-shows after exit-intent dismissal
- Remove unused isAttentionOverlayLocked state and subscribe effect
The Escape key dismiss handler was inside the same useEffect as the
conditions setup (dwell timer, engagement, exit-intent detection).
When the popup became visible, the effect re-ran and hit the session
cap check (impressions >= cap), causing it to return early without
re-registering the Escape handler. The cleanup from the previous
render removed the old handler, leaving Escape non-functional.
Moved the Escape key handler into its own useEffect that only depends
on isVisible, selectedPromo, and resolvedPolicy, ensuring it stays
active whenever the popup is visible regardless of session cap state.