feat: update website layout, components, and assets

This commit is contained in:
R-Bharathraj
2026-07-09 18:12:02 +05:30
parent a5c5ec033a
commit cba47aea2b
52 changed files with 1979 additions and 1221 deletions

View File

@@ -450,7 +450,24 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
// Desktop / tablet: pinned horizontal scroll
mm.add('(min-width: 901px)', () => {
const cards = gsap.utils.toArray<HTMLElement>('.products-card', track);
const distance = () => Math.max(0, track.scrollWidth - section.clientWidth);
const steps = Math.max(1, PRODUCTS.length - 1);
// Depth cue: the card nearest the viewport centre sits at full scale/
// opacity, neighbours ease back slightly — makes the ride feel guided
// rather than a flat strip sliding past, and the eased response
// absorbs small scrub jitter instead of showing it as a raw jump.
const updateDepth = () => {
const centerX = section.clientWidth / 2;
cards.forEach((card) => {
const trackX = Number(gsap.getProperty(track, 'x', 'px'));
const cardCenter = card.offsetLeft + card.offsetWidth / 2 + trackX;
const dist = Math.abs(cardCenter - centerX);
const norm = gsap.utils.clamp(0, 1, dist / (section.clientWidth * 0.62));
gsap.set(card, { scale: 1 - norm * 0.07, opacity: 1 - norm * 0.32 });
});
};
const tween = gsap.to(track, {
x: () => -distance(),
@@ -460,30 +477,42 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
start: 'top top',
end: () => `+=${distance()}`,
pin: true,
pinType: 'transform',
pinSpacing: true,
anticipatePin: 1,
scrub: 1,
scrub: 0.4,
invalidateOnRefresh: true,
refreshPriority: 5,
snap: {
snapTo: (value) => Math.round(value * steps) / steps,
duration: { min: 0.2, max: 0.5 },
ease: 'power2.inOut',
},
onUpdate: (self) => {
if (progressRef.current) progressRef.current.style.transform = `scaleX(${self.progress})`;
if (counterRef.current) {
const idx = Math.min(PRODUCTS.length, Math.max(1, Math.round(self.progress * (PRODUCTS.length - 1)) + 1));
const idx = Math.min(PRODUCTS.length, Math.max(1, Math.round(self.progress * steps) + 1));
counterRef.current.textContent = `0${idx}`;
}
updateDepth();
},
},
});
updateDepth();
ScrollTrigger.refresh();
return () => { tween.scrollTrigger?.kill(); tween.kill(); gsap.set(track, { clearProps: 'transform' }); };
return () => {
tween.scrollTrigger?.kill();
tween.kill();
gsap.set(track, { clearProps: 'transform' });
gsap.set(cards, { clearProps: 'scale,opacity' });
};
});
return () => mm.revert();
}, [isLoaded]);
return (
<div className="products-section-wrapper">
<section
ref={sectionRef}
id="products-section"
@@ -496,24 +525,26 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
{/* Heading (fixed within the pinned viewport) */}
<div className="products-heading">
<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)' }}>
<div style={{ display: 'inline-flex', alignItems: 'center', gap: 9, marginBottom: 20, 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>
<span className="pv-live-dot" style={{ width: 6, height: 6, borderRadius: '50%', background: '#4ade80', display: 'block', marginLeft: 2 }} />
</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 />
<span style={{ background: 'linear-gradient(105deg, #f4be28 0%, #FF9800 100%)', backgroundSize: '200% auto', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', animation: 'prodShimmer 3s linear infinite' }}>One ecosystem.</span>
<h2 style={{ margin: 0, padding: '0 0 clamp(10px, 1.6vw, 22px)', fontSize: 'clamp(1.6rem, 3vw, 2.7rem)', fontWeight: 800, lineHeight: 0.94, letterSpacing: '-0.04em', color: '#111111', fontFamily: 'Sora, sans-serif', whiteSpace: 'nowrap' }}>
Four agents.{' '}
<span style={{ background: 'linear-gradient(105deg, #f4be28 0%, #FF9800 100%)', backgroundSize: '200% auto', WebkitBackgroundClip: 'text', WebkitTextFillColor: 'transparent', animation: 'prodShimmer 3s linear infinite' }}>One AI.</span>
</h2>
</div>
<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' }}>
<div className="products-heading-hint" style={{ maxWidth: 320, textAlign: 'right', borderRight: '2px solid rgba(244,190,40,0.3)', paddingRight: 'clamp(18px, 2.2vw, 28px)' }}>
<p style={{ margin: '0 0 18px', 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: '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>
<div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'flex-end', gap: 10 }}>
<span style={{ fontFamily: 'Sora, sans-serif', fontWeight: 600, fontSize: '0.78rem', color: '#8a8072', textTransform: 'uppercase', letterSpacing: '0.08em' }}>Unified into one platform</span>
<span style={{ fontFamily: 'Sora, sans-serif', fontWeight: 800, fontSize: '1.6rem', color: '#f4be28', letterSpacing: '-0.02em' }}>04</span>
</div>
</div>
@@ -604,11 +635,17 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
}
}
/* Margin lives on this non-pinned wrapper, not on the pinned section
itself — GSAP's ScrollTrigger pin-spacer can miscalculate the
reserved space when the pinned element carries its own margin,
which shows up as a jump/shake right as the pin engages. */
.products-section-wrapper {
margin: 20px;
}
.products-section-root {
--pt: clamp(230px, 28vh, 310px);
--pb: clamp(64px, 8vh, 96px);
width: calc(100% - 40px);
margin: 20px 20px;
width: 100%;
border-radius: 32px;
background: #FAFAFA;
height: 100vh;
@@ -681,5 +718,6 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
}
`}</style>
</section>
</div>
);
}