Skip to main content

Living Word — Lead-Gen Phase 1B

What shipped

The full scope of knowledge/acceptance/living-word-leadgen-phase1b.md:

  1. Scene-completion modal + bloom CTA card (SceneCompletionModal.tsx + SceneCompletionCtaCard.tsx)

    • Brief celebratory modal that wraps the scene transition. "Walk on" stays the primary action (autoFocus, navy button).
    • The bloom card renders BELOW Walk-on starting at the 3rd scene completion (decision B2). Counter persists in localStorage as lw-scene-completions-shown:cta and survives page reloads.
    • First 2 completions: modal renders, card hidden, NO scene_completion_impression event logged.
    • From completion 3+: card renders with sacred-gold "BUILT BY CHURCHWISEAI" badge, adult-targeted copy ("your congregation"), and a CTA link to churchwiseai.com/from-living-word.
    • dismissedThisSession lives in component state only — the card returns on next scene completion (a new modal mount).
    • Wired into LivingWordAdventure.tsx → handleReachExit() — instead of immediately calling goToScene, the handler sets pendingSceneTransition and the modal becomes the transition affordance.
  2. NPC-chat-close CTA line (extends NpcChatModal.tsx)

    • Single italic stone-500 text-xs line appended INSIDE the existing end-conversation block, BELOW the "Walk on" button.
    • Voice tier is "builder credits" — clearly authored-by-the-builders, never NPC-voiced. Typographically separate from the in-character "Walk on" copy above.
    • Fires on every end-state path (turn-cap, user × close, crisis deflection) — anywhere endConversation flips true.
    • CTA link tagged with npc_id for funnel attribution.
  3. Legacy Bloom finale panel (LegacyBloomFinalePanel.tsx)

    • Mounted by LivingWordAdventure.tsx exactly 1500ms AFTER the existing LegacyBloomCinematic.onComplete() fires. The 1500ms silence beat is in the parent — the cinematic itself is byte-for-byte unchanged.
    • Full-width navy-on-cream panel with Playfair heading ("You just walked from Eden to the New Creation."), two prose paragraphs, sacred-gold primary CTA ("See what it does for your church"), and a ghost-outline share CTA (mailto: with pre-filled subject + body containing https://livingword.bible/?ref=share).
    • Share click fires BOTH legacy_bloom_click (action: 'share') AND share_click events per spec §5.
    • Panel does NOT auto-dismiss; player chooses one of three actions (primary CTA, share, ×).
  4. No new migration. All 7 Phase-1B event types (scene_completion_impression, scene_completion_click, npc_chat_close_impression, npc_chat_close_click, legacy_bloom_impression, legacy_bloom_click, share_click) were already in the Phase 1A lw_lead_gen_events table's accepted-values list (migrations/2026-05-23c-lw-lead-gen-events.sql). No schema change required.

  5. Test harness at /living-word/e2e-leadgen (noindex'd, not linked from production navigation). Mounts the modal + finale panel in isolation so the Playwright spec can verify the contract without driving the 5+ minute canvas walk.

  6. Playwright spec at e2e/living-word/leadgen-phase1b.spec.ts — 7 tests, all passing. Covers visibility logic (counter < 3 → card hidden; counter ≥ 3 → card visible), impression + click event shape, UTM param contract, mailto: share contract, dismiss behavior, and cross-surface ≥3-distinct-event-types verification.

Founder-locked decisions reflected

DecisionAnswerHow it's reflected in code
B1 — Domain routingroute to churchwiseai.comEvery CTA href is an absolute https://churchwiseai.com/from-living-word?... URL, even when the player is on livingword.bible. Asserted by the Playwright spec for each surface.
B2 — Early-scene CTA prominencehide first 2 scenesSceneCompletionModal increments a localStorage counter on every mount; card renders only when counter >= 3. First-scene mount → counter 0→1 (card hidden); third mount → counter 2→3 (card visible).
B3 — Legacy Bloom finale panelkeep the panelLegacyBloomFinalePanel ships, mounted 1500ms after the cinematic completes. The cinematic is unchanged.
E2 — Scene-completion CTA mount pointintroduce a brief scene-completion modalSceneCompletionModal.tsx exists; Walk-on inside the modal commits the scene transition; bloom card hosted below.

UTM tagging summary (matches spec §8)

Surfaceutm_sourceutm_mediumutm_campaignExtra params
Scene completion bloom cardliving-wordin-gamescene-completion-v1&scene_id=<active_scene_id>
NPC chat-close lineliving-wordin-gamenpc-chat-close-v1&npc_id=<active_npc_id>
Legacy Bloom finale panel (primary)living-wordin-gamelegacy-bloom-finale-v1
Legacy Bloom share CTA(mailto: — no UTMs on share, link inside body uses ?ref=share)

Anti-goals honored (spec §9)

  • ✓ NO CTAs during gameplay or during NPC chat — completion moments only.
  • ✓ NO NPC-voiced cross-sell (the "Built by ChurchWiseAI" badge is typographically separated; the chat-close line is text-xs italic sans-serif stone-500 to set it apart from the body in-character stone-600).
  • ✓ NO popup interstitials or page-blocking lightboxes outside completion moments.
  • ✓ NO "limited time" / "act now" / fake urgency copy.
  • ✓ NO third-party tracking pixels — first-party only via lw_lead_gen_events.
  • ✓ NO persistent banner ads (footer credit from Phase 1A is the only persistent surface).
  • ✓ NO segmentation by visitor type in Phase 1B.
  • ✓ NO mid-cinematic interruption — 1500ms post-cinematic silence is the floor.
  • ✓ NO behavioural / geo / time-of-day targeting — all players see the same surfaces under the same rules.
  • ✓ NO A/B testing copy in Phase 1B.
  • ✓ NO email capture inside the game.
  • ✓ NO mirroring of /from-living-word on livingword.bible.

Voice rules honored

  • "Never evolves" — copy uses "carry" / "hold" / "talks like a pastor who's read the whole book" (not "evolves").
  • No kid-targeted copy — "your congregation", "your church", "for ministry" — adult-targeted everywhere.
  • No "lost your streak" shame — Phase 1B doesn't touch streaks (live-ops Phase 1B work).

React 19 StrictMode guard

All three impression-firing effects (SceneCompletionModal, LegacyBloomFinalePanel, NpcChatModal's chat-close hook) use a useRef guard to prevent React 19 StrictMode's intentional double-mount in development from double-counting events. The guard is per-mount, so distinct scene completions / panel mounts / chat-end events still each fire exactly once.

Phase 1C (next)

  • Founder lead-gen dashboard for the funnel data — 7+ days of data, then CTR analysis per surface.
  • Spec lives in a future acceptance/living-word-leadgen-phase1c.md (not written yet).

Verification

  • pnpm build clean.
  • pnpm lint shows zero new errors/warnings in Phase-1B files.
  • 7 Playwright tests pass in ~6 seconds against the local dev server (e2e/living-word/leadgen-phase1b.spec.ts).
  • DB spot-check (Supabase MCP) — all 7 event types landed in lw_lead_gen_events after the test run, with utm_campaign + utm_medium correctly populated as columns and metadata JSONB containing scene_id / npc_id / action fields per spec.