updated section design
6
.gitignore
vendored
@@ -43,3 +43,9 @@ next-env.d.ts
|
|||||||
# editors
|
# editors
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
|
# agents & analysis
|
||||||
|
.agents/
|
||||||
|
skills-lock.json
|
||||||
|
Loyaly_Website_Content_Analysis.*
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
BIN
public/ai_agents/agent-1-c.png
Normal file
|
After Width: | Height: | Size: 322 KiB |
BIN
public/ai_agents/agent-1.png
Normal file
|
After Width: | Height: | Size: 515 KiB |
BIN
public/ai_agents/agent-2-c.png
Normal file
|
After Width: | Height: | Size: 396 KiB |
BIN
public/ai_agents/agent-2.png
Normal file
|
After Width: | Height: | Size: 584 KiB |
BIN
public/ai_agents/agent-3-c.png
Normal file
|
After Width: | Height: | Size: 288 KiB |
BIN
public/ai_agents/agent-3.png
Normal file
|
After Width: | Height: | Size: 418 KiB |
BIN
public/ai_agents/agent-4-c.png
Normal file
|
After Width: | Height: | Size: 348 KiB |
BIN
public/ai_agents/agent-4.png
Normal file
|
After Width: | Height: | Size: 511 KiB |
BIN
public/images/bg-01.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/meet_the_agents/meet_the_agents_01.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
public/images/meet_the_agents/meet_the_agents_02.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
public/images/meet_the_agents/meet_the_agents_03.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/meet_the_agents/meet_the_agents_04.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
public/images/stock/discover.zip
Normal file
BIN
public/team/1.png
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
public/team/2.png
Normal file
|
After Width: | Height: | Size: 532 KiB |
BIN
public/team/3.png
Normal file
|
After Width: | Height: | Size: 492 KiB |
BIN
public/team/4.jpg
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/who_its_for-business/01.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/who_its_for-business/02.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
public/who_its_for-business/03.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
public/who_its_for-business/04.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
@@ -5,6 +5,7 @@ import Link from 'next/link';
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useReveal } from '@/hooks/useReveal';
|
import { useReveal } from '@/hooks/useReveal';
|
||||||
import GSAPAnimator from '@/components/GSAPAnimator';
|
import GSAPAnimator from '@/components/GSAPAnimator';
|
||||||
|
import SectionEyebrow from '@/components/SectionEyebrow';
|
||||||
|
|
||||||
function Reveal({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) {
|
function Reveal({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) {
|
||||||
const { ref, visible } = useReveal();
|
const { ref, visible } = useReveal();
|
||||||
@@ -70,7 +71,7 @@ export default function AboutUsPage() {
|
|||||||
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||||
<div className="au-story-grid" 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>
|
<Reveal>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 24 }}>Our Story</div>
|
<SectionEyebrow variant="line" style={{ marginBottom: 24 }}>Our Story</SectionEyebrow>
|
||||||
<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' }}>
|
<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.
|
We started with<br />a simple idea.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -158,7 +159,7 @@ export default function AboutUsPage() {
|
|||||||
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||||
<div className="au-market-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8vw', alignItems: 'start' }}>
|
<div className="au-market-grid" style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8vw', alignItems: 'start' }}>
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 24 }}>Market Opportunity</div>
|
<SectionEyebrow variant="pill" style={{ marginBottom: 24 }}>Market Opportunity</SectionEyebrow>
|
||||||
<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' }}>
|
<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' }}>
|
||||||
13 Million Stores.<br />One Opportunity.
|
13 Million Stores.<br />One Opportunity.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -291,7 +292,7 @@ function TeamCarousel() {
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div style={{ textAlign: 'center', marginBottom: 72, position: 'relative', zIndex: 2, padding: '0 8%' }}>
|
<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>
|
<SectionEyebrow variant="dot" style={{ marginBottom: 16 }}>The Team</SectionEyebrow>
|
||||||
<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' }}>
|
<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.
|
Builders. Thinkers.<br />Retail obsessives.
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import React from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useReveal } from '@/hooks/useReveal';
|
import { useReveal } from '@/hooks/useReveal';
|
||||||
import GSAPAnimator from '@/components/GSAPAnimator';
|
import GSAPAnimator from '@/components/GSAPAnimator';
|
||||||
import ScrollStack, { ScrollStackItem } from '@/components/ScrollStack';
|
import ScrollStack, { ScrollStackItem } from '@/components/ScrollStack';
|
||||||
|
import SectionEyebrow from '@/components/SectionEyebrow';
|
||||||
|
|
||||||
function Reveal({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) {
|
function Reveal({ children, delay = 0 }: { children: React.ReactNode; delay?: number }) {
|
||||||
const { ref, visible } = useReveal();
|
const { ref, visible } = useReveal();
|
||||||
@@ -28,6 +29,7 @@ const VALUES = [
|
|||||||
const PRODUCTS = [
|
const PRODUCTS = [
|
||||||
{
|
{
|
||||||
n: '01', title: 'Behavision', subtitle: 'AI Powered Footfall Analytics', color: '#f4be28',
|
n: '01', title: 'Behavision', subtitle: 'AI Powered Footfall Analytics', color: '#f4be28',
|
||||||
|
image: '/images/meet_the_agents/meet_the_agents_01.png',
|
||||||
desc: 'AI powered footfall analytics and behavioral intelligence for physical retail. Behavision turns every camera and sensor you already have into a live map of how customers move, where they linger, and what makes them walk away, so store layout and staffing decisions are based on evidence, not guesswork.',
|
desc: 'AI powered footfall analytics and behavioral intelligence for physical retail. Behavision turns every camera and sensor you already have into a live map of how customers move, where they linger, and what makes them walk away, so store layout and staffing decisions are based on evidence, not guesswork.',
|
||||||
capabilities: [
|
capabilities: [
|
||||||
'Real-time footfall counting, accurate to 98% across every entry point',
|
'Real-time footfall counting, accurate to 98% across every entry point',
|
||||||
@@ -39,6 +41,7 @@ const PRODUCTS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
n: '02', title: 'SpendSense', subtitle: 'Customer Spending Intelligence', color: '#10B981',
|
n: '02', title: 'SpendSense', subtitle: 'Customer Spending Intelligence', color: '#10B981',
|
||||||
|
image: '/images/meet_the_agents/meet_the_agents_02.png',
|
||||||
desc: 'Deep, continuously updating intelligence on how your customers spend. SpendSense studies basket size, purchase frequency, and category preferences across every visit, surfacing the high-value segments and the early warning signs of churn well before a customer stops coming back.',
|
desc: 'Deep, continuously updating intelligence on how your customers spend. SpendSense studies basket size, purchase frequency, and category preferences across every visit, surfacing the high-value segments and the early warning signs of churn well before a customer stops coming back.',
|
||||||
capabilities: [
|
capabilities: [
|
||||||
'Automatic customer segmentation by spend, frequency, and loyalty tier',
|
'Automatic customer segmentation by spend, frequency, and loyalty tier',
|
||||||
@@ -50,6 +53,7 @@ const PRODUCTS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
n: '03', title: 'Dyscount', subtitle: 'Smart Discount Engine', color: '#EF4444',
|
n: '03', title: 'Dyscount', subtitle: 'Smart Discount Engine', color: '#EF4444',
|
||||||
|
image: '/images/meet_the_agents/meet_the_agents_03.png',
|
||||||
desc: 'A discount engine that treats margin as a budget, not an afterthought. Dyscount decides who gets an offer, how deep it goes, and when it fires, targeting the customers most likely to convert while capping every discount so promotions never quietly eat into profit.',
|
desc: 'A discount engine that treats margin as a budget, not an afterthought. Dyscount decides who gets an offer, how deep it goes, and when it fires, targeting the customers most likely to convert while capping every discount so promotions never quietly eat into profit.',
|
||||||
capabilities: [
|
capabilities: [
|
||||||
'Dynamic offer targeting based on real-time purchase intent signals',
|
'Dynamic offer targeting based on real-time purchase intent signals',
|
||||||
@@ -61,6 +65,7 @@ const PRODUCTS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
n: '04', title: 'Identiq', subtitle: 'Consumer Loyalty Platform', color: '#f4be28',
|
n: '04', title: 'Identiq', subtitle: 'Consumer Loyalty Platform', color: '#f4be28',
|
||||||
|
image: '/images/meet_the_agents/meet_the_agents_04.png',
|
||||||
desc: 'The consumer-facing app that closes the loop. Identiq turns daily logins, walking challenges, and quick mini-games into LYTs, Loyaly’s reward tokens, redeemable instantly at any partner store, so every idle moment on a phone becomes a reason to walk into a real one.',
|
desc: 'The consumer-facing app that closes the loop. Identiq turns daily logins, walking challenges, and quick mini-games into LYTs, Loyaly’s reward tokens, redeemable instantly at any partner store, so every idle moment on a phone becomes a reason to walk into a real one.',
|
||||||
capabilities: [
|
capabilities: [
|
||||||
'Daily login and step-challenge Lyts that build a lasting habit loop',
|
'Daily login and step-challenge Lyts that build a lasting habit loop',
|
||||||
@@ -73,6 +78,17 @@ const PRODUCTS = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default function MeetTheCrewPage() {
|
export default function MeetTheCrewPage() {
|
||||||
|
// Below 860px the product grid stacks vertically, making each card taller than
|
||||||
|
// the viewport — so we drop the sticky scroll-stack and let them flow normally.
|
||||||
|
const [isMobile, setIsMobile] = useState(false);
|
||||||
|
useEffect(() => {
|
||||||
|
const mq = window.matchMedia('(max-width: 860px)');
|
||||||
|
const update = () => setIsMobile(mq.matches);
|
||||||
|
update();
|
||||||
|
mq.addEventListener('change', update);
|
||||||
|
return () => mq.removeEventListener('change', update);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main style={{ background: '#ffffff', minHeight: '100vh', overflowX: 'hidden' }}>
|
<main style={{ background: '#ffffff', minHeight: '100vh', overflowX: 'hidden' }}>
|
||||||
<GSAPAnimator />
|
<GSAPAnimator />
|
||||||
@@ -124,33 +140,75 @@ export default function MeetTheCrewPage() {
|
|||||||
{/* ── VALUES ── */}
|
{/* ── VALUES ── */}
|
||||||
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
<section style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>How We Work</div>
|
<SectionEyebrow variant="bar" style={{ marginBottom: 20 }}>How We Work</SectionEyebrow>
|
||||||
<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' }}>
|
<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.
|
What we believe.
|
||||||
</h2>
|
</h2>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
<div className="mtc-value-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 20 }}>
|
||||||
{VALUES.map((v, i) => (
|
{VALUES.map((v, i) => {
|
||||||
|
const themes = [
|
||||||
|
{ g: 'linear-gradient(135deg, #6366f1 0%, #4338ca 100%)', glow: 'rgba(99,102,241,0.5)' },
|
||||||
|
{ g: 'linear-gradient(135deg, #10b981 0%, #047857 100%)', glow: 'rgba(16,185,129,0.5)' },
|
||||||
|
{ g: 'linear-gradient(135deg, #fb7185 0%, #be123c 100%)', glow: 'rgba(244,63,94,0.5)' },
|
||||||
|
{ g: 'linear-gradient(135deg, #f59e0b 0%, #c9960f 100%)', glow: 'rgba(245,158,11,0.5)' },
|
||||||
|
];
|
||||||
|
const t = themes[i % themes.length];
|
||||||
|
return (
|
||||||
<Reveal key={i} delay={i * 0.08}>
|
<Reveal key={i} delay={i * 0.08}>
|
||||||
<div className="mtc-value-row" style={{
|
<div className="mtc-value-card" style={{
|
||||||
display: 'grid', gridTemplateColumns: '80px 1fr 1fr',
|
position: 'relative', overflow: 'hidden', height: '100%', boxSizing: 'border-box',
|
||||||
gap: '4vw', alignItems: 'center',
|
padding: 'clamp(28px, 3vw, 40px)', borderRadius: 22,
|
||||||
padding: '40px 0', borderBottom: '1px solid rgba(0,0,0,0.07)',
|
backgroundImage: t.g, backgroundSize: '200% 200%',
|
||||||
|
boxShadow: `0 22px 44px -26px ${t.glow}`,
|
||||||
|
['--vc-glow' as string]: t.glow,
|
||||||
|
animationDelay: `${i * -2.5}s`,
|
||||||
|
transition: 'transform 0.4s cubic-bezier(0.22,1,0.36,1), box-shadow 0.4s ease',
|
||||||
}}>
|
}}>
|
||||||
<span style={{ fontSize: 13, fontWeight: 700, color: '#f4be28', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em' }}>{v.n}</span>
|
{/* Floating light blob */}
|
||||||
<h3 style={{ fontSize: 'clamp(20px, 2.5vw, 36px)', fontWeight: 800, color: '#0a0a0f', fontFamily: 'Sora, sans-serif', margin: 0, letterSpacing: '-0.03em' }}>{v.title}</h3>
|
<span aria-hidden className="mtc-vc-glow" style={{
|
||||||
<p style={{ fontSize: 16, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.65 }}>{v.desc}</p>
|
position: 'absolute', top: '-30%', right: '-12%', width: 260, height: 260, borderRadius: '50%',
|
||||||
|
background: 'radial-gradient(circle, rgba(255,255,255,0.28) 0%, transparent 68%)', pointerEvents: 'none',
|
||||||
|
animationDelay: `${i * -1.6}s`,
|
||||||
|
}} />
|
||||||
|
{/* Glossy sheen + hover shine sweep */}
|
||||||
|
<span aria-hidden style={{ position: 'absolute', inset: 0, pointerEvents: 'none', background: 'linear-gradient(135deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0) 44%)' }} />
|
||||||
|
<span aria-hidden className="mtc-vc-shine" style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }} />
|
||||||
|
{/* Faint watermark number */}
|
||||||
|
<span aria-hidden style={{
|
||||||
|
position: 'absolute', right: 18, top: 4, fontSize: 92, fontWeight: 800,
|
||||||
|
color: 'rgba(255,255,255,0.16)', fontFamily: 'Sora, sans-serif', lineHeight: 1, pointerEvents: 'none',
|
||||||
|
}}>{v.n}</span>
|
||||||
|
{/* Number badge */}
|
||||||
|
<span style={{
|
||||||
|
display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
width: 46, height: 46, borderRadius: 13, marginBottom: 22, position: 'relative',
|
||||||
|
background: 'rgba(255,255,255,0.18)', border: '1px solid rgba(255,255,255,0.28)', color: '#fff',
|
||||||
|
fontSize: 15, fontWeight: 800, fontFamily: 'Sora, sans-serif', letterSpacing: '0.02em',
|
||||||
|
}}>{v.n}</span>
|
||||||
|
<h3 style={{ fontSize: 'clamp(22px, 2vw, 30px)', fontWeight: 800, color: '#ffffff', fontFamily: 'Sora, sans-serif', margin: '0 0 12px', letterSpacing: '-0.03em', position: 'relative' }}>{v.title}</h3>
|
||||||
|
<p style={{ fontSize: 16, color: 'rgba(255,255,255,0.82)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.7, position: 'relative', maxWidth: 380 }}>{v.desc}</p>
|
||||||
</div>
|
</div>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── OUR PRODUCTS ── */}
|
{/* ── OUR PRODUCTS ── */}
|
||||||
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
|
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
|
||||||
|
{/* Background photo — confined to the hero band at the top so it isn't
|
||||||
|
stretched/zoomed across the tall product-stack section below */}
|
||||||
|
<div style={{ position: 'absolute', top: 0, left: 0, right: 0, height: 760, pointerEvents: 'none' }}>
|
||||||
|
<Image src="/images/bg-01.png" alt="" fill sizes="100vw" style={{ objectFit: 'cover', objectPosition: 'center', opacity: 0.6 }} />
|
||||||
|
{/* Darker on the left where the copy sits */}
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(90deg, rgba(7,7,13,0.92) 0%, rgba(7,7,13,0.7) 42%, rgba(7,7,13,0.4) 100%)' }} />
|
||||||
|
{/* Fade the band into the section background at the bottom */}
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(7,7,13,0) 55%, #07070d 100%)' }} />
|
||||||
|
</div>
|
||||||
<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: '64px 64px', pointerEvents: 'none' }} />
|
<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: '64px 64px', pointerEvents: 'none' }} />
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20, position: 'relative' }}>Meet the Agents</div>
|
<SectionEyebrow variant="bracket" style={{ marginBottom: 20, position: 'relative' }}>Meet the Agents</SectionEyebrow>
|
||||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', position: 'relative', maxWidth: 700 }}>
|
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', position: 'relative', maxWidth: 700 }}>
|
||||||
Four agents.<br />One AI.
|
Four agents.<br />One AI.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -160,7 +218,7 @@ export default function MeetTheCrewPage() {
|
|||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<div style={{ position: 'relative' }}>
|
<div style={{ position: 'relative' }}>
|
||||||
<ScrollStack useWindowScroll itemDistance={160} itemScale={0.035} itemStackDistance={40} baseScale={0.86} hideBehind>
|
<ScrollStack useWindowScroll disabled={isMobile} stackPosition="14%" scaleEndPosition="8%" itemDistance={160} itemScale={0} itemStackDistance={40} baseScale={1} hideBehind>
|
||||||
{PRODUCTS.map((p, i) => (
|
{PRODUCTS.map((p, i) => (
|
||||||
<ScrollStackItem key={i}>
|
<ScrollStackItem key={i}>
|
||||||
<div style={{
|
<div style={{
|
||||||
@@ -168,22 +226,26 @@ export default function MeetTheCrewPage() {
|
|||||||
background: `radial-gradient(130% 80% at 100% 0%, ${p.color}22 0%, transparent 46%), linear-gradient(155deg, #16161f 0%, #0a0a10 100%)`,
|
background: `radial-gradient(130% 80% at 100% 0%, ${p.color}22 0%, transparent 46%), linear-gradient(155deg, #16161f 0%, #0a0a10 100%)`,
|
||||||
border: `1px solid ${p.color}4d`,
|
border: `1px solid ${p.color}4d`,
|
||||||
boxShadow: `inset 0 1px 0 rgba(255,255,255,0.06)`,
|
boxShadow: `inset 0 1px 0 rgba(255,255,255,0.06)`,
|
||||||
borderRadius: 28, padding: 'clamp(36px, 4vw, 64px)',
|
borderRadius: 28, padding: 'clamp(26px, 3vw, 48px)',
|
||||||
minHeight: 'clamp(380px, 48vh, 500px)', height: '100%', boxSizing: 'border-box',
|
minHeight: 'clamp(320px, 42vh, 440px)', height: '100%', boxSizing: 'border-box',
|
||||||
}}>
|
}}>
|
||||||
{/* Accent glow blob */}
|
{/* Background photo */}
|
||||||
<div aria-hidden style={{ position: 'absolute', top: '-22%', right: '-8%', width: 360, height: 360, borderRadius: '50%', background: `radial-gradient(circle, ${p.color}26 0%, transparent 70%)`, pointerEvents: 'none' }} />
|
<Image src={p.image} alt="" fill sizes="(max-width: 900px) 100vw, 1100px" style={{ objectFit: 'cover', opacity: 0.55 }} />
|
||||||
|
{/* Left-anchored scrim — copy sits on near-solid dark, photo breathes to the right */}
|
||||||
|
<div aria-hidden style={{ position: 'absolute', inset: 0, background: `linear-gradient(100deg, rgba(8,8,12,0.94) 0%, rgba(8,8,12,0.82) 34%, rgba(8,8,12,0.5) 66%, rgba(8,8,12,0.32) 100%)`, pointerEvents: 'none' }} />
|
||||||
|
{/* Accent tint from the top-right + bottom vignette for depth */}
|
||||||
|
<div aria-hidden style={{ position: 'absolute', inset: 0, background: `radial-gradient(120% 90% at 100% 0%, ${p.color}2e 0%, transparent 42%), linear-gradient(180deg, transparent 55%, rgba(6,6,10,0.7) 100%)`, pointerEvents: 'none' }} />
|
||||||
|
|
||||||
<div className="mtc-product-grid" style={{ position: 'relative', display: 'grid', gridTemplateColumns: '1.3fr 1fr', gap: 'clamp(32px, 4.5vw, 68px)', alignItems: 'center', height: '100%' }}>
|
<div className="mtc-product-grid" style={{ position: 'relative', display: 'grid', gridTemplateColumns: '1.3fr 1fr', gap: 'clamp(32px, 4.5vw, 68px)', alignItems: 'center', height: '100%' }}>
|
||||||
|
|
||||||
{/* Left — identity + description */}
|
{/* Left — identity + description */}
|
||||||
<div>
|
<div>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 24 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 16 }}>
|
||||||
<span style={{ fontSize: 15, fontWeight: 800, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '0.05em' }}>{p.n}</span>
|
<span style={{ fontSize: 15, fontWeight: 800, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '0.05em' }}>{p.n}</span>
|
||||||
<span style={{ fontSize: '0.74rem', fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: p.color, fontFamily: 'Sora, sans-serif' }}>{p.subtitle}</span>
|
<span style={{ fontSize: '0.74rem', fontWeight: 700, letterSpacing: '0.12em', textTransform: 'uppercase', color: p.color, fontFamily: 'Sora, sans-serif' }}>{p.subtitle}</span>
|
||||||
</div>
|
</div>
|
||||||
<h3 style={{ fontSize: 'clamp(2.1rem, 3.4vw, 3.4rem)', fontWeight: 800, letterSpacing: '-0.035em', color: '#fff', margin: '0 0 20px', fontFamily: 'Sora, sans-serif', lineHeight: 1 }}>{p.title}</h3>
|
<h3 style={{ fontSize: 'clamp(2rem, 3.2vw, 3.2rem)', fontWeight: 800, letterSpacing: '-0.035em', color: '#fff', margin: '0 0 14px', fontFamily: 'Sora, sans-serif', lineHeight: 1 }}>{p.title}</h3>
|
||||||
<p style={{ fontSize: 'clamp(15px, 1.1vw, 17px)', lineHeight: 1.8, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 30px', maxWidth: 520 }}>{p.desc}</p>
|
<p style={{ fontSize: 'clamp(14px, 1.05vw, 16px)', lineHeight: 1.65, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 22px', maxWidth: 520 }}>{p.desc}</p>
|
||||||
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>
|
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 10 }}>
|
||||||
{p.tags.map((tag) => (
|
{p.tags.map((tag) => (
|
||||||
<span key={tag} style={{
|
<span key={tag} style={{
|
||||||
@@ -195,19 +257,23 @@ export default function MeetTheCrewPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right — metric hero + capabilities */}
|
{/* Right — one frosted-glass panel floating on the photo */}
|
||||||
<div>
|
|
||||||
<div style={{
|
<div style={{
|
||||||
padding: 'clamp(24px, 2.6vw, 34px)', borderRadius: 22, marginBottom: 24,
|
padding: 'clamp(22px, 2.4vw, 30px)', borderRadius: 22,
|
||||||
background: `linear-gradient(150deg, ${p.color}2b 0%, ${p.color}0d 100%)`,
|
background: 'rgba(9,9,14,0.52)',
|
||||||
border: `1px solid ${p.color}55`,
|
backdropFilter: 'blur(18px)', WebkitBackdropFilter: 'blur(18px)',
|
||||||
boxShadow: `0 22px 54px -26px ${p.color}66, inset 0 1px 0 rgba(255,255,255,0.08)`,
|
border: `1px solid ${p.color}40`,
|
||||||
|
boxShadow: `0 40px 90px -40px rgba(0,0,0,0.85), inset 0 1px 0 rgba(255,255,255,0.08)`,
|
||||||
}}>
|
}}>
|
||||||
<div style={{ fontSize: 'clamp(2.4rem, 3.4vw, 3.4rem)', fontWeight: 900, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.04em', lineHeight: 1, textShadow: `0 0 30px ${p.color}55` }}>{p.metric.val}</div>
|
{/* Metric hero */}
|
||||||
|
<div style={{ fontSize: 'clamp(2.6rem, 3.6vw, 3.6rem)', fontWeight: 900, color: p.color, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.04em', lineHeight: 1, textShadow: `0 0 34px ${p.color}66` }}>{p.metric.val}</div>
|
||||||
<div style={{ fontSize: 12, fontWeight: 700, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif', marginTop: 10, textTransform: 'uppercase', letterSpacing: '0.08em' }}>{p.metric.lbl}</div>
|
<div style={{ fontSize: 12, fontWeight: 700, color: 'rgba(255,255,255,0.5)', fontFamily: 'Sora, sans-serif', marginTop: 10, textTransform: 'uppercase', letterSpacing: '0.08em' }}>{p.metric.lbl}</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
{/* Divider */}
|
||||||
|
<div aria-hidden style={{ height: 1, margin: 'clamp(16px, 1.8vw, 22px) 0', background: `linear-gradient(90deg, ${p.color}66 0%, ${p.color}14 45%, transparent 100%)` }} />
|
||||||
|
|
||||||
|
{/* Capabilities */}
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||||
{p.capabilities.map((cap) => (
|
{p.capabilities.map((cap) => (
|
||||||
<div key={cap} style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
|
<div key={cap} style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
|
||||||
<span style={{
|
<span style={{
|
||||||
@@ -218,7 +284,7 @@ export default function MeetTheCrewPage() {
|
|||||||
}}>
|
}}>
|
||||||
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke={p.color} strokeWidth="3.2" strokeLinecap="round" strokeLinejoin="round"><path d="M20 6L9 17l-5-5" /></svg>
|
||||||
</span>
|
</span>
|
||||||
<span style={{ fontSize: 14.5, lineHeight: 1.55, color: 'rgba(255,255,255,0.72)', fontFamily: 'Sora, sans-serif' }}>{cap}</span>
|
<span style={{ fontSize: 14.5, lineHeight: 1.55, color: 'rgba(255,255,255,0.82)', fontFamily: 'Sora, sans-serif' }}>{cap}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -253,8 +319,18 @@ export default function MeetTheCrewPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>{`
|
<style>{`
|
||||||
|
.mtc-value-card { animation: mtcVcGradient 11s ease infinite; }
|
||||||
|
@keyframes mtcVcGradient { 0% { background-position: 0% 50%; } 50% { background-position: 100% 50%; } 100% { background-position: 0% 50%; } }
|
||||||
|
.mtc-vc-glow { animation: mtcVcFloat 9s ease-in-out infinite; }
|
||||||
|
@keyframes mtcVcFloat { 0%,100% { transform: translate(0,0); } 50% { transform: translate(-16px, 18px); } }
|
||||||
|
.mtc-vc-shine { background: linear-gradient(115deg, transparent 32%, rgba(255,255,255,0.22) 48%, transparent 64%); transform: translateX(-130%); transition: transform 0.9s cubic-bezier(0.22,1,0.36,1); }
|
||||||
|
.mtc-value-card:hover { transform: translateY(-8px) scale(1.02); box-shadow: 0 34px 60px -22px var(--vc-glow); }
|
||||||
|
.mtc-value-card:hover .mtc-vc-shine { transform: translateX(130%); }
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.mtc-value-card, .mtc-vc-glow { animation: none !important; }
|
||||||
|
}
|
||||||
@media (max-width: 860px) {
|
@media (max-width: 860px) {
|
||||||
.mtc-value-row { grid-template-columns: 1fr !important; gap: 10px !important; padding: 28px 0 !important; }
|
.mtc-value-grid { grid-template-columns: 1fr !important; }
|
||||||
.mtc-product-grid { grid-template-columns: 1fr !important; }
|
.mtc-product-grid { grid-template-columns: 1fr !important; }
|
||||||
}
|
}
|
||||||
`}</style>
|
`}</style>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import Image from 'next/image';
|
|||||||
import GSAPAnimator from '@/components/GSAPAnimator';
|
import GSAPAnimator from '@/components/GSAPAnimator';
|
||||||
import { useReveal } from '@/hooks/useReveal';
|
import { useReveal } from '@/hooks/useReveal';
|
||||||
import ProductsScrollSection from '@/components/use-cases/ProductsScrollSection';
|
import ProductsScrollSection from '@/components/use-cases/ProductsScrollSection';
|
||||||
|
import SectionEyebrow from '@/components/SectionEyebrow';
|
||||||
|
|
||||||
const OUTCOMES = [
|
const OUTCOMES = [
|
||||||
{ stat: '3.2×', label: 'Conversion rate increase' },
|
{ stat: '3.2×', label: 'Conversion rate increase' },
|
||||||
@@ -16,24 +17,44 @@ const OUTCOMES = [
|
|||||||
|
|
||||||
const CAPS = [
|
const CAPS = [
|
||||||
{
|
{
|
||||||
n: '01', title: 'Merchant Campaign Platform', color: '#f4be28',
|
title: 'Merchant Campaign Platform', accent: '#7dd957',
|
||||||
|
bg: 'linear-gradient(140deg, #1d3f2b 0%, #0f2a1c 100%)', image: '/ai_agents/agent-1-c.png',
|
||||||
|
icon: (<><path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z" /><path d="M3 6h18" /><path d="M16 10a4 4 0 0 1-8 0" /></>),
|
||||||
desc: 'Register your store, upload products and inventory, then launch interactive promotional campaigns in minutes. Define maximum discounts per product and track performance in real time.',
|
desc: 'Register your store, upload products and inventory, then launch interactive promotional campaigns in minutes. Define maximum discounts per product and track performance in real time.',
|
||||||
metrics: [{ val: '5+', lbl: 'Game formats' }, { val: '1-click', lbl: 'Campaign setup' }],
|
metrics: [
|
||||||
|
{ val: '5+', lbl: 'Game formats', icon: (<><line x1="6" x2="10" y1="12" y2="12" /><line x1="8" x2="8" y1="10" y2="14" /><line x1="15" x2="15.01" y1="13" y2="13" /><line x1="18" x2="18.01" y1="11" y2="11" /><rect width="20" height="12" x="2" y="6" rx="2" /></>) },
|
||||||
|
{ val: '1-click', lbl: 'Campaign setup', icon: (<><path d="m3 3 7.07 16.97 2.51-7.39 7.39-2.51L3 3z" /><path d="m13 13 6 6" /></>) },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
n: '02', title: 'Game Based Discount Engine', color: '#a78bfa',
|
title: 'Game Based Discount Engine', accent: '#a78bfa',
|
||||||
|
bg: 'linear-gradient(140deg, #2a2352 0%, #1a1838 100%)', image: '/ai_agents/agent-2-c.png',
|
||||||
|
icon: (<><line x1="6" x2="10" y1="12" y2="12" /><line x1="8" x2="8" y1="10" y2="14" /><line x1="15" x2="15.01" y1="13" y2="13" /><line x1="18" x2="18.01" y1="11" y2="11" /><rect width="20" height="12" x="2" y="6" rx="2" /></>),
|
||||||
desc: 'Select a product, choose a game (Maze, Spin Wheel, Scratch Card, or Match Master) and set a maximum discount. Customers play once and discover their reward only after completing the game.',
|
desc: 'Select a product, choose a game (Maze, Spin Wheel, Scratch Card, or Match Master) and set a maximum discount. Customers play once and discover their reward only after completing the game.',
|
||||||
metrics: [{ val: '4×', lbl: 'Engagement lift' }, { val: '100%', lbl: 'Margin controlled' }],
|
metrics: [
|
||||||
|
{ val: '4×', lbl: 'Engagement lift', icon: (<><polyline points="22 7 13.5 15.5 8.5 10.5 2 17" /><polyline points="16 7 22 7 22 13" /></>) },
|
||||||
|
{ val: '100%', lbl: 'Margin controlled', icon: (<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />) },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
n: '03', title: 'Smart Coupon System', color: '#34d399',
|
title: 'Smart Coupon System', accent: '#34d399',
|
||||||
|
bg: 'linear-gradient(140deg, #16704a 0%, #0d4d33 100%)', image: '/ai_agents/agent-4-c.png',
|
||||||
|
icon: (<path d="M2 9a3 3 0 1 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 1 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z" />),
|
||||||
desc: 'Generate daily surprise coupons through the Battery Challenge and other activities. Offer flat discounts, BOGO, percentage-off deals, festival specials, and brand promotions, fresh every day.',
|
desc: 'Generate daily surprise coupons through the Battery Challenge and other activities. Offer flat discounts, BOGO, percentage-off deals, festival specials, and brand promotions, fresh every day.',
|
||||||
metrics: [{ val: '6', lbl: 'Coupon types' }, { val: 'Daily', lbl: 'Fresh surprises' }],
|
metrics: [
|
||||||
|
{ val: '6', lbl: 'Coupon types', icon: (<><path d="M20.59 13.41 13.42 20.6a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82z" /><line x1="7" x2="7.01" y1="7" y2="7" /></>) },
|
||||||
|
{ val: 'Daily', lbl: 'Fresh surprises', icon: (<><rect width="18" height="18" x="3" y="4" rx="2" /><line x1="16" x2="16" y1="2" y2="6" /><line x1="8" x2="8" y1="2" y2="6" /><line x1="3" x2="21" y1="10" y2="10" /></>) },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
n: '04', title: 'Footfall & Campaign Analytics', color: '#f97316',
|
title: 'Footfall & Campaign Analytics', accent: '#ffd8a8',
|
||||||
|
bg: 'linear-gradient(140deg, #ee7420 0%, #d1560a 100%)', image: '/ai_agents/agent-3-c.png',
|
||||||
|
icon: (<><line x1="18" x2="18" y1="20" y2="10" /><line x1="12" x2="12" y1="20" y2="4" /><line x1="6" x2="6" y1="20" y2="14" /></>),
|
||||||
desc: 'Track which campaigns drove store visits, measure conversion rates, monitor repeat footfall, and optimize your promotional budget with a real-time analytics dashboard.',
|
desc: 'Track which campaigns drove store visits, measure conversion rates, monitor repeat footfall, and optimize your promotional budget with a real-time analytics dashboard.',
|
||||||
metrics: [{ val: '98%', lbl: 'Count accuracy' }, { val: '<9ms', lbl: 'Real-time latency' }],
|
metrics: [
|
||||||
|
{ val: '98%', lbl: 'Count accuracy', icon: (<><circle cx="12" cy="12" r="10" /><circle cx="12" cy="12" r="6" /><circle cx="12" cy="12" r="2" /></>) },
|
||||||
|
{ val: '<9ms', lbl: 'Real-time latency', icon: (<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />) },
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -58,29 +79,34 @@ const PILLARS = [
|
|||||||
const WHO = [
|
const WHO = [
|
||||||
{
|
{
|
||||||
label: 'Retail Stores',
|
label: 'Retail Stores',
|
||||||
|
image: '/who_its_for-business/01.png',
|
||||||
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l1-4h16l1 4"/><path d="M3 9h18v11a1 1 0 01-1 1H4a1 1 0 01-1-1V9z"/><path d="M9 21V12h6v9"/><path d="M3 9a2 2 0 002 2 2 2 0 002-2 2 2 0 002 2 2 2 0 002-2 2 2 0 002 2 2 2 0 002-2"/></svg>,
|
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M3 9l1-4h16l1 4"/><path d="M3 9h18v11a1 1 0 01-1 1H4a1 1 0 01-1-1V9z"/><path d="M9 21V12h6v9"/><path d="M3 9a2 2 0 002 2 2 2 0 002-2 2 2 0 002 2 2 2 0 002-2 2 2 0 002 2 2 2 0 002-2"/></svg>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Shopping Malls',
|
label: 'Shopping Malls',
|
||||||
|
image: '/who_its_for-business/02.png',
|
||||||
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="7" width="20" height="14" rx="1"/><path d="M16 7V5a2 2 0 00-2-2h-4a2 2 0 00-2 2v2"/><path d="M12 12v3"/><path d="M8 12h8"/></svg>,
|
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><rect x="2" y="7" width="20" height="14" rx="1"/><path d="M16 7V5a2 2 0 00-2-2h-4a2 2 0 00-2 2v2"/><path d="M12 12v3"/><path d="M8 12h8"/></svg>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'QSR Chains',
|
label: 'QSR Chains',
|
||||||
|
image: '/who_its_for-business/03.png',
|
||||||
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8h1a4 4 0 010 8h-1"/><path d="M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8z"/><line x1="6" y1="1" x2="6" y2="4"/><line x1="10" y1="1" x2="10" y2="4"/><line x1="14" y1="1" x2="14" y2="4"/></svg>,
|
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><path d="M18 8h1a4 4 0 010 8h-1"/><path d="M2 8h16v9a4 4 0 01-4 4H6a4 4 0 01-4-4V8z"/><line x1="6" y1="1" x2="6" y2="4"/><line x1="10" y1="1" x2="10" y2="4"/><line x1="14" y1="1" x2="14" y2="4"/></svg>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Franchise Businesses',
|
label: 'Franchise Businesses',
|
||||||
|
image: '/who_its_for-business/04.png',
|
||||||
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="5" r="2"/><circle cx="5" cy="19" r="2"/><circle cx="19" cy="19" r="2"/><path d="M12 7v4M12 11l-5 6M12 11l5 6"/></svg>,
|
icon: <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#f4be28" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="5" r="2"/><circle cx="5" cy="19" r="2"/><circle cx="19" cy="19" r="2"/><path d="M12 7v4M12 11l-5 6M12 11l5 6"/></svg>,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
function RevealBlock({ children, delay = 0, className = '' }: { children: React.ReactNode; delay?: number; className?: string }) {
|
function RevealBlock({ children, delay = 0, className = '', from = 'translateY(48px)', duration = 0.9 }: { children: React.ReactNode; delay?: number; className?: string; from?: string; duration?: number }) {
|
||||||
const { ref, visible } = useReveal();
|
const { ref, visible } = useReveal();
|
||||||
return (
|
return (
|
||||||
<div ref={ref} className={className} style={{
|
<div ref={ref} className={className} style={{
|
||||||
opacity: visible ? 1 : 0,
|
opacity: visible ? 1 : 0,
|
||||||
transform: visible ? 'translateY(0)' : 'translateY(48px)',
|
transform: visible ? 'none' : from,
|
||||||
transition: `opacity 0.9s cubic-bezier(0.22,1,0.36,1) ${delay}s, transform 0.9s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
|
transition: `opacity ${duration}s cubic-bezier(0.22,1,0.36,1) ${delay}s, transform ${duration}s cubic-bezier(0.22,1,0.36,1) ${delay}s`,
|
||||||
|
willChange: 'opacity, transform',
|
||||||
}}>
|
}}>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
@@ -89,7 +115,6 @@ function RevealBlock({ children, delay = 0, className = '' }: { children: React.
|
|||||||
|
|
||||||
export default function ForBusinessPage() {
|
export default function ForBusinessPage() {
|
||||||
const [countVisible, setCountVisible] = useState(false);
|
const [countVisible, setCountVisible] = useState(false);
|
||||||
const [hovered, setHovered] = useState<number | null>(null);
|
|
||||||
const statsRef = useRef<HTMLDivElement>(null);
|
const statsRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -180,27 +205,78 @@ export default function ForBusinessPage() {
|
|||||||
</RevealBlock>
|
</RevealBlock>
|
||||||
<div className="uc-challenge-grid" 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>
|
<RevealBlock>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 24 }}>The Challenge</div>
|
<SectionEyebrow variant="bar" style={{ marginBottom: 24 }}>The Challenge</SectionEyebrow>
|
||||||
<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' }}>
|
<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 />
|
You're Getting<br />Visitors. But<br />
|
||||||
<em style={{ fontStyle: 'normal', color: '#c9960f' }}>Missing Revenue.</em>
|
<em style={{ fontStyle: 'normal', color: '#c9960f' }}>Missing Revenue.</em>
|
||||||
</h2>
|
</h2>
|
||||||
</RevealBlock>
|
</RevealBlock>
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 0 }}>
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||||
{[
|
{[
|
||||||
"You don't know who walks into your store.",
|
{
|
||||||
"You don't know what they're looking for.",
|
title: "You don't know who walks into your store.",
|
||||||
"You don't know why they leave.",
|
bg: 'linear-gradient(135deg, #6366f1 0%, #4338ca 100%)', glow: 'rgba(99,102,241,0.45)',
|
||||||
"And your discounts? Just guesswork.",
|
icon: (
|
||||||
].map((line, i) => (
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<RevealBlock key={i} delay={i * 0.1}>
|
<circle cx="12" cy="8" r="4" /><path d="M4 21c1.6-4.2 5-6 8-6s6.4 1.8 8 6" />
|
||||||
<div style={{
|
</svg>
|
||||||
padding: '24px 0',
|
),
|
||||||
borderBottom: '1px solid rgba(0,0,0,0.07)',
|
},
|
||||||
display: 'flex', gap: 20, alignItems: 'flex-start',
|
{
|
||||||
|
title: "You don't know what they're looking for.",
|
||||||
|
bg: 'linear-gradient(135deg, #10b981 0%, #047857 100%)', glow: 'rgba(16,185,129,0.45)',
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "You don't know why they leave.",
|
||||||
|
bg: 'linear-gradient(135deg, #fb7185 0%, #be123c 100%)', glow: 'rgba(244,63,94,0.45)',
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M14 21V4a1 1 0 0 0-1.27-.96l-6 1.7A1 1 0 0 0 6 5.7V21" /><path d="M4 21h16" /><path d="M10.5 12v.5" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "And your discounts? Just guesswork.",
|
||||||
|
bg: 'linear-gradient(135deg, #f59e0b 0%, #c9960f 100%)', glow: 'rgba(245,158,11,0.45)',
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M20.6 13.4 12 22l-9-9V4h9z" /><circle cx="7.5" cy="8.5" r="1.3" /><path d="M8.5 15.5l6-6" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
].map((item, i) => (
|
||||||
|
<RevealBlock key={i} delay={i * 0.14} from="translateX(60px) scale(0.94)" duration={0.8}>
|
||||||
|
<div className="uc-challenge-card" style={{
|
||||||
|
position: 'relative', overflow: 'hidden',
|
||||||
|
padding: '24px 26px', borderRadius: 20,
|
||||||
|
background: item.bg,
|
||||||
|
boxShadow: `0 20px 40px -22px ${item.glow}`,
|
||||||
|
display: 'flex', gap: 18, alignItems: 'center',
|
||||||
|
['--cc-glow' as string]: item.glow,
|
||||||
|
transition: 'transform 0.4s cubic-bezier(0.22,1,0.36,1), box-shadow 0.4s ease',
|
||||||
}}>
|
}}>
|
||||||
<span style={{ fontSize: 12, fontWeight: 700, color: '#f4be28', fontFamily: 'Sora, sans-serif', marginTop: 3, flexShrink: 0 }}>0{i + 1}</span>
|
{/* Glossy sheen overlay */}
|
||||||
<span style={{ fontSize: 'clamp(16px, 1.6vw, 22px)', fontWeight: 600, color: '#1a1a1a', fontFamily: 'Sora, sans-serif', lineHeight: 1.4 }}>{line}</span>
|
<span aria-hidden style={{
|
||||||
|
position: 'absolute', inset: 0, pointerEvents: 'none',
|
||||||
|
background: 'linear-gradient(135deg, rgba(255,255,255,0.22) 0%, rgba(255,255,255,0) 42%)',
|
||||||
|
}} />
|
||||||
|
{/* Icon tile */}
|
||||||
|
<div className="uc-cc-icon" style={{
|
||||||
|
width: 50, height: 50, borderRadius: 15, flexShrink: 0,
|
||||||
|
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
||||||
|
background: 'rgba(255,255,255,0.18)', color: '#ffffff',
|
||||||
|
border: '1px solid rgba(255,255,255,0.25)',
|
||||||
|
transition: 'background 0.4s ease, color 0.4s ease',
|
||||||
|
}}>{item.icon}</div>
|
||||||
|
{/* Copy */}
|
||||||
|
<div style={{ position: 'relative', zIndex: 1, minWidth: 0 }}>
|
||||||
|
<span style={{ fontSize: 'clamp(15px, 1.5vw, 20px)', fontWeight: 600, color: '#ffffff', fontFamily: 'Sora, sans-serif', lineHeight: 1.35 }}>{item.title}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</RevealBlock>
|
</RevealBlock>
|
||||||
))}
|
))}
|
||||||
@@ -216,7 +292,7 @@ export default function ForBusinessPage() {
|
|||||||
<RevealBlock>
|
<RevealBlock>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 80, flexWrap: 'wrap', gap: 24 }}>
|
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-end', marginBottom: 80, flexWrap: 'wrap', gap: 24 }}>
|
||||||
<div>
|
<div>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 16 }}>The AI Agents</div>
|
<SectionEyebrow variant="number" index="01" style={{ marginBottom: 16 }}>The AI Agents</SectionEyebrow>
|
||||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, 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 agents.<br />One AI.
|
Four agents.<br />One AI.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -227,34 +303,56 @@ export default function ForBusinessPage() {
|
|||||||
</div>
|
</div>
|
||||||
</RevealBlock>
|
</RevealBlock>
|
||||||
|
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
|
<div className="uc-cap-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(2, 1fr)', gap: 20 }}>
|
||||||
{CAPS.map((cap, i) => (
|
{CAPS.map((cap, i) => (
|
||||||
<RevealBlock key={i} delay={i * 0.08}>
|
<RevealBlock key={i} delay={i * 0.1}>
|
||||||
<div
|
<div className="uc-cap-card" style={{
|
||||||
onMouseEnter={() => setHovered(i)}
|
position: 'relative', overflow: 'hidden', height: '100%', minHeight: 320, boxSizing: 'border-box',
|
||||||
onMouseLeave={() => setHovered(null)}
|
padding: 'clamp(28px, 3vw, 44px)', borderRadius: 26,
|
||||||
className="uc-cap-row"
|
background: cap.bg,
|
||||||
style={{
|
border: `1px solid ${cap.accent}30`,
|
||||||
display: 'grid', gridTemplateColumns: '80px 1fr 1fr auto',
|
boxShadow: '0 28px 60px -34px rgba(0,0,0,0.65)',
|
||||||
alignItems: 'center', gap: '4vw',
|
transition: 'transform 0.4s cubic-bezier(0.22,1,0.36,1), box-shadow 0.4s ease',
|
||||||
padding: '40px 0',
|
}}>
|
||||||
borderBottom: '1px solid rgba(0,0,0,0.07)',
|
{/* Dotted decoration, top-right */}
|
||||||
cursor: 'default',
|
<div aria-hidden style={{ position: 'absolute', top: 26, right: 26, width: 78, height: 42, backgroundImage: `radial-gradient(${cap.accent}66 1.4px, transparent 1.4px)`, backgroundSize: '12px 12px', opacity: 0.55, pointerEvents: 'none' }} />
|
||||||
transition: 'all 0.3s ease',
|
|
||||||
}}
|
{/* Product image — cropped artwork, centered in the right area with breathing room */}
|
||||||
>
|
<div className="uc-cap-art" style={{ position: 'absolute', top: 0, bottom: 0, right: 'clamp(14px, 2.2vw, 40px)', width: '40%', display: 'flex', alignItems: 'center', justifyContent: 'center', pointerEvents: 'none', zIndex: 0 }}>
|
||||||
<span style={{ fontSize: 13, fontWeight: 700, color: hovered === i ? cap.color : 'rgba(0,0,0,0.2)', fontFamily: 'Sora, sans-serif', letterSpacing: '0.06em', transition: 'color 0.3s' }}>{cap.n}</span>
|
<Image src={cap.image} alt={cap.title} width={520} height={460} style={{ width: '100%', height: 'auto', maxHeight: '76%', objectFit: 'contain' }} />
|
||||||
<h3 style={{ fontSize: 'clamp(22px, 2.8vw, 40px)', fontWeight: 800, color: hovered === i ? '#0a0a0f' : 'rgba(0,0,0,0.8)', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em', margin: 0, transition: 'color 0.3s' }}>{cap.title}</h3>
|
</div>
|
||||||
<p style={{ fontSize: 15, color: 'rgba(0,0,0,0.45)', fontFamily: 'Sora, sans-serif', margin: 0, lineHeight: 1.6, maxWidth: 340 }}>{cap.desc}</p>
|
|
||||||
<div style={{ display: 'flex', gap: 28 }}>
|
{/* Content — left column; right side left empty for a product image */}
|
||||||
|
<div className="uc-cap-content" style={{ position: 'relative', zIndex: 1, width: '58%' }}>
|
||||||
|
{/* Icon + title + accent underline */}
|
||||||
|
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 16, marginBottom: 18 }}>
|
||||||
|
<span style={{ flexShrink: 0, width: 54, height: 54, borderRadius: '50%', background: `${cap.accent}26`, border: `1px solid ${cap.accent}55`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: cap.accent }}>
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">{cap.icon}</svg>
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<h3 style={{ fontSize: 'clamp(20px, 1.9vw, 27px)', fontWeight: 800, color: '#ffffff', letterSpacing: '-0.025em', margin: 0, lineHeight: 1.1, fontFamily: 'Sora, sans-serif' }}>{cap.title}</h3>
|
||||||
|
<span style={{ display: 'block', width: 38, height: 3, borderRadius: 2, background: cap.accent, marginTop: 12 }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style={{ fontSize: 14.5, lineHeight: 1.7, color: 'rgba(255,255,255,0.72)', margin: '0 0 26px', fontFamily: 'Sora, sans-serif' }}>{cap.desc}</p>
|
||||||
|
|
||||||
|
{/* Metric chips with icon badges */}
|
||||||
|
<div style={{ display: 'flex', flexWrap: 'nowrap', gap: 12 }}>
|
||||||
{cap.metrics.map((m, j) => (
|
{cap.metrics.map((m, j) => (
|
||||||
<div key={j} style={{ textAlign: 'right' }}>
|
<div key={j} style={{ flex: '1 1 0', minWidth: 0, display: 'flex', alignItems: 'center', gap: 11, padding: '12px 14px', borderRadius: 14, background: 'rgba(0,0,0,0.22)', border: `1px solid ${cap.accent}26` }}>
|
||||||
<div style={{ fontSize: 22, fontWeight: 900, color: cap.color, fontFamily: 'Sora, sans-serif', letterSpacing: '-0.03em' }}>{m.val}</div>
|
<span style={{ flexShrink: 0, width: 34, height: 34, borderRadius: '50%', background: `${cap.accent}30`, display: 'flex', alignItems: 'center', justifyContent: 'center', color: cap.accent }}>
|
||||||
<div style={{ fontSize: 11, color: 'rgba(0,0,0,0.35)', fontFamily: 'Sora, sans-serif', marginTop: 3 }}>{m.lbl}</div>
|
<svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">{m.icon}</svg>
|
||||||
|
</span>
|
||||||
|
<div>
|
||||||
|
<div style={{ fontSize: 18, fontWeight: 900, color: '#ffffff', fontFamily: 'Sora, sans-serif', letterSpacing: '-0.02em', lineHeight: 1 }}>{m.val}</div>
|
||||||
|
<div style={{ fontSize: 11, color: 'rgba(255,255,255,0.55)', fontFamily: 'Sora, sans-serif', marginTop: 4 }}>{m.lbl}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</RevealBlock>
|
</RevealBlock>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -264,7 +362,7 @@ export default function ForBusinessPage() {
|
|||||||
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
|
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, overflow: 'hidden', position: 'relative' }}>
|
||||||
<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: '64px 64px', pointerEvents: 'none' }} />
|
<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: '64px 64px', pointerEvents: 'none' }} />
|
||||||
<RevealBlock>
|
<RevealBlock>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20, position: 'relative' }}>How It Works</div>
|
<SectionEyebrow variant="dot" style={{ marginBottom: 20, position: 'relative' }}>How It Works</SectionEyebrow>
|
||||||
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 700, position: 'relative' }}>
|
<h2 style={{ fontSize: 'clamp(36px, 5vw, 72px)', fontWeight: 800, letterSpacing: '-0.04em', lineHeight: 0.95, color: '#fff', margin: '0 0 80px', fontFamily: 'Sora, sans-serif', maxWidth: 700, position: 'relative' }}>
|
||||||
Three ways we grow your store.
|
Three ways we grow your store.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -416,7 +514,7 @@ export default function ForBusinessPage() {
|
|||||||
<p style={{ fontSize: 16, lineHeight: 1.8, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 32px', maxWidth: 480 }}>
|
<p style={{ fontSize: 16, lineHeight: 1.8, color: 'rgba(0,0,0,0.55)', fontFamily: 'Sora, sans-serif', margin: '0 0 32px', maxWidth: 480 }}>
|
||||||
Loyaly's proximity engine sends instant notifications to shoppers browsing within your store's radius. No ad spend. No guesswork. Just timely, relevant offers delivered at exactly the right moment.
|
Loyaly's proximity engine sends instant notifications to shoppers browsing within your store's radius. No ad spend. No guesswork. Just timely, relevant offers delivered at exactly the right moment.
|
||||||
</p>
|
</p>
|
||||||
<div className="uc-prox-stats" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 12, maxWidth: 500 }}>
|
<div className="uc-prox-stats" style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12, maxWidth: 500 }}>
|
||||||
{[
|
{[
|
||||||
{ v: 'Instant', l: 'Notification speed', icon: <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" /> },
|
{ v: 'Instant', l: 'Notification speed', icon: <path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z" /> },
|
||||||
{ v: '0', l: 'Ad spend required', icon: <><line x1="12" y1="1" x2="12" y2="23" /><path d="M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6" /></> },
|
{ v: '0', l: 'Ad spend required', icon: <><line x1="12" y1="1" x2="12" y2="23" /><path d="M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6" /></> },
|
||||||
@@ -446,7 +544,7 @@ export default function ForBusinessPage() {
|
|||||||
{/* ── OUTCOMES ── */}
|
{/* ── OUTCOMES ── */}
|
||||||
<section ref={statsRef} style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
<section ref={statsRef} style={{ padding: '140px 8%', background: '#fff', margin: '0 20px 20px', borderRadius: 24 }}>
|
||||||
<RevealBlock>
|
<RevealBlock>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 20 }}>Outcomes</div>
|
<SectionEyebrow variant="line" style={{ marginBottom: 20 }}>Outcomes</SectionEyebrow>
|
||||||
<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' }}>
|
<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.
|
Real Results.<br />Not Assumptions.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -468,14 +566,17 @@ export default function ForBusinessPage() {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ── WHO IT'S FOR ── */}
|
{/* ── WHO IT'S FOR ── */}
|
||||||
<section style={{ padding: '140px 8%', background: '#07070d', margin: '0 20px 20px', borderRadius: 24, position: 'relative', overflow: 'hidden' }}>
|
<section style={{ padding: '140px 8%', background: '#1a1a24', margin: '0 20px 20px', borderRadius: 24, position: 'relative', overflow: 'hidden' }}>
|
||||||
{/* Background photo */}
|
{/* Background photo */}
|
||||||
<Image src="/images/stock/physical retail.png" alt="" fill style={{ objectFit: 'cover', opacity: 0.35 }} />
|
<Image src="/images/stock/physical retail.png" alt="" fill style={{ objectFit: 'cover', opacity: 0.72, filter: 'blur(7px)', transform: 'scale(1.06)' }} />
|
||||||
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(7,7,13,0.8) 0%, rgba(7,7,13,0.95) 100%)', pointerEvents: 'none' }} />
|
{/* Minimal black vignette — keeps the center bright, softly shades only the edges */}
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'radial-gradient(125% 125% at 50% 45%, transparent 55%, rgba(8,8,12,0.55) 100%)', pointerEvents: 'none' }} />
|
||||||
|
{/* Light lift for the heading, top-left */}
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(105deg, rgba(8,8,12,0.45) 0%, transparent 45%)', pointerEvents: 'none' }} />
|
||||||
|
|
||||||
<div style={{ position: 'relative', zIndex: 1 }}>
|
<div style={{ position: 'relative', zIndex: 1 }}>
|
||||||
<RevealBlock>
|
<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>
|
<SectionEyebrow variant="bracket" style={{ marginBottom: 20 }}>Who It's For</SectionEyebrow>
|
||||||
<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 }}>
|
<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.
|
Built for every format of physical retail.
|
||||||
</h2>
|
</h2>
|
||||||
@@ -483,15 +584,23 @@ export default function ForBusinessPage() {
|
|||||||
<div className="uc-who-grid" 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) => (
|
{WHO.map((w, i) => (
|
||||||
<RevealBlock key={i} delay={i * 0.1}>
|
<RevealBlock key={i} delay={i * 0.1}>
|
||||||
<div style={{
|
<div className="uc-who-card" style={{
|
||||||
padding: '48px 32px',
|
position: 'relative', overflow: 'hidden',
|
||||||
background: 'rgba(255,255,255,0.03)', border: '1px solid rgba(255,255,255,0.08)',
|
padding: '32px', minHeight: 300,
|
||||||
borderRadius: 20, backdropFilter: 'blur(12px)',
|
border: '1px solid rgba(255,255,255,0.08)',
|
||||||
transition: 'all 0.3s ease',
|
borderRadius: 20,
|
||||||
|
display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
|
||||||
|
transition: 'transform 0.4s cubic-bezier(0.22,1,0.36,1), border-color 0.4s ease',
|
||||||
}}>
|
}}>
|
||||||
<div style={{ marginBottom: 20 }}>{w.icon}</div>
|
{/* Background photo */}
|
||||||
|
<Image src={w.image} alt="" fill sizes="(max-width: 860px) 50vw, 25vw" style={{ objectFit: 'cover' }} />
|
||||||
|
{/* Legibility overlay — heavier at the bottom where the copy sits */}
|
||||||
|
<div style={{ position: 'absolute', inset: 0, background: 'linear-gradient(180deg, rgba(7,7,13,0.35) 0%, rgba(7,7,13,0.45) 45%, rgba(7,7,13,0.9) 100%)', pointerEvents: 'none' }} />
|
||||||
|
<div style={{ position: 'relative', zIndex: 1 }}>
|
||||||
|
<div style={{ marginBottom: 16 }}>{w.icon}</div>
|
||||||
<div style={{ fontSize: 20, fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif' }}>{w.label}</div>
|
<div style={{ fontSize: 20, fontWeight: 800, color: '#fff', fontFamily: 'Sora, sans-serif' }}>{w.label}</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</RevealBlock>
|
</RevealBlock>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@@ -521,6 +630,7 @@ export default function ForBusinessPage() {
|
|||||||
100% { transform: scale(1.7); opacity: 0; }
|
100% { transform: scale(1.7); opacity: 0; }
|
||||||
}
|
}
|
||||||
.uc-pillar-card:hover { transform: translateY(-8px) scale(1.015); filter: brightness(1.12) saturate(1.08); }
|
.uc-pillar-card:hover { transform: translateY(-8px) scale(1.015); filter: brightness(1.12) saturate(1.08); }
|
||||||
|
.uc-cap-card:hover { transform: translateY(-6px); box-shadow: 0 34px 64px -32px rgba(23,21,18,0.38); }
|
||||||
.uc-radar-dot { animation: ucDotPulse 2s ease-in-out infinite; }
|
.uc-radar-dot { animation: ucDotPulse 2s ease-in-out infinite; }
|
||||||
@keyframes ucDotPulse { 0%, 100% { transform: scale(1); opacity: 0.85; } 50% { transform: scale(1.25); opacity: 1; } }
|
@keyframes ucDotPulse { 0%, 100% { transform: scale(1); opacity: 0.85; } 50% { transform: scale(1.25); opacity: 1; } }
|
||||||
.uc-pin-halo { animation: ucHaloPulse 2.6s ease-in-out infinite; }
|
.uc-pin-halo { animation: ucHaloPulse 2.6s ease-in-out infinite; }
|
||||||
@@ -535,15 +645,20 @@ export default function ForBusinessPage() {
|
|||||||
@keyframes ucMapPing { 0%, 100% { r: 6px; opacity: 0.5; } 50% { r: 13px; opacity: 0.12; } }
|
@keyframes ucMapPing { 0%, 100% { r: 6px; opacity: 0.5; } 50% { r: 13px; opacity: 0.12; } }
|
||||||
.uc-map-livedot { animation: ucMapLive 1.5s ease-in-out infinite; }
|
.uc-map-livedot { animation: ucMapLive 1.5s ease-in-out infinite; }
|
||||||
@keyframes ucMapLive { 0% { box-shadow: 0 0 0 0 rgba(22,163,74,0.5); } 70% { box-shadow: 0 0 0 6px rgba(22,163,74,0); } 100% { box-shadow: 0 0 0 0 rgba(22,163,74,0); } }
|
@keyframes ucMapLive { 0% { box-shadow: 0 0 0 0 rgba(22,163,74,0.5); } 70% { box-shadow: 0 0 0 6px rgba(22,163,74,0); } 100% { box-shadow: 0 0 0 0 rgba(22,163,74,0); } }
|
||||||
|
.uc-who-card:hover { transform: translateY(-6px); border-color: rgba(244,190,40,0.55); }
|
||||||
|
.uc-challenge-card:hover { transform: translateY(-6px) scale(1.015); box-shadow: 0 30px 55px -20px var(--cc-glow); }
|
||||||
|
.uc-challenge-card:hover .uc-cc-icon { background: #ffffff; color: #0a0a0f; }
|
||||||
@media (max-width: 860px) {
|
@media (max-width: 860px) {
|
||||||
.uc-challenge-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
|
.uc-challenge-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
|
||||||
.uc-outcomes-grid { grid-template-columns: repeat(2,1fr) !important; }
|
.uc-outcomes-grid { grid-template-columns: repeat(2,1fr) !important; }
|
||||||
.uc-outcomes-grid > div > div { border-radius: 16px !important; }
|
.uc-outcomes-grid > div > div { border-radius: 16px !important; }
|
||||||
.uc-who-grid { grid-template-columns: repeat(2,1fr) !important; }
|
.uc-who-grid { grid-template-columns: repeat(2,1fr) !important; }
|
||||||
.uc-cap-row { grid-template-columns: 1fr !important; gap: 14px !important; padding: 28px 0 !important; }
|
.uc-cap-grid { grid-template-columns: 1fr !important; }
|
||||||
.uc-cap-row > div:last-child { justify-content: flex-start !important; }
|
.uc-cap-content { width: 62% !important; }
|
||||||
|
.uc-cap-art { width: 40% !important; }
|
||||||
.uc-pillar-grid { grid-template-columns: 1fr !important; }
|
.uc-pillar-grid { grid-template-columns: 1fr !important; }
|
||||||
.uc-proximity-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
|
.uc-proximity-grid { grid-template-columns: 1fr !important; gap: 40px !important; }
|
||||||
|
.uc-prox-stats { grid-template-columns: repeat(2, minmax(0, 1fr)) !important; }
|
||||||
.uc-timeline-row { grid-template-columns: 44px 1fr !important; gap: 18px !important; }
|
.uc-timeline-row { grid-template-columns: 44px 1fr !important; gap: 18px !important; }
|
||||||
.uc-timeline-row > div:first-child { width: 44px !important; height: 44px !important; }
|
.uc-timeline-row > div:first-child { width: 44px !important; height: 44px !important; }
|
||||||
.uc-timeline-line { left: 21px !important; }
|
.uc-timeline-line { left: 21px !important; }
|
||||||
@@ -551,6 +666,8 @@ export default function ForBusinessPage() {
|
|||||||
@media (max-width: 520px) {
|
@media (max-width: 520px) {
|
||||||
.uc-outcomes-grid { grid-template-columns: 1fr !important; }
|
.uc-outcomes-grid { grid-template-columns: 1fr !important; }
|
||||||
.uc-who-grid { grid-template-columns: 1fr !important; }
|
.uc-who-grid { grid-template-columns: 1fr !important; }
|
||||||
|
.uc-cap-content { width: 100% !important; }
|
||||||
|
.uc-cap-art { display: none !important; }
|
||||||
}
|
}
|
||||||
@media (prefers-reduced-motion: reduce) {
|
@media (prefers-reduced-motion: reduce) {
|
||||||
.uc-radar-ring { animation: none !important; opacity: 0.4 !important; }
|
.uc-radar-ring { animation: none !important; opacity: 0.4 !important; }
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.scroll-stack-inner {
|
.scroll-stack-inner {
|
||||||
padding: 20vh 0 20rem;
|
padding: 8vh 0 20rem;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ interface ScrollStackProps {
|
|||||||
blurAmount?: number;
|
blurAmount?: number;
|
||||||
hideBehind?: boolean;
|
hideBehind?: boolean;
|
||||||
useWindowScroll?: boolean;
|
useWindowScroll?: boolean;
|
||||||
|
disabled?: boolean;
|
||||||
onStackComplete?: () => void;
|
onStackComplete?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,6 +46,7 @@ const ScrollStack = ({
|
|||||||
blurAmount = 0,
|
blurAmount = 0,
|
||||||
hideBehind = false,
|
hideBehind = false,
|
||||||
useWindowScroll = false,
|
useWindowScroll = false,
|
||||||
|
disabled = false,
|
||||||
onStackComplete,
|
onStackComplete,
|
||||||
}: ScrollStackProps) => {
|
}: ScrollStackProps) => {
|
||||||
const scrollerRef = useRef<HTMLDivElement>(null);
|
const scrollerRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -288,6 +290,20 @@ const ScrollStack = ({
|
|||||||
cardsRef.current = cards;
|
cardsRef.current = cards;
|
||||||
const transformsCache = lastTransformsRef.current;
|
const transformsCache = lastTransformsRef.current;
|
||||||
|
|
||||||
|
// Disabled (e.g. on mobile): clear any pinning transforms so the cards
|
||||||
|
// flow as a normal scrollable list, and skip the whole sticky-stack setup.
|
||||||
|
if (disabled) {
|
||||||
|
cards.forEach((card) => {
|
||||||
|
card.style.transform = '';
|
||||||
|
card.style.filter = '';
|
||||||
|
card.style.opacity = '';
|
||||||
|
card.style.marginBottom = '';
|
||||||
|
card.style.willChange = '';
|
||||||
|
});
|
||||||
|
transformsCache.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
cards.forEach((card, i) => {
|
cards.forEach((card, i) => {
|
||||||
if (i < cards.length - 1) {
|
if (i < cards.length - 1) {
|
||||||
card.style.marginBottom = `${itemDistance}px`;
|
card.style.marginBottom = `${itemDistance}px`;
|
||||||
@@ -323,7 +339,7 @@ const ScrollStack = ({
|
|||||||
isUpdatingRef.current = false;
|
isUpdatingRef.current = false;
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [itemDistance, itemScale, itemStackDistance, stackPosition, scaleEndPosition, baseScale, rotationAmount, blurAmount, useWindowScroll, onStackComplete, setupLenis, updateCardTransforms, measurePositions]);
|
}, [itemDistance, itemScale, itemStackDistance, stackPosition, scaleEndPosition, baseScale, rotationAmount, blurAmount, useWindowScroll, disabled, onStackComplete, setupLenis, updateCardTransforms, measurePositions]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -331,7 +347,7 @@ const ScrollStack = ({
|
|||||||
ref={scrollerRef}
|
ref={scrollerRef}
|
||||||
style={useWindowScroll ? { overflow: 'visible', height: 'auto', position: 'static' } : undefined}
|
style={useWindowScroll ? { overflow: 'visible', height: 'auto', position: 'static' } : undefined}
|
||||||
>
|
>
|
||||||
<div className="scroll-stack-inner">
|
<div className="scroll-stack-inner" style={disabled ? { padding: 0, minHeight: 0 } : undefined}>
|
||||||
{children}
|
{children}
|
||||||
{/* Spacer so the last pin can release cleanly */}
|
{/* Spacer so the last pin can release cleanly */}
|
||||||
<div className="scroll-stack-end" />
|
<div className="scroll-stack-end" />
|
||||||
|
|||||||
124
src/components/SectionEyebrow.tsx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
/* ── Section eyebrow — the small uppercase label that sits above a section
|
||||||
|
heading. One component, several distinct motifs, so each section on a page
|
||||||
|
can carry a visually unique kicker while staying consistent in quality.
|
||||||
|
|
||||||
|
variant:
|
||||||
|
'line' — a gold rule that fades out, then the label
|
||||||
|
'bar' — a short vertical gradient bar + label
|
||||||
|
'pill' — label inside a soft bordered gold pill
|
||||||
|
'bracket' — label framed by two L-shaped corner brackets
|
||||||
|
'dot' — a pulsing gold dot + label
|
||||||
|
'number' — a large index figure + hairline divider + label
|
||||||
|
|
||||||
|
`index` is used by the 'number' variant (e.g. "01").
|
||||||
|
Colours are gold-accented and read on both light and dark backgrounds. ── */
|
||||||
|
|
||||||
|
const ACCENT = '#f4be28';
|
||||||
|
|
||||||
|
export type EyebrowVariant = 'line' | 'bar' | 'pill' | 'bracket' | 'dot' | 'number';
|
||||||
|
|
||||||
|
export default function SectionEyebrow({
|
||||||
|
children,
|
||||||
|
variant = 'line',
|
||||||
|
index,
|
||||||
|
style,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
variant?: EyebrowVariant;
|
||||||
|
index?: string;
|
||||||
|
style?: React.CSSProperties;
|
||||||
|
}) {
|
||||||
|
const label: React.CSSProperties = {
|
||||||
|
fontFamily: 'Sora, sans-serif', fontWeight: 700, textTransform: 'uppercase',
|
||||||
|
fontSize: 13, letterSpacing: '0.22em', color: ACCENT, lineHeight: 1,
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
};
|
||||||
|
const row: React.CSSProperties = {
|
||||||
|
display: 'inline-flex', alignItems: 'center', gap: 14, ...style,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (variant === 'pill') {
|
||||||
|
return (
|
||||||
|
<span style={{
|
||||||
|
...row, gap: 0, padding: '10px 20px', borderRadius: 999,
|
||||||
|
border: `1px solid ${ACCENT}55`, background: `${ACCENT}14`,
|
||||||
|
boxShadow: `0 8px 24px -16px ${ACCENT}`,
|
||||||
|
}}>
|
||||||
|
<span style={{ ...label, fontSize: 12, letterSpacing: '0.2em' }}>{children}</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === 'bar') {
|
||||||
|
return (
|
||||||
|
<span style={row}>
|
||||||
|
<span aria-hidden style={{
|
||||||
|
width: 5, height: 24, borderRadius: 3,
|
||||||
|
background: `linear-gradient(180deg, ${ACCENT} 0%, ${ACCENT}55 100%)`,
|
||||||
|
}} />
|
||||||
|
<span style={label}>{children}</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === 'bracket') {
|
||||||
|
return (
|
||||||
|
<span style={{ ...row, position: 'relative', gap: 0, padding: '9px 16px' }}>
|
||||||
|
<span aria-hidden style={{ position: 'absolute', top: 0, left: 0, width: 12, height: 12, borderTop: `2px solid ${ACCENT}`, borderLeft: `2px solid ${ACCENT}` }} />
|
||||||
|
<span aria-hidden style={{ position: 'absolute', bottom: 0, right: 0, width: 12, height: 12, borderBottom: `2px solid ${ACCENT}`, borderRight: `2px solid ${ACCENT}` }} />
|
||||||
|
<span style={label}>{children}</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === 'dot') {
|
||||||
|
return (
|
||||||
|
<span style={row}>
|
||||||
|
<span aria-hidden className="se-dot" style={{
|
||||||
|
position: 'relative', width: 9, height: 9, borderRadius: '50%', background: ACCENT, flexShrink: 0,
|
||||||
|
}} />
|
||||||
|
<span style={label}>{children}</span>
|
||||||
|
<style>{`
|
||||||
|
.se-dot::after {
|
||||||
|
content: ''; position: absolute; inset: -5px; border-radius: 50%;
|
||||||
|
border: 1.5px solid ${ACCENT}; opacity: 0.6;
|
||||||
|
animation: seDotPulse 2s ease-out infinite;
|
||||||
|
}
|
||||||
|
@keyframes seDotPulse {
|
||||||
|
0% { transform: scale(0.6); opacity: 0.7; }
|
||||||
|
100% { transform: scale(1.5); opacity: 0; }
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) { .se-dot::after { animation: none; } }
|
||||||
|
`}</style>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variant === 'number') {
|
||||||
|
return (
|
||||||
|
<span style={{ ...row, gap: 16 }}>
|
||||||
|
<span aria-hidden style={{
|
||||||
|
fontFamily: 'Sora, sans-serif', fontWeight: 800, fontSize: 26, lineHeight: 1,
|
||||||
|
letterSpacing: '-0.02em', color: ACCENT,
|
||||||
|
}}>{index}</span>
|
||||||
|
<span aria-hidden style={{ width: 1, height: 22, background: `${ACCENT}66` }} />
|
||||||
|
<span style={label}>{children}</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'line' (default)
|
||||||
|
return (
|
||||||
|
<span style={row}>
|
||||||
|
<span aria-hidden style={{
|
||||||
|
width: 46, height: 2, borderRadius: 2,
|
||||||
|
background: `linear-gradient(90deg, ${ACCENT} 0%, ${ACCENT}00 100%)`,
|
||||||
|
}} />
|
||||||
|
<span style={label}>{children}</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@
|
|||||||
import React, { useEffect, useLayoutEffect, useRef } from 'react';
|
import React, { useEffect, useLayoutEffect, useRef } from 'react';
|
||||||
import { gsap } from 'gsap';
|
import { gsap } from 'gsap';
|
||||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||||
import { PhoneMockupImage } from './PhoneMockup';
|
|
||||||
import { FpTagCard, FpStatCard } from './FpCard';
|
import { FpTagCard, FpStatCard } from './FpCard';
|
||||||
import { useCountUp, useStaggerReveal } from './fpMotion';
|
import { useCountUp, useStaggerReveal } from './fpMotion';
|
||||||
|
|
||||||
@@ -77,15 +76,67 @@ export default function EarnRewardsSection() {
|
|||||||
<div className="fp-shell" style={{ position: 'relative', zIndex: 1 }}>
|
<div className="fp-shell" style={{ position: 'relative', zIndex: 1 }}>
|
||||||
<div className="fp-split" style={{ gridTemplateColumns: '0.95fr 1.05fr' }}>
|
<div className="fp-split" style={{ gridTemplateColumns: '0.95fr 1.05fr' }}>
|
||||||
|
|
||||||
{/* Left — phone mockup, matches the in-app Earn / Home experience */}
|
{/* Left — live LytsUp Earn preview embedded in a real phone frame */}
|
||||||
<div ref={phoneRef} style={{ display: 'flex', justifyContent: 'center', opacity: 0, width: '100%' }}>
|
<div ref={phoneRef} style={{ display: 'flex', justifyContent: 'center', opacity: 0, width: '100%' }}>
|
||||||
<PhoneMockupImage
|
<div
|
||||||
src="/For_people/01_earn-cropped.png"
|
className="erm-phone-shell"
|
||||||
alt="LytsUp Earn screen showing Lyts balance and activity feed"
|
style={{
|
||||||
floatClassName="erm-phone-shell"
|
width: 300, borderRadius: 42, padding: 10,
|
||||||
aspectRatio={1684 / 3196}
|
background: 'linear-gradient(160deg, #2a2a2e 0%, #0a0a0c 100%)',
|
||||||
|
boxShadow: '0 60px 120px -24px rgba(0,0,0,0.7), 0 0 0 1px rgba(255,255,255,0.06)',
|
||||||
|
position: 'relative',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Screen — locked to a real phone's aspect ratio (~9:19.5) */}
|
||||||
|
<div style={{
|
||||||
|
borderRadius: 32, overflow: 'hidden', background: '#ffffff',
|
||||||
|
position: 'relative', aspectRatio: '9 / 19.5',
|
||||||
|
display: 'flex', flexDirection: 'column',
|
||||||
|
}}>
|
||||||
|
{/* Dynamic island */}
|
||||||
|
<div style={{
|
||||||
|
position: 'absolute', top: 8, left: '50%', transform: 'translateX(-50%)',
|
||||||
|
width: 82, height: 22, borderRadius: 14, background: '#000', zIndex: 5,
|
||||||
|
}} />
|
||||||
|
{/* Safe area — reserves the notch strip above the app's top bar */}
|
||||||
|
<div style={{ height: 32, flexShrink: 0, background: '#ffffff' }} />
|
||||||
|
{/* The embed is a Flutter web app built for a real phone's logical
|
||||||
|
width (390px). Rendering it at 390 and scaling to fit keeps the
|
||||||
|
UI proportioned as designed instead of cramped at ~280px. The
|
||||||
|
iframe is overscanned ~16px past the right clip so Flutter's
|
||||||
|
desktop-style list scrollbar falls outside the visible screen. */}
|
||||||
|
<div className="erm-phone-viewport" style={{ position: 'relative', flex: 1, minHeight: 0, overflow: 'hidden' }}>
|
||||||
|
<iframe
|
||||||
|
src="https://anbarasu22004.github.io/lytsup_view/"
|
||||||
|
title="LytsUp Earn screen live preview"
|
||||||
|
loading="lazy"
|
||||||
|
scrolling="no"
|
||||||
|
className="erm-phone-iframe"
|
||||||
|
style={{
|
||||||
|
position: 'absolute', top: 0, left: 0,
|
||||||
|
border: 'none', display: 'block', background: '#ffffff',
|
||||||
|
transformOrigin: 'top left',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
<style>{`
|
||||||
|
@keyframes ermPhoneFloat { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-10px); } }
|
||||||
|
.erm-phone-shell { animation: ermPhoneFloat 7s ease-in-out infinite; }
|
||||||
|
/* --pm-scale = screen width (frame - 2*10px padding) ÷ 390px design width */
|
||||||
|
.erm-phone-shell { --pm-scale: calc(280 / 390); }
|
||||||
|
.erm-phone-iframe {
|
||||||
|
width: calc(100% / var(--pm-scale) + 16px);
|
||||||
|
height: calc(100% / var(--pm-scale));
|
||||||
|
transform: scale(var(--pm-scale));
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) { .erm-phone-shell { animation: none !important; } }
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.erm-phone-shell { width: 280px !important; --pm-scale: calc(260 / 390); }
|
||||||
|
}
|
||||||
|
`}</style>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Right — headline */}
|
{/* Right — headline */}
|
||||||
<div>
|
<div>
|
||||||
@@ -109,12 +160,12 @@ export default function EarnRewardsSection() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Stat strip — same card system as the tags above, distinguished by a filled dark surface */}
|
{/* Stat strip — same card system as the tags above, distinguished by a filled dark surface */}
|
||||||
<div ref={statsRef} style={{ display: 'flex', gap: 10, marginBottom: 28, maxWidth: 460 }}>
|
<div ref={statsRef} style={{ display: 'flex', flexWrap: 'wrap', gap: 10, marginBottom: 28, maxWidth: 460 }}>
|
||||||
<FpStatCard theme="fill" layout="row" style={{ flex: '1 1 0' }} icon="⚡"
|
<FpStatCard theme="fill" layout="row" style={{ flex: '1 1 130px', minWidth: 0 }} icon="⚡"
|
||||||
value={<span ref={waysCountRef}>10</span>} label="ways to earn" />
|
value={<span ref={waysCountRef}>10</span>} label="ways to earn" />
|
||||||
<FpStatCard theme="fill" layout="row" style={{ flex: '1 1 0' }} icon="🔥"
|
<FpStatCard theme="fill" layout="row" style={{ flex: '1 1 130px', minWidth: 0 }} icon="🔥"
|
||||||
value="24/7" label="always live" />
|
value="24/7" label="always live" />
|
||||||
<FpStatCard theme="fill" layout="row" style={{ flex: '1 1 0' }} icon="🚀"
|
<FpStatCard theme="fill" layout="row" style={{ flex: '1 1 130px', minWidth: 0 }} icon="🚀"
|
||||||
value="∞" label="no earn cap" />
|
value="∞" label="no earn cap" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import React, { useEffect, useLayoutEffect, useRef } from 'react';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { gsap } from 'gsap';
|
import { gsap } from 'gsap';
|
||||||
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
import { ScrollTrigger } from 'gsap/ScrollTrigger';
|
||||||
|
import SectionEyebrow from '@/components/SectionEyebrow';
|
||||||
|
|
||||||
if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
|
if (typeof window !== 'undefined') gsap.registerPlugin(ScrollTrigger);
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ export default function ProductsScrollSection() {
|
|||||||
|
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div style={{ position: 'relative', zIndex: 2, marginBottom: 'clamp(56px,8vh,96px)' }}>
|
<div style={{ position: 'relative', zIndex: 2, marginBottom: 'clamp(56px,8vh,96px)' }}>
|
||||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.2em', textTransform: 'uppercase', color: '#f4be28', fontFamily: 'Sora, sans-serif', marginBottom: 16 }}>Our Products</div>
|
<SectionEyebrow variant="pill" style={{ marginBottom: 16 }}>Our Products</SectionEyebrow>
|
||||||
<h2 style={{ fontSize: 'clamp(32px, 4.5vw, 64px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#fff', margin: 0, fontFamily: 'Sora, sans-serif', maxWidth: 640 }}>
|
<h2 style={{ fontSize: 'clamp(32px, 4.5vw, 64px)', fontWeight: 800, letterSpacing: '-0.05em', lineHeight: 0.92, color: '#fff', margin: 0, fontFamily: 'Sora, sans-serif', maxWidth: 640 }}>
|
||||||
AI Powered Retail Intelligence.
|
AI Powered Retail Intelligence.
|
||||||
</h2>
|
</h2>
|
||||||
|
|||||||