Personalized teasers, full funnel tracking, simpler payment routing
Three high-traffic readings — Saju Profile, Horoscope, and Idol Match — now show a personalized teaser before the unlock CTA: a numbered first section with the user's name and chart facts woven in, two locked accordion cards mirroring the real reading layout, and a compact "+ N more sections" hint. The idol-match teaser also has a soulmate-mode variant that doesn't spoil the #1 match. Behind the scenes we wired the entire conversion funnel into Vercel Custom Events (teaser_viewed → unlock_clicked → checkout_opened → payment_succeeded → reading_completed), so we can measure whether this experiment actually moves the needle. Plus the Stripe payment-method routing got rebuilt around URL locale instead of a drifting cookie, which fixes the "Korean methods on /en pages" failure pattern that was killing ~14% of attempted payments.
Personalized teaser on Saju Profile, Horoscope, and Idol Match
NewUnauthenticated visitors now see a personalized 3-section preview before the unlock CTA — section 1 fully visible (with name + day master / sun sign / matched idol baked in via slot replacement), sections 2 and 3 collapsed with a lock icon, then a compact "+ N more sections in your full reading" hint that lists the remaining ToC items. Five rotating variants per locale (EN/ES/KO/JA), one randomized pick per visit. After unlock the user gets the full personalized AI reading, so the teaser sets accurate expectations without spoiling content.
Soulmate-mode teaser hides idol identity
NewOn /idol-match in soulmate mode (find your #1 match), the teaser still appears above the locked match cards but with idol-identifying slots replaced by anonymous placeholders — "your soulmate" instead of the idol's name, "the universe" for the group, "??" for the score. The text reads naturally and communicates the full reading depth without revealing which idol the user is about to unlock.
Soulmate-mode locked chart preview
NewBelow the ranking list, soulmate mode now also shows the same Score Card / Element Synergy / Manseryeok layout that appears post-reveal — but with the idol's photo, name, group, score, level, and pillars all blurred. Users see exactly what they get (chart side-by-side, harmony summary, score box) without seeing who.
Full conversion funnel in Vercel Custom Events
InfraEvery step from page view to paid reading is now tracked: teaser_viewed → unlock_clicked → reading_unlocked_with_credits or checkout_opened → payment_submitted → payment_succeeded / payment_failed → reading_started → reading_completed. Each event carries product (saju-profile, horoscope, idol-match-bias, idol-match-soulmate, idol-match-group) and contextual fields like dayMaster, sunSign, idolName, score, currency, locked-state, so the funnel is segmentable per product and per variant. Events flow into both Vercel Analytics (dashboard) and our own /api/events log (DB).
Stripe payment-method routing rebuilt around URL locale
PaymentsStripe used to pick the Korea PaymentMethodConfiguration (Naver Pay / Kakao Pay / Payco) whenever the saju_currency cookie was KRW — even on /en/* pages, which left non-Korean users staring at unfamiliar Korean methods and 14% of attempted payments expiring at "payment_intent_payment_attempt_expired". Routing is now driven by URL locale instead of cookie currency: /ko/* uses the Korea PMC, every other locale uses the Default PMC. Locale-mismatch payment failures should drop to near-zero.
Locale stops being forced from currency in checkout
PaymentsThe client used to override locale to "ko" whenever currency was KRW, which corrupted the locale field in Stripe metadata (a /en/horoscope checkout would log as locale=ko). Locale now follows URL strictly — analytics and Stripe metadata stay consistent.
Reveal-Saju button visually larger on the entry form
ReadingThe primary CTA on /saju-profile (Reveal Saju Profile) was visually outweighed by the form fields above it. Padded up to py-5 with text-xl font and a 5px brutalist shadow so it dominates the form like a real conversion button should.
Reusable teaser module for future reading types
InfraTeaser intros, locked-section titles, section meta (icon + label + score), and slot-filling logic all live in /lib/teaser-intros.ts. Adding a new reading type (MBTI, Tarot, Yearly, Compatibility, …) means extending the discriminated TeaserContext union and adding 5 variants × 4 locales — no UI changes needed. The component picks up new product types automatically via the type discriminator.
Dependencies refresh
Infra@stripe/react-stripe-js 6.2.0 → 6.3.0, lucide-react 1.11.0 → 1.14.0, next-intl 4.9.1 → 4.11.0, @mediapipe/tasks-vision 0.10.34 → 0.10.35.