Unified Cherry City Landing Page
Replace the dual-LP setup (/cherry-city/tour for monthly, /cherry-city/hourly for hourly) with a single unified landing page at /lp/cherry-city that serves both products from one cold-traffic conversion path.
TL;DR
- New LP at
/lp/cherry-citybecomes the cold-traffic conversion target - Pain-led shared hero → lightweight fork ("monthly or hourly?") → full-width product sections below → shared proof / testimonials / FAQ
- Existing
/cherry-city/tourand/cherry-city/hourlyredirect to/lp/cherry-city - New custom Pixel + CAPI event (
LandingFunnelChoice) fires when a visitor clicks either fork CTA — used as the unified Meta optimization target - Most existing LP content is reused; the rebuild is a recomposition, not a from-scratch redesign
Why
Current state: two separate LPs and two separate Meta campaigns produce ~5 paid conversions/week combined at $100/day spend. Meta's Learning Phase requires ~50 events/week to stabilize. Salem is a single small market (~75k pop); the 5% lookalike is already broad. The volume gap is event-cost driven, not audience-size driven.
Solution: unify the two LPs so paid Meta can optimize against a single mid-funnel event (LandingFunnelChoice, fired on fork CTA click) that's intent-loaded but cheap enough to clear 50/week at $100/day budget.
The custom event is the only event that fires on both monthly-curious and hourly-curious paths — Lead only fires on monthly tour-request submit, InitiateCheckout only fires on hourly money-checkout. Without this event, no single optimization target exists for a unified campaign.
Locked decisions
URL and routing
- New LP at
/lp/cherry-city - Code path:
apps/web/src/app/(landing)/lp/cherry-city/ /cherry-city/tour→ 301 redirect to/lp/cherry-city/cherry-city/hourly→ 301 redirect to/lp/cherry-city/cherry-city(location overview, dynamic[locationSlug]route) — untouched- The
/lp/<location>namespace is intentional — sets up future expansion to other locations and markets without polluting the location content tree
Page structure
[ HERO — pain-led + subline + scroll cue ]
[ FORK — "How do you want to play?" — monthly card | hourly card ]
[ SHARED PROOF BAR — 500+ bands / 49 studios / 30+ Salem / 24/7 ]
[ MONTHLY FULL SECTION — anchor target #1 ]
[ HOURLY FULL SECTION — anchor target #2 ]
[ SHARED TESTIMONIALS ]
[ SHARED FAQ ]
[ CLOSING CTA — back to fork ]
Mobile: stacks naturally top-to-bottom. Desktop: same vertical flow; only the fork is two-column on desktop, stacked on mobile.
Hero
- Headline: "Band practice at home sucks. We fixed that."
- Subline: "Salem's rehearsal studio. Get a monthly lockout or book by the hour."
- Background: full-bleed photo (suggest reusing
/images/marketing/frontman-jump.jpegfrom current /tour with same dark overlay treatment) - No primary CTA button. Optional small text link "See your options ↓" anchored to the fork section
- Fold tease: design so the top of the fork peeks into the viewport at hero's end — pulls scroll without a button
- Why this copy: apartment-displacement is the broadest universal pain; the subline introduces the category (
rehearsal studio), the city, and the dual-product structure in one beat. Cold Salem audience is problem-aware (knows the pain) but not solution-aware (doesn't know rehearsal studios as a category) — pain-led is the right awareness-stage hook
Fork section
- Section heading: "How do you want to play?"
- Two cards side-by-side on desktop, stacked on mobile
Monthly card:
- Headline: "Your own room."
- Visual:
BeforeAfterSlider(existing component) — empty studio → stocked studio. This is the only visual asset that genuinely encodes the monthly-vs-hourly distinction (monthly = "you fill this room over time"; hourly = "always stocked"). Photos alone can't carry it because both rooms look the same once a band has set up gear. - Bullets:
From $285/mo·Your gear, your key·30+ Salem bands - CTA button: "Free tour →"
- CTA action: smooth-scroll/anchor to monthly full section
Hourly card:
- Headline: "Walk in. Plug in. Play." (existing approved /hourly headline — preserves any social proof if this copy ever lifts into ad creative via Use Existing Post)
- Visual: static photo of a stocked hourly studio (suggest reusing
/images/marketing/hourly-marketing-studio-2.jpeg) - Bullets:
From $15/hr·Drums, amps, PA included·Book a studio in 60 seconds - CTA button: "See times →"
- CTA action: smooth-scroll/anchor to hourly full section
Both CTAs fire LandingFunnelChoice on click. See Tracking spec below.
Shared proof bar
Reuse the existing 4-stat layout pattern (current /tour and /hourly both render it):
- 500+ Bands across PDX & Salem
- 49 Studios at Cherry City
- 30+ Salem bands
- 24/7 Access, always
Use "Salem bands" wording (not "Cherry City bands") — Salem reads more universal for cold-traffic context.
Monthly full section
Reuse from current TourLandingContent.tsx — keep:
BeforeAfterSlider(full-size; same asset as fork card)- Email-only
LeadCaptureForm(existing component,variant="email-only",embedded) - Community Manager intro card — keep (social trust signal); render adjacent to or above the form
- "What you get with a lockout"
CardCarouselSection(existing — keep all FeatureCards) - "How it works" 3-step
StepsSection(existing — keep) PhotoGridof location images (existing — keep)
Drop from current /tour:
- The /tour hero (replaced by unified hero)
- Stats bar (moved to shared proof bar)
- Testimonials section (moved to shared section)
- FAQ section (moved to shared section)
- "Not ready for a lockout?" cross-link section to /hourly (no longer needed — hourly is on the same page)
Hourly full section
Reuse from current HourlyLandingContent.tsx — keep:
- Embedded
HourlyBookingContentfor featured Studio B (existing pattern:simplifiedprop, ModalWrapper auth gating) - "All-Inclusive"
CardCarouselSection(existing — keep all FeatureCards) - "Easy to Get Started" 3-step
StepsSection(existing — keep) ResourceCarouselDisplayfor "Browse Other Hourly Studios" (existing — keep)HourlyBookingModalfor non-featured studios (existing — keep)
Drop from current /hourly:
- The /hourly hero (replaced by unified hero)
- Stats bar (moved to shared proof bar)
- Testimonials section (moved to shared section)
- FAQ section (moved to shared section)
- "Playing here all the time?" cross-link section to /tour (no longer needed)
- The mid-page "Book a Studio" button section that scrolls back to scheduler (redundant once page is unified)
Shared testimonials
Merge the two existing testimonial sets. Several quotes appear on both LPs (Michael, Timothy "Hardhead", Joseph) — render once, not twice. Use existing TestimonialsSection and TestimonialCard components. Carousel layout.
Shared FAQ
Merge the two existing FAQ sets into one FAQSection accordion. Order: monthly-relevant questions first, hourly-relevant questions second, shared questions deduplicated. Source of truth for FAQ items lives in the page server components currently — pull and merge.
Closing CTA
Single block at the bottom. Headline along the lines of "Ready?" or "You ready?" (matches existing approved CTA copy from brand-voice.md). Single button or two-button "Free tour" / "See times" — both anchor back to the fork. Keep tight.
Tracking spec
A new custom event is the centerpiece of this change — it's the unified optimization target that makes the unified campaign possible.
Event name: LandingFunnelChoice
Fires when a visitor clicks either fork CTA on /lp/cherry-city.
Pixel side (browser):
- New tracking function in
apps/web/src/lib/tracking.ts, e.g.trackLandingFunnelChoice({ choice: 'monthly' | 'hourly', locationId }) - Calls
trackMetaEvent('LandingFunnelChoice', { content_name: choice, content_category: 'lp-fork' }, { eventID: <generated> }) - Follow the existing pattern from
trackTourRequested(lines 91–104) - Wire to both fork CTA
onClickhandlers in the new LP
CAPI side (server):
- New module:
apps/api/src/lib/meta/track-funnel-choice.ts, following the pattern oftrack-lead.ts - New API endpoint (e.g.,
POST /api/meta/funnel-choice) that the LP calls fire-and-forget on CTA click, passing the matchingevent_idfor dedup - Use the existing
capi-client.ts,hash-identity.ts, and identity-payload machinery (email, fbp, fbc, IP, UA, external_id) - Send via Next.js
after()with fetch timeout, like other CAPI calls
Custom Conversion in Meta Ads Manager:
- Define a custom conversion based on the
LandingFunnelChoicePixel event - This becomes the optimization event for the new unified campaign
Why both Pixel and CAPI: matches existing dedup pattern documented in docs/marketing/paid-meta.md (PR #652). Maintains EMQ ≥ 8.0 target.
Event name: ScrollDepth75
Fires once per session per URL when the visitor scrolls past 75% of the LP's document height. Seeds the ScrollDepth75 — 90d retargeting audience defined in cherry-city-ppc-prospecting/spec.md.
Pixel side (browser):
- New tracking function in
apps/web/src/lib/tracking.ts, e.g.trackScrollDepth75({ url }) - Calls
trackMetaEvent('ScrollDepth75', { content_name: url, content_category: 'lp-scroll-depth' }, { eventID: <generated> }) - New
useScrollDepth75()hook fires the function once whenscrollY + innerHeight >= 0.75 * documentHeight. Guard with sessionStorage keyed on URL so it fires at most once per session per URL.
CAPI side (server):
- New module:
apps/api/src/lib/meta/track-scroll-depth.ts, mirroringtrack-funnel-choice.ts.event_name = 'ScrollDepth75',event_source_url = url,custom_data.content_name = url. - New API endpoint
POST /api/meta/scroll-depththat the LP calls fire-and-forget when the threshold trips, passing the matchingevent_idfor dedup. - Use the existing
capi-client.ts,hash-identity.ts, and identity-payload machinery (fbp, fbc, IP, UA — anonymous visitors, no email/external_id). - Send via Next.js
after()with fetch timeout, like other CAPI calls.
Why both Pixel and CAPI: same dedup pattern as LandingFunnelChoice. Browser-only events get throttled by ATT/iOS Safari; CAPI mirror keeps coverage high.
Existing events continue to fire downstream
| Event | Where it fires |
|---|---|
ViewContent |
LP load (existing trackLocationLandingViewed — or wire equivalent for the new LP) |
Lead |
Monthly form submit (existing trackTourRequested) |
InitiateCheckout |
Hourly path post-route-change at /checkout/reservation (existing — useCheckoutFlow line 578/654) |
Purchase |
Hourly checkout success (existing) |
These are measurement signals. They are not the optimization target — LandingFunnelChoice is.
Additional (separate specs)
The following are upstream of this LP work and need their own specs / decisions:
- Ad creative + ad copy — visual format (single image vs carousel vs video), primary text, headline, CTA button, ad ↔ LP message coherence. Open question: does the ad reveal the dual-product nature, or stay singular?
- Meta campaign migration mechanics — research-backed best practice is to duplicate
META_CherryCity_Monthly, modify the duplicate (URL →/lp/cherry-city, optimization event →LandingFunnelChoice, creative → new), preserve original as paused control. Use "Use Existing Post" to carry social proof from winning ads ("Community – Just Got Louder", "No Commitment").
References
Code:
apps/web/src/app/(landing)/cherry-city/tour/TourLandingContent.tsx— current monthly LP, primary content sourceapps/web/src/app/(landing)/cherry-city/hourly/HourlyLandingContent.tsx— current hourly LP, primary content sourceapps/web/src/app/(marketing)/[locationSlug]/LeadCaptureForm.tsx— email-only form to embed in monthly sectionapps/web/src/components/organisms/booking/HourlyBookingContent.tsx— hourly scheduler embedapps/web/src/components/molecules/media/BeforeAfterSlider.tsx— empty/full slider componentapps/web/src/components/organisms/marketing/sections/— reusable LP section primitives (CardCarouselSection, FAQSection, StepsSection, TestimonialsSection)apps/web/src/lib/tracking.ts— pattern for newtrackLandingFunnelChoiceapps/api/src/lib/meta/track-lead.ts— pattern for newtrack-funnel-choice.ts
Strategy:
docs/marketing/icp.md— ICP archetype + hourly ICP differentiationdocs/marketing/positioning.md— displacement frame (apartment / garage / storage / basement)docs/marketing/brand-voice.md— voice principles, vocabulary, on/off-voice patternsdocs/marketing/paid-meta.md— campaign mechanics, attribution, learning phase rules
External research backing key decisions:
- Jon Loomer Digital — edits that reset learning phase — confirmed: changing optimization event is a hard reset
- Modern Marketing Institute 2026 — exiting learning phase fast — for low-volume markets, move up the funnel to a higher-volume proxy event; duplicate-and-modify recommended for major restructures