リリースノート

新着情報

IdolSajuのすべての変更を透明に記録 — 何を、いつ、なぜ作ったか。

v0.3.0

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

New

Unauthenticated 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

New

On /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

New

Below 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

Infra

Every 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

Payments

Stripe 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

Payments

The 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

Reading

The 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

Infra

Teaser 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.

v0.2.0

1+1 anchor pricing, contact form, and the release system itself

Pricing rework focused on a single hero deal — the smallest pack now carries a permanent 1+1 bonus while the larger packs sit clean as anchors, the way a CU 1+1 deal works. Reading-unlock CTA stops shouting prices at the entry point. Plus a real contact form (with a brutalist confirmation email), a public release page so you can see what we ship, and footer links to the SEO categories that were previously hidden.

1+1 deal lives on the smallest pack only

Pricing

Bonus credits no longer dilute every pack. The €1.49 try pack stays "buy 1, get 1 free" forever, while the 5- and 15-credit packs are plain volume options. The bonus regains anchor power because the other packs don't copy it.

Reading-unlock CTA leads with the deal, not the price

Pricing

The "Reveal Full Reading" button used to expose a per-credit "from €0.49" anchor that read as expensive in some currencies and abstract in others. It now shows a brutalist "1+1 Buy 1, get 1 free" chip right under the title — value framing instead of unit pricing at the conversion point.

Checkout banner cleaned up

Design

Removed the 48-hour countdown timer (a permanent deal needs no urgency) and the duplicate tilted "1+1" sticker that was competing with the try-pack badge. Single bonus marker per element now.

Long button labels no longer overlap prices in KRW

Fix

On Korean Won prices like ₩495, the unlock button text would visually crash into the price tag. Button content now stacks vertically with proper wrapping so labels and chips never collide, in any currency.

Real contact form at /contact

New

Replaces the scattered (and partially non-working) privacy@/dmca@/legal@/support@/contact@idolsaju.com mailto links with a single brutalist form in EN/KO/JA/ES. Topic dropdown covers general questions, privacy, DMCA, billing, bugs, and image attribution. Honeypot anti-spam baked in.

Brutalist confirmation email

New

Every contact submission triggers an instant confirmation in the user's inbox — yellow IdolSaju background, white card with black borders and hard shadow, brand chip, status sticker, summary of their submission, and a CTA back to their language's homepage. Localized in 4 languages.

What's new — public release page

New

New /releases page (linked in the footer as "What's new") lists every IdolSaju update with category chips and dates. This very entry is the first one visible. Built with the same brutalist look as the rest of the site.

Footer links to Numerology, Angel Numbers, Crystals, Chakras

SEO

The Explore footer column now points into the previously orphaned SEO categories (~256 pages with no internal links from the global nav). Each link leads to a representative entry that cross-links to its siblings, so Google has an actual entry path.

Live readings counter on the unlock card

New

A subtle "✦ 1,247 readings unlocked this week" line now appears under the trust badges, showing real activity from the last seven days. Hidden when the count is too low to be impressive — no fake numbers.

Library updates and Stripe API bump

Infra

Pulled minor/patch updates across Stripe, Prisma, Biome, lucide-react, Cloudinary, and the Stripe JS SDKs. Stripe API version moved to 2026-04-22.dahlia. Tests and build green throughout.

Dead teaser code removed

Infra

Cleared an empty `/api/reading-teaser` folder and corrected our internal documentation: the teaser/preview UI is fully client-side and writes nothing to the database until a paid reading runs. No more phantom "cleanup cron" debt on the backlog.