updated pages

This commit is contained in:
R-Bharathraj
2026-07-07 13:21:13 +05:30
parent 57e64f86b6
commit e261cde8f5
25 changed files with 853 additions and 1612 deletions

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 512 512" xml:space="preserve">
<path d="M256,0C114.6,0,0,114.6,0,256s114.6,256,256,256s256-114.6,256-256S397.4,0,256,0z M265.1,142.1
c9.4-11.4,25.4-20.1,39.1-21.1c2.3,15.6-4.1,30.8-12.5,41.6c-9,11.6-24.5,20.5-39.5,20C249.6,167.7,256.6,152.4,265.1,142.1z
M349.4,339.9c-10.8,16.4-26,36.9-44.9,37.1c-16.8,0.2-21.1-10.9-43.8-10.8c-22.7,0.1-27.5,11-44.3,10.8
c-18.9-0.2-33.3-18.7-44.1-35.1c-30.2-46-33.4-99.9-14.7-128.6c13.2-20.4,34.1-32.3,53.8-32.3c20,0,32.5,11,49.1,11
c16,0,25.8-11,48.9-11c17.5,0,36,9.5,49.2,26c-43.2,23.7-36.2,85.4,7.5,101.9C360,322.1,357.1,328.1,349.4,339.9z"/>
</svg>

After

Width:  |  Height:  |  Size: 881 B

