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

@@ -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>