feat: implement solutions page, use cases, and update landing page sections including MagicBento, ScrollStack, and interactive components

This commit is contained in:
R-Bharathraj
2026-07-04 17:28:38 +05:30
parent abcefbb9c9
commit 727a6bcb62
52 changed files with 3061 additions and 835 deletions

View File

@@ -1,6 +1,10 @@
'use client';
import React, { useEffect, useRef, useState } from 'react';
import { gsap } from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
const feed = [
{ store: 'Daily Login', pts: +50, time: '2m ago', type: 'earn' },
@@ -16,6 +20,57 @@ export default function EarnRewardsSection() {
const [pts, setPts] = useState(0);
const panelRef = useRef<HTMLDivElement>(null);
const rafRef = useRef<number>(0);
const rowRefs = useRef<(HTMLDivElement | null)[]>([]);
const activeRowRef = useRef(0);
// Move the highlighted feed card as the section scrolls through the viewport
useEffect(() => {
if (typeof window === 'undefined') return;
const el = panelRef.current;
if (!el) return;
const applyRow = (idx: number) => {
if (idx === activeRowRef.current) return;
activeRowRef.current = idx;
rowRefs.current.forEach((row, i) => {
if (!row) return;
const active = i === idx;
row.style.background = active ? '#16a34a' : 'rgba(23,21,18,0.55)';
row.style.borderColor = active ? '#16a34a' : 'rgba(255,255,255,0.14)';
row.style.transform = active ? 'translateX(10px) scale(1.06)' : 'translateX(0) scale(1)';
row.style.boxShadow = active ? '0 16px 40px -12px rgba(22,163,74,0.5)' : 'none';
row.style.zIndex = active ? '2' : '1';
// Flip inner text/icon colors for contrast per background
const title = row.querySelector<HTMLElement>('.feed-title');
if (title) title.style.color = '#ffffff';
const time = row.querySelector<HTMLElement>('.feed-time');
if (time) time.style.color = active ? 'rgba(255,255,255,0.75)' : 'rgba(255,255,255,0.55)';
const icon = row.querySelector<HTMLElement>('.feed-icon');
if (icon) {
icon.style.background = active ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.1)';
icon.style.borderColor = active ? 'rgba(255,255,255,0.5)' : 'rgba(74,222,128,0.45)';
icon.style.color = active ? '#ffffff' : '#4ade80';
}
const points = row.querySelector<HTMLElement>('.feed-pts');
if (points) {
points.style.color = active ? '#ffffff' : '#4ade80';
points.style.background = active ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.12)';
}
});
};
const st = ScrollTrigger.create({
trigger: el,
start: 'top 65%',
end: 'bottom 60%',
onUpdate: (self) => {
applyRow(Math.min(feed.length - 1, Math.floor(self.progress * feed.length)));
},
});
return () => st.kill();
}, []);
useEffect(() => {
const el = panelRef.current;
@@ -48,7 +103,7 @@ export default function EarnRewardsSection() {
background: '#f0fdf4', border: '1.5px solid #16a34a', borderRadius: 100, padding: '7px 16px',
}}>
<span style={{ width: 7, height: 7, borderRadius: '50%', background: '#16a34a', flexShrink: 0, animation: 'fpPulse 1.5s infinite' }} />
<span style={{ fontSize: 11, fontWeight: 700, color: '#16a34a', fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Live points balance</span>
<span style={{ fontSize: 11, fontWeight: 700, color: '#16a34a', fontFamily: 'Sora, sans-serif', letterSpacing: '0.1em', textTransform: 'uppercase' }}>Live LYT balance</span>
</div>
{/* Giant gold number */}
@@ -70,7 +125,7 @@ export default function EarnRewardsSection() {
<span style={{ fontSize: 13, color: '#4f463a', fontFamily: 'Sora, sans-serif' }}>redeemable value</span>
</div>
<div style={{ marginBottom: 16 }}><span className="fp-kicker fp-rise">Earn · 03</span></div>
<div style={{ marginBottom: 16 }}><span className="fp-kicker fp-rise">Earn<span className="fp-kicker-num">03</span></span></div>
<h2 style={{
fontSize: 'clamp(28px, 3.4vw, 48px)', fontWeight: 800, lineHeight: 1.08,
@@ -84,7 +139,7 @@ export default function EarnRewardsSection() {
<p className="fp-rise" style={{
fontSize: 15, lineHeight: 1.8, color: '#4f463a', fontFamily: 'Sora, sans-serif', maxWidth: 380, margin: 0,
}}>
Earn Loyaly Coins through daily app login, completing 5,000-step walking challenges, playing Maze, Spin Wheel, Scratch Card, and Match Master games, or visiting Selfie Booths at partner malls. Every activity puts more coins in your wallet.
Earn LYT (Loyaly Tokens) through daily app login, completing 5,000-step walking challenges, playing Maze, Spin Wheel, Scratch Card, and Match Master games, or visiting Selfie Booths at partner malls. Every activity adds more LYT to your balance.
</p>
</div>
@@ -107,31 +162,39 @@ export default function EarnRewardsSection() {
{/* Feed rows */}
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
{feed.map((tx, i) => (
<div key={i} style={{
<div key={i} ref={el => { rowRefs.current[i] = el; }} style={{
position: 'relative', zIndex: i === 0 ? 2 : 1,
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
padding: '12px 14px', borderRadius: 12,
background: i === 0 ? '#f0fdf4' : '#fafafa',
border: `1.5px solid ${i === 0 ? '#16a34a' : 'rgba(23,21,18,0.07)'}`,
background: i === 0 ? '#16a34a' : 'rgba(23,21,18,0.55)',
border: `1.5px solid ${i === 0 ? '#16a34a' : 'rgba(255,255,255,0.14)'}`,
backdropFilter: 'blur(16px)',
WebkitBackdropFilter: 'blur(16px)',
transform: i === 0 ? 'translateX(10px) scale(1.06)' : 'translateX(0) scale(1)',
boxShadow: i === 0 ? '0 16px 40px -12px rgba(22,163,74,0.5)' : 'none',
transition: 'background 0.35s ease, border-color 0.35s ease, transform 0.35s cubic-bezier(0.34,1.56,0.64,1), box-shadow 0.35s ease',
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
<div className="feed-icon" style={{
width: 36, height: 36, borderRadius: 10, flexShrink: 0,
background: tx.type === 'earn' ? '#f0fdf4' : '#fef2f2',
border: `1.5px solid ${tx.type === 'earn' ? '#16a34a' : '#ef4444'}`,
background: i === 0 ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.1)',
border: `1.5px solid ${i === 0 ? 'rgba(255,255,255,0.5)' : 'rgba(74,222,128,0.45)'}`,
display: 'flex', alignItems: 'center', justifyContent: 'center',
fontSize: 15, fontWeight: 900,
color: tx.type === 'earn' ? '#16a34a' : '#ef4444',
color: i === 0 ? '#ffffff' : '#4ade80',
transition: 'background 0.35s ease, border-color 0.35s ease, color 0.35s ease',
}}>{tx.type === 'earn' ? '↑' : '↓'}</div>
<div>
<div style={{ fontSize: 13, fontWeight: 700, color: '#171512', fontFamily: 'Sora, sans-serif' }}>{tx.store}</div>
<div style={{ fontSize: 11, color: 'rgba(23,21,18,0.45)', fontFamily: 'Sora, sans-serif', marginTop: 1 }}>{tx.time}</div>
<div className="feed-title" style={{ fontSize: 13, fontWeight: 700, color: '#ffffff', fontFamily: 'Sora, sans-serif', transition: 'color 0.35s ease' }}>{tx.store}</div>
<div className="feed-time" style={{ fontSize: 11, color: i === 0 ? 'rgba(255,255,255,0.75)' : 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', marginTop: 1, transition: 'color 0.35s ease' }}>{tx.time}</div>
</div>
</div>
<span style={{
<span className="feed-pts" style={{
fontSize: 15, fontWeight: 900, fontFamily: 'Sora, sans-serif',
color: tx.type === 'earn' ? '#16a34a' : '#ef4444', letterSpacing: '-0.02em',
background: tx.type === 'earn' ? '#f0fdf4' : '#fef2f2',
color: i === 0 ? '#ffffff' : '#4ade80', letterSpacing: '-0.02em',
background: i === 0 ? 'rgba(255,255,255,0.15)' : 'rgba(74,222,128,0.12)',
padding: '4px 10px', borderRadius: 8,
transition: 'color 0.35s ease, background 0.35s ease',
}}>
{tx.pts > 0 ? '+' : ''}{tx.pts}
</span>
@@ -144,7 +207,7 @@ export default function EarnRewardsSection() {
marginTop: 16, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10,
}}>
{[
{ label: 'Earned this month', val: '660 pts', color: '#16a34a', bg: '#f0fdf4', border: '#16a34a' },
{ label: 'Earned this month', val: '660 LYT', color: '#16a34a', bg: '#f0fdf4', border: '#16a34a' },
{ label: 'Redeemable value', val: '₹660', color: '#f4be28', bg: '#fff7e0', border: '#f4be28' },
].map((s, i) => (
<div key={i} style={{ padding: '14px 16px', borderRadius: 14, background: s.bg, border: `1.5px solid ${s.border}` }}>