feat: opt-in conversion layer — GA4 flight analytics, GHL lead capture, ElevenLabs scroll audio - #1
Open
sbyrd-create wants to merge 1 commit into
Open
Conversation
… audio)
The generated page is currently a dead end — the finale CTA is href:'#',
nothing measured, nothing captured. Add three backward-compatible, opt-in
layers so a shipped scroll-world actually converts; an existing config runs
unchanged (every new field defaults off).
- Engine (scrub-engine.js):
- analytics:{ga4,prefix,debug,params} -> emits sw_scene_view / sw_scroll_depth
(25/50/75/100 once each) / sw_flight_complete / sw_cta_click / sw_lead_submit /
sw_audio_toggle to gtag() or window.dataLayer (GTM). ga4:'G-…' auto-injects gtag;
ga4:true reuses an existing snippet.
- capture:{…} on the finale — mode:'embed' (GHL form/calendar iframe) or
mode:'inline' (on-brand form -> GHL inbound webhook; honeypot, UTM/source
passthrough, no-cors fire-and-trust, success -> sw_lead_submit + thank-you).
No GHL API key client-side; the public webhook URL is the only credential.
- audio:{…} + per-section audio: volume tracks scene opacity (cross-fades with
the flight), muted until unmute tap, off under reduced-motion / data-saver
(preload:'none' -> zero bytes for muted visitors).
- Docs: references/integrations.md (GA4 event schema + validation-gate mapping,
GHL setup, ElevenLabs encoding, Vercel/framework deploy). SKILL.md: interview
item 7 + Step 9 + QA bullet. index-template.html: opt-in config example.
- plugin.json 0.2.0 -> 0.3.0; FORK-CHANGES §6.
Verified: node --check; in-browser mount + capture submit -> sw_lead_submit +
thank-you, cta/audio/scene_view events, SEO block still hidden; scroll-depth
threshold logic (25→100 once each + flight_complete, idempotent under up/down
scroll) verified deterministically.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The generated page is currently a beautiful dead end — the finale CTA is
href:'#', nothing is measured, nothing is captured. This adds three opt-in, backward-compatible layers to the engine so a shipped scroll-world actually converts. An existing config runs unchanged; every new field defaults off.1. GA4 flight analytics —
analytics:{ ga4, prefix, debug, params }A scroll-scrubbed page is analytically blind by default (one pageview, no clicks until the end). The engine now emits:
sw_scene_viewsw_scroll_depthsw_flight_completesw_cta_clicksw_lead_submitsw_audio_toggleRouted to
gtag()when present, else pushed towindow.dataLayer(GTM Custom Event triggers).ga4:'G-…'auto-injects gtag;ga4:truereuses an existing snippet.2. Lead capture on the finale —
capture:{…}mode:'embed'— drops in a GoHighLevel form/booking iframe (GHL owns submission + routing).mode:'inline'— an on-brand form that POSTs name/email/phone + UTM/gclid/fbclid + source/brand/page to a GHL inbound webhook. Honeypot-guarded, required-field checked,no-corsfire-and-trust, success →sw_lead_submit+ thank-you (+ optional redirect).3. ElevenLabs scroll audio —
audio:{…}+ per-sectionaudio:'…mp3'Per-scene narration/ambient whose volume tracks the scene's opacity, so audio cross-fades with the flight (the audio analogue of the frame-locked seams). Muted until an explicit unmute tap (autoplay policy); never instantiated under
prefers-reduced-motion/ data-saver (preload:'none'→ zero bytes for muted visitors).Docs
references/integrations.md: GA4 event schema + how to read the events as validation gates, GHL setup walk-through (both modes), ElevenLabs encoding recipe, Vercel/framework deploy notes.index-template.htmlshows the opt-in config;plugin.json0.2.0 → 0.3.0; FORK-CHANGES §6.Why opt-in / compat
Same doctrine as the rest of this fork: every new config field is optional, nothing changes for an upstream config, and the engine stays zero-dependency vanilla JS (the analytics/capture/audio code is ~200 lines inside the same IIFE-scope, namespaced
sw-CSS).Test plan
node --checkon the enginesw_lead_submitfired + thank-you swap; honeypot field present and hiddensw_cta_click(finale CTA),sw_audio_toggle(aria-pressed flips),sw_scene_viewon scene changedata-sw-seoblock still hidden on mountflight_completeonce, idempotent under up/down scrolling🤖 Generated with Claude Code