updated sections design and content

This commit is contained in:
R-Bharathraj
2026-07-11 12:38:33 +05:30
parent cba47aea2b
commit 03ed08d7dd
24 changed files with 907 additions and 405 deletions

View File

@@ -87,27 +87,65 @@ export default function AboutUsPage() {
</section>
{/* ── MISSION & VISION ── */}
<section style={{ padding: '0 8% 20px', background: '#ffffff' }}>
<div className="au-mv-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20, alignItems: 'stretch' }}>
<section style={{ position: 'relative', overflow: 'hidden', padding: 'clamp(60px, 9vh, 104px) 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 28 }}>
{/* Ambient top glow */}
<div aria-hidden style={{ position: 'absolute', top: '-24%', left: '50%', transform: 'translateX(-50%)', width: 680, height: 420, background: 'radial-gradient(ellipse, rgba(244,190,40,0.12) 0%, transparent 70%)', pointerEvents: 'none' }} />
{/* Section header */}
<div style={{ position: 'relative', zIndex: 1, textAlign: 'center', maxWidth: 640, margin: '0 auto clamp(44px, 6vh, 68px)' }}>
<h2 style={{ margin: 0, fontSize: 'clamp(30px, 4.5vw, 60px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 1, color: '#fff', fontFamily: 'Sora, sans-serif' }}>
What drives <span style={{ color: '#f4be28' }}>everything</span> we do.
</h2>
</div>
<div className="au-mv-grid" style={{ position: 'relative', zIndex: 1, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24, alignItems: 'stretch' }}>
{[
{
label: 'Mission', bg: '#07070d', textColor: '#fff', accent: '#f4be28',
label: 'Mission', emoji: '🎯', bg: 'linear-gradient(160deg, #ffffff 0%, #fdf4e2 100%)', textColor: '#171512',
border: 'rgba(244,190,40,0.45)',
pillBg: 'rgba(244,190,40,0.14)', pillText: '#8a5a14', pillBorder: 'rgba(244,190,40,0.5)',
orb1: 'rgba(244,190,40,0.20)', orb2: 'rgba(255,152,0,0.14)',
gfx: 'rgba(244,190,40,0.38)',
body: 'Loyaly exists to close the gap between digital discovery and physical purchase. We give local stores the tools to compete, and give shoppers a reason to keep coming back.',
},
{
label: 'Vision', bg: '#f4be28', textColor: '#111', accent: '#c9960f',
body: 'Empowering every neighbourhood, making exceptional in-store experiences and real rewards accessible to all.',
label: 'Vision', emoji: '🔮', bg: 'linear-gradient(160deg, #fff7e2 0%, #ffe6a8 100%)', textColor: '#171512',
border: 'rgba(244,190,40,0.55)',
pillBg: '#f4be28', pillText: '#171512', pillBorder: 'rgba(201,150,15,0.6)',
orb1: 'rgba(255,255,255,0.7)', orb2: 'rgba(244,190,40,0.3)',
gfx: 'rgba(201,150,15,0.32)',
body: 'Empowering every neighbourhood, making exceptional in-store experiences and real Lyts accessible to all.',
},
].map((mv, i) => (
<Reveal key={i} delay={i * 0.15}>
<div style={{
padding: '72px 56px', borderRadius: 24, background: mv.bg,
<div className="au-mv-card" style={{
position: 'relative',
padding: 'clamp(40px, 4vw, 60px)', borderRadius: 28, background: mv.bg,
border: `1px solid ${mv.border}`,
boxShadow: '0 24px 60px -30px rgba(23,21,18,0.22)',
height: '100%', minHeight: 360,
display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', gap: 28,
margin: '0 20px 20px', boxSizing: 'border-box',
display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', gap: 24,
margin: 0, boxSizing: 'border-box',
}}>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: mv.accent, fontFamily: 'Sora, sans-serif' }}>{mv.label}</div>
<p style={{ fontSize: 'clamp(22px, 2.5vw, 36px)', fontWeight: 800, lineHeight: 1.25, color: mv.textColor, fontFamily: 'Sora, sans-serif', margin: 0, letterSpacing: '-0.02em' }}>
{/* Animated background orbs (clipped to the card) */}
<div className="au-mv-bg" aria-hidden style={{ position: 'absolute', inset: 0, borderRadius: 28, overflow: 'hidden', pointerEvents: 'none', zIndex: 0 }}>
<span className="au-mv-orb au-mv-orb-1" style={{ position: 'absolute', top: '-18%', left: '-12%', width: 280, height: 280, borderRadius: '50%', background: `radial-gradient(circle, ${mv.orb1} 0%, transparent 70%)` }} />
<span className="au-mv-orb au-mv-orb-2" style={{ position: 'absolute', bottom: '-20%', right: '-10%', width: 240, height: 240, borderRadius: '50%', background: `radial-gradient(circle, ${mv.orb2} 0%, transparent 70%)` }} />
</div>
{/* Pop-out corner sticker */}
<span aria-hidden style={{ position: 'absolute', top: -22, right: -14, fontSize: 46, transform: 'rotate(12deg)', zIndex: 2 }}>{mv.emoji}</span>
{/* Tilted sticker-pill label */}
<span style={{
position: 'relative', zIndex: 1,
display: 'inline-flex', alignItems: 'center', gap: 7, alignSelf: 'flex-start',
transform: 'rotate(-2deg)', padding: '8px 16px', borderRadius: 100,
background: mv.pillBg, color: mv.pillText, border: `1.5px solid ${mv.pillBorder}`,
fontSize: 12, fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', fontFamily: 'Sora, sans-serif',
}}>{mv.emoji} {mv.label}</span>
<p style={{ position: 'relative', zIndex: 1, fontSize: 'clamp(21px, 2.4vw, 34px)', fontWeight: 500, lineHeight: 1.3, color: mv.textColor, fontFamily: 'Sora, sans-serif', margin: 0, letterSpacing: '-0.015em' }}>
{mv.body}
</p>
</div>
@@ -178,6 +216,46 @@ export default function AboutUsPage() {
</section>
<style>{`
/* Animated gradient glow ring behind each card */
.au-mv-card::before {
content: '';
position: absolute;
inset: -2px;
border-radius: inherit;
background: linear-gradient(130deg, #f4be28, #ff9f1c, #f4be28);
background-size: 220% 220%;
z-index: -1;
filter: blur(11px);
opacity: 0.45;
animation: auMvGlow 7s ease-in-out infinite;
}
@keyframes auMvGlow {
0%, 100% { background-position: 0% 50%; opacity: 0.35; }
50% { background-position: 100% 50%; opacity: 0.6; }
}
.au-mv-orb { filter: blur(26px); will-change: transform; }
.au-mv-orb-1 { animation: auMvDrift1 10s ease-in-out infinite; }
.au-mv-orb-2 { animation: auMvDrift2 13s ease-in-out infinite; }
@keyframes auMvDrift1 { 0%, 100% { transform: translate(0, 0) scale(1); } 50% { transform: translate(44px, -34px) scale(1.2); } }
@keyframes auMvDrift2 { 0%, 100% { transform: translate(0, 0) scale(1); } 50% { transform: translate(-38px, 30px) scale(1.16); } }
.au-mv-gfx { animation: auMvGfxFloat 16s ease-in-out infinite; }
@keyframes auMvGfxFloat {
0%, 100% { transform: translate(0, 0) scale(0.82); }
25% { transform: translate(-34px, -22px) scale(1); }
50% { transform: translate(-14px, -48px) scale(1.22); }
75% { transform: translate(-42px, -14px) scale(1); }
}
.au-mv-ringspin { transform-box: fill-box; transform-origin: center; animation: auMvSpin 12s linear infinite; }
@keyframes auMvSpin { to { transform: rotate(360deg); } }
.au-mv-ringpulse { transform-box: fill-box; transform-origin: center; animation: auMvRingPulse 4.5s ease-in-out infinite; }
@keyframes auMvRingPulse { 0%, 100% { transform: scale(1); opacity: 1; } 50% { transform: scale(1.1); opacity: 0.65; } }
.au-mv-dot { transform-box: fill-box; transform-origin: center; animation: auMvDotPulse 1.8s ease-in-out infinite; }
.au-mv-dot-2 { animation-delay: 0.6s; }
.au-mv-dot-3 { animation-delay: 1.2s; }
@keyframes auMvDotPulse { 0%, 100% { transform: scale(1); opacity: 0.75; } 50% { transform: scale(1.9); opacity: 0.2; } }
@media (prefers-reduced-motion: reduce) {
.au-mv-orb-1, .au-mv-orb-2, .au-mv-card::before, .au-mv-gfx, .au-mv-ringspin, .au-mv-ringpulse, .au-mv-dot { animation: none !important; }
}
@media (max-width: 860px) {
.au-story-grid { grid-template-columns: 1fr !important; gap: 36px !important; }
.au-market-grid { grid-template-columns: 1fr !important; gap: 36px !important; }

View File

@@ -264,3 +264,24 @@ body.is-home-page.scroll-up .sticky-active {
/* Hero carousel layout lives in HeroCarousel.tsx as a scoped <style> tag,
matching every other home-page section — keeps it working even if this
global stylesheet has a load/HMR hiccup. */
/* Problem section — the wide "No Real-Time Signal" card (dark-bento-card-4).
Its mobile art/copy sizing lives here (not in the component's scoped <style>)
so it reloads reliably, and the extra .dark-bento-card-4 qualifier gives it
higher specificity so it wins over the generic .problem-card-art rules. */
@media (max-width: 640px) {
#problem .dark-bento-card-4 .problem-card-text {
width: 60% !important;
}
#problem .dark-bento-card-4 .problem-card-art--wide {
width: 46% !important;
height: 52% !important;
right: 0 !important;
bottom: 0 !important;
}
#problem .dark-bento-card-4 .problem-card-art--wide img {
transform: scale(1.25) !important;
object-fit: contain !important;
object-position: bottom right !important;
}
}

View File

@@ -35,7 +35,7 @@ const PRODUCTS = [
'Entry-to-purchase funnel tracking that flags where shoppers drop off',
],
metric: { val: '3.2×', lbl: 'conversion uplift' },
tags: ['Footfall Tracking', 'Heatmaps', 'Behavior Analysis'],
tags: ['Footfall Tracking', 'Behavior Analysis'],
},
{
n: '02', title: 'SpendSense', subtitle: 'Customer Spending Intelligence', color: '#10B981',
@@ -63,12 +63,12 @@ const PRODUCTS = [
n: '04', title: 'Identiq', subtitle: 'Consumer Loyalty Platform', color: '#f4be28',
desc: 'The consumer-facing app that closes the loop. Identiq turns daily logins, walking challenges, and quick mini-games into LYTs, Loyalys reward tokens, redeemable instantly at any partner store, so every idle moment on a phone becomes a reason to walk into a real one.',
capabilities: [
'Daily login and step-challenge rewards that build a lasting habit loop',
'Daily login and step-challenge Lyts that build a lasting habit loop',
'Game based offers like Spin Wheel, Scratch Card, and Maze Challenge',
'Instant offline redemption at any partner store, no card required',
],
metric: { val: '50K+', lbl: 'active shoppers' },
tags: ['Daily Rewards', 'Game Based Offers', 'Offline Redemption'],
tags: ['Daily Lyts', 'Game Based Offers', 'Offline Redemption'],
},
];
@@ -112,7 +112,7 @@ export default function MeetTheCrewPage() {
color: '#ffffff', margin: '0 0 36px', fontFamily: 'Sora, sans-serif',
}}>
Meet the<br />
<span style={{ color: '#f4be28' }}>Crew.</span>
<span style={{ color: '#f4be28' }}>Agents.</span>
</h1>
<p data-gsap="text" style={{ fontSize: 'clamp(16px, 1.5vw, 20px)', lineHeight: 1.75, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', maxWidth: 560, margin: '0 auto' }}>
The builders, thinkers, and retail obsessives behind Loyaly, working every day to make offline shopping as smart as online.
@@ -160,57 +160,65 @@ export default function MeetTheCrewPage() {
</Reveal>
<div style={{ position: 'relative' }}>
<ScrollStack useWindowScroll itemDistance={160} itemScale={0.035} itemStackDistance={40} baseScale={0.86} blurAmount={1.2}>
<ScrollStack useWindowScroll itemDistance={160} itemScale={0.035} itemStackDistance={40} baseScale={0.86} hideBehind>
{PRODUCTS.map((p, i) => (
<ScrollStackItem key={i}>
<div style={{
background: `linear-gradient(155deg, #14141c 0%, #0c0c12 100%)`,
border: `1px solid ${p.color}40`,
borderRadius: 28, padding: 'clamp(28px, 3.2vw, 48px)', height: '100%', boxSizing: 'border-box',
position: 'relative', overflow: 'hidden',
background: `radial-gradient(130% 80% at 100% 0%, ${p.color}22 0%, transparent 46%), linear-gradient(155deg, #16161f 0%, #0a0a10 100%)`,
border: `1px solid ${p.color}4d`,
boxShadow: `inset 0 1px 0 rgba(255,255,255,0.06)`,
borderRadius: 28, padding: 'clamp(36px, 4vw, 64px)',
minHeight: 'clamp(380px, 48vh, 500px)', height: '100%', boxSizing: 'border-box',
}}>
<div className="mtc-product-grid" style={{ display: 'grid', gridTemplateColumns: '1.35fr 1fr', gap: 'clamp(28px, 4vw, 56px)', alignItems: 'start' }}>
{/* Accent glow blob */}
<div aria-hidden style={{ position: 'absolute', top: '-22%', right: '-8%', width: 360, height: 360, borderRadius: '50%', background: `radial-gradient(circle, ${p.color}26 0%, transparent 70%)`, pointerEvents: 'none' }} />
<div className="mtc-product-grid" style={{ position: 'relative', display: 'grid', gridTemplateColumns: '1.3fr 1fr', gap: 'clamp(32px, 4.5vw, 68px)', alignItems: 'center', height: '100%' }}>
{/* Left — identity + description */}
<div>
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}>
<span style={{ fontSize: 13, fontWeight: 800, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '0.05em' }}>{p.n}</span>
<span style={{ width: 24, height: 1, background: `${p.color}66`, display: 'block' }} />
<span style={{ fontSize: '0.7rem', fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: p.color, fontFamily: 'Sora, sans-serif' }}>{p.subtitle}</span>
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 24 }}>
<span style={{ fontSize: 15, fontWeight: 800, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '0.05em' }}>{p.n}</span>
<span style={{ fontSize: '0.74rem', fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: p.color, fontFamily: 'Sora, sans-serif' }}>{p.subtitle}</span>
</div>
<h3 style={{ fontSize: 'clamp(1.8rem, 2.8vw, 2.8rem)', fontWeight: 800, letterSpacing: '-0.03em', color: '#fff', margin: '0 0 16px', fontFamily: 'Sora, sans-serif' }}>{p.title}</h3>
<p style={{ fontSize: 15, lineHeight: 1.75, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif', margin: '0 0 24px', maxWidth: 480 }}>{p.desc}</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 8 }}>
<h3 style={{ fontSize: 'clamp(2.1rem, 3.4vw, 3.4rem)', fontWeight: 800, letterSpacing: '-0.035em', color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', lineHeight: 1 }}>{p.title}</h3>
<p style={{ fontSize: 'clamp(15px, 1.1vw, 17px)', lineHeight: 1.8, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 30px', maxWidth: 520 }}>{p.desc}</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>
{p.tags.map((tag) => (
<span key={tag} style={{
padding: '6px 14px', borderRadius: 100, fontSize: '0.72rem', fontWeight: 600,
color: 'rgba(255,255,255,0.7)', background: 'rgba(255,255,255,0.05)',
border: '1px solid rgba(255,255,255,0.1)', fontFamily: 'Sora, sans-serif',
padding: '8px 16px', borderRadius: 100, fontSize: '0.76rem', fontWeight: 700,
color: '#fff', background: `${p.color}1a`,
border: `1px solid ${p.color}3d`, fontFamily: 'Sora, sans-serif',
}}>{tag}</span>
))}
</div>
</div>
{/* Right — metric + capabilities */}
{/* Right — metric hero + capabilities */}
<div>
<div style={{
padding: 'clamp(18px, 2vw, 24px)', borderRadius: 18, marginBottom: 18,
background: `${p.color}12`, border: `1px solid ${p.color}35`,
padding: 'clamp(24px, 2.6vw, 34px)', borderRadius: 22, marginBottom: 24,
background: `linear-gradient(150deg, ${p.color}2b 0%, ${p.color}0d 100%)`,
border: `1px solid ${p.color}55`,
boxShadow: `0 22px 54px -26px ${p.color}66, inset 0 1px 0 rgba(255,255,255,0.08)`,
}}>
<div style={{ fontSize: 'clamp(1.8rem, 2.6vw, 2.4rem)', fontWeight: 900, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', lineHeight: 1 }}>{p.metric.val}</div>
<div style={{ fontSize: 12, color: 'rgba(255,255,255,0.45)', fontFamily: 'Sora, sans-serif', marginTop: 6, textTransform: 'uppercase', letterSpacing: '0.06em' }}>{p.metric.lbl}</div>
<div style={{ fontSize: 'clamp(2.4rem, 3.4vw, 3.4rem)', fontWeight: 900, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.04em', lineHeight: 1, textShadow: `0 0 30px ${p.color}55` }}>{p.metric.val}</div>
<div style={{ fontSize: 12, fontWeight: 700, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif', marginTop: 10, textTransform: 'uppercase', letterSpacing: '0.08em' }}>{p.metric.lbl}</div>
</div>
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
{p.capabilities.map((cap) => (
<div key={cap} style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>
<div key={cap} style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
<span style={{
width: 18, height: 18, borderRadius: '50%', flexShrink: 0, marginTop: 2,
width: 22, height: 22, borderRadius: '50%', flexShrink: 0, marginTop: 1,
display: 'flex', alignItems: 'center', justifyContent: 'center',
background: `${p.color}18`, border: `1px solid ${p.color}45`,
background: `${p.color}22`, border: `1px solid ${p.color}66`,
boxShadow: `0 0 14px -2px ${p.color}77`,
}}>
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>
</span>
<span style={{ fontSize: 13.5, lineHeight: 1.55, color: 'rgba(255,255,255,0.65)', fontFamily: 'Sora, sans-serif' }}>{cap}</span>
<span style={{ fontSize: 14.5, lineHeight: 1.55, color: 'rgba(255,255,255,0.72)', fontFamily: 'Sora, sans-serif' }}>{cap}</span>
</div>
))}
</div>

View File

@@ -37,43 +37,20 @@ const CAPS = [
},
];
const STEPS = [
{
n: '01', title: 'Sign Up', desc: 'Create your Loyaly merchant account in minutes.', color: '#f4be28',
icon: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M16 21v-2a4 4 0 00-4-4H6a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/></svg>,
},
{
n: '02', title: 'Upload Your Products', desc: 'Add your catalogue, deals, and offers to the platform.', color: '#a78bfa',
icon: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M21 15v4a2 2 0 01-2 2H5a2 2 0 01-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>,
},
{
n: '03', title: 'Publish Your Offers', desc: 'Go live with targeted deals your nearby customers will see instantly.', color: '#34d399',
icon: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 11l18-5v12L3 14v-3z"/><path d="M11.6 16.8a3 3 0 11-5.8-1.6"/></svg>,
},
{
n: '04', title: 'Connect to Customers', desc: 'Loyaly matches you with shoppers in your area who are ready to buy.', color: '#f97316',
icon: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>,
},
{
n: '05', title: 'Close the Deal', desc: 'Customers walk in. They purchase. You grow.', color: '#f4be28',
icon: <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5"/></svg>,
},
];
const PILLARS = [
{
title: 'Get Online', color: '#f4be28',
desc: 'Your store, visible to thousands of local shoppers, without building a website.',
title: 'Get Discovered', color: '#f4be28',
desc: 'List your store on Loyaly and get found by thousands of nearby shoppers — no website, no complex setup.',
icon: <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 010 20 15.3 15.3 0 010-20z"/></svg>,
},
{
title: 'Get Noticed', color: '#a78bfa',
desc: "Push real-time offers and proximity alerts that reach customers the moment they're nearby.",
title: 'Drive Footfall', color: '#a78bfa',
desc: 'Launch game-based campaigns and LYT drops that turn phone time into real, in-store visits.',
icon: <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8a6 6 0 10-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg>,
},
{
title: 'Get Simplified', color: '#34d399',
desc: 'One dashboard. All your deals, customers, and performance, managed in one place.',
title: 'Understand Everything', color: '#34d399',
desc: 'Run campaigns, track live footfall, and read AI customer insights — all from one dashboard.',
icon: <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round"><rect x="3" y="3" width="7" height="9" rx="1"/><rect x="14" y="3" width="7" height="5" rx="1"/><rect x="14" y="12" width="7" height="9" rx="1"/><rect x="3" y="16" width="7" height="5" rx="1"/></svg>,
},
];
@@ -155,19 +132,26 @@ export default function ForBusinessPage() {
letterSpacing: '-0.05em', lineHeight: 0.9,
color: '#ffffff', margin: '0 0 32px', fontFamily: 'Sora, sans-serif',
}}>
Turn Footfall<br />
Turn Every Visit<br />
<span style={{
background: 'linear-gradient(105deg,#f4be28 0%,#fff8e0 45%,#f4be28 60%,#ff9500 100%)',
backgroundSize: '200% auto', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent',
animation: 'shimmer 3s linear infinite',
}}>Into Revenue.</span>
}}>Into A Relationship.</span>
</h1>
<p data-gsap="text" style={{
fontSize: 'clamp(16px, 1.6vw, 21px)', lineHeight: 1.7, color: 'rgba(255,255,255,0.45)',
fontFamily: 'Sora, sans-serif', maxWidth: 560, margin: '0 auto 52px',
fontSize: 'clamp(16px, 1.6vw, 21px)', lineHeight: 1.7, color: 'rgba(255,255,255,0.55)',
fontFamily: 'Sora, sans-serif', maxWidth: 620, margin: '0 auto 18px',
}}>
Loyaly.ai gives you complete visibility and control over your retail operations, understanding customers from entry to purchase.
Loyaly.ai understands customer behavior in real time and helps businesses earn loyalty through personalized experiences powered by AI.
</p>
<p data-gsap="text" style={{
fontSize: 'clamp(14px, 1.3vw, 17px)', lineHeight: 1.7, color: 'rgba(255,255,255,0.4)',
fontFamily: 'Sora, sans-serif', maxWidth: 540, margin: '0 auto 52px',
}}>
Engage customers, understand what they love, and create moments that keep them coming back.
</p>
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', justifyContent: 'center' }}>
@@ -176,14 +160,14 @@ export default function ForBusinessPage() {
padding: '18px 40px', background: '#f4be28', color: '#111',
fontSize: 16, fontWeight: 800, borderRadius: 100, textDecoration: 'none',
fontFamily: 'Sora, sans-serif', boxShadow: '0 16px 60px rgba(244,190,40,0.3)',
}}>Book a Demo </Link>
}}>Get Started </Link>
<Link href="/contacts" style={{
display: 'inline-flex', alignItems: 'center', gap: 10,
padding: '18px 40px', background: 'rgba(255,255,255,0.06)', color: '#fff',
fontSize: 16, fontWeight: 700, borderRadius: 100, textDecoration: 'none',
fontFamily: 'Sora, sans-serif', border: '1px solid rgba(255,255,255,0.12)',
backdropFilter: 'blur(12px)',
}}>Talk to Sales</Link>
}}>Book a Demo</Link>
</div>
</div>
</div>
@@ -232,13 +216,13 @@ export default function ForBusinessPage() {
<RevealBlock>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 80, flexWrap: 'wrap', gap: 24 }}>
<div>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 16 }}>Capabilities</div>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 16 }}>The AI Agents</div>
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: 0, fontFamily: 'Sora, sans-serif' }}>
Four tools.<br />One merchant platform.
Four agents.<br />One AI.
</h2>
</div>
<p style={{ fontSize: 16, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', maxWidth: 360, lineHeight: 1.7 }}>
Every tool works independently and amplifies the others, giving merchants a complete system to attract, engage, and retain customers.
Every agent works independently and amplifies the others, giving merchants a complete system to attract, engage, and retain customers.
</p>
</div>
</RevealBlock>
@@ -282,49 +266,6 @@ export default function ForBusinessPage() {
<RevealBlock>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20, position: 'relative' }}>How It Works</div>
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 700, position: 'relative' }}>
Live in five steps.
</h2>
</RevealBlock>
<div className="uc-timeline" style={{ position: 'relative', maxWidth: 880 }}>
<div className="uc-timeline-line" aria-hidden style={{ position: 'absolute', left: 27, top: 8, bottom: 8, width: 2, background: 'linear-gradient(180deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.04) 100%)' }} />
{STEPS.map((step, i) => (
<RevealBlock key={step.n} delay={i * 0.08}>
<div className="uc-timeline-row" style={{
display: 'grid', gridTemplateColumns: '56px 1fr', gap: 28,
paddingBottom: i < STEPS.length - 1 ? 48 : 0,
position: 'relative',
}}>
<div style={{
width: 56, height: 56, borderRadius: '50%', flexShrink: 0,
background: '#0c0c12', border: `2px solid ${step.color}`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
color: step.color, position: 'relative', zIndex: 1,
boxShadow: `0 0 0 6px #07070d, 0 8px 24px -8px ${step.color}80`,
}}>
{step.icon}
</div>
<div className="uc-timeline-card" style={{
background: 'rgba(255,255,255,0.03)', borderRadius: 18, padding: '26px 30px',
border: '1px solid rgba(255,255,255,0.08)', backdropFilter: 'blur(12px)',
display: 'flex', flexDirection: 'column', gap: 6,
}}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 12 }}>
<span style={{ fontSize: 12, fontWeight: 800, color: step.color, fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em' }}>{step.n}</span>
<h3 style={{ fontSize: 'clamp(19px, 2.2vw, 26px)', fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif', margin: 0, letterSpacing: '-0.02em' }}>{step.title}</h3>
</div>
<p style={{ fontSize: 15, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.6 }}>{step.desc}</p>
</div>
</div>
</RevealBlock>
))}
</div>
</section>
{/* ── MERCHANT BENEFITS ── */}
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
<RevealBlock>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20, position: 'relative' }}>Why Merchants Choose Us</div>
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 700, position: 'relative' }}>
Three ways we grow your store.
</h2>
</RevealBlock>
@@ -332,25 +273,37 @@ export default function ForBusinessPage() {
{PILLARS.map((p, i) => (
<RevealBlock key={p.title} delay={i * 0.1}>
<div className="uc-pillar-card" style={{
padding: '36px 32px', borderRadius: 22, height: '100%', boxSizing: 'border-box',
background: `linear-gradient(160deg, ${p.color}14 0%, #faf9f6 60%)`,
border: `1px solid ${p.color}35`, position: 'relative', overflow: 'hidden',
transition: 'transform 0.35s cubic-bezier(0.22,1,0.36,1), border-color 0.35s ease',
padding: '34px 30px 30px', borderRadius: 24, height: '100%', boxSizing: 'border-box',
background: `radial-gradient(130% 90% at 12% 0%, ${p.color}26 0%, transparent 55%), linear-gradient(180deg, rgba(255,255,255,0.055) 0%, rgba(255,255,255,0.015) 100%)`,
border: `1px solid ${p.color}45`,
boxShadow: `0 26px 60px -30px ${p.color}66, inset 0 1px 0 rgba(255,255,255,0.08)`,
backdropFilter: 'blur(10px)',
position: 'relative', overflow: 'hidden',
transition: 'transform 0.4s cubic-bezier(0.22,1,0.36,1), filter 0.35s ease',
}}>
<span style={{
position: 'absolute', top: -18, right: -10, fontSize: 96, fontWeight: 900,
color: 'transparent', WebkitTextStroke: `1px ${p.color}22`, fontFamily: 'Sora, sans-serif',
lineHeight: 1, userSelect: 'none', pointerEvents: 'none',
}}>0{i + 1}</span>
position: 'absolute', top: 22, right: 22,
display: 'inline-flex', alignItems: 'center', gap: 5,
fontSize: 12.5, fontWeight: 800, letterSpacing: '0.08em',
color: p.color, fontFamily: 'Sora, sans-serif', lineHeight: 1,
padding: '6px 12px', borderRadius: 100,
background: `${p.color}1a`, border: `1px solid ${p.color}55`,
boxShadow: `0 0 18px -6px ${p.color}88`,
userSelect: 'none', pointerEvents: 'none', zIndex: 1,
}}>
<span style={{ width: 5, height: 5, borderRadius: '50%', background: p.color, boxShadow: `0 0 8px ${p.color}` }} />
STEP 0{i + 1}
</span>
<div style={{
width: 54, height: 54, borderRadius: 16, marginBottom: 24,
background: `${p.color}18`, border: `1px solid ${p.color}45`,
width: 56, height: 56, borderRadius: 16, marginBottom: 26,
background: `${p.color}22`, border: `1px solid ${p.color}66`,
boxShadow: `0 0 26px -6px ${p.color}99, inset 0 1px 0 rgba(255,255,255,0.14)`,
display: 'flex', alignItems: 'center', justifyContent: 'center', color: p.color,
position: 'relative', zIndex: 1,
}}>{p.icon}</div>
<h3 style={{ fontSize: 22, fontWeight: 800, color: '#0a0a0f', fontFamily: 'Sora, sans-serif', margin: '0 0 12px', letterSpacing: '-0.02em', position: 'relative', zIndex: 1 }}>{p.title}</h3>
<p style={{ fontSize: 15, lineHeight: 1.7, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 20px', position: 'relative', zIndex: 1 }}>{p.desc}</p>
<div style={{ height: 3, width: 40, borderRadius: 2, background: p.color, position: 'relative', zIndex: 1 }} />
<h3 style={{ fontSize: 23, fontWeight: 800, color: '#ffffff', fontFamily: 'Sora, sans-serif', margin: '0 0 12px', letterSpacing: '-0.02em', position: 'relative', zIndex: 1 }}>{p.title}</h3>
<p style={{ fontSize: 15, lineHeight: 1.7, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 22px', position: 'relative', zIndex: 1 }}>{p.desc}</p>
<div style={{ height: 4, width: 44, borderRadius: 4, background: p.color, boxShadow: `0 0 14px ${p.color}`, position: 'relative', zIndex: 1 }} />
</div>
</RevealBlock>
))}
@@ -358,47 +311,130 @@ export default function ForBusinessPage() {
</section>
{/* ── PROXIMITY MARKETING ── */}
<section style={{ padding: '120px 8%', background: '#0c0a06', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(244,190,40,0.03) 1px,transparent 1px),linear-gradient(90deg,rgba(244,190,40,0.03) 1px,transparent 1px)', backgroundSize: '56px 56px', pointerEvents: 'none' }} />
<section style={{ padding: '120px 8%', background: '#ffffff', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(23,21,18,0.035) 1px,transparent 1px),linear-gradient(90deg,rgba(23,21,18,0.035) 1px,transparent 1px)', backgroundSize: '56px 56px', pointerEvents: 'none' }} />
<RevealBlock>
<div className="uc-proximity-grid" style={{ display: 'grid', gridTemplateColumns: '0.85fr 1.15fr', gap: '6vw', alignItems: 'center', position: 'relative' }}>
<div className="uc-proximity-grid" style={{ display: 'grid', gridTemplateColumns: '0.9fr 1.1fr', gap: '5vw', alignItems: 'center', position: 'relative' }}>
{/* Radar visual */}
<div className="uc-radar" style={{ position: 'relative', width: '100%', aspectRatio: '1 / 1', maxWidth: 320, margin: '0 auto' }}>
{[0, 1, 2].map((r) => (
<div key={r} className="uc-radar-ring" style={{
{/* Animated city map with proximity pins */}
<div className="uc-map-panel" style={{
position: 'relative', borderRadius: 28, width: '100%', aspectRatio: '4 / 3', overflow: 'hidden',
background: 'linear-gradient(165deg, #fffdf7 0%, #faf3e0 100%)',
border: '1px solid rgba(244,190,40,0.28)',
boxShadow: '0 34px 80px -34px rgba(201,150,15,0.4), inset 0 1px 0 rgba(255,255,255,0.7)',
}}>
<svg viewBox="0 0 400 300" preserveAspectRatio="xMidYMid meet" style={{ position: 'absolute', inset: 0, width: '100%', height: '100%' }}>
{/* Building blocks */}
<g fill="#f6edd4">
<rect x="18" y="18" width="66" height="44" rx="6" />
<rect x="316" y="22" width="62" height="60" rx="6" />
<rect x="22" y="196" width="74" height="86" rx="6" />
<rect x="322" y="200" width="56" height="82" rx="6" />
<rect x="150" y="18" width="40" height="40" rx="5" />
<rect x="236" y="238" width="54" height="46" rx="6" />
</g>
{/* Main roads */}
<g stroke="#efe3c6" strokeLinecap="round">
<line x1="0" y1="80" x2="400" y2="80" strokeWidth="8" />
<line x1="0" y1="150" x2="400" y2="150" strokeWidth="9" />
<line x1="0" y1="222" x2="400" y2="222" strokeWidth="8" />
<line x1="120" y1="0" x2="120" y2="300" strokeWidth="8" />
<line x1="200" y1="0" x2="200" y2="300" strokeWidth="9" />
<line x1="300" y1="0" x2="300" y2="300" strokeWidth="8" />
</g>
{/* Secondary streets */}
<g stroke="#ece0c0" strokeWidth="1.4">
<line x1="0" y1="40" x2="400" y2="40" />
<line x1="0" y1="118" x2="400" y2="118" />
<line x1="0" y1="190" x2="400" y2="190" />
<line x1="0" y1="262" x2="400" y2="262" />
<line x1="60" y1="0" x2="60" y2="300" />
<line x1="252" y1="0" x2="252" y2="300" />
<line x1="350" y1="0" x2="350" y2="300" />
</g>
{/* Proximity radar rings from the store */}
<g fill="none" stroke="#f4be28">
<circle className="uc-map-ring" cx="200" cy="150" r="18" strokeWidth="1.6" />
<circle className="uc-map-ring" cx="200" cy="150" r="18" strokeWidth="1.6" style={{ animationDelay: '1.1s' }} />
<circle className="uc-map-ring" cx="200" cy="150" r="18" strokeWidth="1.6" style={{ animationDelay: '2.2s' }} />
</g>
{/* Routes to nearby shoppers */}
<g stroke="#d9a72a" strokeWidth="1.8" fill="none" strokeLinecap="round">
{[[120, 80], [300, 105], [150, 240], [312, 220]].map(([x, y], i) => (
<line key={i} className="uc-map-link" x1="200" y1="150" x2={x} y2={y} style={{ animationDelay: `${i * 0.3}s` }} />
))}
</g>
{/* Store glow */}
<circle cx="200" cy="150" r="42" fill="#f4be28" opacity="0.16" />
{/* Shopper pins */}
{[[120, 80], [300, 105], [150, 240], [312, 220]].map(([x, y], i) => (
<g key={i}>
<circle className="uc-map-shopper" cx={x} cy={y} r="6" fill="#f4be28" style={{ animationDelay: `${i * 0.5}s` }} />
<circle cx={x} cy={y} r="5" fill="#171512" stroke="#fffdf7" strokeWidth="2" />
</g>
))}
</svg>
{/* Store pin (centered, matches SVG store at 200,150) */}
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 76, height: 76 }}>
<span className="uc-pin-halo" aria-hidden style={{ position: 'absolute', inset: '-24%', borderRadius: '50%', background: 'radial-gradient(circle, rgba(244,190,40,0.5) 0%, transparent 70%)' }} />
<div style={{
position: 'absolute', inset: 0, borderRadius: '50%',
border: '1.5px solid rgba(244,190,40,0.35)',
animation: `ucRadarPulse 3s ease-out ${r * 1}s infinite`,
}} />
))}
background: 'radial-gradient(circle at 35% 30%, #ffd75e 0%, #f4be28 45%, #c9960f 100%)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
boxShadow: '0 14px 34px -8px rgba(201,150,15,0.65), inset 0 2px 4px rgba(255,255,255,0.55)',
border: '3px solid #fffdf7',
}}>
<svg viewBox="0 0 24 24" fill="#171512" style={{ width: 30, height: 30 }}>
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1112 6.5a2.5 2.5 0 010 5z" />
</svg>
</div>
</div>
{/* Live counter chip */}
<div style={{
position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)',
width: '34%', height: '34%', borderRadius: '50%',
background: 'radial-gradient(circle, #f4be28 0%, #c9960f 100%)',
display: 'flex', alignItems: 'center', justifyContent: 'center',
boxShadow: '0 0 40px rgba(244,190,40,0.5)',
position: 'absolute', left: 16, bottom: 16, display: 'inline-flex', alignItems: 'center', gap: 8,
padding: '8px 14px', borderRadius: 100, background: 'rgba(255,255,255,0.92)',
border: '1px solid rgba(244,190,40,0.35)', boxShadow: '0 8px 22px -12px rgba(23,21,18,0.3)', backdropFilter: 'blur(6px)',
}}>
<svg width="34%" height="34%" viewBox="0 0 24 24" fill="#111" style={{ width: '38%', height: '38%' }}>
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5A2.5 2.5 0 1112 6.5a2.5 2.5 0 010 5z" />
</svg>
<span className="uc-map-livedot" style={{ width: 7, height: 7, borderRadius: '50%', background: '#16a34a', display: 'block' }} />
<span style={{ fontSize: 12, fontWeight: 800, color: '#171512', fontFamily: 'Sora, sans-serif' }}>18 shoppers nearby</span>
</div>
</div>
{/* Content */}
<div>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>Capability Spotlight</div>
<h2 style={{ fontSize: 'clamp(28px, 3.6vw, 48px)', fontWeight: 800, letterSpacing: '-0.03em', lineHeight: 1.08, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif' }}>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 9, marginBottom: 22, padding: '6px 15px 6px 6px', borderRadius: 100, background: 'rgba(244,190,40,0.1)', border: '1px solid rgba(244,190,40,0.3)' }}>
<span style={{ width: 22, height: 22, borderRadius: '50%', background: '#f4be28', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#171512" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8a6 6 0 10-12 0c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg>
</span>
<span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '0.16em', textTransform: 'uppercase', color: '#8a5a14', fontFamily: 'Sora, sans-serif' }}>Capability Spotlight</span>
</div>
<h2 style={{ fontSize: 'clamp(28px, 3.6vw, 48px)', fontWeight: 800, letterSpacing: '-0.03em', lineHeight: 1.08, color: '#0a0a0f', margin: '0 0 20px', fontFamily: 'Sora, sans-serif' }}>
Reach customers the moment they&apos;re nearby.
</h2>
<p style={{ fontSize: 16, lineHeight: 1.8, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif', margin: '0 0 32px', maxWidth: 480 }}>
<p style={{ fontSize: 16, lineHeight: 1.8, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 32px', maxWidth: 480 }}>
Loyaly&apos;s proximity engine sends instant notifications to shoppers browsing within your store&apos;s radius. No ad spend. No guesswork. Just timely, relevant offers delivered at exactly the right moment.
</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
{[{ v: 'Instant', l: 'Notification speed' }, { v: '0', l: 'Ad spend required' }, { v: 'Real-time', l: 'Radius targeting' }].map((s) => (
<div key={s.l} style={{ padding: '14px 20px', borderRadius: 14, background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.25)' }}>
<div style={{ fontSize: 18, fontWeight: 900, color: '#f4be28', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.02em' }}>{s.v}</div>
<div style={{ fontSize: 11.5, color: 'rgba(255,255,255,0.45)', fontFamily: 'Sora, sans-serif', marginTop: 3 }}>{s.l}</div>
<div className="uc-prox-stats" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12, maxWidth: 500 }}>
{[
{ v: 'Instant', l: 'Notification speed', icon: <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" /> },
{ v: '0', l: 'Ad spend required', icon: <><line x1="12" y1="1" x2="12" y2="23" /><path d="M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6" /></> },
{ v: 'Real-time', l: 'Radius targeting', icon: <><path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z" /><circle cx="12" cy="9" r="2.5" /></> },
].map((s) => (
<div key={s.l} className="uc-prox-stat" style={{
padding: '18px', borderRadius: 16, background: '#ffffff',
border: '1px solid rgba(23,21,18,0.08)', boxShadow: '0 10px 30px -18px rgba(23,21,18,0.3)',
}}>
<div style={{
width: 34, height: 34, borderRadius: 10, marginBottom: 12,
background: 'rgba(244,190,40,0.14)', border: '1px solid rgba(244,190,40,0.32)',
display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#c9960f',
}}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">{s.icon}</svg>
</div>
<div style={{ fontSize: 18, fontWeight: 900, color: '#171512', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.02em' }}>{s.v}</div>
<div style={{ fontSize: 11.5, color: 'rgba(0,0,0,0.5)', fontFamily: 'Sora, sans-serif', marginTop: 3 }}>{s.l}</div>
</div>
))}
</div>
@@ -484,7 +520,21 @@ export default function ForBusinessPage() {
0% { transform: scale(0.3); opacity: 0.9; }
100% { transform: scale(1.7); opacity: 0; }
}
.uc-pillar-card:hover { transform: translateY(-6px); }
.uc-pillar-card:hover { transform: translateY(-8px) scale(1.015); filter: brightness(1.12) saturate(1.08); }
.uc-radar-dot { animation: ucDotPulse 2s ease-in-out infinite; }
@keyframes ucDotPulse { 0%, 100% { transform: scale(1); opacity: 0.85; } 50% { transform: scale(1.25); opacity: 1; } }
.uc-pin-halo { animation: ucHaloPulse 2.6s ease-in-out infinite; }
@keyframes ucHaloPulse { 0%, 100% { transform: scale(1); opacity: 0.75; } 50% { transform: scale(1.28); opacity: 0.35; } }
.uc-prox-stat { transition: transform 0.3s cubic-bezier(0.22,1,0.36,1), box-shadow 0.3s ease, border-color 0.3s ease; }
.uc-prox-stat:hover { transform: translateY(-4px); box-shadow: 0 20px 42px -22px rgba(23,21,18,0.4); border-color: rgba(244,190,40,0.5); }
.uc-map-ring { animation: ucMapRing 3.4s ease-out infinite; }
@keyframes ucMapRing { 0% { r: 18px; opacity: 0.5; } 100% { r: 118px; opacity: 0; } }
.uc-map-link { stroke-dasharray: 5 7; animation: ucMapDash 1s linear infinite; }
@keyframes ucMapDash { to { stroke-dashoffset: -24; } }
.uc-map-shopper { animation: ucMapPing 2s ease-in-out infinite; }
@keyframes ucMapPing { 0%, 100% { r: 6px; opacity: 0.5; } 50% { r: 13px; opacity: 0.12; } }
.uc-map-livedot { animation: ucMapLive 1.5s ease-in-out infinite; }
@keyframes ucMapLive { 0% { box-shadow: 0 0 0 0 rgba(22,163,74,0.5); } 70% { box-shadow: 0 0 0 6px rgba(22,163,74,0); } 100% { box-shadow: 0 0 0 0 rgba(22,163,74,0); } }
@media (max-width: 860px) {
.uc-challenge-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
.uc-outcomes-grid { grid-template-columns: repeat(2,1fr) !important; }
@@ -504,6 +554,7 @@ export default function ForBusinessPage() {
}
@media (prefers-reduced-motion: reduce) {
.uc-radar-ring { animation: none !important; opacity: 0.4 !important; }
.uc-radar-dot, .uc-pin-halo, .uc-map-ring, .uc-map-link, .uc-map-shopper, .uc-map-livedot { animation: none !important; }
}
`}</style>
</main>

View File

@@ -10,7 +10,7 @@ const NAV = [
links: [
{ label: 'For People', href: '/for_people' },
{ label: 'For Business', href: '/use_cases' },
{ label: 'Meet the Crew', href: '/solutions_page' },
{ label: 'Meet the Agents', href: '/solutions_page' },
],
},
{
@@ -119,9 +119,13 @@ export default function Footer() {
alignItems: 'start',
}}>
{/* Brand */}
<div className="ly-footer-brand">
<Link href="/" aria-label="Loyaly.AI home">
<Image src="/images/logo-dark.png" alt="Loyaly.AI" width={560} height={160} style={{ height: 140, width: 'auto', marginBottom: 24, display: 'block' }} />
<div className="ly-footer-brand" style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
<Link href="/" aria-label="Loyaly.AI home" style={{ display: 'block', maxWidth: '100%', marginBottom: 18 }}>
{/* logo-dark.png is 250x150 with the wordmark in the middle strip (y 58107);
an overflow box crops the transparent top/bottom padding so it fills at size */}
<span className="ly-footer-logo" style={{ display: 'block', width: 300, maxWidth: '100%', aspectRatio: '250 / 58', overflow: 'hidden' }}>
<Image src="/images/logo-dark.png" alt="Loyaly.AI" width={250} height={150} style={{ width: '100%', height: '100%', objectFit: 'cover', objectPosition: 'center 55%', display: 'block' }} />
</span>
</Link>
<p style={{ fontSize: 14, lineHeight: 1.75, color: 'rgba(255,255,255,0.35)', fontFamily: 'Sora, sans-serif', margin: '0 0 28px', maxWidth: 280 }}>
AI powered retail intelligence that turns every walk in into a loyal customer.
@@ -195,7 +199,7 @@ export default function Footer() {
@media (max-width: 560px) {
.ly-footer-heading { font-size: clamp(30px, 9vw, 44px) !important; }
.ly-footer-columns { grid-template-columns: 1fr !important; gap: 32px !important; }
.ly-footer-brand img { height: 110px !important; }
.ly-footer-logo { width: 260px !important; }
.ly-footer-bottom { gap: 16px !important; }
.ly-footer-bottom > div { gap: 18px !important; }
}

View File

@@ -9,9 +9,9 @@ const NAV_LINKS = [
{ label: 'Home', href: '/' },
{ label: 'For People', href: '/for_people' },
{ label: 'For Business', href: '/use_cases' },
{ label: 'Meet the Crew', href: '/solutions_page' },
{ label: 'Blogs', href: '/blog_main' },
{ label: 'Meet the Agents', href: '/solutions_page' },
{ label: 'About us', href: '/about_us' },
{ label: 'Blogs', href: '/blog_main' },
];
const MOBILE_QUERY = '(max-width: 1023px)';

View File

@@ -31,6 +31,7 @@
-webkit-transform: translateZ(0);
transform: translateZ(0);
position: relative;
transition: opacity 0.35s ease;
}
.scroll-stack-end {

View File

@@ -27,6 +27,7 @@ interface ScrollStackProps {
scaleDuration?: number;
rotationAmount?: number;
blurAmount?: number;
hideBehind?: boolean;
useWindowScroll?: boolean;
onStackComplete?: () => void;
}
@@ -42,6 +43,7 @@ const ScrollStack = ({
baseScale = 0.85,
rotationAmount = 0,
blurAmount = 0,
hideBehind = false,
useWindowScroll = false,
onStackComplete,
}: ScrollStackProps) => {
@@ -122,6 +124,14 @@ const ScrollStack = ({
const endElementTop = endTopRef.current;
// Which card is currently on top of the stack (used for blur + hide-behind)
let topCardIndex = 0;
for (let j = 0; j < cardsRef.current.length; j++) {
const jCardTop = cardTopsRef.current[j] ?? 0;
const jTriggerStart = jCardTop - stackPositionPx - itemStackDistance * j;
if (scrollTop >= jTriggerStart) topCardIndex = j;
}
cardsRef.current.forEach((card, i) => {
if (!card) return;
@@ -137,20 +147,14 @@ const ScrollStack = ({
const rotation = rotationAmount ? i * rotationAmount * scaleProgress : 0;
let blur = 0;
if (blurAmount) {
let topCardIndex = 0;
for (let j = 0; j < cardsRef.current.length; j++) {
const jCardTop = cardTopsRef.current[j] ?? 0;
const jTriggerStart = jCardTop - stackPositionPx - itemStackDistance * j;
if (scrollTop >= jTriggerStart) {
topCardIndex = j;
}
}
if (blurAmount && i < topCardIndex) {
const depthInStack = topCardIndex - i;
blur = Math.max(0, depthInStack * blurAmount);
}
if (i < topCardIndex) {
const depthInStack = topCardIndex - i;
blur = Math.max(0, depthInStack * blurAmount);
}
// Hide cards stacked behind the current top card entirely
if (hideBehind) {
card.style.opacity = i < topCardIndex ? '0' : '1';
}
let translateY = 0;
@@ -207,6 +211,7 @@ const ScrollStack = ({
baseScale,
rotationAmount,
blurAmount,
hideBehind,
useWindowScroll,
onStackComplete,
calculateProgress,

View File

@@ -12,9 +12,9 @@ if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
const TAGS = [
{ label: 'Live coupons', icon: '🎟️' },
{ label: 'Nearby stores', icon: '📍' },
{ label: 'Game-based deals', icon: '🎮' },
{ label: 'Earn as you live', icon: '' },
{ label: 'Play to win', icon: '🎮' },
{ label: 'Spend anywhere', icon: '🛍️' },
];
export default function DiscoverDealsSection() {
@@ -54,10 +54,10 @@ export default function DiscoverDealsSection() {
<section ref={panelRef} className="fp-panel" style={{ background: '#08080f' }}>
{/* Background photo */}
<Image src="/images/stock/discover.png" alt="" fill style={{ objectFit: 'cover', opacity: 0.16, zIndex: 0 }} />
<Image src="/images/stock/discover.png" alt="" fill style={{ objectFit: 'cover', opacity: 0.72, zIndex: 0 }} />
<div style={{
position: 'absolute', inset: 0, zIndex: 0,
background: 'linear-gradient(100deg, rgba(8,8,15,0.96) 0%, rgba(8,8,15,0.65) 42%, rgba(8,8,15,0.5) 68%, rgba(8,8,15,0.85) 100%), linear-gradient(180deg, rgba(8,8,15,0.2) 0%, rgba(8,8,15,0.8) 85%)',
background: 'linear-gradient(100deg, rgba(8,8,15,0.72) 0%, rgba(8,8,15,0.32) 42%, rgba(8,8,15,0.12) 70%, rgba(8,8,15,0.24) 100%), linear-gradient(180deg, rgba(8,8,15,0) 0%, rgba(8,8,15,0.28) 90%)',
}} />
{/* Soft radial — drifts with parallax for depth */}
@@ -71,37 +71,23 @@ export default function DiscoverDealsSection() {
{/* Headline column */}
<div>
<div style={{ marginBottom: 28 }}><span className="fp-kicker fp-rise">Discover<span className="fp-kicker-num">01</span></span></div>
<div style={{ marginBottom: 28 }}><span className="fp-kicker fp-rise">Meet LytsUp<span className="fp-kicker-num">01</span></span></div>
<h2 className="fp-h1" style={{ color: '#fff' }}>
<span className="fp-mask"><span>Every</span></span>
<span className="fp-mask"><span>deal in</span></span>
<span className="fp-mask"><span className="fp-shimmer">your city.</span></span>
<span className="fp-mask"><span>The app that</span></span>
<span className="fp-mask"><span>rewards you for</span></span>
<span className="fp-mask"><span className="fp-shimmer">living your life.</span></span>
</h2>
<p className="fp-rise fp-desc" style={{ color: 'rgba(255,255,255,0.45)', maxWidth: 380 }}>
Find participating stores nearby and see their active game based discount campaigns, live coupons, and LYTs earning opportunities, all surfaced in real time so you never miss a deal.
LytsUp turns everyday moments walking, playing, exploring, shopping into LYTs you can spend instantly at the brands you love.
</p>
<div ref={tagsRef} style={{ display: 'flex', flexWrap: 'wrap', gap: 10, marginBottom: 20, maxWidth: 460 }}>
<div ref={tagsRef} style={{ display: 'flex', flexWrap: 'wrap', gap: 10, maxWidth: 460 }}>
{TAGS.map((tag) => (
<FpTagCard key={tag.label} theme="dark" icon={tag.icon}>{tag.label}</FpTagCard>
))}
</div>
<FpTagCard
theme="dark"
className="fp-rise"
style={{ display: 'inline-flex' }}
icon={(
<svg width="13" height="13" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="10" r="3" stroke="#f4be28" strokeWidth="2" />
<path d="M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7z" stroke="#f4be28" strokeWidth="2" fill="none" />
</svg>
)}
>
Coimbatore, TN
</FpTagCard>
</div>
{/* Discover GIF */}
@@ -117,7 +103,7 @@ export default function DiscoverDealsSection() {
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
src="/images/stock/discover.gif"
alt="Discover deals in the Loyaly.AI app"
alt="Discover deals in the LytsUp app"
style={{ position: 'relative', zIndex: 1, width: '100%', aspectRatio: '1465 / 1074', objectFit: 'cover', borderRadius: 20, display: 'block' }}
/>

View File

@@ -81,7 +81,7 @@ export default function EarnRewardsSection() {
<div ref={phoneRef} style={{ display: 'flex', justifyContent: 'center', opacity: 0, width: '100%' }}>
<PhoneMockupImage
src="/For_people/01_earn-cropped.png"
alt="Loyaly.AI Earn screen showing Lyts balance and activity feed"
alt="LytsUp Earn screen showing Lyts balance and activity feed"
floatClassName="erm-phone-shell"
aspectRatio={1684 / 3196}
/>
@@ -103,7 +103,7 @@ export default function EarnRewardsSection() {
<div ref={tagsRef} style={{ display: 'flex', flexWrap: 'wrap', gap: 10, marginBottom: 20, maxWidth: 460 }}>
{earnTags.map((tag) => (
<FpTagCard key={tag.label} theme="light" icon={tag.icon} iconBg={tag.bg}>
{tag.label} <span style={{ color: '#9a9184', fontWeight: 600 }}>Lyts</span>
{tag.label}
</FpTagCard>
))}
</div>

View File

@@ -83,7 +83,7 @@ export default function ExperienceSection() {
fontSize: 16, lineHeight: 1.85, color: 'rgba(7, 7, 13, 0.75)', fontFamily: 'Sora, sans-serif', margin: '0 0 36px', maxWidth: 360,
fontWeight: 500,
}}>
No plastic cards. No coupon hunting. No app open required at checkout. Loyaly works quietly in the background while you shop the way you always have.
No plastic cards. No coupon hunting. No app open required at checkout. LytsUp works quietly in the background while you shop the way you always have.
</p>
<div style={{ display: 'flex', flexDirection: 'column' }}>

View File

@@ -30,7 +30,7 @@ function useMagnetic() {
const PROOF = [
{ val: '50K+', label: 'Active users', color: '#f4be28', icon: '👥', iconBg: '#fffbeb' },
{ val: '10K+', label: 'Partner stores', color: '#16a34a', icon: '🏬', iconBg: '#f0fdf4' },
{ val: '₹28L+', label: 'Rewards given', color: '#2563eb', icon: '🎁', iconBg: '#eff6ff' },
{ val: '₹28L+', label: 'Lyts given', color: '#2563eb', icon: '🎁', iconBg: '#eff6ff' },
{ val: '4.9★', label: 'App rating', color: '#9333ea', icon: '⭐', iconBg: '#faf5ff' },
];

View File

@@ -63,23 +63,33 @@ export default function HeroSection() {
fontSize: 'clamp(40px, 5.4vw, 82px)', fontWeight: 800, lineHeight: 1.02,
letterSpacing: '-0.045em', color: '#fff', margin: '50px 0 28px', fontFamily: 'Sora, sans-serif',
}}>
<span className="fp-mask"><span>Get Lit.</span></span>
<span className="fp-mask"><span className="fp-shimmer">Live More.</span></span>
<span className="fp-mask"><span>Earn LYTs.</span></span>
<span className="fp-mask"><span className="fp-shimmer">Lit Yourself.</span></span>
</h1>
<p className="fp-content fp-subtext" style={{
fontSize: 'clamp(16px, 1.4vw, 19px)', lineHeight: 1.7, fontWeight: 400,
color: 'rgba(255,255,255,0.58)', maxWidth: 640, margin: '0 auto 40px', fontFamily: 'Sora, sans-serif',
color: 'rgba(255,255,255,0.6)', maxWidth: 640, margin: '0 auto 16px', fontFamily: 'Sora, sans-serif',
}}>
Meet <strong>Lyts</strong>: rewards for the things you already do. Walk, play, explore, shop, or complete a challenge, then spend your Lyts instantly at the brands you love. No waiting. No cashback. No complicated redemption.
Every purchase unlocks more than points. Earn <strong>LYTs</strong>, discover experiences, and make every shopping trip count.
</p>
<p className="fp-content fp-subtext" style={{
fontSize: 'clamp(14px, 1.2vw, 16px)', lineHeight: 1.7, fontWeight: 400,
color: 'rgba(255,255,255,0.45)', maxWidth: 560, margin: '0 auto 40px', fontFamily: 'Sora, sans-serif',
}}>
Shop nearby, collect LYTs, unlock exclusive drops, and become part of a living ecosystem designed around you.
</p>
<div className="fp-content fp-cta" style={{ display: 'flex', gap: 16, flexWrap: 'wrap', alignItems: 'center', justifyContent: 'center' }}>
<button className="fp-btn fp-btn-primary">
Start Earning LYTs
</button>
<button
ref={magSecondary}
className="fp-btn fp-btn-ghost fp-magnetic"
>
See How It Works
Explore Nearby
</button>
</div>

View File

@@ -51,11 +51,11 @@ export default function LytsUniverseSection() {
<h2 className="fp-h1" style={{ color: '#fff' }}>
<span className="fp-mask"><span>Welcome to the</span></span>
<span className="fp-mask"><span className="fp-shimmer">World of Lyts.</span></span>
<span className="fp-mask"><span className="fp-shimmer">World of Lytians.</span></span>
</h2>
<p className="fp-rise" style={{ fontSize: 15, lineHeight: 1.8, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif', maxWidth: 420, margin: '0 0 24px' }}>
A world where every action has value. Meet the Lytians, real people with real stories and real rewards, straight from the app.
A world where every action has value. Meet the Lytians, real people with real stories and real Lyts, straight from the app.
</p>
<div className="fp-rise" style={{ display: 'flex', flexWrap: 'wrap', gap: 10, marginBottom: 28, maxWidth: 460 }}>

View File

@@ -11,14 +11,14 @@ const PILLARS = [
},
{
n: '02',
title: 'Earn Rewards',
title: 'Earn Lyts',
desc: 'Every rupee spent earns LYT points. Zero expiry. Redeem anytime, at any partner store.',
color: '#16a34a',
},
{
n: '03',
title: 'Make It a Pleasure',
desc: 'Shopping should feel good. Loyaly makes every trip more rewarding, more fun, and more personal.',
desc: 'Shopping should feel good. LytsUp makes every trip more rewarding, more fun, and more personal.',
color: '#a78bfa',
},
];
@@ -41,7 +41,7 @@ export default function PillarsSection() {
fontSize: 16, lineHeight: 1.8, color: '#4f463a', margin: '0 0 56px',
fontFamily: 'Sora, sans-serif', maxWidth: 560,
}}>
Earn Loyaly rewards on everyday shopping, from groceries to fashion to home essentials. Every purchase brings you closer to your next reward.
Earn Lyts on everyday shopping, from groceries to fashion to home essentials. Every purchase brings you closer to your next Lyt.
</p>
<div className="fp-pillar-cols" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24 }}>

View File

@@ -72,7 +72,7 @@ export default function SpendAnywhereSection() {
fontSize: 'clamp(17px, 1.7vw, 21px)', lineHeight: 1.3, color: '#fff', fontFamily: 'Sora, sans-serif',
margin: 0, fontWeight: 800, letterSpacing: '-0.02em',
}}>
One Wallet. <span style={{ color: '#f4be28' }}>Thousands of Rewards.</span>
One Wallet. <span style={{ color: '#f4be28' }}>Thousands of Lyts.</span>
</p>
</div>
</div>

View File

@@ -32,7 +32,7 @@ export default function WhyLoyalySection() {
const bars = [
{ label: 'Online retail rewards', pct: 78, color: '#171512' },
{ label: 'Offline retail rewards', pct: 2, color: '#bc7122' },
{ label: 'Loyaly target', pct: 60, color: '#f4be28' },
{ label: 'LytsUp target', pct: 60, color: '#f4be28' },
];
return (
@@ -77,7 +77,7 @@ export default function WhyLoyalySection() {
{[
{ val: '₹2.1L Cr', label: 'Offline retail market size', accent: '#171512' },
{ val: '<2%', label: 'Currently getting rewards', accent: '#bc7122' },
{ val: '10Cr+', label: 'Shoppers Loyaly can reach', accent: '#16a34a' },
{ val: '10Cr+', label: 'Shoppers LytsUp can reach', accent: '#16a34a' },
].map((m, i) => (
<div key={i} className="fp-rise" style={{
display: 'flex', alignItems: 'center', gap: 16, padding: '14px 18px',
@@ -116,7 +116,7 @@ export default function WhyLoyalySection() {
fontSize: 16, lineHeight: 1.8, color: '#4f463a', margin: '0 0 36px',
fontFamily: 'Sora, sans-serif', maxWidth: 440,
}}>
India&apos;s offline retail market is worth 2.1 lakh crore. Yet rewards penetration sits at effectively zero. Loyaly exists to close that gap, permanently.
India&apos;s offline retail market is worth 2.1 lakh crore. Yet rewards penetration sits at effectively zero. LytsUp exists to close that gap, permanently.
</p>
{/* Data bars */}

View File

@@ -10,7 +10,6 @@ interface HeroContentProps {
}
export default function HeroContent({ slide }: HeroContentProps) {
const badgeRef = useRef<HTMLDivElement | null>(null);
const headlineRef = useRef<HTMLHeadingElement | null>(null);
const descRef = useRef<HTMLParagraphElement | null>(null);
const ctaRef = useRef<HTMLDivElement | null>(null);
@@ -23,14 +22,11 @@ export default function HeroContent({ slide }: HeroContentProps) {
: [];
const desc = descRef.current;
const cta = ctaRef.current;
const badge = badgeRef.current;
gsap.set(wordEls, { yPercent: 120, opacity: 0, rotateZ: 4 });
gsap.set([desc, cta], { opacity: 0, y: 24 });
gsap.set(badge, { opacity: 0, y: -12, scale: 0.92 });
const tl = gsap.timeline({ defaults: { ease: 'power4.out' } });
tl.to(badge, { opacity: 1, y: 0, scale: 1, duration: 0.6 }, 0);
tl.to(
wordEls,
{ yPercent: 0, opacity: 1, rotateZ: 0, duration: 1, stagger: 0.055 },
@@ -44,11 +40,6 @@ export default function HeroContent({ slide }: HeroContentProps) {
return (
<div className="hero-carousel-content">
<div ref={badgeRef} className="hero-carousel-badge">
<span className="hero-carousel-badge-dot" />
{slide.badge}
</div>
<h1 ref={headlineRef} className="hero-carousel-headline">
{words.map((word, i) => {
const isHighlighted = slide.highlightWords.includes(word);

View File

@@ -43,10 +43,12 @@ export default function LytsUpSection({ isLoaded = true }: LytsUpSectionProps) {
const tl = gsap.timeline({
scrollTrigger: { trigger: '.lytsup-header', start: 'top 85%', toggleActions: 'play none none none' },
});
tl.fromTo('.lytsup-word-inner', { yPercent: 115, rotate: 4 }, { yPercent: 0, rotate: 0, duration: 0.85, stagger: 0.08, ease: 'expo.out' }, 0)
.fromTo('.lytsup-sticker-wrap', { opacity: 0, scale: 0.4, rotate: 16 }, { opacity: 1, scale: 1, rotate: -4, duration: 0.7, ease: 'back.out(2.2)' }, 0.18)
tl.fromTo('.lytsup-eyebrow', { opacity: 0, y: -12 }, { opacity: 1, y: 0, duration: 0.6, ease: 'power3.out' }, 0)
.fromTo('.lytsup-word-inner', { yPercent: 115, rotate: 4 }, { yPercent: 0, rotate: 0, duration: 0.85, stagger: 0.08, ease: 'expo.out' }, 0.05)
.fromTo('.lytsup-sticker-wrap', { opacity: 0, scale: 0.4, rotate: 16 }, { opacity: 1, scale: 1, rotate: -4, duration: 0.7, ease: 'back.out(2.2)' }, 0.22)
.fromTo('.lytsup-header-sub', { opacity: 0, y: 24, filter: 'blur(6px)' }, { opacity: 1, y: 0, filter: 'blur(0px)', duration: 0.8, ease: 'expo.out' }, 0.45)
.fromTo('.lytsup-stat-chip', { opacity: 0, y: 18, scale: 0.85 }, { opacity: 1, y: 0, scale: 1, duration: 0.55, stagger: 0.1, ease: 'back.out(1.8)' }, 0.6);
.fromTo('.lytsup-tag', { opacity: 0, y: 12, scale: 0.9 }, { opacity: 1, y: 0, scale: 1, duration: 0.5, stagger: 0.07, ease: 'back.out(1.8)' }, 0.55)
.fromTo('.lytsup-stat-chip', { opacity: 0, y: 18, scale: 0.85 }, { opacity: 1, y: 0, scale: 1, duration: 0.55, stagger: 0.1, ease: 'back.out(1.8)' }, 0.68);
// Stat numbers count up once the chips have landed
PROOF_STATS.forEach((stat, i) => {
@@ -159,8 +161,12 @@ export default function LytsUpSection({ isLoaded = true }: LytsUpSectionProps) {
borderRadius: 32,
padding: 'clamp(64px, 8vw, 110px) clamp(20px, 4vw, 64px) clamp(64px, 8vw, 96px)',
zIndex: 2,
background: 'radial-gradient(ellipse 110% 80% at 50% 0%, #17130d 0%, #0d0b09 55%, #0a0807 100%)',
boxShadow: 'inset 0 0 0 1px rgba(244,190,40,0.12)',
background:
'radial-gradient(120% 90% at 50% -12%, rgba(244,194,26,0.24) 0%, rgba(244,194,26,0) 52%),' +
'radial-gradient(90% 70% at 86% 16%, rgba(255,217,90,0.14) 0%, rgba(255,217,90,0) 55%),' +
'radial-gradient(80% 80% at 10% 96%, rgba(244,194,26,0.09) 0%, rgba(244,194,26,0) 50%),' +
'linear-gradient(180deg, #17140a 0%, #121009 42%, #0b0906 100%)',
boxShadow: 'inset 0 0 0 1px rgba(244,194,26,0.16)',
}}
>
{/* Faint dot-grid texture for depth, matching the light sections elsewhere */}
@@ -192,6 +198,21 @@ export default function LytsUpSection({ isLoaded = true }: LytsUpSectionProps) {
{/* Header */}
<div className="lytsup-header relative z-10 text-center max-w-3xl mx-auto mb-12 flex flex-col items-center">
<div
className="lytsup-eyebrow"
style={{
display: 'inline-flex', alignItems: 'center', gap: 9,
padding: '7px 16px 7px 12px', borderRadius: 100, marginBottom: 24,
background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.28)',
backdropFilter: 'blur(8px)',
}}
>
<span className="lytsup-live-dot" style={{ width: 8, height: 8, borderRadius: '50%', background: '#f4be28' }} />
<span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '0.16em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif' }}>
Earn as you live
</span>
</div>
<h2
style={{
margin: 0,
@@ -237,9 +258,26 @@ export default function LytsUpSection({ isLoaded = true }: LytsUpSectionProps) {
color: 'rgba(255,255,255,0.62)', fontFamily: 'Sora, sans-serif',
}}
>
LytsUp transforms your everyday activities into real shopping rewards. Walk, play, explore, visit your favorite places, complete fun challenges, and earn Lyts that you can spend instantly at participating brands.
Walk. Play. Explore. Earn <strong style={{ color: 'rgba(255,255,255,0.9)', fontWeight: 700 }}>Lyts</strong> just by living your life then spend them instantly at the brands you already love.
</p>
{/* Gen-Z vibe tags */}
<div className="lytsup-tags" style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 8, marginTop: 22 }}>
{['#WalkToEarn', '#PlayDaily', '#ExploreMore', '#LevelUp'].map((t) => (
<span
key={t}
className="lytsup-tag"
style={{
fontSize: '0.8rem', fontWeight: 700, fontFamily: 'Sora, sans-serif',
color: 'rgba(255,255,255,0.8)', padding: '6px 14px', borderRadius: 100,
background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.1)',
}}
>
{t}
</span>
))}
</div>
{/* Social-proof stat chips — numbers count up on reveal */}
<div style={{ display: 'flex', flexWrap: 'wrap', justifyContent: 'center', gap: 10, marginTop: 26 }}>
{PROOF_STATS.map((stat, i) => (
@@ -265,6 +303,22 @@ export default function LytsUpSection({ isLoaded = true }: LytsUpSectionProps) {
</div>
</div>
{/* Gen-Z gradient marquee ticker */}
<div className="lytsup-marquee relative z-10" aria-hidden>
<div className="lytsup-marquee-track">
{[0, 1].map((dup) => (
<div key={dup} style={{ display: 'inline-flex', gap: '2.5rem', alignItems: 'center' }}>
{['Earn Lyts', 'Play Daily', 'Explore', 'Level Up', 'Spend Instantly'].map((w) => (
<React.Fragment key={w}>
<span className="lytsup-marquee-item">{w}</span>
<span className="lytsup-marquee-star"></span>
</React.Fragment>
))}
</div>
))}
</div>
</div>
{/* Visual flow — numbered stepper; active step + connectors are driven by scroll position */}
<div ref={flowRef} className="lytsup-flow relative z-10 flex items-start justify-center gap-2 sm:gap-4 mb-16 sm:mb-20 max-w-xl mx-auto">
{['Earn', 'Spend', 'Live More'].map((step, i) => (
@@ -304,42 +358,70 @@ export default function LytsUpSection({ isLoaded = true }: LytsUpSectionProps) {
<div
className="lytsup-closing relative z-10 mx-auto"
style={{
maxWidth: 720, textAlign: 'center',
padding: 'clamp(36px, 4vw, 52px) clamp(24px, 4vw, 48px)',
borderRadius: 26,
background: 'linear-gradient(180deg, rgba(255,255,255,0.06) 0%, rgba(255,255,255,0.02) 100%)',
border: '1px solid rgba(244,190,40,0.22)',
boxShadow: '0 30px 60px -24px rgba(0,0,0,0.6)',
position: 'relative', overflow: 'hidden',
maxWidth: 680, textAlign: 'center',
padding: 'clamp(40px, 4.5vw, 60px) clamp(28px, 4vw, 52px)',
borderRadius: 30,
background: 'linear-gradient(135deg, #FFE071 0%, #f4be28 46%, #FF9E2C 100%)',
border: '3px solid #171512',
boxShadow: '10px 10px 0 0 #171512',
position: 'relative',
}}
>
<span aria-hidden style={{ position: 'absolute', top: -18, right: 26, fontSize: 34, transform: 'rotate(12deg)' }}>🔥</span>
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse 70% 60% at 30% 0%, rgba(244,190,40,0.14) 0%, transparent 70%)', pointerEvents: 'none' }} />
{/* Rotated sticker badge */}
<span style={{
display: 'inline-block', transform: 'rotate(-3deg)', marginBottom: 18,
background: '#171512', color: '#FFE071', fontFamily: 'Sora, sans-serif',
fontSize: 12, fontWeight: 800, letterSpacing: '0.12em', textTransform: 'uppercase',
padding: '7px 16px', borderRadius: 100, border: '2px solid #FFE071',
}}>
🚀 Join 50K+ vibing
</span>
<h3 style={{
margin: '0 0 14px', position: 'relative',
fontSize: 'clamp(1.7rem, 3.6vw, 2.7rem)', fontWeight: 900,
lineHeight: 1.02, color: '#171512', letterSpacing: '-0.03em',
fontFamily: 'Sora, sans-serif',
}}>
Your life. Your Lyts.<br />Let&apos;s get it.
</h3>
<p
style={{
margin: '0 0 28px', position: 'relative',
fontSize: 'clamp(1.05rem, 1.6vw, 1.4rem)', fontWeight: 600,
lineHeight: 1.6, color: 'rgba(255,255,255,0.85)',
fontFamily: 'Sora, sans-serif', letterSpacing: '-0.01em',
margin: '0 auto 24px', maxWidth: 460, position: 'relative',
fontSize: 'clamp(0.95rem, 1.3vw, 1.1rem)', fontWeight: 600,
lineHeight: 1.55, color: 'rgba(23,21,18,0.72)',
fontFamily: 'Sora, sans-serif',
}}
>
The more you live, the more you earn. Every step, every visit, and every adventure brings you closer to your next reward.
The more you live, the more you earn every step, visit, and adventure drops fresh Lyts straight into your pocket.
</p>
{/* Logo in a sticker chip so it pops on the amber card */}
<div style={{ marginBottom: 22, position: 'relative' }}>
<span style={{
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
padding: '9px 18px', background: '#ffffff',
border: '2.5px solid #171512', borderRadius: 16, boxShadow: '4px 4px 0 0 #171512',
}}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src="/images/lyts.png" alt="LytsUp" style={{ height: 32, width: 'auto', display: 'block', objectFit: 'contain' }} />
</span>
</div>
<Link
ref={ctaRef}
href="/contacts"
style={{
display: 'inline-flex', alignItems: 'center', gap: 10, position: 'relative',
padding: '15px 34px', borderRadius: 100,
fontSize: '1rem', fontWeight: 800, fontFamily: 'Sora, sans-serif',
color: '#171512', textDecoration: 'none',
background: 'linear-gradient(135deg, #f4be28 0%, #FF9800 100%)',
boxShadow: '0 16px 40px -12px rgba(244,190,40,0.5)',
padding: '16px 38px', borderRadius: 100,
fontSize: '1.05rem', fontWeight: 800, fontFamily: 'Sora, sans-serif',
color: '#FFE071', textDecoration: 'none',
background: '#171512', border: '2.5px solid #171512',
boxShadow: '5px 5px 0 0 rgba(23,21,18,0.3)',
}}
>
Download LytsUp
Download
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.6" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 5l7 7m0 0l-7 7m7-7H3" />
</svg>
@@ -352,8 +434,73 @@ export default function LytsUpSection({ isLoaded = true }: LytsUpSectionProps) {
0%, 100% { transform: scale(1) rotate(0deg); }
50% { transform: scale(1.25) rotate(-12deg); }
}
/* Live pulsing dot on the eyebrow badge */
.lytsup-live-dot { animation: lytsupPulse 1.8s ease-out infinite; }
@keyframes lytsupPulse {
0% { box-shadow: 0 0 0 0 rgba(244,190,40,0.6); }
70% { box-shadow: 0 0 0 8px rgba(244,190,40,0); }
100% { box-shadow: 0 0 0 0 rgba(244,190,40,0); }
}
/* Vibe tags */
.lytsup-tag { transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease, transform 0.25s ease; cursor: default; }
.lytsup-tag:hover {
background: rgba(244,190,40,0.12);
border-color: rgba(244,190,40,0.45);
color: #ffffff;
transform: translateY(-2px);
}
/* Scrolling gradient marquee */
.lytsup-marquee {
overflow: hidden;
margin: 10px 0 46px;
-webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.lytsup-marquee-track {
display: inline-flex;
gap: 2.5rem;
white-space: nowrap;
will-change: transform;
animation: lytsupMarquee 24s linear infinite;
}
@keyframes lytsupMarquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
.lytsup-marquee-item {
font-family: 'Sora', sans-serif;
font-weight: 900;
font-size: clamp(1.1rem, 2vw, 1.7rem);
letter-spacing: -0.02em;
text-transform: uppercase;
background: linear-gradient(135deg, #f4be28 0%, #FF9800 100%);
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
}
.lytsup-marquee-star {
color: rgba(244,190,40,0.5);
font-size: clamp(0.9rem, 1.4vw, 1.2rem);
}
/* Funky sticker emojis bob gently */
.lytsup-sticker-emoji { animation: lytsupBob 3.2s ease-in-out infinite; }
.lytsup-sticker-emoji:nth-of-type(2) { animation-delay: 0.5s; }
.lytsup-sticker-emoji:nth-of-type(3) { animation-delay: 1s; }
.lytsup-sticker-emoji:nth-of-type(4) { animation-delay: 1.5s; }
@keyframes lytsupBob {
0%, 100% { margin-top: 0; }
50% { margin-top: -7px; }
}
@media (prefers-reduced-motion: reduce) {
.lytsup-sticker-spark { animation: none; }
.lytsup-sticker-spark,
.lytsup-live-dot,
.lytsup-marquee-track,
.lytsup-sticker-emoji { animation: none; }
}
@media (max-width: 900px) {
.lytsup-flow { max-width: 380px !important; }

View File

@@ -22,7 +22,7 @@ const DARK_CARDS = [
description: 'No compelling reason for customers to visit your store',
image: '/problems/4.png',
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ marginBottom: 12 }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" /><line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
</svg>
),
@@ -32,7 +32,7 @@ const DARK_CARDS = [
description: 'Traditional discounts erode profit margins',
image: '/problems/5.png',
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ marginBottom: 12 }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<polyline points="23 18 13.5 8.5 8.5 13.5 1 6" /><polyline points="17 18 23 18 23 12" />
</svg>
),
@@ -42,28 +42,28 @@ const DARK_CARDS = [
description: 'Digital ads rarely convert into store footfall',
image: '/problems/6.png',
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ marginBottom: 12 }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M11 5L6 9H2v6h4l5 4V5z" /><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" />
</svg>
),
},
{
title: 'No Real-Time Signal',
title: 'No Real-Time Signals',
description: 'No real-time connection between online activity and offline shopping',
image: '/problems/7.png',
wide: true,
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ marginBottom: 12 }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<line x1="1" y1="1" x2="23" y2="23" /><path d="M16.72 11.06A10.94 10.94 0 0 1 19 12.5M5 12.5a10.94 10.94 0 0 1 5.83-2.84M8.59 16.11a5.11 5.11 0 0 1 6.82 0" /><line x1="12" y1="20" x2="12.01" y2="20" />
</svg>
),
},
{
title: 'Customer Amnesia',
description: 'Customers forget about stores between visits',
description: 'Customers forget about brands between visits',
image: '/problems/8.png',
icon: (
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ marginBottom: 12 }}>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#ef4444" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" /><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" /><line x1="12" y1="17" x2="12.01" y2="17" />
</svg>
),
@@ -162,18 +162,45 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
gap: 'clamp(8px, 1.2vw, 14px)',
}}>
{[
{ val: '$2 Tn', label: 'India retail market by 2032, 25% CAGR' },
{ val: '5th', label: 'Largest retail destination globally' },
{ val: '10%', label: "Of India's GDP from retail" },
{ val: '12%', label: 'Of retail is organised, the rest is offline' },
{ val: '500M', label: 'Indians shopping online by 2030, up from 220M' },
{
val: '$2 Tn', label: 'Indian retail market by 2032, 26% CAGR',
icon: (<><line x1="12" y1="1" x2="12" y2="23" /><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6" /></>),
},
{
val: '5th', label: 'Largest retail destination globally',
icon: (<><circle cx="12" cy="12" r="10" /><line x1="2" y1="12" x2="22" y2="12" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></>),
},
{
val: '10%', label: "Of India's GDP from retail",
icon: (<><path d="M21.21 15.89A10 10 0 1 1 8 2.83" /><path d="M22 12A10 10 0 0 0 12 2v10z" /></>),
},
{
val: '12%', label: 'Of retail is organized, the rest is offline',
icon: (<><path d="M3 9l1.5-5h15L21 9" /><path d="M5 9v10a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V9" /><path d="M9 21v-6h6v6" /></>),
},
{
val: '500M', label: "India's online shoppers by 2030, up from 250M",
icon: (<><polyline points="23 6 13.5 16.5 8.5 11.5 1 19" /><polyline points="17 6 23 6 23 12" /></>),
},
].map((s, i) => (
<div key={i} style={{
<div key={i} className="problem-india-stat" style={{
padding: 'clamp(14px, 1.8vw, 20px)', borderRadius: 16,
background: '#f9f7f2', border: '1px solid rgba(23,21,18,0.08)',
}}>
<div style={{ fontSize: 'clamp(1.3rem, 2vw, 1.7rem)', fontWeight: 900, color: '#171512', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', lineHeight: 1 }}>{s.val}</div>
<div style={{ marginTop: 6, fontSize: '0.72rem', color: '#4f463a', fontFamily: 'Sora, sans-serif', lineHeight: 1.4 }}>{s.label}</div>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
<span style={{
width: 38, height: 38, borderRadius: '50%', flexShrink: 0,
display: 'flex', alignItems: 'center', justifyContent: 'center',
background: 'linear-gradient(150deg, #ee3a34 0%, #c81e1e 100%)',
boxShadow: '0 6px 14px -6px rgba(200,30,30,0.6)',
}}>
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#ffffff" strokeWidth="2.3" strokeLinecap="round" strokeLinejoin="round">
{s.icon}
</svg>
</span>
<div style={{ fontSize: 'clamp(1.3rem, 2vw, 1.7rem)', fontWeight: 900, color: '#171512', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', lineHeight: 1 }}>{s.val}</div>
</div>
<div style={{ fontSize: '0.72rem', color: '#4f463a', fontFamily: 'Sora, sans-serif', lineHeight: 1.4 }}>{s.label}</div>
</div>
))}
</div>
@@ -196,28 +223,25 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
clickEffect={true}
particleCount={6}
style={{
background: '#e12d2d',
borderRadius: '24px',
padding: 'clamp(20px, 2.4vw, 28px)',
border: '1px solid rgba(255, 255, 255, 0.15)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
minHeight: '230px',
boxShadow: '0 10px 30px -12px rgba(225, 45, 45, 0.4)',
position: 'relative',
overflow: 'hidden',
}}
>
<div style={{ width: '60%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
<div className="problem-stat-text" style={{ width: '60%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
<div style={{ fontSize: 'clamp(2rem, 3.2vw, 3rem)', fontWeight: 900, color: '#ffffff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', lineHeight: 1 }}>
68%+
68%<span style={{ color: '#ffd27a' }}>+</span>
</div>
<div style={{ marginTop: 8, fontSize: '0.75rem', fontWeight: 800, color: '#ffffff', fontFamily: 'Sora, sans-serif', textTransform: 'uppercase', letterSpacing: '0.06em', lineHeight: 1.3 }}>
Walk in once,<br />never return
</div>
</div>
<div style={{
<div className="problem-stat-art" style={{
position: 'absolute',
right: '-18px',
bottom: '-18px',
@@ -251,28 +275,25 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
clickEffect={true}
particleCount={6}
style={{
background: '#e12d2d',
borderRadius: '24px',
padding: 'clamp(20px, 2.4vw, 28px)',
border: '1px solid rgba(255, 255, 255, 0.15)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
minHeight: '230px',
boxShadow: '0 10px 30px -12px rgba(225, 45, 45, 0.4)',
position: 'relative',
overflow: 'hidden',
}}
>
<div style={{ width: '60%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
<div className="problem-stat-text" style={{ width: '60%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
<div style={{ fontSize: 'clamp(2rem, 3.2vw, 3rem)', fontWeight: 900, color: '#ffffff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', lineHeight: 1 }}>
0+
0<span style={{ color: '#ffd27a' }}>+</span>
</div>
<div style={{ marginTop: 8, fontSize: '0.75rem', fontWeight: 800, color: '#ffffff', fontFamily: 'Sora, sans-serif', textTransform: 'uppercase', letterSpacing: '0.06em', lineHeight: 1.3 }}>
Visibility<br />after they leave
</div>
</div>
<div style={{
<div className="problem-stat-art" style={{
position: 'absolute',
right: '-18px',
bottom: '-18px',
@@ -306,28 +327,25 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
clickEffect={true}
particleCount={6}
style={{
background: '#e12d2d',
borderRadius: '24px',
padding: 'clamp(20px, 2.4vw, 28px)',
border: '1px solid rgba(255, 255, 255, 0.15)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
minHeight: '230px',
boxShadow: '0 10px 30px -12px rgba(225, 45, 45, 0.4)',
position: 'relative',
overflow: 'hidden',
}}
>
<div style={{ width: '60%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
<div className="problem-stat-text" style={{ width: '60%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
<div style={{ fontSize: 'clamp(2rem, 3.2vw, 3rem)', fontWeight: 900, color: '#ffffff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', lineHeight: 1 }}>
5+
5<span style={{ color: '#ffd27a' }}>+</span>
</div>
<div style={{ marginTop: 8, fontSize: '0.75rem', fontWeight: 800, color: '#ffffff', fontFamily: 'Sora, sans-serif', textTransform: 'uppercase', letterSpacing: '0.06em', lineHeight: 1.3 }}>
Recurring pain points,<br />every quarter
</div>
</div>
<div style={{
<div className="problem-stat-art" style={{
position: 'absolute',
right: '-18px',
bottom: '-18px',
@@ -365,10 +383,8 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
clickEffect={true}
particleCount={6}
style={{
background: '#171512',
borderRadius: '24px',
padding: 'clamp(20px, 2.4vw, 28px)',
border: '1px solid rgba(255, 255, 255, 0.12)',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
@@ -377,8 +393,8 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
overflow: 'hidden',
}}
>
<div style={{ width: card.wide ? '42%' : '52%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
{card.icon}
<div className="problem-card-text" style={{ width: card.wide ? '42%' : '52%', paddingRight: '12px', position: 'relative', zIndex: 2 }}>
<div className="problem-icon-chip">{card.icon}</div>
<h3 style={{ fontSize: '1.1rem', fontWeight: 800, color: '#ffffff', fontFamily: 'Sora, sans-serif', margin: '0 0 6px' }}>
{card.title}
</h3>
@@ -387,7 +403,7 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
</p>
</div>
{card.wide ? (
<div style={{
<div className="problem-card-art problem-card-art--wide" style={{
position: 'absolute',
right: '-6px',
bottom: '-6px',
@@ -410,7 +426,7 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
/>
</div>
) : (
<div style={{
<div className="problem-card-art" style={{
position: 'absolute',
right: '-22px',
bottom: '-22px',
@@ -440,6 +456,86 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
</div>
<style>{`
/* Shared modern card surface treatment */
.problem-stat-chip, .dark-bento-card {
transition: border-color 0.3s ease, box-shadow 0.4s ease;
}
/* Glassy top-edge highlight for a crisp, premium finish */
.problem-stat-chip::before, .dark-bento-card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(180deg, rgba(255,255,255,0.18), rgba(255,255,255,0) 42%);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
z-index: 3;
}
.problem-stat-chip {
background:
radial-gradient(130% 130% at 12% 8%, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0) 46%),
linear-gradient(150deg, #f24a3d 0%, #dc2323 52%, #a81515 100%);
border: 1px solid rgba(255, 255, 255, 0.16);
box-shadow:
0 20px 48px -20px rgba(178, 20, 20, 0.6),
inset 0 1px 0 rgba(255,255,255,0.16);
}
.problem-stat-chip:hover {
border-color: rgba(255, 255, 255, 0.32);
box-shadow:
0 30px 62px -22px rgba(178, 20, 20, 0.68),
inset 0 1px 0 rgba(255,255,255,0.18);
}
/* Depth on the big stat number */
.problem-stat-chip .problem-stat-text > div:first-child {
text-shadow: 0 2px 12px rgba(120, 0, 0, 0.38);
}
/* Gold divider between the number and its label */
.problem-stat-chip .problem-stat-text > div:nth-child(2)::before {
content: '';
display: block;
width: 26px;
height: 2px;
border-radius: 2px;
background: #ffd27a;
opacity: 0.9;
margin: 4px 0 10px;
}
.dark-bento-card {
background: linear-gradient(160deg, #1d1b17 0%, #131210 100%);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 16px 40px -18px rgba(0,0,0,0.6);
}
.dark-bento-card:hover {
border-color: rgba(239, 68, 68, 0.28);
box-shadow: 0 26px 54px -20px rgba(0,0,0,0.62);
}
/* Icon chip on the dark bento cards */
.problem-icon-chip {
width: 46px;
height: 46px;
border-radius: 13px;
margin-bottom: 16px;
display: flex;
align-items: center;
justify-content: center;
background: rgba(239, 68, 68, 0.12);
border: 1px solid rgba(239, 68, 68, 0.24);
box-shadow: inset 0 1px 0 rgba(255,255,255,0.06);
}
.problem-india-stat {
transition: border-color 0.3s ease, box-shadow 0.3s ease, transform 0.3s cubic-bezier(0.22,1,0.36,1);
}
.problem-india-stat:hover {
border-color: rgba(239, 68, 68, 0.35);
box-shadow: 0 14px 30px -18px rgba(23,21,18,0.28);
transform: translateY(-3px);
}
.dark-bento-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -448,6 +544,48 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
.dark-bento-card-4 { grid-column: span 2; }
.dark-bento-card-5 { grid-column: span 1; }
/* Mobile: papercraft art was overflowing onto the copy — pin it small
to the bottom-right corner and give the text room to breathe */
@media (max-width: 640px) {
/* Tighten the tall cards so art + copy fill them instead of leaving gaps */
#problem .problem-stat-chip { min-height: 190px !important; }
#problem .dark-bento-card { min-height: 210px !important; }
#problem .problem-stat-text {
width: 50% !important;
padding-right: 8px !important;
}
#problem .problem-card-text {
width: 56% !important;
padding-right: 8px !important;
}
/* Red stat art — larger, anchored to the corner so it reads clearly */
#problem .problem-stat-art {
width: 58% !important;
height: 96% !important;
right: -8px !important;
bottom: -8px !important;
}
#problem .problem-card-art {
width: 54% !important;
height: 74% !important;
right: -10px !important;
bottom: -10px !important;
}
/* Wide card (No Real-Time Signal) mobile art + copy sizing lives in
globals.css — kept there so it reliably reloads (see comment there) */
#problem .problem-stat-art img {
transform: scale(1.55) !important;
object-fit: contain !important;
object-position: bottom right !important;
}
#problem .problem-card-art img {
transform: scale(1.65) !important;
object-fit: contain !important;
object-position: bottom right !important;
}
}
@media (max-width: 900px) {
#problem .problem-header-grid { grid-template-columns: 1fr; }
#problem .problem-header-right { border-left: none; padding-left: 0; border-top: 2px solid rgba(23,21,18,0.1); padding-top: 20px; margin-top: 20px; }

View File

@@ -33,7 +33,7 @@ const PRODUCTS: Product[] = [
'AI powered footfall analytics and behavioral intelligence. Understand how customers move, dwell, and engage, turning raw foot traffic into rich, actionable insights.',
accent: '#f4be28',
accentRgb: '244, 190, 40',
tags: ['Footfall Tracking', 'Heatmaps', 'Behavior Analysis'],
tags: ['Footfall Tracking', 'Behavior Analysis'],
},
{
id: 2,
@@ -63,10 +63,10 @@ const PRODUCTS: Product[] = [
title: 'Identiq',
subtitle: 'Consumer Loyalty Platform',
description:
'Earn LYTs through daily logins and games, then redeem rewards at real offline stores.',
'Earn LYTs through daily logins and games, then redeem Lyts at real offline stores.',
accent: '#f4be28',
accentRgb: '244, 190, 40',
tags: ['Daily Rewards', 'Game Based Offers', 'Offline Redemption'],
tags: ['Daily Lyts', 'Game Based Offers', 'Offline Redemption'],
},
];
@@ -272,9 +272,9 @@ function ProductVisual({ product }: { product: Product }) {
<span style={{
width: 34, height: 34, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
background: `linear-gradient(135deg, ${accent} 0%, #e09820 100%)`, color: '#111', fontWeight: 800, fontSize: 13, fontFamily: 'Sora, sans-serif',
}}>A</span>
}}>L</span>
<div>
<div style={{ fontSize: 13, fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif', lineHeight: 1.1 }}>Loyaly</div>
<div style={{ fontSize: 13, fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif', lineHeight: 1.1 }}>LytsUp</div>
<div style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif' }}>Member since 2024</div>
</div>
</div>
@@ -288,7 +288,7 @@ function ProductVisual({ product }: { product: Product }) {
<div className="pv-sheen" />
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.6)', fontFamily: 'Sora, sans-serif' }}>Reward LYTs</div>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.6)', fontFamily: 'Sora, sans-serif' }}>Lyts Balance</div>
<div style={{ fontSize: 32, fontWeight: 900, color: '#111', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', marginTop: 2 }}>284</div>
</div>
<span style={{ width: 34, height: 34, borderRadius: 10, background: 'rgba(255,255,255,0.35)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 15, fontWeight: 900, color: '#111', fontFamily: 'Sora, sans-serif' }}></span>

View File

@@ -19,25 +19,28 @@ interface ShiftSectionProps {
const GREEN_CARDS = [
{
value: '3.4x',
label: 'MORE REPEAT VISITS',
label: 'MORE RETURN VISITS',
description: 'Businesses using LYTs see customers come back more often and stay engaged longer.',
image: '/shift/1.png',
},
{
value: '20%',
label: 'LIVE IN-APP OFFER',
label: 'MORE CUSTOMER ENGAGEMENT',
description: 'AI-powered LYT experiences turn everyday shopping into something worth coming back for.',
image: '/shift/2.png',
},
{
value: '5',
label: "EVERYDAY MOMENTS,\nTURNED INTO LOYALTY",
label: 'LYT EXPERIENCES EVERY DAY',
description: 'From check-ins to surprise drops, there is always a new way to earn LYTs.',
image: '/shift/3.png',
},
];
const BLACK_CARDS = [
{
title: 'Earn Daily',
description: 'Customers earn LYTs through daily app activities',
title: 'Earn LYTs',
description: 'Every visit, check-in, and interaction helps you stack more LYTs.',
icon: (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect>
@@ -49,8 +52,8 @@ const BLACK_CARDS = [
image: '/shift/4.png',
},
{
title: 'Game Campaigns',
description: 'Game-based campaigns drive footfall with excitement',
title: 'LYT Challenges',
description: 'Complete missions, discover new places, and unlock exclusive experiences.',
icon: (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<line x1="6" y1="12" x2="10" y2="12"></line>
@@ -63,8 +66,8 @@ const BLACK_CARDS = [
image: '/shift/5.png',
},
{
title: 'Smart Coupons',
description: 'Smart surprise coupons create a daily reason to engage',
title: 'LYT Drops',
description: 'AI delivers personalized drops and surprises when they matter most.',
icon: (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z"></path>
@@ -74,8 +77,8 @@ const BLACK_CARDS = [
image: '/shift/6.png',
},
{
title: 'Live Discovery',
description: 'Real-time discovery of nearby stores and live offers',
title: 'Discover Nearby',
description: 'Explore local businesses, trending places, and experiences happening around you.',
icon: (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
@@ -85,8 +88,8 @@ const BLACK_CARDS = [
image: '/shift/7.png',
},
{
title: 'Deepens Loyalty',
description: 'Every store visit earns more rewards and deepens loyalty',
title: 'Get LIT',
description: 'Spend your LYTs on experiences, unlock perks, and level up your shopping journey.',
icon: (
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
@@ -182,7 +185,7 @@ export default function ShiftSection({ isLoaded = true }: ShiftSectionProps) {
<div className="shift-header-right" style={{ paddingBottom: 'clamp(4px, 1vw, 10px)', borderLeft: `2px solid ${accent}44`, paddingLeft: 'clamp(20px, 2.4vw, 32px)' }}>
<p className="shift-subtitle" style={{ color: '#4f463a', fontSize: 'clamp(1rem, 1.2vw, 1.15rem)', fontWeight: 500, margin: 0, lineHeight: 1.7, fontFamily: 'Sora, sans-serif' }}>
Loyaly bridges digital engagement and offline shopping, with daily rewards, interactive games, and surprise coupons redeemable in person.
Loyaly bridges digital engagement and offline shopping, with daily Lyts, interactive games, and surprise coupons redeemable in person.
</p>
</div>
</div>
@@ -201,35 +204,48 @@ export default function ShiftSection({ isLoaded = true }: ShiftSectionProps) {
clickEffect={true}
particleCount={6}
>
<div style={{ position: 'relative', zIndex: 2, width: '52%', display: 'flex', flexDirection: 'column', height: '100%', justifyContent: 'space-between' }}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 1 }}>
<span style={{ fontSize: 'clamp(2.5rem, 3.8vw, 3.5rem)', fontWeight: 900, color: '#ffffff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', lineHeight: 1 }}>
{card.value}+
<div style={{ position: 'relative', zIndex: 2, width: '62%', display: 'flex', flexDirection: 'column', height: '100%', justifyContent: 'flex-start' }}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 2 }}>
<span style={{ fontSize: 'clamp(2.6rem, 4vw, 3.6rem)', fontWeight: 900, color: '#ffffff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.04em', lineHeight: 1 }}>
{card.value}
</span>
<span style={{ fontSize: 'clamp(1.6rem, 2.4vw, 2.1rem)', fontWeight: 800, color: accent, fontFamily: 'Sora, sans-serif', lineHeight: 1 }}>+</span>
</div>
<div style={{
marginTop: 'auto',
fontSize: '0.68rem',
fontWeight: 800,
color: 'rgba(255, 255, 255, 0.8)',
fontFamily: 'Sora, sans-serif',
textTransform: 'uppercase',
letterSpacing: '0.06em',
lineHeight: 1.4,
whiteSpace: 'pre-line',
maxWidth: '180px'
}}>
{card.label}
<div style={{ marginTop: 18, maxWidth: '230px' }}>
<div style={{
fontSize: '0.7rem',
fontWeight: 800,
color: '#ffffff',
fontFamily: 'Sora, sans-serif',
textTransform: 'uppercase',
letterSpacing: '0.07em',
lineHeight: 1.35,
whiteSpace: 'pre-line',
}}>
{card.label}
</div>
<div style={{ width: 26, height: 2, borderRadius: 2, background: accent, margin: '10px 0 10px', opacity: 0.85 }} />
<p style={{
margin: 0,
fontSize: '0.74rem',
fontWeight: 500,
color: 'rgba(255, 255, 255, 0.66)',
fontFamily: 'Sora, sans-serif',
lineHeight: 1.5,
}}>
{card.description}
</p>
</div>
</div>
{/* Papercraft Image */}
<div className="shift-card-art" style={{
position: 'absolute',
right: i === 2 ? '-12px' : '-16px',
bottom: i === 2 ? '-12px' : '-16px',
width: i === 2 ? '56%' : '52%',
height: '90%',
right: '-14px',
bottom: '-14px',
width: i === 2 ? '46%' : '42%',
height: '82%',
opacity: 0.96,
pointerEvents: 'none',
zIndex: 1,
}}>
@@ -266,7 +282,13 @@ export default function ShiftSection({ isLoaded = true }: ShiftSectionProps) {
particleCount={6}
>
<div style={{ position: 'relative', zIndex: 2, width: cardNumber === 7 ? '36%' : (cardNumber === 8 ? '42%' : '48%'), display: 'flex', flexDirection: 'column', height: '100%' }}>
<div style={{ color: iconAccent, marginBottom: 14, display: 'flex', alignItems: 'center' }}>
<div style={{
width: 44, height: 44, borderRadius: 13, marginBottom: 16,
background: 'rgba(34,197,94,0.12)',
border: '1px solid rgba(34,197,94,0.22)',
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.06)',
color: iconAccent, display: 'flex', alignItems: 'center', justifyContent: 'center',
}}>
{card.icon}
</div>
<h3 style={{
@@ -380,23 +402,40 @@ export default function ShiftSection({ isLoaded = true }: ShiftSectionProps) {
position: relative;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0,0,0,0.03);
transition: border-color 0.3s ease, box-shadow 0.3s ease;
transition: border-color 0.3s ease, box-shadow 0.4s ease;
}
/* Glassy top highlight for a crisp, modern edge */
.shift-bento-card::before {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
padding: 1px;
background: linear-gradient(180deg, rgba(255,255,255,0.16), rgba(255,255,255,0) 42%);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
pointer-events: none;
z-index: 3;
opacity: 0.9;
}
.shift-bento-card--green {
background: linear-gradient(135deg, #134e2e 0%, #0d3821 100%);
border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 10px 30px -12px rgba(19, 78, 46, 0.35);
background: linear-gradient(150deg, #1a6b3e 0%, #124a2b 52%, #0c3620 100%);
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 16px 40px -18px rgba(12, 54, 32, 0.55);
}
.shift-bento-card--black {
background: #171512;
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 10px 30px -12px rgba(0,0,0,0.5);
background: linear-gradient(160deg, #1d1b17 0%, #131210 100%);
border: 1px solid rgba(255, 255, 255, 0.07);
box-shadow: 0 16px 40px -18px rgba(0,0,0,0.6);
}
.shift-bento-card--green:hover {
border-color: rgba(255, 255, 255, 0.24);
border-color: rgba(255, 255, 255, 0.22);
box-shadow: 0 26px 54px -20px rgba(12, 54, 32, 0.6);
}
.shift-bento-card--black:hover {
border-color: rgba(255, 255, 255, 0.16);
border-color: rgba(34, 197, 94, 0.28);
box-shadow: 0 26px 54px -20px rgba(0,0,0,0.62);
}
/* Card column mappings */
@@ -437,15 +476,16 @@ export default function ShiftSection({ isLoaded = true }: ShiftSectionProps) {
/* Pin card art to the bottom-right corner on narrow screens,
where full-width cards would otherwise let it sprawl over the text */
#shift .shift-card-art {
width: 62% !important;
height: 100% !important;
width: 56% !important;
height: 66% !important;
right: 0 !important;
bottom: 0 !important;
}
#shift .shift-card-art-img {
object-fit: contain !important;
object-position: bottom right !important;
transform: none !important;
transform: scale(1.08) !important;
transform-origin: bottom right !important;
}
}

View File

@@ -33,7 +33,7 @@ const STAGES: Stage[] = [
},
{
title: 'Engage',
tagline: 'Playable daily rewards turn one visit into a habit.',
tagline: 'Playable daily Lyts turn one visit into a habit.',
detail: 'Daily streaks, challenges & mini games',
icon: (
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
@@ -145,49 +145,71 @@ export default function WhatWeDo({ isLoaded = false }: WhatWeDoProps) {
if (typeof window === 'undefined') return;
const ctx = gsap.context(() => {
// Header: blur fade-up with stagger
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
// Header: clean fade-up with a refined blur transition
gsap.fromTo(
'.wwd-header-el',
{ opacity: 0, y: 36, filter: 'blur(6px)' },
{
opacity: 0,
y: prefersReducedMotion ? 0 : 30,
filter: prefersReducedMotion ? 'none' : 'blur(8px)'
},
{
opacity: 1, y: 0, filter: 'blur(0px)',
duration: 1, stagger: 0.18, ease: 'expo.out',
scrollTrigger: { trigger: '.wwd-header', start: 'top 85%', toggleActions: 'play none none reset' },
opacity: 1,
y: 0,
filter: 'blur(0px)',
duration: DEFAULT_DURATION,
stagger: 0.15,
ease: DEFAULT_EASE,
scrollTrigger: {
trigger: '.wwd-header',
start: 'top 85%',
toggleActions: 'play none none reset'
},
}
);
// Cards: 3D flip-up reveal — each card rises and unfolds from its bottom
// edge, then its inner content cascades in. One scroll-triggered
// timeline keeps card + content in sync.
// Cards: modern 3D lift + unblur reveal, smoothly staggered across the row
const cards = gsap.utils.toArray<HTMLElement>('.wwd-card');
const grid = document.querySelector('.wwd-grid-container');
const revealTl = gsap.timeline({
scrollTrigger: { trigger: grid, start: 'top 82%', toggleActions: 'play none none reset' },
});
revealTl.fromTo(cards,
{ opacity: 0, y: 90, rotationX: -32, transformOrigin: '50% 100%', transformPerspective: 1100 },
{ opacity: 1, y: 0, rotationX: 0, duration: 0.9, ease: 'power4.out', stagger: 0.14 },
0
);
cards.forEach((card, i) => {
const at = i * 0.14 + 0.35;
revealTl
.fromTo(card.querySelector('.wwd-icon'),
{ opacity: 0, scale: 0.4, rotate: -20 },
{ opacity: 1, scale: 1, rotate: 0, duration: 0.5, ease: 'back.out(2.2)' }, at)
.fromTo(card.querySelector('.wwd-title'),
{ opacity: 0, y: 16 },
{ opacity: 1, y: 0, duration: 0.45, ease: 'power3.out' }, at + 0.12)
.fromTo(card.querySelector('.wwd-tagline'),
{ opacity: 0, y: 12 },
{ opacity: 1, y: 0, duration: 0.4, ease: 'power3.out' }, at + 0.22)
.fromTo(card.querySelector('.wwd-detail'),
{ opacity: 0, y: 10 },
{ opacity: 1, y: 0, duration: 0.4, ease: 'power3.out' }, at + 0.3);
});
if (prefersReducedMotion) {
gsap.fromTo(
cards,
{ opacity: 0 },
{
opacity: 1,
duration: 0.5,
stagger: 0.08,
scrollTrigger: { trigger: grid, start: 'top 85%', toggleActions: 'play none none reset' },
}
);
} else {
gsap.fromTo(
cards,
{
opacity: 0,
y: 72,
scale: 0.92,
rotationX: 14,
transformOrigin: '50% 100%',
transformPerspective: 900,
filter: 'blur(10px)',
},
{
opacity: 1,
y: 0,
scale: 1,
rotationX: 0,
filter: 'blur(0px)',
duration: 1,
ease: 'power3.out',
stagger: 0.13,
scrollTrigger: { trigger: grid, start: 'top 82%', toggleActions: 'play none none reset' },
}
);
}
}, containerRef);
return () => ctx.revert();

View File

@@ -16,11 +16,11 @@ export interface HeroSlideData {
export const HERO_SLIDES: HeroSlideData[] = [
{
id: 'intro',
badge: 'AI-Powered Loyalty',
headline: 'Turn Foot Traffic Into Fans',
highlightWords: ['Fans'],
badge: 'AI-Powered Retail Intelligence',
headline: 'Turn Every Visit Into Loyalty',
highlightWords: ['Loyalty'],
description:
'Loyaly reads shopper behavior in real time and turns every visit into a reason to come back.',
'Loyaly understands shopper behavior in real time turning everyday footfall into lasting relationships and measurable growth.',
ctaLabel: 'Book a Demo',
ctaHref: '/contacts',
secondaryLabel: 'See How It Works',
@@ -30,11 +30,11 @@ export const HERO_SLIDES: HeroSlideData[] = [
},
{
id: 'app',
badge: 'Built For Every Shopper',
headline: 'Shopping, But Make It Rewarding',
highlightWords: ['Rewarding'],
badge: 'Crafted For Every Shopper',
headline: 'Shopping, Elevated To An Experience',
highlightWords: ['Experience'],
description:
'Find stores nearby, stack LYTs, unlock drops made for you, and turn every trip into a game.',
'Discover stores near you, earn LYTs as you live, and unlock experiences designed around you — every trip effortless and rewarding.',
ctaLabel: 'Explore the App',
ctaHref: '/for_people',
secondaryLabel: 'Get Early Access',
@@ -44,11 +44,11 @@ export const HERO_SLIDES: HeroSlideData[] = [
},
{
id: 'ecosystem',
badge: 'The Full Stack',
headline: 'Six Tools. One Mega Brain.',
highlightWords: ['Mega', 'Brain.'],
badge: 'One Intelligent Platform',
headline: 'Six Products. One Brain.',
highlightWords: ['One', 'Brain.'],
description:
'Behavision, SpendSense, Dyscount, Identiq, Flowmind, and the Loyaly App — built to know your customers better than they know themselves.',
'Behavision, SpendSense, Dyscount, Identiq, Flowmind, and the Loyaly App — a connected AI suite that knows your customers inside out.',
ctaLabel: 'Explore Platform',
ctaHref: '/solutions_page',
secondaryLabel: 'Meet the Tools',