// Landing sections — hero variants, features, how, story, privacy, faq, final, footer const T = () => window.LANDING_TOKENS; // ══════════════ shared UI ══════════════ function Chip({ children, kind = 'default' }) { const { primary, primarySoft, ink, line } = T(); const styles = { default: { bg: '#fff', color: ink, border: `1px solid ${line}` }, soft: { bg: primarySoft, color: '#B04A5F', border: '1px solid transparent' }, }; const s = styles[kind]; return ( {children} ); } function BtnPrimary({ children, onClick, size = 'md' }) { const pad = size === 'lg' ? '16px 28px' : '12px 22px'; const fs = size === 'lg' ? 16 : 14; return ( ); } function BtnGhost({ children, onClick }) { return ( ); } function SectionLabel({ eyebrow, title, center, sub }) { return (
{eyebrow}

{title}

{sub &&

{sub}

}
); } // ══════════════ NAV ══════════════ function Nav({ copy, lang, setLang, onCTA }) { const [scrolled, setScrolled] = React.useState(false); React.useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 20); window.addEventListener('scroll', onScroll); return () => window.removeEventListener('scroll', onScroll); }, []); return ( ); } function WordmarkLogo() { return (
Memoura .
); } // ══════════════ HERO — 3 variants ══════════════ function HeroA({ copy, lang, scrollY }) { // Variant A: Scrapbook parallax — tilted polaroids + centered headline const parallax = scrollY * 0.3; return (
{/* Soft bg wash */}
{copy.heroEyebrow}

{copy.heroTitleA}
{copy.heroTitleB}

{copy.heroSub}

{copy.waitlistCount}
{/* Polaroid stack — parallax */}
{/* ticket stub */}
ADMIT TWO
14.11
kỷ niệm 1 năm
); } function HeroB({ copy, lang }) { // Variant B: Editorial — massive serif, single centered phone, quiet return (
{copy.heroEyebrow}

{copy.heroTitleA} {copy.heroTitleB.replace('.','')} .

{copy.heroSub}

{/* Single phone beneath */}
); } function HeroC({ copy, lang, scrollY }) { // Variant C: Cinematic — gradient wash + letter motif + 2 phones const parallax = scrollY * 0.4; return (
{copy.heroEyebrow}

{copy.heroTitleA} {copy.heroTitleB}

{copy.heroSub}

{copy.waitlistCount}
{/* Envelope + 2 phones */}
{/* envelope */}
M
); } // ══════════════ EmailCapture ══════════════ function EmailCapture({ copy }) { const [submitted, setSubmitted] = React.useState(false); return (
{ e.preventDefault(); setSubmitted(true); }} style={{ display: 'flex', gap: 8, background: '#fff', padding: 6, borderRadius: 999, border: `1px solid ${T().line}`, boxShadow: '0 8px 24px -10px rgba(42,26,30,0.12)', maxWidth: 440, }}>
{copy.emailNote}
); } function AvatarStack() { const colors = [[T().heroA, T().heroB], [T().secondary, '#C17A3A'], [T().accent, '#4A9A85'], ['#C1A5D4','#6C4D8C']]; return (
{colors.map((g, i) => (
))}
); } // ══════════════ Polaroid + PhoneMock ══════════════ function Polaroid({ style, caption, script, img }) { const { top, left, right, bottom, rotate, w, h, grad } = style; return (
{img && ( {caption )} {/* simulated grain */}
{caption}
); } function PhoneMock({ which = 'home', w = 280 }) { const h = w * 2.05; const { primary, primaryDeep, heroA, heroB, heroC, ink, bg, sans, serif, script, secondary, accent } = T(); return (
{/* Dynamic island */}
{/* Status bar time */}
9:41
{which === 'home' && } {which === 'letter' && } {which === 'moments' && }
); } function PhoneContentHome({ w }) { const { heroA, heroB, heroC, ink, inkSoft, primary, bg, sans, serif, script } = T(); return (
Chào Linh,
sáng nay trời đẹp ghê
{/* Timer hero */}
Chúng mình đã bên nhau
1,254
ngày yêu nhau
{/* two avatars */}
{[[heroA, heroB], ['#F4A261', '#C17A3A']].map((g, i) => (
))}
{/* Partner moment card */}
MINH · VỪA THÊM
Sáng nay ở quán quen
); } function PhoneContentLetter({ w }) { const { heroA, heroB, heroC, ink, bg, sans, serif, script } = T(); return (
Thư từ Minh
Gửi em,
Hôm nay anh đi ngang cafe Tùng, nhớ lại cái chiều hai đứa ngồi chờ mưa. Trời Đà Lạt lạnh mà em cứ đòi kem…
Em của ngày hôm qua
Sơn Tùng M-TP
); } function PhoneContentMoments({ w }) { const { ink, bg, sans, serif, heroA, heroB, accent, secondary } = T(); return (
Khoảnh khắc
{[ [heroA, heroB, 'Đà Lạt'], [secondary, '#C17A3A', 'Cafe'], [accent, '#4A9A85', 'Biển'], ['#C1A5D4', '#6C4D8C', 'Tối nay'], ].map((m, i) => (
{m[2]}
))}
); } // ══════════════ FEATURES ══════════════ function Features({ copy }) { const icons = [ , , , , , , ]; return (
{copy.f.map((f, i) => (
{icons[i]}

{f.t}

{f.d}

))}
); } // ══════════════ HOW IT WORKS ══════════════ function How({ copy }) { return (
{copy.steps.map((s, i) => (
{s.n}

{s.t}

{s.d}

))}
); } // ══════════════ STORY — quote-driven ══════════════ function Story({ copy }) { return (
{copy.storyEyebrow}

{copy.storyTitle}

{copy.storyBody}

— {copy.storySign}
); } // ══════════════ PRIVACY ══════════════ function Privacy({ copy }) { return (
{copy.privacyEyebrow}

{copy.privacyTitle}

{copy.privacyPoints.map((p, i) => (
{i === 0 && <>} {i === 1 && <>} {i === 2 && <>}

{p.t}

{p.d}

))}
); } // ══════════════ FAQ ══════════════ function FAQ({ copy }) { const [open, setOpen] = React.useState(0); return (
{copy.faq.map((item, i) => (
{open === i && (
{item.a}
)}
))}
); } // ══════════════ FINAL CTA ══════════════ function FinalCTA({ copy }) { return (

{copy.finalTitle}

{copy.finalSub}

); } // ══════════════ FOOTER ══════════════ function Footer({ copy }) { return ( ); } Object.assign(window, { Nav, HeroA, HeroB, HeroC, Features, How, Story, Privacy, FAQ, FinalCTA, Footer, Polaroid, PhoneMock, EmailCapture, SectionLabel, BtnPrimary, });