Cleanup old PHP source files and update Next.js pages and components
This commit is contained in:
@@ -60,17 +60,8 @@ export default function AboutUsPage() {
|
||||
|
||||
<div style={{ position: 'relative', zIndex: 2, width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
|
||||
<div style={{ maxWidth: 760 }}>
|
||||
<div data-gsap="badge" style={{
|
||||
fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase',
|
||||
color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 32,
|
||||
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||
padding: '8px 18px', borderRadius: 100,
|
||||
background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.22)',
|
||||
}}>
|
||||
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#f4be28', display: 'block' }} />Meet the Crew
|
||||
</div>
|
||||
<h1 data-gsap="heading" style={{
|
||||
fontSize: 'clamp(52px, 9vw, 140px)', fontWeight: 900,
|
||||
fontSize: 'clamp(52px, 9vw, 140px)', fontWeight: 800,
|
||||
letterSpacing: '-0.06em', lineHeight: 0.88,
|
||||
color: '#ffffff', margin: '0 0 36px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
@@ -86,10 +77,10 @@ export default function AboutUsPage() {
|
||||
|
||||
{/* ── OUR STORY ── */}
|
||||
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8vw', alignItems: 'start' }}>
|
||||
<div className="au-story-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8vw', alignItems: 'start' }}>
|
||||
<Reveal>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 24 }}>Our Story</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 4.5vw, 68px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 32px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 4.5vw, 68px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 32px', fontFamily: 'Sora, sans-serif' }}>
|
||||
We started with<br />a simple idea.
|
||||
</h2>
|
||||
</Reveal>
|
||||
@@ -106,7 +97,7 @@ export default function AboutUsPage() {
|
||||
|
||||
{/* ── MISSION & VISION ── */}
|
||||
<section style={{ padding: '0 8% 20px', background: '#ffffff' }}>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20, alignItems: 'stretch' }}>
|
||||
<div className="au-mv-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 20, alignItems: 'stretch' }}>
|
||||
{[
|
||||
{
|
||||
label: 'Mission', bg: '#07070d', textColor: '#fff', accent: '#f4be28',
|
||||
@@ -138,14 +129,14 @@ export default function AboutUsPage() {
|
||||
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||
<Reveal>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>How We Work</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 80px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 80px', fontFamily: 'Sora, sans-serif' }}>
|
||||
What we believe.
|
||||
</h2>
|
||||
</Reveal>
|
||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
{VALUES.map((v, i) => (
|
||||
<Reveal key={i} delay={i * 0.08}>
|
||||
<div style={{
|
||||
<div className="au-value-row" style={{
|
||||
display: 'grid', gridTemplateColumns: '80px 1fr 1fr',
|
||||
gap: '4vw', alignItems: 'center',
|
||||
padding: '40px 0', borderBottom: '1px solid rgba(0,0,0,0.07)',
|
||||
@@ -182,6 +173,15 @@ export default function AboutUsPage() {
|
||||
}}>Join Us →</Link>
|
||||
</Reveal>
|
||||
</section>
|
||||
|
||||
<style>{`
|
||||
@media (max-width: 860px) {
|
||||
.au-story-grid { grid-template-columns: 1fr !important; gap: 36px !important; }
|
||||
.au-mv-grid { grid-template-columns: 1fr !important; }
|
||||
.au-mv-grid > div > div { padding: 48px 32px !important; min-height: 280px !important; }
|
||||
.au-value-row { grid-template-columns: 1fr !important; gap: 10px !important; padding: 28px 0 !important; }
|
||||
}
|
||||
`}</style>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@@ -211,7 +211,7 @@ function TeamCarousel() {
|
||||
{/* Header */}
|
||||
<div style={{ textAlign: 'center', marginBottom: 72, position: 'relative', zIndex: 2, padding: '0 8%' }}>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 16 }}>The Team</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 16px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 16px', fontFamily: 'Sora, sans-serif' }}>
|
||||
Builders. Thinkers.<br />Retail obsessives.
|
||||
</h2>
|
||||
<p style={{ fontSize: 15, color: 'rgba(255,255,255,0.35)', fontFamily: 'Sora, sans-serif', margin: 0 }}>
|
||||
|
||||
@@ -82,15 +82,7 @@ export default function BlogMainPage() {
|
||||
|
||||
<div style={{ position: 'relative', zIndex: 2, width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
|
||||
<div style={{ maxWidth: 720 }}>
|
||||
<div style={{
|
||||
fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 28,
|
||||
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||
padding: '8px 18px', borderRadius: 100,
|
||||
background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.22)',
|
||||
}}>
|
||||
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#f4be28', display: 'block' }} />Market Insights
|
||||
</div>
|
||||
<h1 data-gsap="heading" style={{ fontSize: 'clamp(48px, 8vw, 120px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.9, color: '#fff', margin: '0 0 28px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h1 data-gsap="heading" style={{ fontSize: 'clamp(48px, 8vw, 120px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.9, color: '#fff', margin: '0 0 28px', fontFamily: 'Sora, sans-serif' }}>
|
||||
Insights for the<br />
|
||||
<span style={{ color: '#f4be28' }}>Future of Retail.</span>
|
||||
</h1>
|
||||
@@ -121,7 +113,7 @@ export default function BlogMainPage() {
|
||||
{featured && (
|
||||
<section style={{ padding: '64px 8%', background: '#fff' }}>
|
||||
<Reveal>
|
||||
<div style={{
|
||||
<div className="bm-featured-grid" style={{
|
||||
display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6vw',
|
||||
padding: '64px', background: '#07070d', borderRadius: 24,
|
||||
alignItems: 'center', cursor: 'pointer',
|
||||
@@ -134,7 +126,7 @@ export default function BlogMainPage() {
|
||||
<span style={{ fontSize: 12, color: 'rgba(255,255,255,0.3)', fontFamily: 'Sora, sans-serif' }}>{featured.readTime} read</span>
|
||||
<span style={{ fontSize: 11, fontWeight: 700, color: '#f4be28', fontFamily: 'Sora, sans-serif', background: 'rgba(244,190,40,0.08)', padding: '4px 10px', borderRadius: 100 }}>Featured</span>
|
||||
</div>
|
||||
<h2 style={{ fontSize: 'clamp(28px, 3.5vw, 52px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 1.05, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif' }}>{featured.title}</h2>
|
||||
<h2 style={{ fontSize: 'clamp(28px, 3.5vw, 52px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 1.05, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif' }}>{featured.title}</h2>
|
||||
<p style={{ fontSize: 16, lineHeight: 1.75, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif', margin: '0 0 36px' }}>{featured.excerpt}</p>
|
||||
<Link href="/blog_single_page" style={{ display: 'inline-flex', alignItems: 'center', gap: 8, fontSize: 15, fontWeight: 700, color: '#f4be28', fontFamily: 'Sora, sans-serif', textDecoration: 'none', borderBottom: '1px solid rgba(244,190,40,0.3)', paddingBottom: 4 }}>
|
||||
Read Article →
|
||||
@@ -201,7 +193,7 @@ export default function BlogMainPage() {
|
||||
<section style={{ padding: '120px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, textAlign: 'center', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)', width: 600, height: 600, borderRadius: '50%', background: 'radial-gradient(circle,rgba(244,190,40,0.06) 0%,transparent 70%)', pointerEvents: 'none' }} />
|
||||
<Reveal>
|
||||
<h2 style={{ fontSize: 'clamp(32px, 5vw, 72px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2 }}>
|
||||
<h2 style={{ fontSize: 'clamp(32px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2 }}>
|
||||
Stay ahead of retail.
|
||||
</h2>
|
||||
<p style={{ fontSize: 16, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif', margin: '0 auto 44px', maxWidth: 400, position: 'relative', zIndex: 2 }}>
|
||||
@@ -222,6 +214,12 @@ export default function BlogMainPage() {
|
||||
</div>
|
||||
</Reveal>
|
||||
</section>
|
||||
|
||||
<style>{`
|
||||
@media (max-width: 860px) {
|
||||
.bm-featured-grid { grid-template-columns: 1fr !important; padding: 36px 28px !important; gap: 36px !important; }
|
||||
}
|
||||
`}</style>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function BlogSinglePage() {
|
||||
<span style={{ color: 'rgba(255,255,255,0.2)' }}>/</span>
|
||||
<span style={{ fontSize: 11, fontWeight: 700, color: '#f4be28', background: 'rgba(244,190,40,0.1)', border: '1px solid rgba(244,190,40,0.2)', padding: '3px 10px', borderRadius: 100, fontFamily: 'Sora, sans-serif', textTransform: 'uppercase', letterSpacing: '0.08em' }}>{ARTICLE.tag}</span>
|
||||
</div>
|
||||
<h1 style={{ fontSize: 'clamp(36px, 6vw, 88px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#fff', margin: '0 0 28px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h1 style={{ fontSize: 'clamp(36px, 6vw, 88px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#fff', margin: '0 0 28px', fontFamily: 'Sora, sans-serif' }}>
|
||||
{ARTICLE.title}
|
||||
</h1>
|
||||
<p style={{ fontSize: 'clamp(16px, 1.5vw, 20px)', lineHeight: 1.75, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', maxWidth: 600, margin: '0 0 40px' }}>
|
||||
@@ -111,7 +111,7 @@ export default function BlogSinglePage() {
|
||||
{/* CTA */}
|
||||
<section style={{ padding: '80px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 32 }}>
|
||||
<div>
|
||||
<h2 style={{ fontSize: 'clamp(28px, 3.5vw, 52px)', fontWeight: 900, letterSpacing: '-0.04em', color: '#fff', margin: '0 0 12px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(28px, 3.5vw, 52px)', fontWeight: 800, letterSpacing: '-0.04em', color: '#fff', margin: '0 0 12px', fontFamily: 'Sora, sans-serif' }}>
|
||||
Ready to see your store's data?
|
||||
</h2>
|
||||
<p style={{ fontSize: 16, color: 'rgba(255,255,255,0.4)', fontFamily: 'Sora, sans-serif', margin: 0 }}>Book a 30-minute demo and see Behavision AI in action.</p>
|
||||
|
||||
@@ -35,17 +35,8 @@ export default function ContactsPage() {
|
||||
|
||||
<div style={{ position: 'relative', zIndex: 2, width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
|
||||
<div style={{ maxWidth: 760 }}>
|
||||
<div style={{
|
||||
fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase',
|
||||
color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 32,
|
||||
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||
padding: '8px 18px', borderRadius: 100,
|
||||
background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.22)',
|
||||
}}>
|
||||
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#f4be28', display: 'block' }} />Contact
|
||||
</div>
|
||||
<h1 data-gsap="heading" style={{
|
||||
fontSize: 'clamp(48px, 8vw, 120px)', fontWeight: 900,
|
||||
fontSize: 'clamp(48px, 8vw, 120px)', fontWeight: 800,
|
||||
letterSpacing: '-0.05em', lineHeight: 0.9,
|
||||
color: '#fff', margin: '0 0 28px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
|
||||
@@ -65,15 +65,7 @@ export default function FAQPage() {
|
||||
<div style={{ position: 'absolute', inset: 0, backgroundImage: 'linear-gradient(rgba(244,190,40,0.025) 1px,transparent 1px),linear-gradient(90deg,rgba(244,190,40,0.025) 1px,transparent 1px)', backgroundSize: '60px 60px', pointerEvents: 'none' }} />
|
||||
<div style={{ position: 'relative', zIndex: 2, width: '100%', display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
|
||||
<div style={{ maxWidth: 720 }}>
|
||||
<div style={{
|
||||
fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 28,
|
||||
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||
padding: '8px 18px', borderRadius: 100,
|
||||
background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.22)',
|
||||
}}>
|
||||
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#f4be28', display: 'block' }} />FAQ
|
||||
</div>
|
||||
<h1 data-gsap="heading" style={{ fontSize: 'clamp(48px, 8vw, 110px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.9, color: '#fff', margin: '0 0 24px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h1 data-gsap="heading" style={{ fontSize: 'clamp(48px, 8vw, 110px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.9, color: '#fff', margin: '0 0 24px', fontFamily: 'Sora, sans-serif' }}>
|
||||
Got<br />
|
||||
<span style={{ color: '#f4be28' }}>Questions?</span>
|
||||
</h1>
|
||||
@@ -93,7 +85,7 @@ export default function FAQPage() {
|
||||
{/* Category header */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 40 }}>
|
||||
<div style={{ width: 10, height: 10, borderRadius: '50%', background: section.color, boxShadow: `0 0 14px ${section.color}` }} />
|
||||
<h2 style={{ fontSize: 'clamp(24px, 3vw, 40px)', fontWeight: 900, letterSpacing: '-0.03em', color: '#0a0a0f', margin: 0, fontFamily: 'Sora, sans-serif' }}>{section.category}</h2>
|
||||
<h2 style={{ fontSize: 'clamp(24px, 3vw, 40px)', fontWeight: 800, letterSpacing: '-0.03em', color: '#0a0a0f', margin: 0, fontFamily: 'Sora, sans-serif' }}>{section.category}</h2>
|
||||
</div>
|
||||
|
||||
{/* Accordion */}
|
||||
@@ -142,7 +134,7 @@ export default function FAQPage() {
|
||||
|
||||
{/* CTA */}
|
||||
<section style={{ padding: '80px 8%', background: '#f4be28', margin: '0 20px 20px', borderRadius: 24, textAlign: 'center' }}>
|
||||
<h2 style={{ fontSize: 'clamp(28px, 4vw, 56px)', fontWeight: 900, letterSpacing: '-0.04em', color: '#111', margin: '0 0 16px', fontFamily: 'Sora, sans-serif' }}>Still have questions?</h2>
|
||||
<h2 style={{ fontSize: 'clamp(28px, 4vw, 56px)', fontWeight: 800, letterSpacing: '-0.04em', color: '#111', margin: '0 0 16px', fontFamily: 'Sora, sans-serif' }}>Still have questions?</h2>
|
||||
<p style={{ fontSize: 16, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 36px' }}>Our team is happy to help — get in touch and we'll respond within one business day.</p>
|
||||
<Link href="/contacts" style={{ display: 'inline-flex', alignItems: 'center', gap: 10, padding: '16px 40px', background: '#111', color: '#f4be28', fontSize: 15, fontWeight: 800, borderRadius: 100, textDecoration: 'none', fontFamily: 'Sora, sans-serif' }}>
|
||||
Contact Us →
|
||||
|
||||
@@ -54,19 +54,32 @@ html.lenis, html.lenis body {
|
||||
.loyaly-home .hero-heading {
|
||||
color: var(--loyaly-carbon) !important; /* Premium brand carbon (#171512) for high readability */
|
||||
font-weight: 900 !important; /* Ultra-bold / Black font weight override */
|
||||
text-shadow: 0 2px 24px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.loyaly-home .hero-gradient-text {
|
||||
background-image: linear-gradient(102deg, #f4be28 0%, #ff9500 100%) !important;
|
||||
background-image: linear-gradient(105deg, #bc7122 0%, #f4be28 45%, #c9960f 60%, #8a4f0f 100%) !important;
|
||||
background-size: 200% auto !important;
|
||||
-webkit-background-clip: text !important;
|
||||
background-clip: text !important;
|
||||
-webkit-text-fill-color: transparent !important;
|
||||
color: transparent !important;
|
||||
display: inline-block;
|
||||
animation: heroGradientLoop 7s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes heroGradientLoop {
|
||||
0% { background-position: 200% center; }
|
||||
100% { background-position: -200% center; }
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.loyaly-home .hero-gradient-text { animation: none; }
|
||||
}
|
||||
|
||||
.loyaly-home .hero-description {
|
||||
color: var(--loyaly-warm-text) !important; /* Premium brand warm charcoal (#4f463a) */
|
||||
color: var(--loyaly-carbon) !important; /* darker, higher-contrast text now that the hero image shows through clearly */
|
||||
text-shadow: 0 2px 16px rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.loyaly-home .hero-btn-primary {
|
||||
|
||||
@@ -483,10 +483,10 @@ export default function IndexPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* White gradient overlay (fades in with hero text at frame 120) */}
|
||||
{/* White shade behind the text content on the left only — right side stays fully clear (fades in with hero text at frame 120) */}
|
||||
<div
|
||||
ref={overlayRef}
|
||||
className="absolute inset-0 z-[5] pointer-events-none bg-gradient-to-r from-white/95 via-white/70 to-transparent"
|
||||
className="absolute inset-0 z-[5] pointer-events-none bg-gradient-to-r from-white/90 via-white/40 via-35% to-transparent to-55%"
|
||||
style={{ opacity: 0 }}
|
||||
/>
|
||||
|
||||
@@ -497,12 +497,6 @@ export default function IndexPage() {
|
||||
style={{ opacity: 0, transform: 'translateY(30px)' }}
|
||||
>
|
||||
<div className="w-full lg:w-[50vw] px-8 sm:px-12 md:px-16 lg:pl-20 lg:pr-8 pointer-events-auto flex flex-col justify-center py-12 md:py-16">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<span className="w-12 h-[3px] bg-[#f4be28] rounded-full" />
|
||||
<span className="text-[#f4be28] hero-subheading text-sm sm:text-base md:text-lg font-bold tracking-widest uppercase">
|
||||
AI-Powered Retail Intelligence
|
||||
</span>
|
||||
</div>
|
||||
<h1 className="text-[#171512] hero-heading text-3xl sm:text-4xl md:text-5xl lg:text-5xl xl:text-6xl font-extrabold tracking-tight leading-[1.12] mb-6 select-none">
|
||||
Turn Every <br />
|
||||
Walk-In Into a <br />
|
||||
|
||||
@@ -75,15 +75,7 @@ export default function SolutionsPage() {
|
||||
|
||||
<div style={{ width: '100%', position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
|
||||
<div style={{ maxWidth: 760 }}>
|
||||
<div data-gsap="badge" style={{
|
||||
fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 28,
|
||||
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||
padding: '8px 18px', borderRadius: 100,
|
||||
background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.22)',
|
||||
}}>
|
||||
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#f4be28', display: 'block' }} />Solutions
|
||||
</div>
|
||||
<h1 data-gsap="heading" style={{ fontSize: 'clamp(52px, 8.5vw, 130px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.88, color: '#fff', margin: '0 0 36px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h1 data-gsap="heading" style={{ fontSize: 'clamp(52px, 8.5vw, 130px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.88, color: '#fff', margin: '0 0 36px', fontFamily: 'Sora, sans-serif' }}>
|
||||
Built for<br />Modern<br />
|
||||
<span style={{ color: '#f4be28' }}>Retail Growth.</span>
|
||||
</h1>
|
||||
@@ -104,7 +96,7 @@ export default function SolutionsPage() {
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 80, flexWrap: 'wrap', gap: 24 }}>
|
||||
<div>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 16 }}>Capabilities</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: 0, fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: 0, fontFamily: 'Sora, sans-serif' }}>
|
||||
Four tools.<br />One merchant platform.
|
||||
</h2>
|
||||
</div>
|
||||
@@ -120,6 +112,7 @@ export default function SolutionsPage() {
|
||||
<div
|
||||
onMouseEnter={() => setHovered(i)}
|
||||
onMouseLeave={() => setHovered(null)}
|
||||
className="sp-cap-row"
|
||||
style={{
|
||||
display: 'grid', gridTemplateColumns: '80px 1fr 1fr auto',
|
||||
alignItems: 'center', gap: '4vw',
|
||||
@@ -166,9 +159,9 @@ export default function SolutionsPage() {
|
||||
</section>
|
||||
|
||||
{/* ── FINAL CTA ── */}
|
||||
<section style={{ padding: '140px 8%', background: '#fff8e8', margin: '0 20px 20px', borderRadius: 24, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6vw', alignItems: 'center' }}>
|
||||
<section className="sp-cta-grid" style={{ padding: '140px 8%', background: '#fff8e8', margin: '0 20px 20px', borderRadius: 24, display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '6vw', alignItems: 'center' }}>
|
||||
<RevealBlock>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 68px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 24px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 68px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 24px', fontFamily: 'Sora, sans-serif' }}>
|
||||
See every solution working together.
|
||||
</h2>
|
||||
<p style={{ fontSize: 16, color: 'rgba(0,0,0,0.5)', fontFamily: 'Sora, sans-serif', margin: '0 0 40px', lineHeight: 1.7 }}>
|
||||
@@ -193,6 +186,17 @@ export default function SolutionsPage() {
|
||||
</div>
|
||||
</RevealBlock>
|
||||
</section>
|
||||
|
||||
<style>{`
|
||||
@media (max-width: 860px) {
|
||||
.sp-cap-row { grid-template-columns: 1fr !important; gap: 14px !important; padding: 28px 0 !important; }
|
||||
.sp-cap-row > div:last-child { justify-content: flex-start !important; }
|
||||
.sp-cta-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.sp-cta-grid > div > div { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
`}</style>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -109,17 +109,8 @@ export default function ForBusinessPage() {
|
||||
|
||||
<div style={{ width: '100%', position: 'relative', zIndex: 2, display: 'flex', flexDirection: 'column', alignItems: 'center', textAlign: 'center' }}>
|
||||
<div style={{ maxWidth: 760 }}>
|
||||
<div style={{
|
||||
display: 'inline-flex', alignItems: 'center', gap: 8, marginBottom: 32,
|
||||
background: 'rgba(244,190,40,0.08)', border: '1px solid rgba(244,190,40,0.2)',
|
||||
borderRadius: 100, padding: '8px 20px',
|
||||
}}>
|
||||
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#f4be28', display: 'block' }} />
|
||||
<span style={{ fontSize: 12, fontWeight: 700, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', letterSpacing: '0.12em', textTransform: 'uppercase' }}>For Business</span>
|
||||
</div>
|
||||
|
||||
<h1 data-gsap="heading" style={{
|
||||
fontSize: 'clamp(52px, 8vw, 120px)', fontWeight: 900,
|
||||
fontSize: 'clamp(52px, 8vw, 120px)', fontWeight: 800,
|
||||
letterSpacing: '-0.05em', lineHeight: 0.9,
|
||||
color: '#ffffff', margin: '0 0 32px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
@@ -162,10 +153,10 @@ export default function ForBusinessPage() {
|
||||
<RevealBlock>
|
||||
<div style={{ maxWidth: 200, height: 2, background: '#f4be28', marginBottom: 48 }} />
|
||||
</RevealBlock>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8vw', alignItems: 'center' }}>
|
||||
<div className="uc-challenge-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8vw', alignItems: 'center' }}>
|
||||
<RevealBlock>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 24 }}>The Challenge</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 28px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 28px', fontFamily: 'Sora, sans-serif' }}>
|
||||
You're Getting<br />Visitors. But<br />
|
||||
<em style={{ fontStyle: 'normal', color: '#c9960f' }}>Missing Revenue.</em>
|
||||
</h2>
|
||||
@@ -196,7 +187,7 @@ export default function ForBusinessPage() {
|
||||
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||
<RevealBlock>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>Our Products</div>
|
||||
<h2 style={{ fontSize: 'clamp(40px, 5.5vw, 80px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#fff', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 700 }}>
|
||||
<h2 style={{ fontSize: 'clamp(40px, 5.5vw, 80px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#fff', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 700 }}>
|
||||
AI-Powered Retail Intelligence.
|
||||
</h2>
|
||||
</RevealBlock>
|
||||
@@ -216,7 +207,7 @@ export default function ForBusinessPage() {
|
||||
|
||||
{/* Product detail */}
|
||||
{PRODUCTS.map((p, i) => (
|
||||
<div key={i} style={{
|
||||
<div key={i} className="uc-product-grid" style={{
|
||||
display: activeProduct === i ? 'grid' : 'none',
|
||||
gridTemplateColumns: '1fr 1fr', gap: '6vw', alignItems: 'center',
|
||||
}}>
|
||||
@@ -227,7 +218,7 @@ export default function ForBusinessPage() {
|
||||
marginBottom: 20, background: `${p.color}15`, padding: '6px 14px', borderRadius: 100,
|
||||
border: `1px solid ${p.color}30`,
|
||||
}}>{p.tag}</div>
|
||||
<h3 style={{ fontSize: 'clamp(32px, 4vw, 60px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 24px', fontFamily: 'Sora, sans-serif' }}>{p.name}</h3>
|
||||
<h3 style={{ fontSize: 'clamp(32px, 4vw, 60px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 24px', fontFamily: 'Sora, sans-serif' }}>{p.name}</h3>
|
||||
<p style={{ fontSize: 18, lineHeight: 1.75, color: 'rgba(255,255,255,0.45)', fontFamily: 'Sora, sans-serif', margin: '0 0 40px' }}>{p.desc}</p>
|
||||
<Link href="/contacts" style={{
|
||||
display: 'inline-flex', alignItems: 'center', gap: 8,
|
||||
@@ -252,11 +243,11 @@ export default function ForBusinessPage() {
|
||||
<section ref={statsRef} style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||
<RevealBlock>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>Outcomes</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 68px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 80px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 68px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#0a0a0f', margin: '0 0 80px', fontFamily: 'Sora, sans-serif' }}>
|
||||
Real Results.<br />Not Assumptions.
|
||||
</h2>
|
||||
</RevealBlock>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 2 }}>
|
||||
<div className="uc-outcomes-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 2 }}>
|
||||
{OUTCOMES.map((o, i) => (
|
||||
<RevealBlock key={i} delay={i * 0.1}>
|
||||
<div style={{
|
||||
@@ -276,11 +267,11 @@ export default function ForBusinessPage() {
|
||||
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||
<RevealBlock>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>Who It's For</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 68px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 600 }}>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 68px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 600 }}>
|
||||
Built for every format of physical retail.
|
||||
</h2>
|
||||
</RevealBlock>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 16 }}>
|
||||
<div className="uc-who-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(4,1fr)', gap: 16 }}>
|
||||
{WHO.map((w, i) => (
|
||||
<RevealBlock key={i} delay={i * 0.1}>
|
||||
<div style={{
|
||||
@@ -301,7 +292,7 @@ export default function ForBusinessPage() {
|
||||
<section style={{ padding: '160px 8%', background: '#f4be28', margin: '0 20px 20px', borderRadius: 24, textAlign: 'center', position: 'relative', overflow: 'hidden' }}>
|
||||
<div style={{ position: 'absolute', top: '-20%', left: '50%', transform: 'translateX(-50%)', width: '120%', height: '140%', background: 'radial-gradient(ellipse,rgba(255,255,255,0.15) 0%,transparent 60%)', pointerEvents: 'none' }} />
|
||||
<RevealBlock>
|
||||
<h2 style={{ fontSize: 'clamp(44px, 7vw, 100px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#111', margin: '0 0 24px', fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2 }}>
|
||||
<h2 style={{ fontSize: 'clamp(44px, 7vw, 100px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#111', margin: '0 0 24px', fontFamily: 'Sora, sans-serif', position: 'relative', zIndex: 2 }}>
|
||||
Ready to Transform<br />Your Retail Business?
|
||||
</h2>
|
||||
<p style={{ fontSize: 18, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 auto 52px', maxWidth: 480, position: 'relative', zIndex: 2 }}>Schedule a demo and see your store's potential in 30 minutes.</p>
|
||||
@@ -315,6 +306,17 @@ export default function ForBusinessPage() {
|
||||
<style>{`
|
||||
@keyframes shimmer { 0%{background-position:200% center} 100%{background-position:-200% center} }
|
||||
@keyframes floatY { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-12px)} }
|
||||
@media (max-width: 860px) {
|
||||
.uc-challenge-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
|
||||
.uc-product-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
|
||||
.uc-outcomes-grid { grid-template-columns: repeat(2,1fr) !important; }
|
||||
.uc-outcomes-grid > div > div { border-radius: 16px !important; }
|
||||
.uc-who-grid { grid-template-columns: repeat(2,1fr) !important; }
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.uc-outcomes-grid { grid-template-columns: 1fr !important; }
|
||||
.uc-who-grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
`}</style>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -61,7 +61,7 @@ export default function Footer() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<footer style={{
|
||||
<footer className="ly-footer" style={{
|
||||
background: 'linear-gradient(to top, #3a2800 0%, #1e1600 18%, #0e0b04 36%, #07070d 58%)',
|
||||
color: '#ffffff',
|
||||
position: 'relative',
|
||||
@@ -71,7 +71,7 @@ export default function Footer() {
|
||||
}}>
|
||||
|
||||
{/* ── TOP CTA STRIP ── */}
|
||||
<div style={{
|
||||
<div className="ly-footer-cta" style={{
|
||||
padding: '72px 64px 64px',
|
||||
borderBottom: '1px solid rgba(255,255,255,0.06)',
|
||||
display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between',
|
||||
@@ -83,15 +83,15 @@ export default function Footer() {
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20, display: 'flex', alignItems: 'center', gap: 10 }}>
|
||||
<span style={{ width: 24, height: 1, background: '#f4be28', display: 'block' }} />Let's build together
|
||||
</div>
|
||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 900, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: 0, fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 className="ly-footer-heading" style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: 0, fontFamily: 'Sora, sans-serif' }}>
|
||||
Turn every visit<br />
|
||||
<span style={{ color: '#f4be28' }}>into value.</span>
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12, alignItems: 'flex-end' }}>
|
||||
<Link href="/contacts" style={{
|
||||
display: 'inline-flex', alignItems: 'center', gap: 10,
|
||||
<div className="ly-footer-cta-actions" style={{ display: 'flex', flexDirection: 'column', gap: 12, alignItems: 'flex-end' }}>
|
||||
<Link href="/contacts" className="ly-footer-cta-btn" style={{
|
||||
display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 10,
|
||||
padding: '18px 40px', background: '#f4be28', color: '#111',
|
||||
fontSize: 16, fontWeight: 800, borderRadius: 100, textDecoration: 'none',
|
||||
fontFamily: 'Sora, sans-serif', boxShadow: '0 16px 48px rgba(244,190,40,0.25)',
|
||||
@@ -112,14 +112,14 @@ export default function Footer() {
|
||||
</div>
|
||||
|
||||
{/* ── MAIN COLUMNS ── */}
|
||||
<div style={{
|
||||
<div className="ly-footer-columns" style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1.6fr 1fr 1fr 1fr',
|
||||
gap: '48px', padding: '64px 64px 48px',
|
||||
alignItems: 'start',
|
||||
}}>
|
||||
{/* Brand */}
|
||||
<div>
|
||||
<div className="ly-footer-brand">
|
||||
<Link href="/" aria-label="Loyaly.AI home">
|
||||
<Image src="/images/logo-dark.png" alt="Loyaly.AI" width={560} height={160} style={{ height: 140, width: 'auto', marginBottom: 24, display: 'block' }} />
|
||||
</Link>
|
||||
@@ -145,7 +145,7 @@ export default function Footer() {
|
||||
|
||||
{/* Nav columns */}
|
||||
{NAV.map(col => (
|
||||
<nav key={col.title} aria-label={col.title}>
|
||||
<nav key={col.title} className="ly-footer-nav" aria-label={col.title}>
|
||||
<h5 style={{ margin: '0 0 20px', fontSize: 12, fontWeight: 700, color: 'rgba(255,255,255,0.3)', letterSpacing: '0.16em', textTransform: 'uppercase', fontFamily: 'Sora, sans-serif' }}>
|
||||
{col.title}
|
||||
</h5>
|
||||
@@ -164,7 +164,7 @@ export default function Footer() {
|
||||
</div>
|
||||
|
||||
{/* ── BOTTOM BAR ── */}
|
||||
<div style={{
|
||||
<div className="ly-footer-bottom" style={{
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||
flexWrap: 'wrap', gap: 12,
|
||||
padding: '20px 64px 36px',
|
||||
@@ -181,6 +181,25 @@ export default function Footer() {
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@media (max-width: 900px) {
|
||||
.ly-footer { margin: 0 12px 12px; border-radius: 0 0 20px 20px; }
|
||||
.ly-footer-cta { padding: 48px 28px 40px !important; align-items: flex-start !important; }
|
||||
.ly-footer-cta-actions { align-items: flex-start !important; width: 100%; }
|
||||
.ly-footer-cta-btn { width: 100%; padding: 16px 28px !important; }
|
||||
.ly-footer-columns { grid-template-columns: 1fr 1fr !important; gap: 36px 24px !important; padding: 48px 28px 36px !important; }
|
||||
.ly-footer-brand { grid-column: 1 / -1; }
|
||||
.ly-footer-bottom { padding: 20px 28px 28px !important; flex-direction: column !important; align-items: flex-start !important; }
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.ly-footer-heading { font-size: clamp(30px, 9vw, 44px) !important; }
|
||||
.ly-footer-columns { grid-template-columns: 1fr !important; gap: 32px !important; }
|
||||
.ly-footer-brand img { height: 110px !important; }
|
||||
.ly-footer-bottom { gap: 16px !important; }
|
||||
.ly-footer-bottom > div { gap: 18px !important; }
|
||||
}
|
||||
`}</style>
|
||||
</footer>
|
||||
|
||||
{/* Scroll-to-top */}
|
||||
|
||||
@@ -79,7 +79,7 @@ export default function GetInTouch() {
|
||||
}}
|
||||
>
|
||||
|
||||
<div style={{
|
||||
<div className="git-grid" style={{
|
||||
position: 'relative', zIndex: 2,
|
||||
maxWidth: 1200, margin: '0 auto',
|
||||
padding: 'clamp(56px, 8vh, 100px) clamp(28px, 5vw, 72px)',
|
||||
@@ -100,7 +100,7 @@ export default function GetInTouch() {
|
||||
|
||||
<h2 style={{
|
||||
margin: '0 0 24px',
|
||||
fontSize: 'clamp(2rem, 4vw, 3.4rem)', fontWeight: 900,
|
||||
fontSize: 'clamp(2rem, 4vw, 3.4rem)', fontWeight: 800,
|
||||
lineHeight: 0.98, letterSpacing: '-0.04em',
|
||||
color: '#fff', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
@@ -242,6 +242,9 @@ export default function GetInTouch() {
|
||||
color: rgba(255,255,255,0.5); transition: all 0.2s ease;
|
||||
}
|
||||
.git-social:hover { background: rgba(244,190,40,0.12); border-color: rgba(244,190,40,0.3); color: #f4be28; }
|
||||
@media (max-width: 420px) {
|
||||
.git-grid { grid-template-columns: 1fr !important; }
|
||||
}
|
||||
`}</style>
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -14,7 +14,7 @@ export default function MapSection() {
|
||||
overflow: 'hidden',
|
||||
background: '#fff',
|
||||
}}>
|
||||
<div style={{
|
||||
<div className="map-section-grid" style={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr 1fr',
|
||||
alignItems: 'stretch',
|
||||
@@ -37,7 +37,7 @@ export default function MapSection() {
|
||||
</div>
|
||||
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(32px, 4vw, 56px)', fontWeight: 900,
|
||||
fontSize: 'clamp(32px, 4vw, 56px)', fontWeight: 800,
|
||||
letterSpacing: '-0.04em', lineHeight: 1.0,
|
||||
color: '#0a0a0f', margin: '0 0 14px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function AIRecommendationsSection() {
|
||||
<div style={{ marginBottom: 28 }}><span className="fp-kicker fp-rise">AI Intelligence · 05</span></div>
|
||||
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(40px, 5vw, 72px)', fontWeight: 900, letterSpacing: '-0.05em', lineHeight: 0.94,
|
||||
fontSize: 'clamp(40px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.94,
|
||||
color: '#171512', margin: '0 0 28px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
<span className="fp-mask"><span>Built for</span></span>
|
||||
|
||||
@@ -35,7 +35,7 @@ export default function DiscoverDealsSection() {
|
||||
<div style={{ marginBottom: 28 }}><span className="fp-kicker fp-rise">Discover · 02</span></div>
|
||||
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(44px, 5.6vw, 88px)', fontWeight: 900, lineHeight: 0.94,
|
||||
fontSize: 'clamp(44px, 5.6vw, 88px)', fontWeight: 800, lineHeight: 0.94,
|
||||
letterSpacing: '-0.05em', color: '#fff', margin: '0 0 28px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
<span className="fp-mask"><span>Every</span></span>
|
||||
|
||||
@@ -73,7 +73,7 @@ export default function EarnRewardsSection() {
|
||||
<div style={{ marginBottom: 16 }}><span className="fp-kicker fp-rise">Earn · 03</span></div>
|
||||
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(28px, 3.4vw, 48px)', fontWeight: 900, lineHeight: 1.08,
|
||||
fontSize: 'clamp(28px, 3.4vw, 48px)', fontWeight: 800, lineHeight: 1.08,
|
||||
letterSpacing: '-0.04em', color: '#171512', margin: '0 0 20px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
<span className="fp-mask"><span>Every step.</span></span>
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function FinalCTASection() {
|
||||
|
||||
{/* Headline */}
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(48px, 7.5vw, 112px)', fontWeight: 900, letterSpacing: '-0.06em', lineHeight: 0.92,
|
||||
fontSize: 'clamp(48px, 7.5vw, 112px)', fontWeight: 800, letterSpacing: '-0.06em', lineHeight: 0.92,
|
||||
color: '#171512', margin: '0 0 28px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
<span className="fp-mask"><span>Your rewards</span></span>
|
||||
|
||||
@@ -61,10 +61,6 @@ export default function HeroSection() {
|
||||
|
||||
{/* Text column */}
|
||||
<div style={{ maxWidth: 720 }}>
|
||||
<div className="fp-content fp-eyebrow" style={{ marginBottom: 26, display: 'flex', justifyContent: 'center' }}>
|
||||
<span className="fp-kicker">For People · By Loyaly</span>
|
||||
</div>
|
||||
|
||||
<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',
|
||||
|
||||
@@ -30,7 +30,7 @@ export default function SpendAnywhereSection() {
|
||||
<div>
|
||||
<div style={{ marginBottom: 22 }}><span className="fp-kicker fp-rise">Spend · 04</span></div>
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(46px, 7vw, 110px)', fontWeight: 900, letterSpacing: '-0.06em', lineHeight: 0.9,
|
||||
fontSize: 'clamp(46px, 7vw, 110px)', fontWeight: 800, letterSpacing: '-0.06em', lineHeight: 0.9,
|
||||
color: '#fff', margin: 0, fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
<span className="fp-mask"><span>Spend it</span></span>
|
||||
|
||||
@@ -105,7 +105,7 @@ export default function WhyLoyalySection() {
|
||||
</div>
|
||||
|
||||
<h2 style={{
|
||||
fontSize: 'clamp(30px, 3.6vw, 50px)', fontWeight: 900, lineHeight: 1.08,
|
||||
fontSize: 'clamp(30px, 3.6vw, 50px)', fontWeight: 800, lineHeight: 1.08,
|
||||
letterSpacing: '-0.04em', color: '#171512', margin: '0 0 20px', fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
<span className="fp-mask"><span>Millions shop offline.</span></span>
|
||||
|
||||
@@ -234,8 +234,7 @@ export default function ProblemsShift({ isLoaded = true }: ProblemsShiftProps) {
|
||||
scrollTrigger: { trigger: '.ps-bottom-bar', start: 'top 92%', toggleActions: 'play none none reset' },
|
||||
});
|
||||
bottomTl
|
||||
.fromTo('.ps-bottom-bar', { opacity: 0, y: 36, scale: 0.97 }, { opacity: 1, y: 0, scale: 1, duration: 0.8, ease: 'power3.out' })
|
||||
.fromTo('.ps-feature-item', { opacity: 0, y: 16 }, { opacity: 1, y: 0, stagger: 0.09, duration: 0.5, ease: 'power3.out' }, '-=0.5');
|
||||
.fromTo('.ps-feature-item', { opacity: 0, y: 36, scale: 0.88 }, { opacity: 1, y: 0, scale: 1, stagger: 0.1, duration: 0.65, ease: 'back.out(1.7)' });
|
||||
|
||||
ScrollTrigger.refresh();
|
||||
}, sectionRef);
|
||||
@@ -263,7 +262,7 @@ export default function ProblemsShift({ isLoaded = true }: ProblemsShiftProps) {
|
||||
<span style={{ width: 6, height: 6, borderRadius: '50%', background: '#f4be28', display: 'block' }} />
|
||||
<span style={{ fontSize: 11, fontWeight: 800, letterSpacing: '0.18em', textTransform: 'uppercase', color: '#C49600', fontFamily: 'Sora, sans-serif' }}>The Opportunity</span>
|
||||
</div>
|
||||
<h2 className="ps-title" style={{ color: '#171512', fontWeight: 900, fontSize: 'clamp(2.4rem, 6vw, 4.8rem)', letterSpacing: '-0.05em', lineHeight: 0.94, margin: '0 0 20px', fontFamily: 'Sora, sans-serif' }}>
|
||||
<h2 className="ps-title" style={{ color: '#171512', fontWeight: 800, fontSize: 'clamp(2.4rem, 6vw, 4.8rem)', letterSpacing: '-0.05em', lineHeight: 0.94, margin: '0 0 20px', fontFamily: 'Sora, sans-serif' }}>
|
||||
Retail is busy.<br /><span style={{ color: '#f4be28' }}>But blind.</span>
|
||||
</h2>
|
||||
<p className="ps-subtitle" style={{ color: '#4f463a', fontSize: 'clamp(1rem, 1.3vw, 1.2rem)', fontWeight: 500, maxWidth: 620, margin: '0 auto', lineHeight: 1.7, fontFamily: 'Sora, sans-serif' }}>
|
||||
@@ -305,26 +304,43 @@ export default function ProblemsShift({ isLoaded = true }: ProblemsShiftProps) {
|
||||
</div>
|
||||
|
||||
{/* Bottom features bar */}
|
||||
<div className="ps-bottom-bar relative z-10 mx-auto" style={{ maxWidth: 980, marginTop: 'clamp(40px, 5vw, 72px)', background: '#fff', borderRadius: 20, border: '1px solid rgba(0,0,0,0.05)', boxShadow: '0 20px 50px -30px rgba(23,21,18,0.2)', padding: 'clamp(16px, 1.6vw, 22px) clamp(20px, 3vw, 44px)', display: 'flex', flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'center', gap: 16 }}>
|
||||
{BOTTOM_FEATURES.map((f, i) => (
|
||||
<React.Fragment key={f.label}>
|
||||
<div className="ps-feature-item" style={{ flex: '1 1 180px', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 11, cursor: 'default' }}>
|
||||
<span style={{ width: 36, height: 36, display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: 10, background: '#faf9f5', border: '1px solid rgba(0,0,0,0.05)' }}>
|
||||
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="#171512" strokeWidth="2">{f.icon}</svg>
|
||||
</span>
|
||||
<span style={{ color: '#171512', fontWeight: 700, fontSize: '0.95rem', fontFamily: 'Sora, sans-serif' }}>{f.label}</span>
|
||||
</div>
|
||||
{i < BOTTOM_FEATURES.length - 1 && <span className="ps-sep" style={{ width: 1, height: 28, background: 'rgba(0,0,0,0.07)' }} />}
|
||||
</React.Fragment>
|
||||
<div className="ps-bottom-bar relative z-10 mx-auto" style={{ maxWidth: 980, marginTop: 'clamp(40px, 5vw, 72px)', display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(170px, 1fr))', gap: 'clamp(12px, 1.6vw, 18px)' }}>
|
||||
{BOTTOM_FEATURES.map((f) => (
|
||||
<div
|
||||
key={f.label}
|
||||
className="ps-feature-item"
|
||||
style={{
|
||||
display: 'flex', alignItems: 'center', gap: 14,
|
||||
background: '#fff', borderRadius: 18,
|
||||
border: '1px solid rgba(0,0,0,0.06)',
|
||||
boxShadow: '0 1px 2px rgba(0,0,0,0.03), 0 16px 36px -22px rgba(23,21,18,0.22)',
|
||||
padding: 'clamp(16px, 1.6vw, 20px) clamp(16px, 2vw, 22px)',
|
||||
cursor: 'default',
|
||||
transition: 'transform 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease',
|
||||
}}
|
||||
>
|
||||
<span style={{
|
||||
width: 44, height: 44, flexShrink: 0, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
borderRadius: 13, background: 'linear-gradient(145deg,#fff8e8 0%,#fff2c8 100%)',
|
||||
border: '1px solid rgba(244,190,40,0.25)',
|
||||
}}>
|
||||
<svg width="19" height="19" viewBox="0 0 24 24" fill="none" stroke="#bc7122" strokeWidth="2">{f.icon}</svg>
|
||||
</span>
|
||||
<span style={{ color: '#171512', fontWeight: 700, fontSize: '0.95rem', fontFamily: 'Sora, sans-serif', lineHeight: 1.3 }}>{f.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Responsive: stack cards on narrow screens, rotate connector */}
|
||||
<style>{`
|
||||
#problems-shift .ps-feature-item:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.04), 0 24px 48px -22px rgba(23,21,18,0.3);
|
||||
border-color: rgba(244,190,40,0.35);
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
#problems-shift .ps-cards-row { flex-direction: column; }
|
||||
#problems-shift .ps-connector-arrow { transform: rotate(90deg); }
|
||||
#problems-shift .ps-sep { display: none; }
|
||||
}
|
||||
`}</style>
|
||||
</section>
|
||||
|
||||
@@ -275,7 +275,7 @@ function ProductCard({ product, index }: { product: Product; index: number }) {
|
||||
<h3 style={{
|
||||
margin: 0,
|
||||
fontSize: 'clamp(1.7rem, 2.8vw, 3rem)',
|
||||
fontWeight: 900, lineHeight: 0.98, letterSpacing: '-0.04em',
|
||||
fontWeight: 800, lineHeight: 0.98, letterSpacing: '-0.04em',
|
||||
color: '#ffffff', fontFamily: 'Sora, sans-serif',
|
||||
}}>{product.title}</h3>
|
||||
|
||||
@@ -410,7 +410,7 @@ export default function ProductsSection({ isLoaded = false }: ProductsSectionPro
|
||||
<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>
|
||||
<h2 style={{ margin: 0, fontSize: 'clamp(1.9rem, 3.6vw, 3.2rem)', fontWeight: 900, lineHeight: 0.94, letterSpacing: '-0.04em', color: '#111111', fontFamily: 'Sora, sans-serif' }}>
|
||||
<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>
|
||||
|
||||
@@ -155,7 +155,7 @@ export default function TheLoop() {
|
||||
<h2 style={{
|
||||
margin: '0 0 18px',
|
||||
fontSize: 'clamp(2.6rem, 6vw, 5rem)',
|
||||
fontWeight: 900, color: '#fff',
|
||||
fontWeight: 800, color: '#fff',
|
||||
lineHeight: 0.92, letterSpacing: '-0.05em',
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
}}>
|
||||
@@ -222,6 +222,7 @@ export default function TheLoop() {
|
||||
style={{
|
||||
flex: 1,
|
||||
position: 'relative',
|
||||
zIndex: 1,
|
||||
padding: 'clamp(26px, 2.2vw, 38px) clamp(20px, 1.8vw, 28px)',
|
||||
borderRadius: 26,
|
||||
background: isActive ? 'rgba(244,190,40,0.07)' : 'rgba(255,255,255,0.025)',
|
||||
@@ -276,14 +277,15 @@ export default function TheLoop() {
|
||||
width: 'clamp(28px, 3vw, 56px)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
position: 'relative',
|
||||
zIndex: 0,
|
||||
}}>
|
||||
<div style={{
|
||||
<div className="loop-connector-line" style={{
|
||||
position: 'absolute', left: 0, right: 0, height: 2, borderRadius: 2,
|
||||
background: 'linear-gradient(90deg, rgba(244,190,40,0.1), rgba(244,190,40,0.45), rgba(244,190,40,0.1))',
|
||||
backgroundSize: '200% 100%',
|
||||
animation: revealed ? 'loopFlow 1.8s linear infinite' : 'none',
|
||||
}} />
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ position: 'relative', zIndex: 2, opacity: 0.8 }}>
|
||||
<svg className="loop-connector-arrow" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ position: 'relative', zIndex: 2, opacity: 0.8 }}>
|
||||
<path d="M5 12h14M13 6l6 6-6 6" />
|
||||
</svg>
|
||||
</div>
|
||||
@@ -330,9 +332,25 @@ export default function TheLoop() {
|
||||
@keyframes loopShimmer { 0%{background-position:200% center} 100%{background-position:-200% center} }
|
||||
@keyframes loopDash { to { stroke-dashoffset: -32; } }
|
||||
@keyframes loopFlow { 0%{background-position:200% 0} 100%{background-position:-200% 0} }
|
||||
@keyframes loopFlowVertical { 0%{background-position:0 200%} 100%{background-position:0 -200%} }
|
||||
@keyframes loopArrowBounceH { 0%,100% { transform: translateX(0); } 50% { transform: translateX(5px); } }
|
||||
@keyframes loopArrowBounceV { 0%,100% { transform: rotate(90deg) translateX(0); } 50% { transform: rotate(90deg) translateX(5px); } }
|
||||
.loop-connector-arrow { animation: loopArrowBounceH 1.4s ease-in-out infinite; }
|
||||
@media (max-width: 880px) {
|
||||
.loop-row { flex-direction: column; }
|
||||
.loop-connector { width: 100% !important; height: 40px; transform: rotate(90deg); }
|
||||
.loop-card { z-index: 1; }
|
||||
.loop-connector {
|
||||
width: 100% !important; height: 40px; z-index: 0;
|
||||
}
|
||||
.loop-connector-line {
|
||||
left: 50% !important; right: auto !important; top: 0 !important; bottom: 0 !important;
|
||||
width: 2px !important; height: 100% !important;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(180deg, rgba(244,190,40,0.1), rgba(244,190,40,0.45), rgba(244,190,40,0.1)) !important;
|
||||
background-size: 100% 200% !important;
|
||||
animation-name: loopFlowVertical !important;
|
||||
}
|
||||
.loop-connector-arrow { animation: loopArrowBounceV 1.4s ease-in-out infinite; }
|
||||
.loop-arc, .loop-arc-label { display: none !important; }
|
||||
}
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import React, { useEffect, useLayoutEffect, useRef, useState, useCallback } from 'react';
|
||||
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { gsap } from 'gsap';
|
||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||
import { DEFAULT_EASE, DEFAULT_DURATION } from '@/utils/animation';
|
||||
@@ -12,93 +12,204 @@ if (typeof window !== 'undefined') {
|
||||
|
||||
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
||||
|
||||
interface CardProps {
|
||||
interface Stage {
|
||||
num: string;
|
||||
title: string;
|
||||
description: string;
|
||||
icon: React.ReactNode;
|
||||
cardRef: React.RefObject<HTMLDivElement | null>;
|
||||
orderClass: string;
|
||||
}
|
||||
|
||||
function StageCard({ num, title, description, icon, cardRef, orderClass }: CardProps) {
|
||||
const STAGES: Stage[] = [
|
||||
{
|
||||
num: '01',
|
||||
title: 'Understand',
|
||||
description: 'Track store footfall, customer behavior, and real-time engagement with AI — giving merchants the intelligence to run smarter campaigns and targeted offers that actually convert.',
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
num: '02',
|
||||
title: 'Engage',
|
||||
description: 'Customers earn Loyaly Coins through daily login, 5,000-step walking challenges, and interactive games — Maze, Spin Wheel, Scratch Card, and Match Master — keeping them engaged every day.',
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
num: '03',
|
||||
title: 'Convert',
|
||||
description: 'Merchants create game-based discount campaigns where customers play once to reveal their reward — turning product discovery into an exciting, high-conversion in-store experience.',
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
num: '04',
|
||||
title: 'Retain',
|
||||
description: 'Daily rewards, surprise coupons, and game-based offers keep customers returning to earn, play, and redeem — creating a self-reinforcing loyalty loop that compounds value with every visit.',
|
||||
icon: (
|
||||
<svg className="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function StageCard({
|
||||
stage,
|
||||
index,
|
||||
active,
|
||||
onHover,
|
||||
}: {
|
||||
stage: Stage;
|
||||
index: number;
|
||||
active: boolean;
|
||||
onHover: (i: number | null) => void;
|
||||
}) {
|
||||
const cardRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
const el = cardRef.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 * 6).toFixed(2)}deg`);
|
||||
el.style.setProperty('--ry', `${(px * 8).toFixed(2)}deg`);
|
||||
el.style.setProperty('--mx', `${((px + 0.5) * 100).toFixed(1)}%`);
|
||||
el.style.setProperty('--my', `${((py + 0.5) * 100).toFixed(1)}%`);
|
||||
};
|
||||
|
||||
const onMouseLeave = () => {
|
||||
const el = cardRef.current;
|
||||
if (!el) return;
|
||||
el.style.setProperty('--rx', '0deg');
|
||||
el.style.setProperty('--ry', '0deg');
|
||||
onHover(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={cardRef}
|
||||
className={`wwd-card relative group ${orderClass}`}
|
||||
data-stage-card={index}
|
||||
className={`wwd-card relative group ${active ? 'wwd-card-active' : ''}`}
|
||||
onMouseMove={onMouseMove}
|
||||
onMouseEnter={() => onHover(index)}
|
||||
onMouseLeave={onMouseLeave}
|
||||
style={{
|
||||
background: 'rgba(255,255,255,0.025)',
|
||||
backdropFilter: 'blur(24px) saturate(160%)',
|
||||
WebkitBackdropFilter: 'blur(24px) saturate(160%)',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
borderRadius: '28px',
|
||||
padding: 'clamp(30px, 3vw, 48px)',
|
||||
transition: 'transform 0.5s cubic-bezier(0.22,1,0.36,1), box-shadow 0.5s ease, border-color 0.5s ease',
|
||||
cursor: 'default',
|
||||
overflow: 'hidden',
|
||||
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.05)',
|
||||
}}
|
||||
onMouseEnter={e => {
|
||||
(e.currentTarget as HTMLElement).style.transform = 'translateY(-8px)';
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = '0 32px 80px rgba(244, 190, 40,0.12), 0 0 0 1px rgba(244, 190, 40,0.25), inset 0 1px 0 rgba(255,255,255,0.08)';
|
||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(244, 190, 40,0.3)';
|
||||
}}
|
||||
onMouseLeave={e => {
|
||||
(e.currentTarget as HTMLElement).style.transform = 'translateY(0)';
|
||||
(e.currentTarget as HTMLElement).style.boxShadow = 'inset 0 1px 0 rgba(255,255,255,0.05)';
|
||||
(e.currentTarget as HTMLElement).style.borderColor = 'rgba(255,255,255,0.08)';
|
||||
position: 'relative',
|
||||
transform: 'perspective(1100px) rotateX(var(--rx,0deg)) rotateY(var(--ry,0deg)) translateY(var(--cascade, 0px))',
|
||||
transformStyle: 'preserve-3d',
|
||||
transition: 'transform 0.45s cubic-bezier(0.22,1,0.36,1), box-shadow 0.45s ease, border-color 0.45s ease',
|
||||
}}
|
||||
>
|
||||
{/* Giant editorial number watermark */}
|
||||
<div style={{
|
||||
position: 'absolute', top: '-24px', right: '8px',
|
||||
fontSize: 'clamp(8rem, 11vw, 13rem)', fontWeight: 900, lineHeight: 1,
|
||||
color: 'transparent',
|
||||
WebkitTextStroke: '1px rgba(244, 190, 40,0.06)',
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
letterSpacing: '-0.06em',
|
||||
userSelect: 'none', pointerEvents: 'none',
|
||||
}}>
|
||||
{num}
|
||||
<div
|
||||
className="wwd-card-inner"
|
||||
style={{
|
||||
position: 'relative',
|
||||
background: 'rgba(255,255,255,0.025)',
|
||||
backdropFilter: 'blur(24px) saturate(160%)',
|
||||
WebkitBackdropFilter: 'blur(24px) saturate(160%)',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
borderRadius: 26,
|
||||
padding: 'clamp(28px, 2.4vw, 38px)',
|
||||
overflow: 'hidden',
|
||||
cursor: 'default',
|
||||
height: '100%',
|
||||
boxShadow: active
|
||||
? '0 32px 70px -20px rgba(244,190,40,0.22), 0 0 0 1px rgba(244,190,40,0.3), inset 0 1px 0 rgba(255,255,255,0.08)'
|
||||
: 'inset 0 1px 0 rgba(255,255,255,0.05)',
|
||||
borderColor: active ? 'rgba(244,190,40,0.32)' : 'rgba(255,255,255,0.08)',
|
||||
}}
|
||||
>
|
||||
{/* Cursor-following glow */}
|
||||
<div
|
||||
className="wwd-card-glow"
|
||||
style={{
|
||||
position: 'absolute', inset: 0, pointerEvents: 'none', opacity: active ? 1 : 0,
|
||||
background: 'radial-gradient(280px circle at var(--mx,50%) var(--my,50%), rgba(244,190,40,0.12), transparent 70%)',
|
||||
transition: 'opacity 0.4s ease',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Giant editorial number, bleeding off the top edge */}
|
||||
<div style={{
|
||||
position: 'absolute', top: '-0.32em', right: '-0.06em',
|
||||
fontSize: 'clamp(6rem, 8vw, 9rem)', fontWeight: 900, lineHeight: 1,
|
||||
color: 'transparent',
|
||||
WebkitTextStroke: active ? '1px rgba(244,190,40,0.18)' : '1px rgba(244,190,40,0.07)',
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
letterSpacing: '-0.06em',
|
||||
userSelect: 'none', pointerEvents: 'none',
|
||||
transition: 'all 0.45s ease',
|
||||
}}>
|
||||
{stage.num}
|
||||
</div>
|
||||
|
||||
{/* Step rail dot */}
|
||||
<div style={{
|
||||
display: 'flex', alignItems: 'center', gap: 10, marginBottom: 22,
|
||||
position: 'relative', zIndex: 2,
|
||||
}}>
|
||||
<span style={{
|
||||
fontSize: 11, fontWeight: 800, letterSpacing: '0.18em', textTransform: 'uppercase',
|
||||
color: active ? '#f4be28' : 'rgba(255,255,255,0.32)',
|
||||
fontFamily: 'Sora, sans-serif', transition: 'color 0.4s ease',
|
||||
}}>
|
||||
Stage {stage.num}
|
||||
</span>
|
||||
<span style={{ flex: 1, height: 1, background: active ? 'rgba(244,190,40,0.35)' : 'rgba(255,255,255,0.08)', transition: 'background 0.4s ease' }} />
|
||||
</div>
|
||||
|
||||
{/* Icon */}
|
||||
<div style={{
|
||||
width: 54, height: 54,
|
||||
borderRadius: 16,
|
||||
background: active ? 'rgba(244,190,40,0.16)' : 'rgba(244,190,40,0.1)',
|
||||
border: `1px solid rgba(244,190,40,${active ? 0.4 : 0.22})`,
|
||||
color: '#f4be28',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
marginBottom: 28,
|
||||
position: 'relative', zIndex: 2,
|
||||
boxShadow: active ? '0 0 28px rgba(244,190,40,0.3), inset 0 1px 0 rgba(255,255,255,0.08)' : 'inset 0 1px 0 rgba(255,255,255,0.08)',
|
||||
transform: active ? 'rotate(-6deg) scale(1.05)' : 'none',
|
||||
transition: 'all 0.45s cubic-bezier(0.22,1,0.36,1)',
|
||||
}}>
|
||||
{stage.icon}
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 style={{
|
||||
margin: '0 0 14px',
|
||||
fontSize: 'clamp(1.4rem, 2vw, 1.85rem)',
|
||||
fontWeight: 800, color: '#ffffff',
|
||||
lineHeight: 1.1, letterSpacing: '-0.03em',
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
position: 'relative', zIndex: 2,
|
||||
}}>
|
||||
{stage.title}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p style={{
|
||||
margin: 0,
|
||||
fontSize: '0.92rem',
|
||||
color: 'rgba(255,255,255,0.45)',
|
||||
lineHeight: 1.75,
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
position: 'relative', zIndex: 2,
|
||||
}}>
|
||||
{stage.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Icon */}
|
||||
<div style={{
|
||||
width: '56px', height: '56px',
|
||||
borderRadius: '16px',
|
||||
background: 'rgba(244, 190, 40,0.1)',
|
||||
border: '1px solid rgba(244, 190, 40,0.22)',
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||
marginBottom: '32px',
|
||||
position: 'relative', zIndex: 2,
|
||||
boxShadow: 'inset 0 1px 0 rgba(255,255,255,0.08)',
|
||||
}}>
|
||||
{icon}
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h3 style={{
|
||||
margin: '0 0 16px',
|
||||
fontSize: 'clamp(1.5rem, 2.2vw, 2rem)',
|
||||
fontWeight: 800, color: '#ffffff',
|
||||
lineHeight: 1.1, letterSpacing: '-0.03em',
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
position: 'relative', zIndex: 2,
|
||||
}}>
|
||||
{title}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p style={{
|
||||
margin: 0,
|
||||
fontSize: 'clamp(0.9rem, 1vw, 1rem)',
|
||||
color: 'rgba(255,255,255,0.45)',
|
||||
lineHeight: 1.75, maxWidth: '360px',
|
||||
fontFamily: 'Sora, sans-serif',
|
||||
position: 'relative', zIndex: 2,
|
||||
}}>
|
||||
{description}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -109,129 +220,43 @@ interface WhatWeDoProps {
|
||||
|
||||
export default function WhatWeDo({ isLoaded = false }: WhatWeDoProps) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const gridContainerRef = useRef<HTMLDivElement>(null);
|
||||
const card1Ref = useRef<HTMLDivElement>(null);
|
||||
const card2Ref = useRef<HTMLDivElement>(null);
|
||||
const card3Ref = useRef<HTMLDivElement>(null);
|
||||
const card4Ref = useRef<HTMLDivElement>(null);
|
||||
const [active, setActive] = useState<number | null>(null);
|
||||
|
||||
const [points, setPoints] = useState({
|
||||
p1: { x: 0, y: 0 },
|
||||
p2: { x: 0, y: 0 },
|
||||
p3: { x: 0, y: 0 },
|
||||
p4: { x: 0, y: 0 }
|
||||
});
|
||||
|
||||
const [hasMeasured, setHasMeasured] = useState(false);
|
||||
|
||||
const updatePoints = useCallback(() => {
|
||||
if (!gridContainerRef.current || !card1Ref.current || !card2Ref.current || !card3Ref.current || !card4Ref.current) return;
|
||||
|
||||
const containerRect = gridContainerRef.current.getBoundingClientRect();
|
||||
const r1 = card1Ref.current.getBoundingClientRect();
|
||||
const r2 = card2Ref.current.getBoundingClientRect();
|
||||
const r3 = card3Ref.current.getBoundingClientRect();
|
||||
const r4 = card4Ref.current.getBoundingClientRect();
|
||||
|
||||
// Check that width is non-zero to verify the elements have rendered and have dimensions
|
||||
if (r1.width === 0 || r2.width === 0 || r3.width === 0 || r4.width === 0) return;
|
||||
|
||||
setPoints({
|
||||
p1: {
|
||||
x: r1.left - containerRect.left + r1.width / 2,
|
||||
y: r1.top - containerRect.top + r1.height / 2
|
||||
},
|
||||
p2: {
|
||||
x: r2.left - containerRect.left + r2.width / 2,
|
||||
y: r2.top - containerRect.top + r2.height / 2
|
||||
},
|
||||
p3: {
|
||||
x: r3.left - containerRect.left + r3.width / 2,
|
||||
y: r3.top - containerRect.top + r3.height / 2
|
||||
},
|
||||
p4: {
|
||||
x: r4.left - containerRect.left + r4.width / 2,
|
||||
y: r4.top - containerRect.top + r4.height / 2
|
||||
}
|
||||
});
|
||||
setHasMeasured(true);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
// Use ResizeObserver for highly responsive layout tracking
|
||||
const observer = new ResizeObserver(() => {
|
||||
updatePoints();
|
||||
});
|
||||
|
||||
if (gridContainerRef.current) observer.observe(gridContainerRef.current);
|
||||
if (card1Ref.current) observer.observe(card1Ref.current);
|
||||
if (card2Ref.current) observer.observe(card2Ref.current);
|
||||
if (card3Ref.current) observer.observe(card3Ref.current);
|
||||
if (card4Ref.current) observer.observe(card4Ref.current);
|
||||
|
||||
// Initial calculation
|
||||
updatePoints();
|
||||
|
||||
// Secondary delay to ensure clean alignment post-font/css loading
|
||||
const timer = setTimeout(updatePoints, 200);
|
||||
|
||||
window.addEventListener('resize', updatePoints);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
window.removeEventListener('resize', updatePoints);
|
||||
clearTimeout(timer);
|
||||
};
|
||||
}, [updatePoints]);
|
||||
|
||||
// GSAP animations for stagger entrances on entering viewport
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
if (!isLoaded) return;
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
const ctx = gsap.context(() => {
|
||||
// Header Animation
|
||||
gsap.fromTo(
|
||||
'.wwd-header-el',
|
||||
{ opacity: 0, y: 40 },
|
||||
{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: DEFAULT_DURATION,
|
||||
stagger: 0.15,
|
||||
ease: DEFAULT_EASE,
|
||||
scrollTrigger: {
|
||||
trigger: '.wwd-header',
|
||||
start: 'top 85%',
|
||||
toggleActions: 'play none none reset'
|
||||
}
|
||||
opacity: 1, y: 0, duration: DEFAULT_DURATION, stagger: 0.15, ease: DEFAULT_EASE,
|
||||
scrollTrigger: { trigger: '.wwd-header', start: 'top 85%', toggleActions: 'play none none reset' },
|
||||
}
|
||||
);
|
||||
|
||||
// Card Stagger Animation
|
||||
gsap.fromTo(
|
||||
'.wwd-card',
|
||||
{ opacity: 0, y: 60 },
|
||||
{ opacity: 0, y: 70, scale: 0.94 },
|
||||
{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
duration: DEFAULT_DURATION,
|
||||
stagger: 0.15,
|
||||
ease: DEFAULT_EASE,
|
||||
onComplete: updatePoints,
|
||||
scrollTrigger: {
|
||||
trigger: '.wwd-grid-container',
|
||||
start: 'top 80%',
|
||||
toggleActions: 'play none none reset'
|
||||
}
|
||||
opacity: 1, y: 0, scale: 1, duration: 0.9, stagger: 0.12, ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.wwd-grid-container', start: 'top 80%', toggleActions: 'play none none reset' },
|
||||
}
|
||||
);
|
||||
|
||||
gsap.fromTo(
|
||||
'.wwd-rail-step',
|
||||
{ opacity: 0, y: 14 },
|
||||
{
|
||||
opacity: 1, y: 0, duration: 0.5, stagger: 0.08, ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.wwd-rail', start: 'top 85%', toggleActions: 'play none none reset' },
|
||||
}
|
||||
);
|
||||
}, containerRef);
|
||||
|
||||
return () => ctx.revert();
|
||||
}, [isLoaded, updatePoints]);
|
||||
}, [isLoaded]);
|
||||
|
||||
return (
|
||||
<section
|
||||
@@ -241,136 +266,83 @@ export default function WhatWeDo({ isLoaded = false }: WhatWeDoProps) {
|
||||
style={{
|
||||
width: 'calc(100% - 40px)',
|
||||
margin: '0 20px 20px',
|
||||
borderRadius: '32px',
|
||||
borderRadius: 32,
|
||||
zIndex: 2,
|
||||
}}
|
||||
>
|
||||
{/* Animated dark background (replaces video) */}
|
||||
<AnimatedDarkBg variant="amber" />
|
||||
|
||||
{/* Header Container */}
|
||||
<div className="wwd-header relative z-10 text-center max-w-4xl mx-auto mb-24 sm:mb-28 md:mb-32 lg:mb-36 flex flex-col items-center">
|
||||
{/* Pill Badge */}
|
||||
{/* Header */}
|
||||
<div className="wwd-header relative z-10 text-center max-w-4xl mx-auto mb-16 sm:mb-20 flex flex-col items-center">
|
||||
<div className="wwd-header-el inline-flex items-center gap-2 px-4 py-1.5 rounded-full border border-white/10 bg-white/5 text-[#f4be28] text-xs font-bold tracking-wider uppercase mb-5">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-[#f4be28] animate-pulse" />
|
||||
What We Do
|
||||
</div>
|
||||
|
||||
{/* Main Heading */}
|
||||
<h2 className="wwd-header-el text-white font-black mb-6" style={{ fontSize: 'clamp(2.8rem, 6vw, 5.5rem)', letterSpacing: '-0.05em', lineHeight: 0.9, fontFamily: 'Sora, sans-serif' }}>
|
||||
Understand.<br />
|
||||
<span className="text-[#f4be28]">Engage. Convert.</span><br />
|
||||
Retain.
|
||||
</h2>
|
||||
|
||||
{/* Subtitle */}
|
||||
<p className="wwd-header-el text-gray-400 text-base sm:text-lg max-w-xl font-medium" style={{ fontFamily: 'Sora, sans-serif', lineHeight: 1.75 }}>
|
||||
Four stages. One platform. A smarter retail cycle that turns every walk-in into a loyal customer.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Grid and Paths Container */}
|
||||
<div ref={gridContainerRef} className="relative z-10 wwd-grid-container max-w-6xl mx-auto">
|
||||
|
||||
|
||||
|
||||
{/* 2 x 2 Responsive Grid */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 md:gap-16 lg:gap-24 relative z-20 px-2 md:px-0">
|
||||
|
||||
{/* Card 1 — Understand */}
|
||||
<StageCard
|
||||
num="01"
|
||||
title="Understand"
|
||||
description="Track store footfall, customer behavior, and real-time engagement with AI — giving merchants the intelligence to run smarter campaigns and targeted offers that actually convert."
|
||||
cardRef={card1Ref}
|
||||
orderClass="order-1 md:order-1"
|
||||
icon={
|
||||
<svg className="w-6 h-6 text-[#f4be28]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Card 2 — Engage */}
|
||||
<StageCard
|
||||
num="02"
|
||||
title="Engage"
|
||||
description="Customers earn Loyaly Coins through daily login, 5,000-step walking challenges, and interactive games — Maze, Spin Wheel, Scratch Card, and Match Master — keeping them engaged every day."
|
||||
cardRef={card2Ref}
|
||||
orderClass="order-2 md:order-2"
|
||||
icon={
|
||||
<svg className="w-6 h-6 text-[#f4be28]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Card 4 — Retain (positioned bottom-left on desktop via order-3) */}
|
||||
<StageCard
|
||||
num="04"
|
||||
title="Retain"
|
||||
description="Daily rewards, surprise coupons, and game-based offers keep customers returning to earn, play, and redeem — creating a self-reinforcing loyalty loop that compounds value with every visit."
|
||||
cardRef={card4Ref}
|
||||
orderClass="order-4 md:order-3"
|
||||
icon={
|
||||
<svg className="w-6 h-6 text-[#f4be28]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" />
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Card 3 — Convert (positioned bottom-right on desktop via order-4) */}
|
||||
<StageCard
|
||||
num="03"
|
||||
title="Convert"
|
||||
description="Merchants create game-based discount campaigns where customers play once to reveal their reward — turning product discovery into an exciting, high-conversion in-store experience."
|
||||
cardRef={card3Ref}
|
||||
orderClass="order-3 md:order-4"
|
||||
icon={
|
||||
<svg className="w-6 h-6 text-[#f4be28]" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2.2">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M13 10V3L4 14h7v7l9-11h-7z" />
|
||||
</svg>
|
||||
}
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
{/* Center Area Ornament */}
|
||||
{hasMeasured && (
|
||||
<div
|
||||
className="absolute z-30 hidden md:flex items-center justify-center"
|
||||
{/* Step rail — desktop flow indicator synced to card hover, aligned to the card columns below */}
|
||||
<div className="wwd-rail relative z-10 max-w-6xl mx-auto mb-6 hidden lg:grid lg:grid-cols-4 gap-7">
|
||||
{STAGES.map((s, i) => (
|
||||
<button
|
||||
key={s.num}
|
||||
type="button"
|
||||
onMouseEnter={() => setActive(i)}
|
||||
onMouseLeave={() => setActive(null)}
|
||||
className="wwd-rail-step"
|
||||
style={{
|
||||
pointerEvents: 'none',
|
||||
left: `${(points.p1.x + points.p2.x + points.p3.x + points.p4.x) / 4}px`,
|
||||
top: `${(points.p1.y + points.p2.y + points.p3.y + points.p4.y) / 4}px`,
|
||||
transform: 'translate(-50%, -50%)'
|
||||
display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8,
|
||||
padding: '6px 14px', borderRadius: 100, border: '1px solid',
|
||||
borderColor: active === i ? 'rgba(244,190,40,0.4)' : 'rgba(255,255,255,0.1)',
|
||||
background: active === i ? 'rgba(244,190,40,0.1)' : 'rgba(255,255,255,0.03)',
|
||||
color: active === i ? '#f4be28' : 'rgba(255,255,255,0.4)',
|
||||
fontSize: 11, fontWeight: 800, letterSpacing: '0.1em', textTransform: 'uppercase',
|
||||
fontFamily: 'Sora, sans-serif', cursor: 'default',
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
>
|
||||
{/* Glowing container */}
|
||||
<div className="relative w-16 h-16 rounded-full bg-[#0D0D0D] border border-[#f4be28]/35 flex items-center justify-center shadow-[0_0_35px_rgba(244, 190, 40,0.22)]">
|
||||
{/* Pulsing outer aura */}
|
||||
<div className="absolute inset-0 rounded-full border border-[#f4be28]/20 animate-ping opacity-60 pointer-events-none" />
|
||||
|
||||
{/* Rotating ↻ Icon */}
|
||||
<svg
|
||||
className="w-7 h-7 text-[#f4be28] animate-[spin_12s_linear_infinite]"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{s.num} · {s.title}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Cascading 4-up grid */}
|
||||
<div className="relative z-10 wwd-grid-container max-w-6xl mx-auto">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 lg:gap-7">
|
||||
{STAGES.map((stage, i) => (
|
||||
<div key={stage.num} className="wwd-cascade-item" style={{ ['--cascade-offset' as string]: `${(i % 2 === 1 ? 32 : 0)}px` }}>
|
||||
<StageCard stage={stage} index={i} active={active === i} onHover={setActive} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>{`
|
||||
@media (min-width: 1024px) {
|
||||
.wwd-cascade-item:nth-child(2n) .wwd-card { --cascade: 36px; }
|
||||
}
|
||||
.wwd-card-inner::before {
|
||||
content: '';
|
||||
position: absolute; inset: 0; border-radius: inherit; padding: 1px;
|
||||
background: linear-gradient(135deg, rgba(244,190,40,0.5), transparent 40%, transparent 60%, rgba(244,190,40,0.25));
|
||||
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
.wwd-card-active .wwd-card-inner::before { opacity: 1; }
|
||||
`}</style>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user