BIN
public/logo_icons/apple.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -17,15 +17,6 @@ function Reveal({ children, delay = 0 }: { children: React.ReactNode; delay?: nu
);
}
const VALUES = [
{ n: '01', title: 'Intelligence first', desc: 'Every product decision starts with data, not instinct.' },
{ n: '02', title: 'Merchant obsessed', desc: 'We build for the store owner who works 14-hour days.' },
{ n: '03', title: 'Radical transparency', desc: 'No black boxes. Our AI explains every recommendation.' },
{ n: '04', title: 'Build to last', desc: 'We optimise for long-term impact, not vanity metrics.' },
];
const TEAM: { name: string; role: string; initials: string; color: string }[] = [];
export default function AboutUsPage() {
return (
<main style={{ background: '#ffffff', minHeight: '100vh', overflowX: 'hidden' }}>
@@ -101,18 +92,18 @@ export default function AboutUsPage() {
{[
{
label: 'Mission', bg: '#07070d', textColor: '#fff', accent: '#f4be28',
body: 'To transform physical retail with AI-driven intelligence and meaningful customer engagement.',
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: 'To become the leading retail intelligence platform across global markets.',
body: 'Empowering every neighbourhood, making exceptional in-store experiences and real rewards accessible to all.',
},
].map((mv, i) => (
<Reveal key={i} delay={i * 0.15}>
<div style={{
padding: '72px 56px', borderRadius: 24, background: mv.bg,
height: '100%', minHeight: 360,
display: 'flex', flexDirection: 'column', justifyContent: 'space-between',
display: 'flex', flexDirection: 'column', justifyContent: 'flex-start', gap: 28,
margin: '0 20px 20px', boxSizing: 'border-box',
}}>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: mv.accent, fontFamily: 'Sora, sans-serif' }}>{mv.label}</div>
@@ -125,35 +116,39 @@ export default function AboutUsPage() {
</div>
</section>
{/* ── VALUES ── */}
{/* ── MARKET OPPORTUNITY ── */}
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
<Reveal>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>How We Work</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' }}>
What we believe.
</h2>
</Reveal>
<div style={{ display: 'flex', flexDirection: 'column' }}>
{VALUES.map((v, i) => (
<Reveal key={i} delay={i * 0.08}>
<div className="au-value-row" style={{
display: 'grid', gridTemplateColumns: '80px 1fr 1fr',
gap: '4vw', alignItems: 'center',
padding: '40px 0', borderBottom: '1px solid rgba(0,0,0,0.07)',
}}>
<span style={{ fontSize: 13, fontWeight: 700, color: '#f4be28', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em' }}>{v.n}</span>
<h3 style={{ fontSize: 'clamp(20px, 2.5vw, 36px)', fontWeight: 800, color: '#0a0a0f', fontFamily: 'Sora, sans-serif', margin: 0, letterSpacing: '-0.03em' }}>{v.title}</h3>
<p style={{ fontSize: 16, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.65 }}>{v.desc}</p>
</div>
</Reveal>
))}
<div className="au-market-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8vw', alignItems: 'start' }}>
<Reveal>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 24 }}>Market Opportunity</div>
<h2 style={{ fontSize: 'clamp(36px, 4.5vw, 68px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 32px', fontFamily: 'Sora, sans-serif' }}>
13 Million Stores.<br />One Opportunity.
</h2>
<p style={{ fontSize: 'clamp(17px, 1.6vw, 22px)', lineHeight: 1.8, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', margin: 0 }}>
India&apos;s retail market is one of the largest and fastest-growing in the world. We&apos;re building Loyaly for the stores that power it, and the shoppers who love them.
</p>
</Reveal>
<Reveal delay={0.15}>
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
{[
{ val: '13 Million+', label: 'Total Retail Stores Across India' },
{ val: '2 Million+', label: 'Organised & High-Footfall Stores' },
{ val: '50,000+', label: 'Stores In Our Launch Cities' },
].map((m) => (
<div key={m.label} style={{ padding: '28px 32px', borderRadius: 20, background: '#faf9f6', border: '1px solid rgba(0,0,0,0.06)' }}>
<div style={{ fontSize: 'clamp(26px, 3vw, 40px)', fontWeight: 900, color: '#0a0a0f', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em' }}>{m.val}</div>
<div style={{ fontSize: 14, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', marginTop: 6 }}>{m.label}</div>
</div>
))}
</div>
</Reveal>
</div>
</section>
{/* ── TEAM ── */}
<TeamCarousel />
{/* ── CULTURE CLOSE ── */}
{/* ── CLOSE: POINT TO THE CREW ── */}
<section style={{
padding: '160px 8%', background: '#f4be28',
margin: '0 20px 20px', borderRadius: 24,
@@ -165,21 +160,29 @@ export default function AboutUsPage() {
<p style={{ fontSize: 'clamp(32px, 6vw, 90px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.95, color: '#111', margin: '0 0 48px', fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2 }}>
"We're not just building a product. We're redefining retail."
</p>
<Link href="/contacts" style={{
display: 'inline-flex', alignItems: 'center', gap: 10,
padding: '18px 44px', background: '#111', color: '#f4be28',
fontSize: 16, fontWeight: 800, borderRadius: 100, textDecoration: 'none',
fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2,
}}>Join Us </Link>
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', justifyContent: 'center', position: 'relative', zIndex: 2 }}>
<Link href="/solutions_page" style={{
display: 'inline-flex', alignItems: 'center', gap: 10,
padding: '18px 44px', background: '#111', color: '#f4be28',
fontSize: 16, fontWeight: 800, borderRadius: 100, textDecoration: 'none',
fontFamily: 'Sora, sans-serif',
}}>See Our Products </Link>
<Link href="/contacts" style={{
display: 'inline-flex', alignItems: 'center', gap: 10,
padding: '18px 44px', background: 'rgba(0,0,0,0.08)', color: '#111',
fontSize: 16, fontWeight: 700, borderRadius: 100, textDecoration: 'none',
fontFamily: 'Sora, sans-serif', border: '1px solid rgba(0,0,0,0.15)',
}}>Join Us</Link>
</div>
</Reveal>
</section>
<style>{`
@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; }
.au-mv-grid { grid-template-columns: 1fr !important; }
.au-mv-grid > div > div { padding: 48px 32px !important; min-height: 280px !important; }
.au-value-row { grid-template-columns: 1fr !important; gap: 10px !important; padding: 28px 0 !important; }
}
`}</style>
</main>

View File

@@ -3,6 +3,7 @@
import React from 'react';
import VerticalScrollWrapper from '@/components/for-people/VerticalScrollWrapper';
import HeroSection from '@/components/for-people/HeroSection';
import PillarsSection from '@/components/for-people/PillarsSection';
import WhyLoyalySection from '@/components/for-people/WhyLoyalySection';
import DiscoverDealsSection from '@/components/for-people/DiscoverDealsSection';
import EarnRewardsSection from '@/components/for-people/EarnRewardsSection';
@@ -14,6 +15,7 @@ import FinalCTASection from '@/components/for-people/FinalCTASection';
const SECTIONS = [
'Intro',
'The Gap',
'Save More',
'Discover',
'Earn',
'Spend',
@@ -28,6 +30,7 @@ export default function ForPeoplePage() {
<VerticalScrollWrapper sections={SECTIONS}>
<HeroSection />
<WhyLoyalySection />
<PillarsSection />
<DiscoverDealsSection />
<EarnRewardsSection />
<SpendAnywhereSection />

View File

@@ -319,6 +319,9 @@ export default function IndexPage() {
Walk In Into a <br />
<span className="hero-gradient-text">Loyal Customer</span>
</h1>
<p className="text-[#f4be28] hero-tagline text-sm sm:text-base font-bold tracking-tight mb-3 select-none">
Offline is the new Online.
</p>
<p className="text-[#4f463a] hero-description text-sm sm:text-base mb-8 leading-relaxed select-none">
Loyaly.ai turns everyday digital engagement into real world shopping rewards. Customers earn LYTs through games and daily activities, then redeem them at offline stores near them.
</p>
@@ -444,6 +447,9 @@ export default function IndexPage() {
Walk In Into a <br />
<span className="hero-gradient-text">Loyal Customer</span>
</h1>
<p className="text-[#f4be28] hero-tagline text-sm sm:text-base md:text-lg font-bold tracking-tight mb-4 select-none">
Offline is the new Online.
</p>
<p className="text-[#4f463a] hero-description text-sm sm:text-base md:text-lg xl:text-xl mb-8 leading-relaxed max-w-2xl select-none">
Loyaly.ai turns everyday digital engagement into real world shopping rewards. Customers earn LYTs through games and daily activities, then redeem them at offline stores near them.
</p>

View File

@@ -1,23 +1,30 @@
'use client';
import React, { useState } from 'react';
import React from 'react';
import Link from 'next/link';
import Image from 'next/image';
import GSAPAnimator from '@/components/GSAPAnimator';
import { useReveal } from '@/hooks/useReveal';
import GSAPAnimator from '@/components/GSAPAnimator';
import ScrollStack, { ScrollStackItem } from '@/components/ScrollStack';
function RevealBlock({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) {
function Reveal({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) {
const { ref, visible } = useReveal();
return (
<div ref={ref} style={{
opacity: visible ? 1 : 0,
transform: visible ? 'translateY(0)' : 'translateY(52px)',
transform: visible ? 'translateY(0)' : 'translateY(48px)',
transition: `opacity 0.9s cubic-bezier(0.22,1,0.36,1) ${delay}s, transform 0.9s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
}}>{children}</div>
);
}
const VALUES = [
{ n: '01', title: 'Intelligence first', desc: 'Every product decision starts with data, not instinct.' },
{ n: '02', title: 'Merchant obsessed', desc: 'We build for the store owner who works 14-hour days.' },
{ n: '03', title: 'Radical transparency', desc: 'No black boxes. Our AI explains every recommendation.' },
{ n: '04', title: 'Build to last', desc: 'We optimise for long-term impact, not vanity metrics.' },
];
const PRODUCTS = [
{
n: '01', title: 'Behavision', subtitle: 'AI Powered Footfall Analytics', color: '#f4be28',
@@ -65,124 +72,76 @@ const PRODUCTS = [
},
];
const CAPS = [
{
n: '01', title: 'Merchant Campaign Platform', color: '#f4be28',
desc: 'Register your store, upload products and inventory, then launch interactive promotional campaigns in minutes. Define maximum discounts per product and track performance in real time.',
metrics: [{ val: '5+', lbl: 'Game formats' }, { val: '1-click', lbl: 'Campaign setup' }],
},
{
n: '02', title: 'Game Based Discount Engine', color: '#a78bfa',
desc: 'Select a product, choose a game (Maze, Spin Wheel, Scratch Card, or Match Master) and set a maximum discount. Customers play once and discover their reward only after completing the game.',
metrics: [{ val: '4×', lbl: 'Engagement lift' }, { val: '100%', lbl: 'Margin controlled' }],
},
{
n: '03', title: 'Smart Coupon System', color: '#34d399',
desc: 'Generate daily surprise coupons through the Battery Challenge and other activities. Offer flat discounts, BOGO, percentage-off deals, festival specials, and brand promotions, fresh every day.',
metrics: [{ val: '6', lbl: 'Coupon types' }, { val: 'Daily', lbl: 'Fresh surprises' }],
},
{
n: '04', title: 'Footfall & Campaign Analytics', color: '#f97316',
desc: 'Track which campaigns drove store visits, measure conversion rates, monitor repeat footfall, and optimize your promotional budget with a real-time analytics dashboard.',
metrics: [{ val: '98%', lbl: 'Count accuracy' }, { val: '<9ms', lbl: 'Real-time latency' }],
},
];
export default function SolutionsPage() {
const [hovered, setHovered] = useState<number | null>(null);
export default function MeetTheCrewPage() {
return (
<main style={{ background: '#ffffff', minHeight: '100vh', overflowX: 'hidden' }}>
<GSAPAnimator />
{/* ── HERO ── */}
<section style={{
minHeight: '92vh', background: 'radial-gradient(ellipse 120% 90% at 70% 0%, #2c2350 0%, #211c3e 38%, #181429 100%)',
minHeight: '100vh', background: 'radial-gradient(ellipse 120% 90% at 22% 0%, #3a2b5e 0%, #251c40 38%, #1a1530 100%)',
display: 'flex', alignItems: 'center',
padding: '120px 8% 100px',
padding: '120px 8% 80px',
margin: '20px', borderRadius: '32px',
position: 'relative', overflow: 'hidden',
}}>
{/* Background photo */}
<Image src="/images/stock/solutions.jpg" alt="" fill priority style={{ objectFit: 'cover', opacity: 0.9 }} />
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(24,20,41,0.1) 0%, rgba(24,20,41,0.55) 85%)' }} />
<Image src="/images/stock/about-team.jpg" alt="" fill priority style={{ objectFit: 'cover', opacity: 0.9 }} />
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(26,21,48,0.1) 0%, rgba(26,21,48,0.55) 85%)' }} />
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse 60% 65% at 50% 48%, rgba(18,14,30,0.78) 0%, rgba(18,14,30,0.35) 60%, transparent 100%)' }} />
{/* Accent blobs */}
<div style={{ position: 'absolute', top: '-12%', left: '4%', width: 480, height: 480, borderRadius: '50%', background: 'radial-gradient(circle, rgba(244,190,40,0.2) 0%, transparent 70%)', pointerEvents: 'none' }} />
<div style={{ position: 'absolute', bottom: '-14%', right: '0%', width: 440, height: 440, borderRadius: '50%', background: 'radial-gradient(circle, rgba(167,139,250,0.18) 0%, transparent 70%)', pointerEvents: 'none' }} />
{/* Animated rings */}
{[600, 900, 1200].map((s, i) => (
<div key={i} style={{
position: 'absolute', top: '50%', right: '-10%',
width: s, height: s, borderRadius: '50%',
border: '1px solid rgba(244,190,40,0.07)',
transform: 'translateY(-50%)',
pointerEvents: 'none',
}} />
))}
<div style={{ position: 'absolute', top: '-12%', right: '-8%', width: 520, height: 520, borderRadius: '50%', background: 'radial-gradient(circle, rgba(244,190,40,0.22) 0%, transparent 70%)', filter: 'blur(10px)', pointerEvents: 'none' }} />
<div style={{ position: 'absolute', bottom: '-15%', left: '-10%', width: 460, height: 460, borderRadius: '50%', background: 'radial-gradient(circle, rgba(167,139,250,0.18) 0%, transparent 70%)', pointerEvents: 'none' }} />
{/* Big ghost text */}
<div style={{
position: 'absolute', top: '50%', left: '50%',
transform: 'translate(-50%,-50%)',
fontSize: 'clamp(120px, 22vw, 320px)', fontWeight: 900,
fontFamily: 'Sora, sans-serif', color: 'transparent',
WebkitTextStroke: '1px rgba(244,190,40,0.08)',
letterSpacing: '-0.06em', lineHeight: 1,
whiteSpace: 'nowrap', userSelect: 'none',
pointerEvents: 'none', zIndex: 0,
}}>LOYALY</div>
<div style={{ width: '100%', position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
<div style={{ position: 'relative', zIndex: 2, width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
<div style={{ maxWidth: 760 }}>
<h1 data-gsap="heading" style={{ fontSize: 'clamp(52px, 8.5vw, 130px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.88, color: '#fff', margin: '0 0 36px', fontFamily: 'Sora, sans-serif' }}>
Built for<br />Modern<br />
<span style={{ color: '#f4be28' }}>Retail Growth.</span>
<h1 data-gsap="heading" style={{
fontSize: 'clamp(52px, 9vw, 140px)', fontWeight: 800,
letterSpacing: '-0.06em', lineHeight: 0.88,
color: '#ffffff', margin: '0 0 36px', fontFamily: 'Sora, sans-serif',
}}>
Meet the<br />
<span style={{ color: '#f4be28' }}>Crew.</span>
</h1>
<p data-gsap="text" style={{ fontSize: 'clamp(16px, 1.6vw, 20px)', lineHeight: 1.75, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', maxWidth: 540, margin: '0 auto 52px' }}>
Instead of waiting for customers to walk in, create exciting reasons for them to come, with game based campaigns, smart coupons, and a loyalty ecosystem that rewards every visit.
<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.
</p>
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap', justifyContent: 'center' }}>
<Link href="/contacts" style={{ padding: '18px 40px', background: '#f4be28', color: '#111', fontSize: 16, fontWeight: 800, borderRadius: 100, textDecoration: 'none', fontFamily: 'Sora, sans-serif', boxShadow: '0 16px 48px rgba(244,190,40,0.25)' }}>Explore Solutions </Link>
<Link href="/contacts" style={{ 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)' }}>Book Demo</Link>
</div>
</div>
</div>
</section>
{/* ── CAPABILITIES ── */}
{/* ── VALUES ── */}
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
<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>
<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.
</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.
</p>
</div>
</RevealBlock>
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{CAPS.map((cap, i) => (
<RevealBlock key={i} delay={i * 0.08}>
<div
onMouseEnter={() => setHovered(i)}
onMouseLeave={() => setHovered(null)}
className="sp-cap-row"
style={{
display: 'grid', gridTemplateColumns: '80px 1fr 1fr auto',
alignItems: 'center', gap: '4vw',
padding: '40px 0',
borderBottom: '1px solid rgba(0,0,0,0.07)',
cursor: 'default',
transition: 'all 0.3s ease',
}}
>
<span style={{ fontSize: 13, fontWeight: 700, color: hovered === i ? cap.color : 'rgba(0,0,0,0.2)', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em', transition: 'color 0.3s' }}>{cap.n}</span>
<h3 style={{ fontSize: 'clamp(22px, 2.8vw, 40px)', fontWeight: 800, color: hovered === i ? '#0a0a0f' : 'rgba(0,0,0,0.8)', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', margin: 0, transition: 'color 0.3s' }}>{cap.title}</h3>
<p style={{ fontSize: 15, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.6, maxWidth: 340 }}>{cap.desc}</p>
<div style={{ display: 'flex', gap: 28 }}>
{cap.metrics.map((m, j) => (
<div key={j} style={{ textAlign: 'right' }}>
<div style={{ fontSize: 22, fontWeight: 900, color: cap.color, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em' }}>{m.val}</div>
<div style={{ fontSize: 11, color: 'rgba(0,0,0,0.35)', fontFamily: 'Sora, sans-serif', marginTop: 3 }}>{m.lbl}</div>
</div>
))}
</div>
<Reveal>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>How We Work</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' }}>
What we believe.
</h2>
</Reveal>
<div style={{ display: 'flex', flexDirection: 'column' }}>
{VALUES.map((v, i) => (
<Reveal key={i} delay={i * 0.08}>
<div className="mtc-value-row" style={{
display: 'grid', gridTemplateColumns: '80px 1fr 1fr',
gap: '4vw', alignItems: 'center',
padding: '40px 0', borderBottom: '1px solid rgba(0,0,0,0.07)',
}}>
<span style={{ fontSize: 13, fontWeight: 700, color: '#f4be28', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em' }}>{v.n}</span>
<h3 style={{ fontSize: 'clamp(20px, 2.5vw, 36px)', fontWeight: 800, color: '#0a0a0f', fontFamily: 'Sora, sans-serif', margin: 0, letterSpacing: '-0.03em' }}>{v.title}</h3>
<p style={{ fontSize: 16, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.65 }}>{v.desc}</p>
</div>
</RevealBlock>
</Reveal>
))}
</div>
</section>
@@ -190,15 +149,15 @@ export default function SolutionsPage() {
{/* ── OUR PRODUCTS ── */}
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(244,190,40,0.025) 1px,transparent 1px),linear-gradient(90deg,rgba(244,190,40,0.025) 1px,transparent 1px)', backgroundSize: '64px 64px', pointerEvents: 'none' }} />
<RevealBlock>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20, position: 'relative' }}>Our Products</div>
<Reveal>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20, position: 'relative' }}>Meet the Products</div>
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', position: 'relative', maxWidth: 700 }}>
Four products.<br />One ecosystem.
</h2>
<p style={{ fontSize: 16, color: 'rgba(255,255,255,0.45)', fontFamily: 'Sora, sans-serif', maxWidth: 460, lineHeight: 1.7, marginBottom: 80, position: 'relative' }}>
Each product solves a distinct part of the retail journey, and together they form one connected platform, from footfall to spend to loyalty.
Behavision, SpendSense, Dyscount, and Identiq, each solves a distinct part of the retail journey, and together they form one connected platform, from footfall to spend to loyalty.
</p>
</RevealBlock>
</Reveal>
<div style={{ position: 'relative' }}>
<ScrollStack useWindowScroll itemDistance={160} itemScale={0.035} itemStackDistance={40} baseScale={0.86} blurAmount={1.2}>
@@ -209,7 +168,7 @@ export default function SolutionsPage() {
border: `1px solid ${p.color}40`,
borderRadius: 28, padding: 'clamp(28px, 3.2vw, 48px)', height: '100%', boxSizing: 'border-box',
}}>
<div className="sp-product-grid" style={{ display: 'grid', gridTemplateColumns: '1.35fr 1fr', gap: 'clamp(28px, 4vw, 56px)', alignItems: 'start' }}>
<div className="mtc-product-grid" style={{ display: 'grid', gridTemplateColumns: '1.35fr 1fr', gap: 'clamp(28px, 4vw, 56px)', alignItems: 'start' }}>
{/* Left — identity + description */}
<div>
@@ -264,63 +223,31 @@ export default function SolutionsPage() {
</div>
</section>
{/* ── WHY IT MATTERS ── */}
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(244,190,40,0.025) 1px,transparent 1px),linear-gradient(90deg,rgba(244,190,40,0.025) 1px,transparent 1px)', backgroundSize: '64px 64px', pointerEvents: 'none' }} />
<RevealBlock>
<blockquote style={{
margin: 0, padding: 0,
fontSize: 'clamp(32px, 5vw, 72px)', fontWeight: 900,
letterSpacing: '-0.04em', lineHeight: 1.05,
color: '#fff', fontFamily: 'Sora, sans-serif',
maxWidth: 900,
}}>
"Every digital interaction should lead to a{' '}
<span style={{ color: '#f4be28' }}>meaningful in-store experience.</span>"
</blockquote>
<div style={{ height: 2, width: 80, background: '#f4be28', margin: '48px 0 32px' }} />
<p style={{ fontSize: 16, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif', margin: 0 }}>Loyaly.ai, 2025</p>
</RevealBlock>
</section>
{/* ── FINAL CTA ── */}
<section className="sp-cta-grid" style={{ padding: '140px 8%', background: '#fff8e8', margin: '0 20px 20px', borderRadius: 24, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6vw', alignItems: 'center' }}>
<RevealBlock>
<h2 style={{ fontSize: 'clamp(36px, 5vw, 68px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 24px', fontFamily: 'Sora, sans-serif' }}>
See every solution working together.
</h2>
<p style={{ fontSize: 16, color: 'rgba(0,0,0,0.5)', fontFamily: 'Sora, sans-serif', margin: '0 0 40px', lineHeight: 1.7 }}>
Get a tailored walkthrough of how Loyaly.ai&apos;s merchant platform, game campaigns, and coupon engine fit your specific retail format.
{/* ── CULTURE CLOSE ── */}
<section style={{
padding: '160px 8%', background: '#f4be28',
margin: '0 20px 20px', borderRadius: 24,
display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center',
position: 'relative', overflow: 'hidden',
}}>
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(ellipse at 50% 0%,rgba(255,255,255,0.2) 0%,transparent 60%)', pointerEvents: 'none' }} />
<Reveal>
<p style={{ fontSize: 'clamp(32px, 6vw, 90px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.95, color: '#111', margin: '0 0 48px', fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2 }}>
"We're not just building a product. We're redefining retail."
</p>
<div style={{ display: 'flex', gap: 14, flexWrap: 'wrap' }}>
<Link href="/contacts" style={{ padding: '18px 40px', background: '#f4be28', color: '#111', fontSize: 16, fontWeight: 800, borderRadius: 100, textDecoration: 'none', fontFamily: 'Sora, sans-serif', boxShadow: '0 12px 40px rgba(244,190,40,0.3)' }}>Book Demo </Link>
<Link href="/contacts" style={{ padding: '18px 40px', background: 'transparent', color: '#111', fontSize: 16, fontWeight: 700, borderRadius: 100, textDecoration: 'none', fontFamily: 'Sora, sans-serif', border: '1.5px solid rgba(0,0,0,0.15)' }}>Explore Solutions</Link>
</div>
</RevealBlock>
<RevealBlock delay={0.15}>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12 }}>
{[
{ v: '10K+', l: 'Partner stores' }, { v: '50K+', l: 'Active users' },
{ v: '98%', l: 'Uptime SLA' }, { v: '<9ms', l: 'Response time' },
].map((s, i) => (
<div key={i} style={{ padding: '32px 28px', background: '#fff', borderRadius: 20, boxShadow: '0 4px 32px rgba(0,0,0,0.06)', border: '1px solid rgba(0,0,0,0.05)' }}>
<div style={{ fontSize: 'clamp(28px, 3.5vw, 44px)', fontWeight: 900, color: '#0a0a0f', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.04em' }}>{s.v}</div>
<div style={{ fontSize: 13, color: 'rgba(0,0,0,0.4)', fontFamily: 'Sora, sans-serif', marginTop: 8 }}>{s.l}</div>
</div>
))}
</div>
</RevealBlock>
<Link href="/contacts" style={{
display: 'inline-flex', alignItems: 'center', gap: 10,
padding: '18px 44px', background: '#111', color: '#f4be28',
fontSize: 16, fontWeight: 800, borderRadius: 100, textDecoration: 'none',
fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2,
}}>Join Us </Link>
</Reveal>
</section>
<style>{`
@media (max-width: 860px) {
.sp-cap-row { grid-template-columns: 1fr !important; gap: 14px !important; padding: 28px 0 !important; }
.sp-cap-row > div:last-child { justify-content: flex-start !important; }
.sp-cta-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
.sp-product-grid { grid-template-columns: 1fr !important; }
}
@media (max-width: 480px) {
.sp-cta-grid > div > div { grid-template-columns: 1fr !important; }
.mtc-value-row { grid-template-columns: 1fr !important; gap: 10px !important; padding: 28px 0 !important; }
.mtc-product-grid { grid-template-columns: 1fr !important; }
}
`}</style>
</main>

View File

@@ -14,6 +14,70 @@ const OUTCOMES = [
{ stat: '9ms', label: 'Real-time decision latency' },
];
const CAPS = [
{
n: '01', title: 'Merchant Campaign Platform', color: '#f4be28',
desc: 'Register your store, upload products and inventory, then launch interactive promotional campaigns in minutes. Define maximum discounts per product and track performance in real time.',
metrics: [{ val: '5+', lbl: 'Game formats' }, { val: '1-click', lbl: 'Campaign setup' }],
},
{
n: '02', title: 'Game Based Discount Engine', color: '#a78bfa',
desc: 'Select a product, choose a game (Maze, Spin Wheel, Scratch Card, or Match Master) and set a maximum discount. Customers play once and discover their reward only after completing the game.',
metrics: [{ val: '4×', lbl: 'Engagement lift' }, { val: '100%', lbl: 'Margin controlled' }],
},
{
n: '03', title: 'Smart Coupon System', color: '#34d399',
desc: 'Generate daily surprise coupons through the Battery Challenge and other activities. Offer flat discounts, BOGO, percentage-off deals, festival specials, and brand promotions, fresh every day.',
metrics: [{ val: '6', lbl: 'Coupon types' }, { val: 'Daily', lbl: 'Fresh surprises' }],
},
{
n: '04', title: 'Footfall & Campaign Analytics', color: '#f97316',
desc: 'Track which campaigns drove store visits, measure conversion rates, monitor repeat footfall, and optimize your promotional budget with a real-time analytics dashboard.',
metrics: [{ val: '98%', lbl: 'Count accuracy' }, { val: '<9ms', lbl: 'Real-time latency' }],
},
];
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.',
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.",
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.',
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>,
},
];
const WHO = [
{
label: 'Retail Stores',
@@ -48,6 +112,7 @@ function RevealBlock({ children, delay = 0, className = '' }: { children: React.
export default function ForBusinessPage() {
const [countVisible, setCountVisible] = useState(false);
const [hovered, setHovered] = useState<number | null>(null);
const statsRef = useRef<HTMLDivElement>(null);
useEffect(() => {
@@ -162,6 +227,186 @@ export default function ForBusinessPage() {
{/* ── PRODUCTS ── */}
<ProductsScrollSection />
{/* ── CAPABILITIES ── */}
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
<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>
<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.
</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.
</p>
</div>
</RevealBlock>
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{CAPS.map((cap, i) => (
<RevealBlock key={i} delay={i * 0.08}>
<div
onMouseEnter={() => setHovered(i)}
onMouseLeave={() => setHovered(null)}
className="uc-cap-row"
style={{
display: 'grid', gridTemplateColumns: '80px 1fr 1fr auto',
alignItems: 'center', gap: '4vw',
padding: '40px 0',
borderBottom: '1px solid rgba(0,0,0,0.07)',
cursor: 'default',
transition: 'all 0.3s ease',
}}
>
<span style={{ fontSize: 13, fontWeight: 700, color: hovered === i ? cap.color : 'rgba(0,0,0,0.2)', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em', transition: 'color 0.3s' }}>{cap.n}</span>
<h3 style={{ fontSize: 'clamp(22px, 2.8vw, 40px)', fontWeight: 800, color: hovered === i ? '#0a0a0f' : 'rgba(0,0,0,0.8)', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', margin: 0, transition: 'color 0.3s' }}>{cap.title}</h3>
<p style={{ fontSize: 15, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.6, maxWidth: 340 }}>{cap.desc}</p>
<div style={{ display: 'flex', gap: 28 }}>
{cap.metrics.map((m, j) => (
<div key={j} style={{ textAlign: 'right' }}>
<div style={{ fontSize: 22, fontWeight: 900, color: cap.color, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em' }}>{m.val}</div>
<div style={{ fontSize: 11, color: 'rgba(0,0,0,0.35)', fontFamily: 'Sora, sans-serif', marginTop: 3 }}>{m.lbl}</div>
</div>
))}
</div>
</div>
</RevealBlock>
))}
</div>
</section>
{/* ── HOW IT WORKS ── */}
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(244,190,40,0.025) 1px,transparent 1px),linear-gradient(90deg,rgba(244,190,40,0.025) 1px,transparent 1px)', backgroundSize: '64px 64px', pointerEvents: 'none' }} />
<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>
<div className="uc-pillar-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24, position: 'relative' }}>
{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',
}}>
<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>
<div style={{
width: 54, height: 54, borderRadius: 16, marginBottom: 24,
background: `${p.color}18`, border: `1px solid ${p.color}45`,
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 }} />
</div>
</RevealBlock>
))}
</div>
</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' }} />
<RevealBlock>
<div className="uc-proximity-grid" style={{ display: 'grid', gridTemplateColumns: '0.85fr 1.15fr', gap: '6vw', 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={{
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`,
}} />
))}
<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)',
}}>
<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>
</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' }}>
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 }}>
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>
))}
</div>
</div>
</div>
</RevealBlock>
</section>
{/* ── OUTCOMES ── */}
<section ref={statsRef} style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
<RevealBlock>
@@ -235,16 +480,31 @@ export default function ForBusinessPage() {
<style>{`
@keyframes shimmer { 0%{background-position:200% center} 100%{background-position:-200% center} }
@keyframes floatY { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-12px)} }
@keyframes ucRadarPulse {
0% { transform: scale(0.3); opacity: 0.9; }
100% { transform: scale(1.7); opacity: 0; }
}
.uc-pillar-card:hover { transform: translateY(-6px); }
@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; }
.uc-outcomes-grid > div > div { border-radius: 16px !important; }
.uc-who-grid { grid-template-columns: repeat(2,1fr) !important; }
.uc-cap-row { grid-template-columns: 1fr !important; gap: 14px !important; padding: 28px 0 !important; }
.uc-cap-row > div:last-child { justify-content: flex-start !important; }
.uc-pillar-grid { grid-template-columns: 1fr !important; }
.uc-proximity-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
.uc-timeline-row { grid-template-columns: 44px 1fr !important; gap: 18px !important; }
.uc-timeline-row > div:first-child { width: 44px !important; height: 44px !important; }
.uc-timeline-line { left: 21px !important; }
}
@media (max-width: 520px) {
.uc-outcomes-grid { grid-template-columns: 1fr !important; }
.uc-who-grid { grid-template-columns: 1fr !important; }
}
@media (prefers-reduced-motion: reduce) {
.uc-radar-ring { animation: none !important; opacity: 0.4 !important; }
}
`}</style>
</main>
);

View File

@@ -1,154 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
const FEATURES = [
{
icon: '👁',
title: 'Turn Footfall into Insight',
description:
'Real-time visitor tracking and movement analytics reveal exactly how customers move through your store, enabling smarter layout, staffing, and marketing decisions.',
},
{
icon: '🎯',
title: 'Personalisation, Perfected',
description:
'AI-driven profiles let you deliver the right offer to the right customer at precisely the right moment — online, in-store, or both.',
},
{
icon: '⚙️',
title: 'One Engine. Total Intelligence.',
description:
'A single unified platform connects footfall, loyalty, promotions, and revenue data so every team works from the same source of truth.',
},
];
export default function About() {
return (
<section id="about" className="about-section" aria-label="About Loyaly.AI" style={{ margin: '0 20px 20px', borderRadius: '24px', overflow: 'hidden' }}>
{/* Two-column intro */}
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))',
gap: '48px',
alignItems: 'center',
maxWidth: '1200px',
margin: '0 auto',
padding: '80px 24px 60px',
}}
>
{/* Image */}
<div style={{ position: 'relative', minHeight: '340px', borderRadius: '16px', overflow: 'hidden' }}>
<Image
src="/loyaly_php/assets/images/about.png"
alt="Loyaly.AI platform dashboard showing customer intelligence data"
fill
style={{ objectFit: 'cover' }}
sizes="(max-width: 768px) 100vw, 50vw"
/>
</div>
{/* Text */}
<div>
<p
style={{
margin: '0 0 12px',
fontSize: '13px',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.1em',
color: '#f4be28',
}}
>
About Us
</p>
<h2
style={{
margin: '0 0 20px',
fontSize: 'clamp(1.6rem, 3vw, 2.4rem)',
fontWeight: 800,
lineHeight: 1.2,
color: '#171512',
}}
>
AI Powered Customer Intelligence for Retail
</h2>
<p style={{ margin: '0 0 16px', fontSize: '16px', lineHeight: 1.75, color: '#4f463a' }}>
Loyaly.ai is an AI powered customer intelligence platform built for modern retail. We help
businesses understand who their customers are, what drives their behaviour, and how to
build lasting loyalty at every touchpoint.
</p>
<p style={{ margin: '0 0 28px', fontSize: '16px', lineHeight: 1.75, color: '#4f463a' }}>
From footfall tracking and personalised engagement to smart loyalty programmes and revenue
optimisation, Loyaly.AI gives retailers one unified platform to know their customers deeply
and act on that knowledge instantly.
</p>
<Link
href="/solutions_page"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
padding: '12px 28px',
background: '#f4be28',
color: '#171512',
borderRadius: '4px',
fontWeight: 700,
textDecoration: 'none',
fontSize: '15px',
}}
>
Explore Our Solutions <span aria-hidden="true"></span>
</Link>
</div>
</div>
{/* Three-feature grid */}
<div
style={{
background: '#fff2c8',
padding: '60px 24px',
}}
>
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(240px, 1fr))',
gap: '32px',
maxWidth: '1200px',
margin: '0 auto',
}}
>
{FEATURES.map(({ icon, title, description }) => (
<div
key={title}
style={{
background: '#fff8e8',
borderRadius: '12px',
padding: '32px 28px',
boxShadow: '0 2px 12px rgba(23,21,18,0.06)',
}}
>
<div style={{ fontSize: '32px', marginBottom: '16px' }} aria-hidden="true">
{icon}
</div>
<h3
style={{
margin: '0 0 12px',
fontSize: '18px',
fontWeight: 700,
color: '#171512',
}}
>
{title}
</h3>
<p style={{ margin: 0, fontSize: '15px', lineHeight: 1.7, color: '#4f463a' }}>
{description}
</p>
</div>
))}
</div>
</div>
</section>
);
}

View File

@@ -1,193 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
const CASE_STUDIES = [
{
slug: 'ai-maximises-discount-strategy',
title: 'Stop Wasting Discounts: How AI Maximises Every Offer You Give',
excerpt:
'Most retail discounts go to customers who would have bought anyway. Discover how Loyaly.AI\'s promotion intelligence engine targets the right buyers at the right margin.',
tags: ['AI Solutions', 'Retail Intelligence'],
image: '/loyaly_php/assets/images/case-study-2-img-960x718.jpeg',
imageAlt: 'Data visualisation showing AI-optimised discount targeting across customer segments',
readTime: '5 min read',
},
{
slug: 'ai-redefining-retail-loyalty-2026',
title: 'Beyond Points: How AI is Redefining Retail Loyalty in 2026',
excerpt:
'Traditional points schemes are losing ground. Learn how AI-driven loyalty programmes build genuine relationships through personalisation, timing, and context-aware rewards.',
tags: ['AI Retail', 'Loyalty Intelligence'],
image: '/loyaly_php/assets/images/case-study-1-img-960x718.jpeg',
imageAlt: 'Retail customer using a loyalty app powered by Loyaly.AI',
readTime: '7 min read',
},
];
export default function BlogSection() {
return (
<section
id="case-studies"
aria-label="Case studies"
style={{ padding: '80px 24px', background: '#fff8e8', margin: '0 20px 20px', borderRadius: '24px', overflow: 'hidden' }}
>
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
{/* Header */}
<div style={{ marginBottom: '48px', display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', flexWrap: 'wrap', gap: '16px' }}>
<div>
<p
style={{
margin: '0 0 10px',
fontSize: '13px',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.1em',
color: '#f4be28',
}}
>
Case Studies
</p>
<h2
style={{
margin: 0,
fontSize: 'clamp(1.6rem, 3vw, 2.4rem)',
fontWeight: 800,
color: '#171512',
lineHeight: 1.2,
}}
>
Insights from the Loyaly.AI field
</h2>
</div>
<Link
href="/blog_main"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '6px',
padding: '10px 22px',
border: '2px solid #f4be28',
borderRadius: '4px',
color: '#171512',
fontWeight: 700,
textDecoration: 'none',
fontSize: '14px',
whiteSpace: 'nowrap',
}}
>
All Articles <span aria-hidden="true"></span>
</Link>
</div>
{/* Card grid */}
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
gap: '32px',
}}
>
{CASE_STUDIES.map(({ slug, title, excerpt, tags, image, imageAlt, readTime }) => (
<article
key={slug}
style={{
background: '#fff',
borderRadius: '16px',
overflow: 'hidden',
boxShadow: '0 2px 16px rgba(23,21,18,0.07)',
display: 'flex',
flexDirection: 'column',
transition: 'transform 0.25s ease, box-shadow 0.25s ease',
}}
onMouseEnter={e => {
const el = e.currentTarget as HTMLElement;
el.style.transform = 'translateY(-4px)';
el.style.boxShadow = '0 8px 32px rgba(23,21,18,0.12)';
}}
onMouseLeave={e => {
const el = e.currentTarget as HTMLElement;
el.style.transform = 'translateY(0)';
el.style.boxShadow = '0 2px 16px rgba(23,21,18,0.07)';
}}
>
{/* Image */}
<Link href={`/use_cases/${slug}`} tabIndex={-1} aria-hidden="true">
<div style={{ position: 'relative', height: '220px' }}>
<Image
src={image}
alt={imageAlt}
fill
style={{ objectFit: 'cover' }}
sizes="(max-width: 768px) 100vw, 50vw"
/>
</div>
</Link>
{/* Content */}
<div style={{ padding: '28px', flex: 1, display: 'flex', flexDirection: 'column', gap: '12px' }}>
{/* Tags */}
<div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap' }}>
{tags.map(tag => (
<span
key={tag}
style={{
padding: '4px 10px',
background: '#fff2c8',
color: '#bc7122',
borderRadius: '20px',
fontSize: '12px',
fontWeight: 700,
letterSpacing: '0.04em',
}}
>
{tag}
</span>
))}
</div>
<h3
style={{
margin: 0,
fontSize: '18px',
fontWeight: 700,
lineHeight: 1.35,
color: '#171512',
}}
>
<Link
href={`/use_cases/${slug}`}
style={{ textDecoration: 'none', color: 'inherit' }}
>
{title}
</Link>
</h3>
<p style={{ margin: 0, fontSize: '14px', lineHeight: 1.7, color: '#4f463a', flex: 1 }}>
{excerpt}
</p>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginTop: '8px' }}>
<span style={{ fontSize: '13px', color: '#bc7122', fontWeight: 600 }}>{readTime}</span>
<Link
href={`/use_cases/${slug}`}
style={{
fontSize: '14px',
fontWeight: 700,
color: '#171512',
textDecoration: 'none',
display: 'inline-flex',
alignItems: 'center',
gap: '4px',
}}
>
Read more <span aria-hidden="true"></span>
</Link>
</div>
</div>
</article>
))}
</div>
</div>
</section>
);
}

View File

@@ -16,9 +16,9 @@ const NAV = [
{
title: 'Platform',
links: [
{ label: 'Behavision AI', href: '/solutions_page' },
{ label: 'SpendSense AI', href: '/solutions_page' },
{ label: 'Dyscount AI', href: '/solutions_page' },
{ label: 'Behavision AI', href: '/use_cases' },
{ label: 'SpendSense AI', href: '/use_cases' },
{ label: 'Dyscount AI', href: '/use_cases' },
],
},
{

View File

@@ -1,28 +0,0 @@
'use client';
import React from 'react';
import { useGSAPReveal, RevealAnimation } from '@/hooks/useGSAPReveal';
interface Props {
children: React.ReactNode;
delay?: number;
animation?: RevealAnimation;
duration?: number;
ease?: string;
start?: string;
className?: string;
style?: React.CSSProperties;
as?: keyof React.JSX.IntrinsicElements;
}
export default function GSAPReveal({
children, delay = 0, animation = 'fade-up',
duration = 1.0, ease, start, className, style,
}: Props) {
const ref = useGSAPReveal({ delay, animation, duration, ease, start });
return (
<div ref={ref} className={className} style={{ willChange: 'transform, opacity', ...style }}>
{children}
</div>
);
}

View File

@@ -1,153 +0,0 @@
import Image from 'next/image';
const PARTNERS = [
{ name: 'Partner 1', src: '/loyaly_php/assets/images/partners-1.png' },
{ name: 'Partner 2', src: '/loyaly_php/assets/images/partners-2.png' },
{ name: 'Partner 3', src: '/loyaly_php/assets/images/partners-3.png' },
{ name: 'Partner 4', src: '/loyaly_php/assets/images/partners-4.png' },
{ name: 'Partner 5', src: '/loyaly_php/assets/images/partners-5.png' },
{ name: 'Partner 6', src: '/loyaly_php/assets/images/partners-6.png' },
];
// Duplicate list so the CSS marquee loops seamlessly
const TRACK = [...PARTNERS, ...PARTNERS];
export default function Partners() {
return (
<section
id="partners"
aria-label="Our partners"
style={{ padding: '60px 0', overflow: 'hidden', background: '#fff8e8', margin: '0 20px 20px', borderRadius: '24px' }}
>
<div style={{ textAlign: 'center', padding: '0 24px 40px' }}>
<p
style={{
margin: '0 0 10px',
fontSize: '13px',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.1em',
color: '#f4be28',
}}
>
Our Partners
</p>
<h2
style={{
margin: 0,
fontSize: 'clamp(1.2rem, 2.5vw, 1.8rem)',
fontWeight: 700,
color: '#171512',
}}
>
Our trusted collaborators in progress and success
</h2>
</div>
{/* Marquee track */}
<div
style={{ position: 'relative', overflow: 'hidden' }}
aria-hidden="true" // purely decorative screen readers can skip
>
{/* Fade edges */}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '120px',
height: '100%',
background: 'linear-gradient(to right, #fff8e8, transparent)',
zIndex: 1,
pointerEvents: 'none',
}}
/>
<div
style={{
position: 'absolute',
top: 0,
right: 0,
width: '120px',
height: '100%',
background: 'linear-gradient(to left, #fff8e8, transparent)',
zIndex: 1,
pointerEvents: 'none',
}}
/>
<div
className="partners-track"
style={{
display: 'flex',
alignItems: 'center',
gap: '56px',
width: 'max-content',
animation: 'marquee 28s linear infinite',
padding: '8px 0',
}}
>
{TRACK.map(({ name, src }, i) => (
<div
key={`${name}-${i}`}
style={{
flex: '0 0 auto',
width: '140px',
height: '64px',
position: 'relative',
filter: 'grayscale(1)',
opacity: 0.6,
transition: 'filter 0.3s, opacity 0.3s',
}}
onMouseEnter={e => {
(e.currentTarget as HTMLDivElement).style.filter = 'grayscale(0)';
(e.currentTarget as HTMLDivElement).style.opacity = '1';
}}
onMouseLeave={e => {
(e.currentTarget as HTMLDivElement).style.filter = 'grayscale(1)';
(e.currentTarget as HTMLDivElement).style.opacity = '0.6';
}}
>
<Image
src={src}
alt={name}
fill
style={{ objectFit: 'contain' }}
sizes="140px"
/>
</div>
))}
</div>
</div>
<style>{`
@keyframes marquee {
from { transform: translateX(0); }
to { transform: translateX(-50%); }
}
@media (prefers-reduced-motion: reduce) {
.partners-track { animation: none; }
}
`}</style>
{/* Accessible static list for screen readers */}
<ul
aria-label="Partner logos"
style={{
position: 'absolute',
width: '1px',
height: '1px',
overflow: 'hidden',
clip: 'rect(0,0,0,0)',
whiteSpace: 'nowrap',
listStyle: 'none',
margin: 0,
padding: 0,
}}
>
{PARTNERS.map(({ name }) => (
<li key={name}>{name}</li>
))}
</ul>
</section>
);
}

View File

@@ -1,175 +0,0 @@
'use client';
import React, { useRef, useEffect, useCallback } from 'react';
export default function PhoneWidget() {
const stageRef = useRef<HTMLDivElement>(null);
const onMove = useCallback((e: MouseEvent) => {
const el = stageRef.current;
if (!el) return;
const r = el.getBoundingClientRect();
const px = (e.clientX - r.left) / r.width - 0.5;
const py = (e.clientY - r.top) / r.height - 0.5;
el.style.setProperty('--rx', `${(-py * 8).toFixed(2)}deg`);
el.style.setProperty('--ry', `${(px * 10).toFixed(2)}deg`);
el.style.setProperty('--tx', `${(px * 18).toFixed(1)}px`);
el.style.setProperty('--ty', `${(py * 18).toFixed(1)}px`);
}, []);
const onLeave = useCallback(() => {
const el = stageRef.current;
if (!el) return;
el.style.setProperty('--rx', '0deg');
el.style.setProperty('--ry', '0deg');
el.style.setProperty('--tx', '0px');
el.style.setProperty('--ty', '0px');
}, []);
useEffect(() => {
const el = stageRef.current;
if (!el) return;
el.addEventListener('mousemove', onMove);
el.addEventListener('mouseleave', onLeave);
return () => {
el.removeEventListener('mousemove', onMove);
el.removeEventListener('mouseleave', onLeave);
};
}, [onMove, onLeave]);
return (
<>
<style>{`
@keyframes pwOrbFloat { 0%,100%{transform:translateY(0) scale(1)} 50%{transform:translateY(-22px) scale(1.04)} }
@keyframes pwOrb2Float { 0%,100%{transform:translateY(0) scale(1)} 50%{transform:translateY(18px) scale(0.97)} }
@keyframes pwGlow { 0%,100%{box-shadow:0 0 30px rgba(244,190,40,0.2)} 50%{box-shadow:0 0 60px rgba(244,190,40,0.5),0 0 120px rgba(244,190,40,0.2)} }
@keyframes pwScreenShimmer { 0%{left:-40%} 60%{left:120%} 100%{left:120%} }
.pw-hero-grid { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 6vw; width: 100%; }
@media (max-width: 900px) { .pw-hero-grid { grid-template-columns: 1fr; } .pw-widget-col { display: none; } }
`}</style>
<div
ref={stageRef}
style={{
position: 'relative', display: 'flex',
justifyContent: 'center', alignItems: 'center',
perspective: 1200, cursor: 'default',
}}
>
<div style={{
transformStyle: 'preserve-3d',
transform: 'rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translate(var(--tx,0px), var(--ty,0px))',
transition: 'transform 0.5s cubic-bezier(0.22,1,0.36,1)',
position: 'relative',
}}>
{/* Glow ring */}
<div style={{
position: 'absolute', inset: -36, borderRadius: '56px',
background: 'radial-gradient(circle at 50% 50%, rgba(244,190,40,0.2) 0%, transparent 70%)',
animation: 'pwGlow 3s ease-in-out infinite',
}} />
{/* Phone shell */}
<div style={{
width: 268, height: 548, borderRadius: '48px',
background: 'linear-gradient(160deg, #22223a 0%, #10101c 100%)',
border: '1.5px solid rgba(255,255,255,0.1)',
boxShadow: '0 70px 130px rgba(0,0,0,0.8), 0 0 0 1px rgba(255,255,255,0.06), inset 0 1px 0 rgba(255,255,255,0.1)',
display: 'flex', flexDirection: 'column', overflow: 'hidden',
animation: 'pwOrbFloat 6s ease-in-out infinite', position: 'relative',
}}>
{/* Shimmer sweep */}
<div style={{ position: 'absolute', inset: 0, pointerEvents: 'none', zIndex: 20, overflow: 'hidden', borderRadius: '48px' }}>
<div style={{
position: 'absolute', top: '-20%', left: 0, width: '40%', height: '140%',
background: 'linear-gradient(105deg, transparent 0%, rgba(255,255,255,0.07) 50%, transparent 100%)',
animation: 'pwScreenShimmer 4.5s ease-in-out infinite', animationDelay: '1s',
}} />
</div>
{/* Dynamic island */}
<div style={{ display: 'flex', justifyContent: 'center', paddingTop: 16, paddingBottom: 4 }}>
<div style={{ width: 96, height: 28, borderRadius: 20, background: '#080810', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 6 }}>
<div style={{ width: 10, height: 10, borderRadius: '50%', background: '#1a1a28', border: '1px solid rgba(255,255,255,0.08)' }} />
<div style={{ width: 6, height: 6, borderRadius: '50%', background: '#0d0d18' }} />
</div>
</div>
{/* Header */}
<div style={{ padding: '8px 22px 12px' }}>
<div style={{ fontSize: 10, color: '#f4be28', fontWeight: 700, fontFamily: 'Sora, sans-serif', letterSpacing: '0.12em', textTransform: 'uppercase' }}>Good Morning</div>
<div style={{ fontSize: 16, color: '#fff', fontWeight: 800, fontFamily: 'Sora, sans-serif', marginTop: 2 }}>Loyaly </div>
</div>
{/* LYTs balance card */}
<div style={{
margin: '0 14px', borderRadius: 22,
background: 'linear-gradient(135deg, #f4be28 0%, #e09820 100%)',
padding: '18px 20px', boxShadow: '0 8px 24px rgba(244,190,40,0.35)',
position: 'relative', overflow: 'hidden',
}}>
<div style={{ position: 'absolute', top: '-30%', right: '-10%', width: 110, height: 110, borderRadius: '50%', background: 'rgba(255,255,255,0.12)' }} />
<div style={{ fontSize: 10, color: 'rgba(23,21,18,0.65)', fontWeight: 700, fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Total LYTs</div>
<div style={{ fontSize: 34, color: '#171512', fontWeight: 900, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', marginTop: 2 }}>284</div>
<div style={{ fontSize: 10, color: 'rgba(23,21,18,0.6)', fontFamily: 'Sora, sans-serif', marginTop: 4 }}> 284 redeemable</div>
</div>
{/* Activity feed */}
<div style={{ padding: '14px 12px', display: 'flex', flexDirection: 'column', gap: 7, flex: 1 }}>
{[
{ label: 'Daily Login', pts: '+50', color: '#4ade80' },
{ label: 'Walking 5k Steps', pts: '+80', color: '#4ade80' },
{ label: 'Spin Wheel', pts: '-50', color: '#f87171' },
].map((item, i) => (
<div key={i} style={{
display: 'flex', justifyContent: 'space-between', alignItems: 'center',
background: 'rgba(255,255,255,0.04)', borderRadius: 12, padding: '10px 13px',
border: '1px solid rgba(255,255,255,0.06)',
}}>
<span style={{ fontSize: 12, color: 'rgba(255,255,255,0.78)', fontFamily: 'Sora, sans-serif' }}>{item.label}</span>
<span style={{ fontSize: 12, color: item.color, fontWeight: 700, fontFamily: 'Sora, sans-serif' }}>{item.pts}</span>
</div>
))}
</div>
{/* Nav dots */}
<div style={{ display: 'flex', justifyContent: 'center', gap: 6, padding: '10px 0 20px' }}>
{[1, 0, 0, 0].map((a, i) => (
<div key={i} style={{ width: a ? 22 : 6, height: 4, borderRadius: 2, background: a ? '#f4be28' : 'rgba(255,255,255,0.2)' }} />
))}
</div>
</div>
{/* Floating chip — top left */}
<div style={{
position: 'absolute', top: '12%', left: '-22%', borderRadius: 16,
padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 10,
background: 'rgba(255,255,255,0.08)', backdropFilter: 'blur(20px)',
border: '1px solid rgba(255,255,255,0.12)',
boxShadow: '0 8px 32px rgba(0,0,0,0.4)',
animation: 'pwOrb2Float 7s ease-in-out infinite',
}}>
<div style={{ width: 30, height: 30, borderRadius: 9, background: 'rgba(74,222,128,0.16)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#4ade80', fontSize: 14, fontWeight: 800 }}></div>
<div>
<div style={{ fontSize: 11, fontWeight: 700, color: '#fff', fontFamily: 'Sora, sans-serif' }}>+120 LYTs</div>
<div style={{ fontSize: 9, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif' }}>just now</div>
</div>
</div>
{/* Floating chip — bottom right */}
<div style={{
position: 'absolute', bottom: '14%', right: '-22%', borderRadius: 16,
padding: '12px 16px',
background: 'rgba(255,255,255,0.08)', backdropFilter: 'blur(20px)',
border: '1px solid rgba(255,255,255,0.12)',
boxShadow: '0 8px 32px rgba(0,0,0,0.4)',
animation: 'pwOrbFloat 8s ease-in-out infinite',
}}>
<div style={{ fontSize: 9, color: 'rgba(255,255,255,0.45)', fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 3 }}>Redeemable</div>
<div style={{ fontSize: 17, fontWeight: 900, color: '#f4be28', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.02em' }}>284.00</div>
</div>
</div>
</div>
</>
);
}

View File

@@ -1,196 +0,0 @@
'use client';
import { useEffect, useRef } from 'react';
interface Particle {
x: number;
y: number;
vx: number;
vy: number;
radius: number;
alpha: number;
type: 'lost' | 'converted' | 'regular';
pulseSpeed: number;
pulseTime: number;
}
export default function ProblemParticles() {
const canvasRef = useRef<HTMLCanvasElement | null>(null);
useEffect(() => {
const canvas = canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
let animationFrameId = 0;
let cssWidth = canvas.offsetWidth;
let cssHeight = canvas.offsetHeight;
const particles: Particle[] = [];
const storeNode = { x: cssWidth / 2, y: cssHeight / 2, radius: 18 };
const setCanvasSize = () => {
const dpr = Math.max(1, window.devicePixelRatio || 1);
cssWidth = canvas.offsetWidth;
cssHeight = canvas.offsetHeight;
canvas.width = Math.round(cssWidth * dpr);
canvas.height = Math.round(cssHeight * dpr);
// Map drawing coordinates to CSS pixels
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
storeNode.x = cssWidth / 2;
storeNode.y = cssHeight / 2;
};
const createParticle = (isInitial = false): Particle => {
const typeRand = Math.random();
const type = typeRand < 0.2 ? 'lost' : typeRand < 0.45 ? 'converted' : 'regular';
let x: number, y: number;
if (isInitial) {
x = Math.random() * cssWidth;
y = Math.random() * cssHeight;
} else {
// Spawn from edges
if (Math.random() < 0.5) {
x = Math.random() < 0.5 ? 0 : cssWidth;
y = Math.random() * cssHeight;
} else {
x = Math.random() * cssWidth;
y = Math.random() < 0.5 ? 0 : cssHeight;
}
}
// Vector pointing towards the store node (center)
const angle = Math.atan2(storeNode.y - y, storeNode.x - x);
const speed = 0.4 + Math.random() * 0.8;
return {
x,
y,
vx: Math.cos(angle) * speed + (Math.random() - 0.5) * 0.2,
vy: Math.sin(angle) * speed + (Math.random() - 0.5) * 0.2,
radius: 2 + Math.random() * 4,
alpha: 0.1 + Math.random() * 0.7,
type,
pulseSpeed: 0.05 + Math.random() * 0.05,
pulseTime: Math.random() * Math.PI,
};
};
// Initialize
setCanvasSize();
for (let i = 0; i < 45; i++) particles.push(createParticle(true));
const handleResize = () => {
if (!canvas) return;
setCanvasSize();
};
window.addEventListener('resize', handleResize);
const draw = () => {
ctx.clearRect(0, 0, cssWidth, cssHeight);
// Background glow
const bgGlow = ctx.createRadialGradient(
storeNode.x,
storeNode.y,
10,
storeNode.x,
storeNode.y,
cssWidth * 0.4
);
bgGlow.addColorStop(0, 'rgba(0, 102, 255, 0.08)');
bgGlow.addColorStop(1, 'rgba(0, 0, 0, 0)');
ctx.fillStyle = bgGlow;
ctx.fillRect(0, 0, cssWidth, cssHeight);
// Store hub
ctx.shadowBlur = 30;
ctx.shadowColor = '#0066ff';
ctx.fillStyle = 'rgba(0, 102, 255, 0.9)';
ctx.beginPath();
ctx.arc(storeNode.x, storeNode.y, storeNode.radius, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = 'rgba(255, 255, 255, 0.6)';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.arc(storeNode.x, storeNode.y, storeNode.radius - 6, 0, Math.PI * 2);
ctx.stroke();
ctx.shadowBlur = 0;
// Particles
particles.forEach((p, idx) => {
p.x += p.vx;
p.y += p.vy;
p.pulseTime += p.pulseSpeed;
let alpha = p.alpha;
let radius = p.radius;
if (p.type === 'converted') {
alpha = Math.max(0.4, p.alpha + Math.sin(p.pulseTime) * 0.3);
radius = p.radius + Math.sin(p.pulseTime) * 1.5;
} else if (p.type === 'lost') {
const dist = Math.hypot(storeNode.x - p.x, storeNode.y - p.y);
if (dist < 150) p.alpha -= 0.008;
}
// Connections between particles
for (let j = idx + 1; j < particles.length; j++) {
const p2 = particles[j];
const dist = Math.hypot(p2.x - p.x, p2.y - p.y);
if (dist < 80 && p.alpha > 0 && p2.alpha > 0) {
const lineAlpha = (1 - dist / 80) * 0.18 * Math.min(alpha, p2.alpha);
ctx.strokeStyle = `rgba(0, 102, 255, ${lineAlpha})`;
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
}
// Connection to store hub
const distToStore = Math.hypot(storeNode.x - p.x, storeNode.y - p.y);
if (distToStore < 120 && p.alpha > 0) {
const lineAlpha = (1 - distToStore / 120) * 0.25 * p.alpha;
ctx.strokeStyle = p.type === 'converted' ? `rgba(0, 220, 255, ${lineAlpha})` : `rgba(0, 102, 255, ${lineAlpha})`;
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(storeNode.x, storeNode.y);
ctx.stroke();
}
// Respawn check
if (distToStore < storeNode.radius || p.x < 0 || p.x > cssWidth || p.y < 0 || p.y > cssHeight || p.alpha <= 0) {
particles[idx] = createParticle();
} else {
ctx.fillStyle = p.type === 'converted' ? `rgba(0, 220, 255, ${alpha})` : p.type === 'lost' ? `rgba(255, 68, 68, ${alpha})` : `rgba(255, 255, 255, ${alpha})`;
if (p.type === 'converted') {
ctx.shadowBlur = 10;
ctx.shadowColor = '#00dcff';
}
ctx.beginPath();
ctx.arc(p.x, p.y, radius, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
});
animationFrameId = requestAnimationFrame(draw);
};
draw();
return () => {
window.removeEventListener('resize', handleResize);
cancelAnimationFrame(animationFrameId);
};
}, []);
return <canvas ref={canvasRef} className="w-full h-full absolute inset-0" />;
}

View File

@@ -1,237 +0,0 @@
'use client';
import { useState } from 'react';
import Image from 'next/image';
import Link from 'next/link';
const SOLUTIONS = [
{
id: 'footfall',
category: 'AI Solution',
title: 'Footfall Intelligence',
subtitle: 'Understand Customer Movement in Real Time',
excerpt:
'Track visitor count, movement patterns, and peak hours across your retail spaces. Turn anonymous foot traffic into actionable intelligence that drives layout, staffing, and promotions.',
image: '/loyaly_php/assets/images/deepvision-fi-640x640.jpg',
imageAlt: 'Footfall Intelligence dashboard showing real-time customer movement heatmaps',
href: '/solutions/footfall-intelligence',
},
{
id: 'journey',
category: 'AI Solution',
title: 'Customer Journey',
subtitle: 'Map Every Interaction Seamlessly',
excerpt:
'Track and understand each step of your customer\'s journey — online and in-store — to identify friction points, improve conversion, and deliver experiences that build loyalty.',
image: '/loyaly_php/assets/images/nlpgenius-natural-language-processing-fi-640x640.jpg',
imageAlt: 'Customer journey map visualisation inside Loyaly.AI platform',
href: '/solutions/customer-journey',
},
{
id: 'engagement',
category: 'AI Solution',
title: 'Customer Engagement',
subtitle: 'Drive Meaningful Interactions',
excerpt:
'Analyse behaviour and preferences to deliver personalised experiences, timely offers, and targeted messaging that keep customers coming back.',
image: '/loyaly_php/assets/images/smarttrader-predictive-stock-market-analysis-fi-640x640.jpg',
imageAlt: 'Customer engagement analytics showing personalisation performance metrics',
href: '/solutions/customer-engagement',
},
{
id: 'loyalty',
category: 'AI Solution',
title: 'Loyalty Management',
subtitle: 'Build Lasting Relationships',
excerpt:
'Reward customer loyalty with smart programmes that adapt in real time — from LYTs and tiers to personalised perks that feel genuinely earned.',
image: '/loyaly_php/assets/images/cognivision-transformative-ai-for-intelligent-fi-640x640.jpg',
imageAlt: 'Loyalty management module showing customer tier performance',
href: '/solutions/loyalty-management',
},
{
id: 'revenue',
category: 'AI Solution',
title: 'Revenue Optimisation',
subtitle: 'Maximise Business Performance',
excerpt:
'Leverage data insights to optimise pricing, promotions, and strategies for higher revenue growth — without sacrificing margin.',
image: '/loyaly_php/assets/images/neuraforge-crafting-future-ready-ai-solutions-fi-640x640.jpg',
imageAlt: 'Revenue optimisation charts showing margin improvements',
href: '/solutions/revenue-optimisation',
},
];
export default function Solutions() {
const [hoveredId, setHoveredId] = useState<string | null>(null);
return (
<section
id="solutions"
aria-label="Our solutions"
style={{ padding: '80px 24px', background: '#fff', margin: '0 20px 20px', borderRadius: '24px', overflow: 'hidden' }}
>
<div style={{ maxWidth: '1200px', margin: '0 auto' }}>
{/* Header */}
<div style={{ marginBottom: '56px', maxWidth: '640px' }}>
<p
style={{
margin: '0 0 12px',
fontSize: '13px',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.1em',
color: '#f4be28',
}}
>
Our Solutions
</p>
<h2
style={{
margin: '0 0 16px',
fontSize: 'clamp(1.6rem, 3vw, 2.4rem)',
fontWeight: 800,
lineHeight: 1.2,
color: '#171512',
}}
>
Exploring groundbreaking AI solutions transforming retail
</h2>
<p style={{ margin: 0, fontSize: '16px', lineHeight: 1.7, color: '#4f463a' }}>
From understanding footfall to optimising revenue, our five AI modules work independently
or together as one unified intelligence engine.
</p>
</div>
{/* Grid */}
<div
style={{
display: 'grid',
gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))',
gap: '28px',
marginBottom: '48px',
}}
>
{SOLUTIONS.map(({ id, category, title, subtitle, excerpt, image, imageAlt, href }) => {
const isHovered = hoveredId === id;
return (
<article
key={id}
onMouseEnter={() => setHoveredId(id)}
onMouseLeave={() => setHoveredId(null)}
style={{
background: '#fff8e8',
borderRadius: '16px',
overflow: 'hidden',
boxShadow: isHovered
? '0 12px 40px rgba(23,21,18,0.14)'
: '0 2px 12px rgba(23,21,18,0.06)',
transform: isHovered ? 'translateY(-6px)' : 'translateY(0)',
transition: 'transform 0.3s ease, box-shadow 0.3s ease',
display: 'flex',
flexDirection: 'column',
}}
>
{/* Card image */}
<Link href={href} tabIndex={-1} aria-hidden="true" style={{ display: 'block' }}>
<div style={{ position: 'relative', height: '220px', overflow: 'hidden' }}>
<Image
src={image}
alt={imageAlt}
fill
style={{
objectFit: 'cover',
transform: isHovered ? 'scale(1.05)' : 'scale(1)',
transition: 'transform 0.4s ease',
}}
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
/>
</div>
</Link>
{/* Card content */}
<div
style={{
padding: '24px 24px 28px',
flex: 1,
display: 'flex',
flexDirection: 'column',
gap: '10px',
}}
>
{/* Category badge */}
<span
style={{
display: 'inline-block',
padding: '4px 10px',
background: '#fff2c8',
color: '#bc7122',
borderRadius: '20px',
fontSize: '12px',
fontWeight: 700,
letterSpacing: '0.04em',
alignSelf: 'flex-start',
}}
>
{category}
</span>
<h3 style={{ margin: 0, fontSize: '19px', fontWeight: 800, color: '#171512', lineHeight: 1.2 }}>
<Link href={href} style={{ textDecoration: 'none', color: 'inherit' }}>
{title}
</Link>
</h3>
<p style={{ margin: 0, fontSize: '14px', fontWeight: 600, color: '#bc7122' }}>
{subtitle}
</p>
<p style={{ margin: 0, fontSize: '14px', lineHeight: 1.7, color: '#4f463a', flex: 1 }}>
{excerpt}
</p>
<Link
href={href}
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '6px',
marginTop: '8px',
fontWeight: 700,
fontSize: '14px',
color: '#171512',
textDecoration: 'none',
}}
>
Explore more <span aria-hidden="true"></span>
</Link>
</div>
</article>
);
})}
</div>
{/* CTA */}
<div style={{ textAlign: 'center' }}>
<Link
href="/solutions_page"
style={{
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
padding: '14px 36px',
background: '#f4be28',
color: '#171512',
borderRadius: '4px',
fontWeight: 700,
textDecoration: 'none',
fontSize: '16px',
}}
>
View all solutions <span aria-hidden="true"></span>
</Link>
</div>
</div>
</section>
);
}

View File

@@ -1,211 +0,0 @@
'use client';
import { useState, useEffect, useRef, useCallback } from 'react';
const TESTIMONIALS = [
{
quote:
'Loyaly.AI transformed how we understand our customers. Foot traffic insights alone helped us rearrange our store layout and boost average basket size by 18% in the first quarter.',
author: 'Priya Nair',
role: 'Head of Retail Operations',
company: 'Trendsetter Fashion Stores',
avatar: '/loyaly_php/assets/images/partners-1.png',
},
{
quote:
'The AI powered loyalty engine learns fast. Within weeks we were delivering personalised offers that felt genuinely relevant — our repeat visit rate climbed 23%.',
author: 'Arjun Mehta',
role: 'VP of Customer Experience',
company: 'FreshMart Supermarkets',
avatar: '/loyaly_php/assets/images/partners-2.png',
},
{
quote:
"We tried three loyalty platforms before Loyaly.AI. This is the first one that actually connects footfall data, promotions, and CRM in a single dashboard. It's changed how we run campaigns.",
author: 'Sarah Connelly',
role: 'Chief Marketing Officer',
company: 'Urban Living Group',
avatar: '/loyaly_php/assets/images/partners-3.png',
},
];
export default function Testimonial() {
const [active, setActive] = useState(0);
const [paused, setPaused] = useState(false);
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const total = TESTIMONIALS.length;
const go = useCallback(
(index: number) => {
setActive((index + total) % total);
},
[total]
);
// Auto-advance every 6 s
useEffect(() => {
if (paused) return;
timerRef.current = setTimeout(() => go(active + 1), 6000);
return () => {
if (timerRef.current) clearTimeout(timerRef.current);
};
}, [active, paused, go]);
const { quote, author, role, company } = TESTIMONIALS[active];
return (
<section
id="testimonials"
aria-label="Customer testimonials"
style={{ background: '#171512', color: '#fff', padding: '80px 24px', margin: '0 20px 20px', borderRadius: '24px', overflow: 'hidden' }}
onMouseEnter={() => setPaused(true)}
onMouseLeave={() => setPaused(false)}
>
<div style={{ maxWidth: '860px', margin: '0 auto', textAlign: 'center' }}>
{/* Section label */}
<p
style={{
margin: '0 0 12px',
fontSize: '13px',
fontWeight: 700,
textTransform: 'uppercase',
letterSpacing: '0.1em',
color: '#f4be28',
}}
>
Customer Stories
</p>
<h2
style={{
margin: '0 0 52px',
fontSize: 'clamp(1.4rem, 3vw, 2rem)',
fontWeight: 700,
lineHeight: 1.3,
color: '#fff',
}}
>
See how retailers are transforming customer loyalty with Loyaly.AI
</h2>
{/* Quote card */}
<div
key={active}
style={{
background: 'rgba(255,255,255,0.05)',
borderRadius: '16px',
padding: '48px 40px',
position: 'relative',
animation: 'fadeIn 0.5s ease',
}}
>
{/* Decorative quote mark */}
<span
aria-hidden="true"
style={{
position: 'absolute',
top: '24px',
left: '32px',
fontSize: '80px',
lineHeight: 1,
color: '#f4be28',
opacity: 0.25,
fontFamily: 'Sora, serif',
}}
>
"
</span>
<blockquote
style={{
margin: '0 0 32px',
fontSize: 'clamp(1rem, 2.2vw, 1.2rem)',
lineHeight: 1.8,
color: 'rgba(255,255,255,0.9)',
fontStyle: 'italic',
position: 'relative',
zIndex: 1,
}}
>
{quote}
</blockquote>
<footer style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px' }}>
<cite style={{ fontStyle: 'normal', fontWeight: 700, fontSize: '16px', color: '#f4be28' }}>
{author}
</cite>
<span style={{ fontSize: '14px', color: 'rgba(255,255,255,0.6)' }}>
{role}, {company}
</span>
</footer>
</div>
{/* Navigation dots */}
<div
role="tablist"
aria-label="Testimonial navigation"
style={{ display: 'flex', justifyContent: 'center', gap: '10px', marginTop: '32px' }}
>
{TESTIMONIALS.map((_, i) => (
<button
key={i}
role="tab"
aria-selected={i === active}
aria-label={`Show testimonial ${i + 1}`}
onClick={() => { go(i); setPaused(true); }}
style={{
width: i === active ? '28px' : '10px',
height: '10px',
borderRadius: '5px',
background: i === active ? '#f4be28' : 'rgba(255,255,255,0.25)',
border: 'none',
cursor: 'pointer',
padding: 0,
transition: 'width 0.3s ease, background 0.3s ease',
}}
/>
))}
</div>
{/* Prev / Next */}
<div style={{ display: 'flex', justifyContent: 'center', gap: '12px', marginTop: '20px' }}>
<button
onClick={() => { go(active - 1); setPaused(true); }}
aria-label="Previous testimonial"
style={arrowBtnStyle}
>
</button>
<button
onClick={() => { go(active + 1); setPaused(true); }}
aria-label="Next testimonial"
style={arrowBtnStyle}
>
</button>
</div>
</div>
<style>{`
@keyframes fadeIn {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0); }
}
`}</style>
</section>
);
}
const arrowBtnStyle: React.CSSProperties = {
width: '40px',
height: '40px',
borderRadius: '50%',
background: 'rgba(255,255,255,0.08)',
color: '#fff',
border: '1px solid rgba(255,255,255,0.15)',
cursor: 'pointer',
fontSize: '16px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'background 0.2s',
};

View File

@@ -1,8 +1,14 @@
'use client';
import React, { useState } from 'react';
import React, { useState, useLayoutEffect, useEffect, useRef } from 'react';
import Image from 'next/image';
import { PhoneFrame, PhoneBottomNav, BellIcon, PinIcon, ChevronIcon, SearchIcon, MicIcon, CameraIcon, MapIcon, HeartLogoIcon } from './PhoneMockup';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
const deals = [
{ store: 'Starbucks', category: 'Coffee & Drinks', offer: '2× LYTs', savings: '₹120 saved', color: '#00704A', dot: '#22c55e', distance: '0.3 km', rating: 4.8 },
@@ -14,9 +20,37 @@ const deals = [
export default function DiscoverDealsSection() {
const [hovered, setHovered] = useState<number | null>(null);
const panelRef = useRef<HTMLDivElement>(null);
const phoneRef = useRef<HTMLDivElement>(null);
useIsomorphicLayoutEffect(() => {
if (typeof window === 'undefined') return;
const section = panelRef.current;
const phone = phoneRef.current;
if (!section || !phone) return;
const ctx = gsap.context(() => {
gsap.fromTo(phone,
{ y: 160, opacity: 0 },
{
y: 0,
opacity: 1,
ease: 'power2.out',
scrollTrigger: {
trigger: section,
start: 'top 92%',
end: 'top 25%',
scrub: 1.2,
}
}
);
}, section);
return () => ctx.revert();
}, []);
return (
<section className="fp-panel" style={{ background: '#08080f' }}>
<section ref={panelRef} className="fp-panel" style={{ background: '#08080f' }}>
{/* Background photo */}
<Image src="/images/stock/deals-retail.png" alt="" fill style={{ objectFit: 'cover', opacity: 0.25, zIndex: 0 }} />
@@ -63,7 +97,7 @@ export default function DiscoverDealsSection() {
</div>
{/* Phone mockup — white app screen, matches the in-app Discover experience */}
<div className="fp-rise" style={{ transitionDelay: '0.1s', display: 'flex', justifyContent: 'center' }}>
<div ref={phoneRef} style={{ display: 'flex', justifyContent: 'center', opacity: 0, width: '100%' }}>
<PhoneFrame floatClassName="ddm-phone-shell">
{/* Amber header */}
@@ -151,7 +185,7 @@ export default function DiscoverDealsSection() {
<ChevronIcon />
</div>
<PhoneBottomNav active="heart" />
<PhoneBottomNav active="tag" />
</PhoneFrame>
</div>
</div>

View File

@@ -1,6 +1,6 @@
'use client';
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import Image from 'next/image';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
@@ -8,6 +8,8 @@ import { PhoneFrame, PhoneBottomNav, BellIcon, FlameIcon } from './PhoneMockup';
if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
const feed = [
{ store: 'Daily Login', pts: +50, time: '2m ago', type: 'earn' },
{ store: 'Walking 5,000 steps', pts: +80, time: '1h ago', type: 'earn' },
@@ -18,10 +20,38 @@ const feed = [
export default function EarnRewardsSection() {
const [pts, setPts] = useState(0);
const panelRef = useRef<HTMLDivElement>(null);
const phoneRef = useRef<HTMLDivElement>(null);
const rafRef = useRef<number>(0);
const rowRefs = useRef<(HTMLDivElement | null)[]>([]);
const activeRowRef = useRef(0);
// Phone mockup rises in from the bottom as the section scrolls into view
useIsomorphicLayoutEffect(() => {
if (typeof window === 'undefined') return;
const section = panelRef.current;
const phone = phoneRef.current;
if (!section || !phone) return;
const ctx = gsap.context(() => {
gsap.fromTo(phone,
{ y: 160, opacity: 0 },
{
y: 0,
opacity: 1,
ease: 'power2.out',
scrollTrigger: {
trigger: section,
start: 'top 92%',
end: 'top 25%',
scrub: 1.2,
}
}
);
}, section);
return () => ctx.revert();
}, []);
// Move the highlighted feed card as the section scrolls through the viewport
useEffect(() => {
if (typeof window === 'undefined') return;
@@ -136,7 +166,7 @@ export default function EarnRewardsSection() {
</div>
{/* Right — phone mockup, matches the in-app Earn / Home experience */}
<div className="fp-rise" style={{ transitionDelay: '0.15s', display: 'flex', justifyContent: 'center' }}>
<div ref={phoneRef} style={{ display: 'flex', justifyContent: 'center', opacity: 0, width: '100%' }}>
<PhoneFrame floatClassName="erm-phone-shell">
{/* Amber header */}
@@ -230,7 +260,7 @@ export default function EarnRewardsSection() {
))}
</div>
<PhoneBottomNav active="home" />
<PhoneBottomNav active="user" />
</PhoneFrame>
</div>
</div>

View File

@@ -1,6 +1,12 @@
'use client';
import React from 'react';
import React, { useLayoutEffect, useEffect, useRef } from 'react';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
const words = [
{ text: 'Effortless.', size: 'clamp(58px, 9vw, 134px)', color: '#ffffff' },
@@ -14,8 +20,37 @@ const steps = [
];
export default function ExperienceSection() {
const panelRef = useRef<HTMLDivElement>(null);
const cardRef = useRef<HTMLDivElement>(null);
useIsomorphicLayoutEffect(() => {
if (typeof window === 'undefined') return;
const section = panelRef.current;
const card = cardRef.current;
if (!section || !card) return;
const ctx = gsap.context(() => {
gsap.fromTo(card,
{ x: 200, opacity: 0 },
{
x: 0,
opacity: 1,
ease: 'power2.out',
scrollTrigger: {
trigger: section,
start: 'top 92%',
end: 'top 25%',
scrub: 1.2,
}
}
);
}, section);
return () => ctx.revert();
}, []);
return (
<section className="fp-panel" style={{ background: '#07070d' }}>
<section ref={panelRef} className="fp-panel" style={{ background: '#07070d' }}>
<div className="fp-shell">
<div style={{ marginBottom: 'clamp(36px, 5vh, 60px)' }}>
@@ -36,35 +71,41 @@ export default function ExperienceSection() {
))}
</div>
{/* Steps */}
<div>
<p className="fp-rise" style={{
fontSize: 16, lineHeight: 1.85, color: 'rgba(255,255,255,0.45)', fontFamily: 'Sora, sans-serif', margin: '0 0 44px', maxWidth: 360,
<div ref={cardRef} style={{
background: '#f4be28',
padding: 'clamp(28px, 4.5vw, 44px)',
borderRadius: '28px',
boxShadow: '0 24px 48px rgba(244, 190, 40, 0.12)',
width: '100%',
opacity: 0,
}}>
<p style={{
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.
</p>
<div style={{ display: 'flex', flexDirection: 'column' }}>
{steps.map((s, i) => (
<div key={i} className="fp-rise" style={{
<div key={i} style={{
display: 'flex', gap: 20, alignItems: 'center', padding: '20px 0',
borderBottom: i < steps.length - 1 ? '1px solid rgba(255,255,255,0.07)' : 'none',
transitionDelay: `${0.2 + i * 0.1}s`,
borderBottom: i < steps.length - 1 ? '1px solid rgba(7, 7, 13, 0.1)' : 'none',
}}>
<span style={{ fontSize: 12, fontWeight: 700, color: '#f4be28', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em', flexShrink: 0, width: 24 }}>{s.n}</span>
<span style={{ fontSize: 16, fontWeight: 600, color: 'rgba(255,255,255,0.75)', fontFamily: 'Sora, sans-serif', lineHeight: 1.4 }}>{s.label}</span>
<span style={{ fontSize: 13, fontWeight: 800, color: '#07070d', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em', flexShrink: 0, width: 24 }}>{s.n}</span>
<span style={{ fontSize: 16, fontWeight: 700, color: '#07070d', fontFamily: 'Sora, sans-serif', lineHeight: 1.4 }}>{s.label}</span>
</div>
))}
</div>
<div className="fp-rise" style={{ marginTop: 40, display: 'flex', alignItems: 'center', gap: 12, transitionDelay: '0.5s' }}>
<div style={{ marginTop: 36, display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{ display: 'flex' }}>
{['#f4be28', '#4ade80', '#a855f7', '#3b82f6'].map((c, i) => (
<div key={i} style={{ width: 30, height: 30, borderRadius: '50%', background: c, border: '2px solid #07070d', marginLeft: i > 0 ? -9 : 0 }} />
{['#ff7b39', '#4ade80', '#a855f7', '#3b82f6'].map((c, i) => (
<div key={i} style={{ width: 30, height: 30, borderRadius: '50%', background: c, border: '2px solid #f4be28', marginLeft: i > 0 ? -9 : 0 }} />
))}
</div>
<span style={{ fontSize: 13, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif' }}>
Joined by <span style={{ color: '#fff', fontWeight: 700 }}>50,000+</span> shoppers
<span style={{ fontSize: 13, color: 'rgba(7, 7, 13, 0.65)', fontFamily: 'Sora, sans-serif', fontWeight: 500 }}>
Joined by <span style={{ color: '#07070d', fontWeight: 800 }}>50,000+</span> shoppers
</span>
</div>
</div>

View File

@@ -44,13 +44,9 @@ export default function FinalCTASection() {
const tl = gsap.timeline({
scrollTrigger: { trigger: sectionRef.current, start: 'top 80%', toggleActions: 'play none none reset' },
});
tl.fromTo('.fcta-badge',
{ opacity: 0, y: 20, scale: 0.9 },
{ opacity: 1, y: 0, scale: 1, duration: 0.6, ease: 'back.out(2)' }
)
.fromTo('.fcta-line-1',
tl.fromTo('.fcta-line-1',
{ opacity: 0, y: 50, filter: 'blur(8px)' },
{ opacity: 1, y: 0, filter: 'blur(0px)', duration: 0.8, ease: 'expo.out' }, '-=0.2'
{ opacity: 1, y: 0, filter: 'blur(0px)', duration: 0.8, ease: 'expo.out' }
)
.fromTo('.fcta-line-2',
{ opacity: 0, y: 50, filter: 'blur(8px)' },
@@ -60,9 +56,32 @@ export default function FinalCTASection() {
{ opacity: 0, y: 24 },
{ opacity: 1, y: 0, duration: 0.65, ease: 'power3.out' }, '-=0.4'
)
.fromTo('.fcta-hanging-logo',
{ opacity: 0, y: -60, rotation: -16 },
{ opacity: 1, y: 0, rotation: 0, duration: 1.1, stagger: 0.16, ease: 'back.out(1.5)' },
'-=0.45'
)
.fromTo('.fcta-logo-link',
{ rotation: 10 },
{ rotation: 0, duration: 1.2, stagger: 0.16, ease: 'back.out(1.6)' },
'<'
)
.call(() => {
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
gsap.utils.toArray<HTMLElement>('.fcta-hanging-logo').forEach((el, i) => {
gsap.to(el, {
rotation: i % 2 === 0 ? 3.5 : -3.5,
duration: 2.4 + i * 0.3,
ease: 'sine.inOut',
yoyo: true,
repeat: -1,
transformOrigin: 'top center',
});
});
})
.fromTo('.fcta-btns',
{ opacity: 0, y: 20 },
{ opacity: 1, y: 0, duration: 0.55, ease: 'power3.out' }, '-=0.3'
{ opacity: 1, y: 0, duration: 0.55, ease: 'power3.out' }, '-=0.45'
)
.fromTo('.fcta-stat',
{ opacity: 0, y: 28, scale: 0.88 },
@@ -94,30 +113,10 @@ export default function FinalCTASection() {
{/* Amber glow orb top-center */}
<div style={{ position: 'absolute', top: '-20%', left: '50%', transform: 'translateX(-50%)', width: '70%', height: '60%', borderRadius: '50%', background: 'radial-gradient(ellipse, rgba(244,190,40,0.12) 0%, transparent 65%)', filter: 'blur(50px)', pointerEvents: 'none' }} />
{/* Ghost wordmark */}
<div style={{ position: 'absolute', bottom: -20, left: '50%', transform: 'translateX(-50%)', whiteSpace: 'nowrap', pointerEvents: 'none', zIndex: 0 }}>
<span style={{
fontSize: 'clamp(100px,20vw,280px)', fontWeight: 900,
fontFamily: 'Sora, sans-serif', letterSpacing: '-0.06em', lineHeight: 1,
color: 'transparent', WebkitTextStroke: '1px rgba(23,21,18,0.05)',
display: 'block',
}}>LOYALY</span>
</div>
{/* Content */}
<div style={{ position: 'relative', zIndex: 10, maxWidth: 820, margin: '0 auto' }}>
{/* Live badge */}
<div className="fcta-badge" style={{
display: 'inline-flex', alignItems: 'center', gap: 8, marginBottom: 36,
background: '#f0fdf4', border: '1.5px solid rgba(22,163,74,0.35)',
borderRadius: 100, padding: '9px 20px',
}}>
<span style={{ width: 7, height: 7, borderRadius: '50%', background: '#22c55e', display: 'block', animation: 'fctaPulse 1.6s ease-in-out infinite', boxShadow: '0 0 0 3px rgba(34,197,94,0.2)' }} />
<span style={{ fontSize: 12, fontWeight: 700, color: '#16a34a', fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em', textTransform: 'uppercase' }}>
Now live in Coimbatore
</span>
</div>
{/* Headline */}
<h2 style={{ margin: '0 0 28px', fontFamily: 'Sora, sans-serif', lineHeight: 0.92, letterSpacing: '-0.055em' }}>
@@ -144,13 +143,130 @@ export default function FinalCTASection() {
{/* Subtitle */}
<p className="fcta-sub" style={{
position: 'relative',
zIndex: 5,
fontSize: 'clamp(1rem,1.2vw,1.15rem)', lineHeight: 1.75,
color: 'rgba(23,21,18,0.5)', fontFamily: 'Sora, sans-serif',
maxWidth: 440, margin: '0 auto 52px',
color: '#171512', fontFamily: 'Sora, sans-serif',
maxWidth: 520, width: '100%', margin: '0 auto 0px',
background: '#f4be28',
border: '1.5px solid #f4be28',
borderRadius: 100,
padding: '14px 28px',
display: 'inline-block',
boxSizing: 'border-box',
}}>
Download Loyaly and start earning on your very next purchase. Free. Instant. Yours.
</p>
{/* Hanging Logos */}
<div className="fcta-hanging-logos-wrapper" style={{
position: 'relative',
height: 250,
margin: '-34px auto 40px',
display: 'flex',
justifyContent: 'center',
alignItems: 'flex-start',
gap: 140,
width: 'fit-content',
zIndex: 1,
}}>
{/* Play Store Logo (hanging) */}
<div className="fcta-hanging-logo play-store" style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
transformOrigin: 'top center',
position: 'relative',
}}>
{/* Keyring loop */}
<div style={{
width: 16, height: 16, borderRadius: '50%',
border: '2px solid #f4be28', marginTop: 14, marginBottom: -3,
boxShadow: '0 0 10px rgba(244,190,40,0.35)',
flexShrink: 0,
}} />
{/* String */}
<div style={{
width: 2,
height: 62,
background: '#f4be28',
opacity: 0.7,
boxShadow: '0 0 6px rgba(244,190,40,0.25)',
}} />
{/* Logo Circle */}
<Link href="/contacts" className="fcta-logo-link" style={{
width: 116,
height: 116,
borderRadius: '50%',
overflow: 'hidden',
background: '#000000',
boxShadow: '0 18px 40px rgba(0,0,0,0.24), 0 0 0 1px rgba(244,190,40,0.15)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '5px solid #f4be28',
cursor: 'pointer',
textDecoration: 'none',
transformOrigin: 'top center',
marginTop: -1,
}}>
<img src="/logo_icons/images.png" alt="Google Play" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
</Link>
{/* Tooltip */}
<div className="fcta-tooltip">
Download In Playstore
</div>
</div>
{/* Apple Logo (hanging) */}
<div className="fcta-hanging-logo apple-store" style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
transformOrigin: 'top center',
position: 'relative',
}}>
{/* Keyring loop */}
<div style={{
width: 16, height: 16, borderRadius: '50%',
border: '2px solid #f4be28', marginTop: 14, marginBottom: -3,
boxShadow: '0 0 10px rgba(244,190,40,0.35)',
flexShrink: 0,
}} />
{/* String */}
<div style={{
width: 2,
height: 92,
background: '#f4be28',
opacity: 0.7,
boxShadow: '0 0 6px rgba(244,190,40,0.25)',
}} />
{/* Logo Circle */}
<Link href="/contacts" className="fcta-logo-link" style={{
width: 116,
height: 116,
borderRadius: '50%',
overflow: 'hidden',
background: '#000000',
boxShadow: '0 18px 40px rgba(0,0,0,0.24), 0 0 0 1px rgba(244,190,40,0.15)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
border: '5px solid #f4be28',
cursor: 'pointer',
textDecoration: 'none',
transformOrigin: 'top center',
marginTop: -1,
}}>
<img src="/logo_icons/apple.jpg" alt="App Store" style={{ width: '100%', height: '100%', objectFit: 'cover' }} />
</Link>
{/* Tooltip */}
<div className="fcta-tooltip">
Download in App store
</div>
</div>
</div>
{/* CTAs */}
<div className="fcta-btns" style={{ display: 'flex', gap: 14, justifyContent: 'center', flexWrap: 'wrap', marginBottom: 64 }}>
<Link
@@ -210,6 +326,58 @@ export default function FinalCTASection() {
<style>{`
@keyframes fctaPulse { 0%,100%{box-shadow:0 0 0 0 rgba(34,197,94,0.5)} 50%{box-shadow:0 0 0 6px rgba(34,197,94,0)} }
@keyframes fctaShimmer { 0%{background-position:200% center} 100%{background-position:-200% center} }
.fcta-hanging-logo a {
transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.fcta-hanging-logo a:hover {
transform: scale(1.08);
}
.fcta-tooltip {
position: absolute;
top: calc(100% + 4px);
left: 50%;
transform: translateX(-50%) translateY(8px);
background: #171512;
color: #ffffff;
padding: 6px 14px;
border-radius: 6px;
font-size: 11px;
font-weight: 700;
font-family: 'Sora', sans-serif;
white-space: nowrap;
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s cubic-bezier(0.25, 1, 0.22, 1);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 10;
}
.fcta-hanging-logo:hover .fcta-tooltip {
opacity: 1;
transform: translateX(-50%) translateY(0);
}
.fcta-tooltip::before {
content: '';
position: absolute;
top: -4px;
left: 50%;
transform: translateX(-50%) rotate(45deg);
width: 8px;
height: 8px;
background: #171512;
}
@media (max-width: 1023px) {
.fcta-hanging-logos-wrapper {
display: none !important;
}
.fcta-sub {
margin-bottom: 40px !important;
}
}
@media (min-width: 1024px) {
.fcta-btns {
display: none !important;
}
}
`}</style>
</section>
);

View File

@@ -61,7 +61,7 @@ export default function HeroSection() {
<div style={{ maxWidth: 720 }}>
<h1 className="fp-headline" style={{
fontSize: 'clamp(40px, 5.4vw, 82px)', fontWeight: 800, lineHeight: 1.02,
letterSpacing: '-0.045em', color: '#fff', margin: '0 0 28px', fontFamily: 'Sora, sans-serif',
letterSpacing: '-0.045em', color: '#fff', margin: '50px 0 28px', fontFamily: 'Sora, sans-serif',
}}>
<span className="fp-mask"><span>Shop Smart.</span></span>
<span className="fp-mask"><span className="fp-shimmer">Live Rewarded.</span></span>
@@ -71,7 +71,7 @@ export default function HeroSection() {
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',
}}>
Meet <strong>LYT (Loyaly Token)</strong>: the single currency behind every reward in the app. Earn them through daily tasks, walking challenges, and games, then spend them instantly at partner stores near you with zero expiry.
Meet <strong>LYTs (Loyaly Tokens)</strong>: the single currency behind every reward. Earn them through daily challenges and games, then spend instantly at partner stores near you with zero expiry.
</p>
<div className="fp-content fp-cta" style={{ display: 'flex', gap: 16, flexWrap: 'wrap', alignItems: 'center', justifyContent: 'center' }}>

View File

@@ -56,9 +56,20 @@ export function MapIcon() {
</svg>
);
}
export function HeartLogoIcon({ size = 16 }: { size?: number }) {
export function HeartLogoIcon({ size = 16, active = false }: { size?: number; active?: boolean }) {
return (
<Image src="/images/loyalyai-logo.png" alt="" width={32} height={32} style={{ width: size, height: size, objectFit: 'contain' }} />
<Image
src="/images/loyalyai-logo.png"
alt=""
width={32}
height={32}
style={{
width: size,
height: size,
objectFit: 'contain',
filter: active ? 'brightness(0)' : 'none',
}}
/>
);
}
export function UpArrowIcon({ color = '#16a34a' }: { color?: string }) {
@@ -85,12 +96,12 @@ export function FlameIcon({ color = '#f4be28' }: { color?: string }) {
/* ── Bottom nav — pass the active tab key to highlight it ── */
export function PhoneBottomNav({ active = 'home' }: { active?: string }) {
const items: { key: string; icon: React.ReactNode }[] = [
{ key: 'home', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 10.5L12 3l9 7.5" /><path d="M5 9.5V21h14V9.5" /></svg> },
{ key: 'tag', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20.59 13.41 12 22l-9-9V4h9z" /><circle cx="7.5" cy="8.5" r="1.2" /></svg> },
{ key: 'heart', icon: <HeartLogoIcon size={18} /> },
{ key: 'shop', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l1.5-5h15L21 9" /><path d="M3 9h18v11H3z" /><path d="M9 20v-6h6v6" /></svg> },
{ key: 'user', icon: <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="8" r="4" /><path d="M4 21c1.5-4 5-6 8-6s6.5 2 8 6" /></svg> },
const items: { key: string; icon: (isActive: boolean) => React.ReactNode }[] = [
{ key: 'home', icon: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 10.5L12 3l9 7.5" /><path d="M5 9.5V21h14V9.5" /></svg> },
{ key: 'tag', icon: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20.59 13.41 12 22l-9-9V4h9z" /><circle cx="7.5" cy="8.5" r="1.2" /></svg> },
{ key: 'heart', icon: (isActive) => <HeartLogoIcon size={18} active={isActive} /> },
{ key: 'shop', icon: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l1.5-5h15L21 9" /><path d="M3 9h18v11H3z" /><path d="M9 20v-6h6v6" /></svg> },
{ key: 'user', icon: () => <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="8" r="4" /><path d="M4 21c1.5-4 5-6 8-6s6.5 2 8 6" /></svg> },
];
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-around', padding: '10px 8px', borderTop: '1px solid #f0f0f0' }}>
@@ -99,11 +110,11 @@ export function PhoneBottomNav({ active = 'home' }: { active?: string }) {
return (
<div key={n.key} style={{
width: 34, height: 34, borderRadius: 12, display: 'flex', alignItems: 'center', justifyContent: 'center',
color: isActive && n.key !== 'heart' ? '#171512' : '#9ca3af',
background: isActive ? '#fff7e0' : 'transparent',
color: isActive ? '#171512' : '#9ca3af',
background: isActive ? '#f4be28' : 'transparent',
border: isActive ? '1.5px solid #f4be28' : 'none',
}}>
{n.icon}
{n.icon(isActive)}
</div>
);
})}

View File

@@ -0,0 +1,68 @@
'use client';
import React from 'react';
const PILLARS = [
{
n: '01',
title: 'Shop Smarter',
desc: 'Unlock top deals, discounts, and exclusive offers at stores near you, just by walking in.',
color: '#f4be28',
},
{
n: '02',
title: 'Earn Rewards',
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.',
color: '#a78bfa',
},
];
export default function PillarsSection() {
return (
<section className="fp-panel fp-light" style={{ background: '#ffffff' }}>
<div className="fp-shell">
<div style={{ marginBottom: 'clamp(40px, 6vh, 64px)' }}>
<span className="fp-kicker fp-rise">Save More<span className="fp-kicker-num">02</span></span>
</div>
<h2 className="fp-rise" style={{
fontSize: 'clamp(30px, 3.6vw, 50px)', fontWeight: 800, lineHeight: 1.08,
letterSpacing: '-0.04em', color: '#171512', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', maxWidth: 640,
}}>
Save more on every in-store visit.
</h2>
<p className="fp-rise" style={{
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.
</p>
<div className="fp-pillar-cols" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 24 }}>
{PILLARS.map((p, i) => (
<div key={p.n} className="fp-rise" style={{
transitionDelay: `${0.1 + i * 0.1}s`,
padding: 32, borderRadius: 20, background: '#f9f7f2', border: `1px solid ${p.color}30`,
}}>
<div style={{ width: 10, height: 10, borderRadius: '50%', background: p.color, marginBottom: 20 }} />
<h3 style={{ fontSize: 21, fontWeight: 800, color: '#171512', fontFamily: 'Sora, sans-serif', margin: '0 0 12px', letterSpacing: '-0.02em' }}>{p.title}</h3>
<p style={{ fontSize: 14.5, lineHeight: 1.7, color: '#4f463a', fontFamily: 'Sora, sans-serif', margin: 0 }}>{p.desc}</p>
</div>
))}
</div>
</div>
<style>{`
@media (max-width: 900px) {
.fp-pillar-cols { grid-template-columns: 1fr !important; }
}
`}</style>
</section>
);
}

View File

@@ -153,6 +153,31 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
</div>
</div>
{/* India retail market stats strip */}
<div className="relative z-10 mx-auto problem-india-stats" style={{
maxWidth: 1200,
marginBottom: 'clamp(32px, 4vw, 48px)',
display: 'grid',
gridTemplateColumns: 'repeat(5, 1fr)',
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' },
].map((s, i) => (
<div key={i} 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>
))}
</div>
{/* Grid wrapper */}
<div className="relative z-10 mx-auto" style={{ maxWidth: 1200 }}>
{/* Row 1: Red Stat Cards */}
@@ -429,6 +454,7 @@ export default function ProblemSection({ isLoaded = true }: ProblemSectionProps)
#problem .problem-stats-row { grid-template-columns: 1fr !important; }
#problem .dark-bento-grid { grid-template-columns: 1fr !important; }
#problem .dark-bento-card-4 { grid-column: span 1; }
#problem .problem-india-stats { grid-template-columns: repeat(2, 1fr) !important; }
}
`}</style>
</section>