Update homepage, use-cases, and people pages; add preloader and new section components

This commit is contained in:
R-Bharathraj
2026-07-01 18:06:06 +05:30
parent 7a15ee2b52
commit abcefbb9c9
26 changed files with 1178 additions and 897 deletions

View File

@@ -60,10 +60,10 @@ const PRODUCTS: Product[] = [
{
id: 4,
href: '/solutions/loyaly-app',
title: 'Loyaly App',
title: 'Identiq',
subtitle: 'Consumer Loyalty Platform',
description:
'Earn Loyaly Coins through daily login, walking challenges, and interactive games. Unlock surprise coupons, discover nearby store campaigns, and redeem rewards at real offline stores.',
'Earn Loyaly Coins through daily logins and games, then redeem rewards at real offline stores.',
accent: '#f4be28',
accentRgb: '244, 190, 40',
tags: ['Daily Rewards', 'Game Based Offers', 'Offline Redemption'],
@@ -132,14 +132,23 @@ function ProductVisual({ product }: { product: Product }) {
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontSize: 11, letterSpacing: '0.12em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif', fontWeight: 600 }}>Live footfall</span>
<span style={{ fontSize: 11, color: accent, fontFamily: 'Sora, sans-serif', fontWeight: 700, display: 'flex', alignItems: 'center', gap: 5 }}>
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#4ade80', display: 'block' }} />2,847
<span className="pv-live-dot" style={{ width: 6, height: 6, borderRadius: '50%', background: '#4ade80', display: 'block' }} />2,847
</span>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(8,1fr)', gap: 5 }}>
{cells.map((_, i) => {
const intensity = Math.abs(Math.sin(i * 1.7) * Math.cos(i * 0.6));
const alpha = (0.08 + intensity * 0.7).toFixed(3);
return <div key={i} style={{ aspectRatio: '1 / 1', borderRadius: 5, background: `rgba(${accentRgb}, ${alpha})` }} />;
return (
<div
key={i}
className="pv-cell"
style={{
aspectRatio: '1 / 1', borderRadius: 5, background: `rgba(${accentRgb}, ${alpha})`,
animationDelay: `${(i % 8) * 0.12 + Math.floor(i / 8) * 0.07}s`,
}}
/>
);
})}
</div>
<div style={{ display: 'flex', gap: 10 }}>
@@ -177,8 +186,17 @@ function ProductVisual({ product }: { product: Product }) {
</linearGradient>
</defs>
<polygon points={`0,${h} ${path} ${w},${h}`} fill={`url(#sg${id})`} />
<polyline points={path} fill="none" stroke={accent} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" />
{pts.map((p, i) => <circle key={i} cx={(i / (pts.length - 1)) * w} cy={h - (p / max) * h} r={i === pts.length - 1 ? 4 : 0} fill={accent} />)}
<polyline className="pv-chart-line" points={path} fill="none" stroke={accent} strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" pathLength={100} />
{pts.map((p, i) => (
i === pts.length - 1
? (
<g key={i}>
<circle className="pv-chart-pulse-ring" cx={(i / (pts.length - 1)) * w} cy={h - (p / max) * h} r={4} fill="none" stroke={accent} strokeWidth="2" />
<circle cx={(i / (pts.length - 1)) * w} cy={h - (p / max) * h} r={4} fill={accent} />
</g>
)
: null
))}
</svg>
</div>
);
@@ -187,43 +205,115 @@ function ProductVisual({ product }: { product: Product }) {
// 3 — Dyscount: smart offer + conversion uplift
if (id === 3) {
return (
<div style={{ width: '82%', display: 'flex', flexDirection: 'column', gap: 14 }}>
<div style={{ borderRadius: 16, padding: '18px 20px', background: `linear-gradient(135deg, ${accent} 0%, #c0392b 100%)`, position: 'relative', overflow: 'hidden' }}>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.6)', fontFamily: 'Sora, sans-serif' }}>Smart Offer</div>
<div style={{ fontSize: 30, fontWeight: 900, color: '#fff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.02em', marginTop: 2 }}>15% OFF</div>
<div style={{ fontSize: 12, color: 'rgba(255,255,255,0.85)', fontFamily: 'Sora, sans-serif', marginTop: 2 }}>Targeted · high-intent shoppers</div>
<div style={{ width: '82%', position: 'relative', display: 'flex', flexDirection: 'column', gap: 14 }}>
{/* Live campaign badge, floating on the card edge */}
<div className="pv-streak-badge" style={{ position: 'absolute', top: -34, right: 18, display: 'flex', alignItems: 'center', gap: 6, padding: '6px 12px', borderRadius: 99, background: '#171512', border: `1px solid ${accent}55`, boxShadow: '0 8px 20px -8px rgba(0,0,0,0.6)' }}>
<span className="pv-live-dot" style={{ width: 6, height: 6, borderRadius: '50%', background: accent, display: 'block' }} />
<span style={{ fontSize: 11, fontWeight: 800, color: accent, fontFamily: 'Sora, sans-serif' }}>Live campaign</span>
</div>
{/* Coupon-style offer card with perforated divider */}
<div style={{ borderRadius: 16, background: `linear-gradient(135deg, ${accent} 0%, #c0392b 100%)`, position: 'relative', overflow: 'hidden', display: 'flex' }}>
<div className="pv-sheen" />
<div style={{ flex: 1, padding: '16px 18px' }}>
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.6)', fontFamily: 'Sora, sans-serif' }}>Smart Offer</div>
<div style={{ fontSize: 30, fontWeight: 900, color: '#fff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.02em', marginTop: 2 }}>15% OFF</div>
<div style={{ fontSize: 12, color: 'rgba(255,255,255,0.85)', fontFamily: 'Sora, sans-serif', marginTop: 2 }}>Targeted · high-intent shoppers</div>
</div>
{/* Perforated tear-off edge */}
<div style={{ position: 'relative', width: 0, borderLeft: '2px dashed rgba(0,0,0,0.25)' }}>
<span style={{ position: 'absolute', top: -8, left: -7, width: 14, height: 14, borderRadius: '50%', background: '#171512' }} />
<span style={{ position: 'absolute', bottom: -8, left: -7, width: 14, height: 14, borderRadius: '50%', background: '#171512' }} />
</div>
<div style={{ width: 64, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<span style={{ fontSize: 11, fontWeight: 800, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', writingMode: 'vertical-rl', letterSpacing: '0.1em' }}>DYSCOUNT</span>
</div>
</div>
<div style={{ padding: '14px 16px', borderRadius: 14, background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 8 }}>
<span style={{ fontSize: 12, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif' }}>Conversion uplift</span>
<span style={{ fontSize: 13, fontWeight: 800, color: accent, fontFamily: 'Sora, sans-serif' }}>+41%</span>
<span className="pv-uplift-pulse" style={{ fontSize: 13, fontWeight: 800, color: accent, fontFamily: 'Sora, sans-serif' }}>+41%</span>
</div>
<div style={{ height: 6, borderRadius: 3, background: 'rgba(255,255,255,0.08)', overflow: 'hidden' }}>
<div style={{ height: '100%', width: '72%', borderRadius: 3, background: accent }} />
<div className="pv-progress-fill" style={{ height: '100%', width: '72%', borderRadius: 3, background: accent }} />
</div>
<div style={{ fontSize: 11, color: 'rgba(255,255,255,0.35)', fontFamily: 'Sora, sans-serif', marginTop: 10 }}>Margin protected · 0 waste</div>
</div>
{/* Mini stat grid */}
<div style={{ display: 'flex', gap: 10 }}>
{[['Margin protected', '100%'], ['Waste', '0'], ['Redemptions', '1.2k']].map(([k, v]) => (
<div key={k} style={{ flex: 1, padding: '10px 12px', borderRadius: 12, background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.08)' }}>
<div style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif' }}>{k}</div>
<div style={{ fontSize: 14, fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif', marginTop: 2 }}>{v}</div>
</div>
))}
</div>
</div>
);
}
// 4 — Loyaly App: rewards card
const REDEMPTIONS: [string, string, string][] = [
['Starbucks', '+120', '#1e6b52'],
['Nike Store', '+85', '#374151'],
];
return (
<div style={{ width: '80%', borderRadius: 22, padding: 22, background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.1)', display: 'flex', flexDirection: 'column', gap: 16 }}>
<div style={{ width: '82%', borderRadius: 24, padding: 22, background: 'linear-gradient(165deg, rgba(255,255,255,0.05) 0%, rgba(255,255,255,0.02) 100%)', border: '1px solid rgba(255,255,255,0.1)', boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.06)', position: 'relative', display: 'flex', flexDirection: 'column', gap: 18 }}>
{/* Streak badge, floating on the card edge */}
<div className="pv-streak-badge" style={{ position: 'absolute', top: -14, right: 18, display: 'flex', alignItems: 'center', gap: 6, padding: '6px 12px', borderRadius: 99, background: '#171512', border: `1px solid ${accent}55`, boxShadow: '0 8px 20px -8px rgba(0,0,0,0.6)' }}>
<span style={{ fontSize: 12 }}>🔥</span>
<span style={{ fontSize: 11, fontWeight: 800, color: accent, fontFamily: 'Sora, sans-serif' }}>6-day streak</span>
</div>
{/* Member header */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontSize: 12, fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif' }}>Loyaly </span>
<span style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif' }}>GOLD MEMBER</span>
</div>
<div style={{ borderRadius: 16, padding: '18px 20px', background: `linear-gradient(135deg, ${accent} 0%, #e09820 100%)` }}>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.6)', fontFamily: 'Sora, sans-serif' }}>Reward Points</div>
<div style={{ fontSize: 32, fontWeight: 900, color: '#111', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', marginTop: 2 }}>284</div>
</div>
{[['Starbucks', '+120'], ['Nike Store', '+85']].map(([s, p]) => (
<div key={s} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '8px 12px', borderRadius: 10, background: 'rgba(255,255,255,0.04)' }}>
<span style={{ fontSize: 12, color: 'rgba(255,255,255,0.7)', fontFamily: 'Sora, sans-serif' }}>{s}</span>
<span style={{ fontSize: 12, fontWeight: 700, color: '#4ade80', fontFamily: 'Sora, sans-serif' }}>{p}</span>
<div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
<span style={{
width: 34, height: 34, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center',
background: `linear-gradient(135deg, ${accent} 0%, #e09820 100%)`, color: '#111', fontWeight: 800, fontSize: 13, fontFamily: 'Sora, sans-serif',
}}>A</span>
<div>
<div style={{ fontSize: 13, fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif', lineHeight: 1.1 }}>Loyaly</div>
<div style={{ fontSize: 10, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif' }}>Member since 2024</div>
</div>
</div>
))}
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 10, fontWeight: 800, letterSpacing: '0.06em', color: accent, background: `${accent}1a`, border: `1px solid ${accent}40`, padding: '5px 10px', borderRadius: 99, fontFamily: 'Sora, sans-serif' }}>
GOLD
</span>
</div>
{/* Points card with progress to next tier */}
<div style={{ borderRadius: 18, padding: '18px 20px', background: `linear-gradient(135deg, ${accent} 0%, #e09820 100%)`, position: 'relative', overflow: 'hidden' }}>
<div className="pv-sheen" />
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<div>
<div style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.6)', fontFamily: 'Sora, sans-serif' }}>Reward Points</div>
<div style={{ fontSize: 32, fontWeight: 900, color: '#111', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', marginTop: 2 }}>284</div>
</div>
<span style={{ width: 34, height: 34, borderRadius: '50%', background: 'rgba(255,255,255,0.35)', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: 16 }}>🪙</span>
</div>
<div style={{ marginTop: 12 }}>
<div style={{ height: 5, borderRadius: 3, background: 'rgba(0,0,0,0.15)', overflow: 'hidden' }}>
<div className="pv-progress-fill" style={{ height: '100%', width: '68%', borderRadius: 3, background: '#111' }} />
</div>
<div style={{ fontSize: 10, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', marginTop: 6, fontWeight: 600 }}>116 pts to Platinum</div>
</div>
</div>
{/* Recent redemptions */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
<span style={{ fontSize: 10, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(255,255,255,0.35)', fontFamily: 'Sora, sans-serif' }}>Recently redeemed</span>
{REDEMPTIONS.map(([s, p, dot], i) => (
<div key={s} className="pv-row" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', padding: '9px 12px', borderRadius: 12, background: 'rgba(255,255,255,0.04)', border: '1px solid rgba(255,255,255,0.06)', animationDelay: `${i * 0.9}s` }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
<span style={{ width: 8, height: 8, borderRadius: '50%', background: dot, flexShrink: 0 }} />
<span style={{ fontSize: 12, color: 'rgba(255,255,255,0.75)', fontFamily: 'Sora, sans-serif', fontWeight: 600 }}>{s}</span>
</div>
<span style={{ fontSize: 12, fontWeight: 700, color: '#4ade80', fontFamily: 'Sora, sans-serif' }}>{p}</span>
</div>
))}
</div>
</div>
);
}
@@ -405,10 +495,12 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
{/* Heading (fixed within the pinned viewport) */}
<div className="products-heading">
<div style={{ maxWidth: 480 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 14 }}>
<span style={{ width: 32, height: 2, background: '#f4be28', display: 'block', borderRadius: 2 }} />
<span style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#C49600', fontFamily: 'Sora, sans-serif' }}>Platform</span>
<div style={{ maxWidth: 520 }}>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 9, marginBottom: 18, padding: '6px 16px 6px 8px', borderRadius: 100, background: '#ffffff', border: '1px solid rgba(244,190,40,0.3)', boxShadow: '0 1px 2px rgba(0,0,0,0.03), 0 12px 30px -18px rgba(23,21,18,0.2)' }}>
<span style={{ width: 24, height: 24, borderRadius: '50%', display: 'flex', alignItems: 'center', justifyContent: 'center', background: 'rgba(244,190,40,0.15)' }}>
<img src="/images/loyalyai-logo.png" alt="" width={15} height={15} style={{ display: 'block', objectFit: 'contain' }} />
</span>
<span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '0.18em', textTransform: 'uppercase', color: '#8a5a14', fontFamily: 'Sora, sans-serif' }}>Platform</span>
</div>
<h2 style={{ margin: 0, fontSize: 'clamp(1.9rem, 3.6vw, 3.2rem)', fontWeight: 800, lineHeight: 0.94, letterSpacing: '-0.04em', color: '#111111', fontFamily: 'Sora, sans-serif' }}>
Four products.<br />
@@ -416,15 +508,13 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
</h2>
</div>
<div className="products-heading-hint" style={{ maxWidth: 280, textAlign: 'right' }}>
<p style={{ margin: '0 0 14px', fontSize: 'clamp(0.85rem, 1vw, 1rem)', lineHeight: 1.6, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif' }}>
<div className="products-heading-hint" style={{ maxWidth: 300, textAlign: 'right', borderRight: '2px solid rgba(244,190,40,0.3)', paddingRight: 'clamp(18px, 2.2vw, 28px)' }}>
<p style={{ margin: '0 0 16px', fontSize: 'clamp(0.85rem, 1vw, 1rem)', lineHeight: 1.6, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif' }}>
Powering every stage of the retail journey with intelligent AI solutions.
</p>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 11, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', color: 'rgba(0,0,0,0.32)', fontFamily: 'Sora, sans-serif' }}>
Scroll to explore
<svg viewBox="0 0 24 16" fill="none" width="22" height="14">
<path d="M2 8h18M14 3l5 5-5 5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: 10 }}>
<span style={{ fontFamily: 'Sora, sans-serif', fontWeight: 600, fontSize: '0.85rem', color: '#8a8072', textTransform: 'uppercase', letterSpacing: '0.08em' }}>Built as one connected platform</span>
<span style={{ fontFamily: 'Sora, sans-serif', fontWeight: 800, fontSize: '1.6rem', color: '#f4be28', letterSpacing: '-0.02em' }}>04</span>
</div>
</div>
</div>
@@ -448,6 +538,72 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
<style>{`
@keyframes prodShimmer { 0%{background-position:200% center} 100%{background-position:-200% center} }
/* ── Product-visual loop animations ── */
.pv-cell { animation: pvCellPulse 3.6s ease-in-out infinite; }
@keyframes pvCellPulse {
0%, 100% { opacity: 0.55; transform: scale(1); }
50% { opacity: 1; transform: scale(1.06); }
}
.pv-live-dot { animation: pvLiveDot 1.8s ease-in-out infinite; }
@keyframes pvLiveDot {
0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(74,222,128,0.5); }
50% { opacity: 0.6; box-shadow: 0 0 0 4px rgba(74,222,128,0); }
}
.pv-chart-line {
stroke-dasharray: 100;
stroke-dashoffset: 100;
animation: pvDrawLine 3s ease-out infinite;
}
@keyframes pvDrawLine {
0% { stroke-dashoffset: 100; }
55%, 100% { stroke-dashoffset: 0; }
}
.pv-chart-pulse-ring {
transform-origin: center;
transform-box: fill-box;
animation: pvRingPulse 2.4s ease-out infinite;
}
@keyframes pvRingPulse {
0% { opacity: 0.9; transform: scale(1); }
100% { opacity: 0; transform: scale(2.6); }
}
.pv-uplift-pulse { display: inline-block; animation: pvUpliftPulse 2.6s ease-in-out infinite; }
@keyframes pvUpliftPulse {
0%, 100% { opacity: 0.75; }
50% { opacity: 1; }
}
.pv-progress-fill { position: relative; animation: pvProgressGrow 3.2s ease-in-out infinite; transform-origin: left center; }
@keyframes pvProgressGrow {
0%, 100% { transform: scaleX(0.94); }
50% { transform: scaleX(1); }
}
.pv-sheen {
position: absolute; inset: 0;
background: linear-gradient(115deg, transparent 30%, rgba(255,255,255,0.35) 48%, transparent 66%);
background-size: 200% 100%;
animation: pvSheenSweep 4.5s ease-in-out infinite;
pointer-events: none;
}
@keyframes pvSheenSweep {
0% { background-position: 160% 0; }
60%, 100% { background-position: -60% 0; }
}
.pv-row { animation: pvRowGlow 3.6s ease-in-out infinite; }
@keyframes pvRowGlow {
0%, 100% { background: rgba(255,255,255,0.04); }
50% { background: rgba(74,222,128,0.08); }
}
.pv-streak-badge { animation: pvStreakFloat 2.8s ease-in-out infinite; }
@keyframes pvStreakFloat {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-3px); }
}
@media (prefers-reduced-motion: reduce) {
.pv-cell, .pv-live-dot, .pv-chart-line, .pv-chart-pulse-ring, .pv-uplift-pulse, .pv-progress-fill, .pv-sheen, .pv-row, .pv-streak-badge {
animation: none;
}
}
.products-section-root {
--pt: clamp(230px, 28vh, 310px);
--pb: clamp(64px, 8vh, 96px);