/* Sections — all the page chunks except hero (which lives in app.jsx) */ const { useState, useEffect, useRef } = React; /* ==== reveal-on-scroll wrapper ==== */ function Reveal({ children, delay = 0, as: Tag = 'div', className = '', ...rest }) { const ref = useRef(null); const [visible, setVisible] = useState(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => {if (e.isIntersecting) {setVisible(true);io.disconnect();}}); }, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' }); io.observe(el); return () => io.disconnect(); }, []); return ( {children} ); } /* ============ NAV ============ */ function Nav() { const [scrolled, setScrolled] = useState(false); useEffect(() => { const onScroll = () => setScrolled(window.scrollY > 24); onScroll(); window.addEventListener('scroll', onScroll, { passive: true }); return () => window.removeEventListener('scroll', onScroll); }, []); return ( ); } /* ============ PROBLEM ============ */ function ProblemSection() { const stats = [ { num: '$23B', label: 'in unused US gift cards' }, { num: '43%', label: 'of Americans hold at least one' }, { num: '$244', label: 'average value per person' }]; return (
The problem

The largest dormant balance in your wallet.

Forgotten gift cards in a dimly lit drawer
{stats.map((s, i) =>
{s.num}
{s.label}
)}
Existing buyers take 8–30%. Most cards never get used. That value belongs to you.
); } /* ============ SOLUTION ============ */ function SolutionSection() { return (
The solution

Every brand. One protocol.

Register cards from any major brand and earn $LOOPG. Keep your card. Get tokens. The protocol does the rest.

A grid of nine major retail and food brand gift card logos
300+ brands supported · Priority verification for Tier 1 retailers
); } /* ============ HOW IT WORKS ============ */ function StepIcon({ kind }) { // Abstract geometric icons — not literal gift cards if (kind === 'register') return ( ); if (kind === 'earn') return ( ); return ( ); } /* ===== HOW IT WORKS ANIMATED DEMO ===== */ function TypedText({ text, active, speed = 60 }) { const [shown, setShown] = useState(''); useEffect(() => { if (!active) {setShown('');return;} setShown(''); let i = 0; const t = setInterval(() => { i++; setShown(text.slice(0, i)); if (i >= text.length) clearInterval(t); }, speed); return () => clearInterval(t); }, [text, active, speed]); return {shown}|; } function ScreenRegister({ active }) { const [success, setSuccess] = useState(false); useEffect(() => { if (!active) {setSuccess(false);return;} const t = setTimeout(() => setSuccess(true), 2200); return () => clearTimeout(t); }, [active]); return (
Register card
Brand
Target
Balance
$
Provisional allocation
8,742 $LOOPG
); } function ScreenEarn({ active }) { const [bal, setBal] = useState(8742.00); useEffect(() => { if (!active) {setBal(8742.00);return;} let raf; const start = performance.now(); const tick = (now) => { const t = (now - start) / 1000; setBal(8742.00 + t * 0.85); raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [active]); const whole = Math.floor(bal).toLocaleString(); const frac = (bal % 1).toFixed(2).slice(2); return (
$LOOPG balance
{whole} .{frac} $LOOPG
+24.50 today
Verified
Unlocking 30%
); } function ScreenKeep({ active }) { return (
Your card
•••• •••• •••• 4287
Balance
$87.42
STILL YOURS
Spend it whenever — at face value.
BALANCE: $87.42 · STATUS: ACTIVE
); } function HowItWorksDemo({ activeStep }) { return (
9:41
LoopG
); } function HiwArrows({ activeStep }) { // Three curved paths converging toward the top of the phone (x=600, y=150 in viewBox). // Step centers: 200 (left), 600 (mid), 1000 (right). const paths = [ 'M 200,4 C 200,70 360,116 600,150', 'M 600,4 L 600,150', 'M 1000,4 C 1000,70 840,116 600,150']; return ( ); } function HowItWorks() { const steps = [ { num: '01', icon: 'register', title: 'Register', copy: 'Submit card details. We log the value on-chain. Provisional allocation begins immediately.', meta: 'FACE VALUE · UNCHANGED · YOURS FOREVER' }, { num: '02', icon: 'earn', title: 'Earn $LOOPG', copy: 'Get tokens proportional to what you registered. Verification tier sets the unlock pace.', meta: 'ON-CHAIN · INSTANT · NO CARD HANDOVER' }, { num: '03', icon: 'keep', title: 'Keep your card', copy: 'It stays yours. Spend it whenever — at face value, on whatever you actually want.', meta: 'RAYDIUM · SOLANA SPL · 9 DECIMALS' }]; const screens = [ScreenRegister, ScreenEarn, ScreenKeep]; const [activeStep, setActiveStep] = useState(0); const sectionRef = useRef(null); useEffect(() => { let inView = false; const io = new IntersectionObserver((entries) => { entries.forEach((e) => {inView = e.isIntersecting;}); }, { threshold: 0.2 }); if (sectionRef.current) io.observe(sectionRef.current); const t = setInterval(() => { if (inView) setActiveStep((s) => (s + 1) % 3); }, 4000); return () => {clearInterval(t);io.disconnect();}; }, []); return (
How it works

Three steps. No surrendered cards.

{steps.map((s, i) =>
setActiveStep(i)} onMouseEnter={() => setActiveStep(i)} style={{ cursor: 'pointer' }}>
{s.num}
{s.title}
{s.copy}
{s.meta}
)}
9:41
LoopG
{screens.map((Screen, i) =>
)}
); } /* ============ FACTION WARS ============ */ const BRAND_LOGOS = { 'Target': 'assets/logos/target.png', 'Walmart': 'assets/logos/walmart.png', 'Home Depot': 'assets/logos/homedepot.png', 'Starbucks': 'assets/logos/starbucks.png', 'Apple': 'assets/logos/apple.png', 'DoorDash': 'assets/logos/doordash.png' }; function BrandLogo({ name }) { const src = BRAND_LOGOS[name]; if (!src) { return ( ); } return ( ); } function FactionWars() { const initial = [ { name: 'Target', val: 1247392 }, { name: 'Walmart', val: 983104 }, { name: 'Home Depot', val: 742856 }, { name: 'Starbucks', val: 621003 }, { name: 'Apple', val: 418772 }, { name: 'DoorDash', val: 287440 }]; const max = initial[0].val; const [values, setValues] = useState(initial.map((b) => ({ ...b, animated: 0 }))); const ref = useRef(null); const startedRef = useRef(false); useEffect(() => { const el = ref.current; if (!el) return; const io = new IntersectionObserver((entries) => { entries.forEach((e) => { if (e.isIntersecting && !startedRef.current) { startedRef.current = true; // animate bars requestAnimationFrame(() => { setValues(initial.map((b) => ({ ...b, animated: b.val }))); }); } }); }, { threshold: 0.2 }); io.observe(el); return () => io.disconnect(); }, []); // Live tick: random small bumps to make it feel live useEffect(() => { if (!startedRef.current) return; const t = setInterval(() => { setValues((prev) => prev.map((b) => ({ ...b, val: b.val + Math.floor(Math.random() * 80) }))); }, 2200); return () => clearInterval(t); }, []); return (
Faction wars

Brands ranked by trapped value.

Live · updated {new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })}
{values.map((b, i) => { const pct = Math.min(100, b.animated / max * 100); return (
{String(i + 1).padStart(2, '0')}
{b.name}
${b.val.toLocaleString()}
); })} Help your brand win the Genesis Window.  Priority Brands unlock fastest.
); } /* ============ TOKEN ============ */ function useInViewOnce(ref, threshold = 0.3) { const [seen, setSeen] = useState(false); useEffect(() => { if (!ref.current || seen) return; const io = new IntersectionObserver((ents) => { ents.forEach((e) => { if (e.isIntersecting) { setSeen(true); io.disconnect(); } }); }, { threshold }); io.observe(ref.current); return () => io.disconnect(); }, [seen]); return seen; } function AnimatedNumber({ to, suffix = '', duration = 1800, start }) { const [val, setVal] = useState(0); useEffect(() => { if (!start) { setVal(0); return; } const t0 = performance.now(); let raf; const ease = (t) => 1 - Math.pow(1 - t, 4); const tick = (now) => { const t = Math.min(1, (now - t0) / duration); setVal(to * ease(t)); if (t < 1) raf = requestAnimationFrame(tick); }; raf = requestAnimationFrame(tick); return () => cancelAnimationFrame(raf); }, [start, to, duration]); // format: show with one decimal under 10, else integer with commas const fmt = to >= 1000 ? Math.round(val).toLocaleString() : to >= 10 ? Math.round(val).toString() : val.toFixed(1); return {fmt}{suffix}; } function Donut() { const segments = [ { name: 'Claimant Pool', pct: 60, color: '#FF0080' }, { name: 'Team', pct: 15, color: '#9333EA' }, { name: 'Treasury', pct: 10, color: '#534BB1' }, { name: 'Growth', pct: 10, color: '#AB9FF2' }, { name: 'DEX Float', pct: 5, color: '#3B0764' }, ]; const r = 86, c = 2 * Math.PI * r; const ref = useRef(null); const seen = useInViewOnce(ref, 0.35); const [hover, setHover] = useState(-1); // pre-compute starts in % so we can stagger animation per segment const starts = []; let acc = 0; segments.forEach((s) => { starts.push(acc); acc += s.pct; }); return (
{segments.map((s, i) => { const fullDash = (s.pct / 100) * c; const startFrac = starts[i] / 100; const segDelay = i * 220; // ms const isHover = hover === i; const isDim = hover !== -1 && !isHover; return ( setHover(i)} onMouseLeave={() => setHover(-1)} style={{ cursor: 'pointer' }}> {/* Glow under hover */} {isHover && ( )} ); })}
= 0 ? segments[hover].pct : 10} suffix={hover >= 0 ? '%' : 'B'} start={seen} duration={hover >= 0 ? 400 : 1800} />
{hover >= 0 ? segments[hover].name : '$LOOPG · Fixed'}
{segments.map((s, i) => (
setHover(i)} onMouseLeave={() => setHover(-1)}> {s.name}
))}
); } function TokenSection() { return (
$LOOPG token

Utility, not speculation.

$LOOPG is the protocol's coordination layer. Holders get queue priority, governance votes, referral rewards, and access to staking. Solana SPL. Fixed supply, no inflation.

Chain
Solana SPL
Supply
10,000,000,000
Inflation
None · fixed
Listing
Raydium · Genesis
See launch economics
); } /* ============ LOOPCHAIN ============ */ function LoopChainSection() { return (
); } /* ============ VERIFICATION ============ */ function VerifyIcon({ kind }) { const s = { fill: 'none', stroke: 'currentColor', strokeWidth: 1.4, strokeLinecap: 'round', strokeLinejoin: 'round' }; if (kind === 'shield') return ( ); if (kind === 'lock') return ( ); if (kind === 'clock') return ( ); // globe return ( ); } function VerificationSection() { const tiers = [ { tag: 'Tier 0', name: 'Registered', copy: 'Card details submitted. Provisional allocation. Counts in the global counter.', mark: 't0', meter: 33 }, { tag: 'Tier 1', name: 'Soft Verified', copy: 'Video proof submitted. Sybil-resistance layer activated. Faster unlocks.', mark: 't1', meter: 66 }, { tag: 'Tier 2', name: 'Hard Verified', copy: 'Protocol verifies card balance directly. Full unlock pace. Top of the queue.', mark: 't2', meter: 100 }]; return (
Verification protocol

Three tiers. Honest allocations.

{[ { kind: 'shield', tone: 'green', t1: 'Sybil-resistant', t2: 'verification' }, { kind: 'lock', tone: 'brand', t1: 'Privacy-first', t2: 'by design' }, { kind: 'clock', tone: 'brand', t1: 'Fair queue', t2: 'execution' }, { kind: 'globe', tone: 'brand', t1: 'Global counter.', t2: 'Transparent pace.' }, ].map((p, i) => (
{p.t1} {p.t2}
))}
{tiers.map((t, i) =>
{i}
{t.tag}
{t.name}
{t.copy}
)}
Brand tiers
Cards from Priority Brands (top retailers, large balances) unlock first. Standard Queue cards unlock as protocol throughput allows. Both earn $LOOPG — pace differs.
); } /* ============ STAKE ============ */ function StakeQueueViz() { return ( {/* Queue lanes */} {[80, 140, 200, 260].map((y, i) => )} {/* Standard claims (gray) drifting slowly */} {[ { y: 80, x: 60, w: 50 }, { y: 80, x: 130, w: 50 }, { y: 80, x: 200, w: 50 }, { y: 200, x: 100, w: 50 }, { y: 200, x: 170, w: 50 }, { y: 200, x: 240, w: 50 }, { y: 200, x: 310, w: 50 }, { y: 260, x: 80, w: 50 }, { y: 260, x: 150, w: 50 }, { y: 260, x: 220, w: 50 }, { y: 260, x: 290, w: 50 }]. map((c, i) => )} {/* Staked claims jumping ahead with glow */} {/* Genesis line */} Genesis → {/* Labels */} Tier 2 Tier 1 + Stake Tier 1 Tier 0 ); } function StakeSection() { return (
Coming soon · Phase 2

Stake-to-Accelerate.

Stake $LOOPG to push specific claims to the front of the verification queue. Day-one utility for token holders. Position scales with stake size and duration.

); } /* ============ ROADMAP ============ */ function RoadmapSection() { const phases = [ { num: 'Phase 1', when: 'Live now', status: 'live', title: 'Registry & Genesis', items: ['Portal & onboarding', 'Claims registry', 'Genesis Window allocations', '$LOOPG launch on Raydium'] }, { num: 'Phase 2', when: 'Q3 2026', status: 'next', title: 'Verification scale', items: ['Account abstraction (no seed phrase)', 'Video verification (Tier 1)', 'Stake-to-Accelerate', 'Auto-scrape balance verification'] }, { num: 'Phase 3', when: 'Beyond', status: 'future', title: 'Network effects', items: ['Mobile app', 'Processor partnerships', 'Deep LoopChain settlement integration', 'Cross-protocol RWA bridges'] }]; return (
Roadmap

Three phases. Shipping in public.

{phases.map((p, i) =>
{p.num} · {p.when}
{p.status === 'live' && } {p.status === 'live' ? 'Live' : p.status === 'next' ? 'Next' : 'Future'}
{p.title}
    {p.items.map((it) =>
  • {it}
  • )}
)}
); } /* ============ FAQ ============ */ function FAQ() { const items = [ { q: 'Do I lose my card?', a: "No. You keep it. We just log its trapped value on-chain. The card never leaves your wallet — physical or digital." }, { q: 'Do I get cash?', a: "No. You get $LOOPG tokens. This is a registry, not a redemption service. We're not a gift card buyer." }, { q: 'When do tokens unlock?', a: "Per protocol schedule. Verification tier (0/1/2) and brand tier (Priority/Standard) determine pace. Higher tier = faster unlock." }, { q: 'Is $LOOPG a security?', a: "No. It's a utility token providing queue priority, governance voting, fee abatement, and referral rewards. Holders use it to interact with the protocol." }, { q: 'What if I spend my card before unlocks complete?', a: "Future unlocks pause. Past unlocks remain yours. If you spend down before full unlock, the remaining schedule simply ends." }, { q: 'How do you verify balances?', a: "Three tiers: self-attestation (provisional), video proof (sybil-resistant), and protocol-side balance scrape (ground truth). Priority Brands run first — they have programmatic balance APIs." }]; const [open, setOpen] = useState(0); return (
FAQ

Questions, answered honestly.

{items.map((it, i) => { const isOpen = open === i; return (
{it.a}
); })}
); } /* ============ GENESIS CTA ============ */ function GenesisCTA() { const [email, setEmail] = useState(''); const [submitted, setSubmitted] = useState(false); const onSubmit = (e) => {e.preventDefault();if (email.includes('@')) setSubmitted(true);}; return (
); } /* ============ FOOTER ============ */ function SocialIcon({ kind }) { if (kind === 'x') return ( ); if (kind === 'discord') return ( ); return ( ); } function Footer() { return ( ); } Object.assign(window, { Nav, ProblemSection, SolutionSection, HowItWorks, FactionWars, TokenSection, LoopChainSection, VerificationSection, StakeSection, RoadmapSection, FAQ, GenesisCTA, Footer, Reveal });