import { useState, useEffect, useRef, useCallback } from "react";
import { motion, AnimatePresence, useMotionValue, useTransform } from "framer-motion";
// ─── Card Data ────────────────────────────────────────────────────────────────
const ALL_CARDS = [
{ id: 1, rarity: "normal", name: "Scaredy Cat", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f7a204d73c37fb399_spooked.jpg" },
{ id: 2, rarity: "shiny", name: "Dangerous", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f123322fe0b5e19f4_predator.jpg" },
{ id: 3, rarity: "normal", name: "Photogenic", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83fab4d97bcae3ea33b_grimace.jpeg" },
{ id: 4, rarity: "normal", name: "Facebook Savvy", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f17d47cb6632a17ec_medday.jpg" },
{ id: 5, rarity: "shiny", name: "Dodgy", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f497a61541d848ac1_mustache.jpeg" },
{ id: 6, rarity: "shiny", name: "Oh My", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f900ba6a4157bc5df_poker.jpeg" },
{ id: 7, rarity: "normal", name: "Heartbreaker", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f25d072898a281487_heartbreaker.jpeg" },
{ id: 8, rarity: "normal", name: "Snazzy", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f1749c81e31e8fc96_dressed.jpeg" },
{ id: 9, rarity: "normal", name: "Smart Arse", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83fca118a1530c7ee0b_smartie.jpeg" },
{ id: 10, rarity: "normal", name: "Shagger", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f1ba927afe176e6fa_shagger.jpeg" },
{ id: 11, rarity: "normal", name: "Send Help", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f6f2f620ef0bfbf4c_hungover.jpg" },
{ id: 12, rarity: "normal", name: "Needs Chaperoning", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f9e01aa9976ac3d93_drunk.jpg" },
{ id: 13, rarity: "normal", name: "Snow Bunny", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f7a204d73c37fb381_skiing.jpg" },
{ id: 14, rarity: "shiny", name: "Suss", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f888faeaac05681bc_underage.jpg" },
{ id: 15, rarity: "normal", name: "The Truth", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f9eeef2f4b1c3d661_equal.jpg" },
{ id: 16, rarity: "shiny", name: "That Beater Life", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83fbc79b77d4e61d251_beater.jpg" },
{ id: 17, rarity: "normal", name: "Politically Minded", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe83f120dfade398223e1_corrupt.jpeg" },
{ id: 18, rarity: "shiny", name: "Friends in High Places", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfea90eb4522043cfbc93d_friends.png" },
{ id: 19, rarity: "shiny", name: "Values", image: "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfec89fb3c0b997cf02a6a_mcgregor.png" },
];
const CARD_BACK = "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe8327a2e606cc78afd70_card-back.png";
const FOIL_IMG = "https://cdn.prod.website-files.com/5bf56f3c39236d9c81f57c6a/69cfe833a1095b7fe72d5e23_foil.png";
const STAT_NAMES = [
["Pint Speed", "Banter Level", "Dad Energy"],
["Stamina", "Chat Ratio", "Snack Power"],
["Rizz", "Regret IQ", "Clout Score"],
["Party Rage", "Smooth Talk", "Night Survival"],
["Side-eye HP", "Chaos Lvl", "Cringe Res"],
];
function getRandStats(id) {
const pool = STAT_NAMES[id % STAT_NAMES.length];
return pool.map((name) => ({ name, value: Math.floor(Math.random() * 51) + 50 }));
}
function getRandCards() {
const shuffled = [...ALL_CARDS].sort(() => Math.random() - 0.5);
return shuffled.slice(0, 5).map((c) => ({ ...c, stats: getRandStats(c.id) }));
}
// ─── Confetti ─────────────────────────────────────────────────────────────────
function fireConfetti() {
const script = document.createElement("script");
script.src = "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/dist/confetti.browser.min.js";
script.onload = () => {
window.confetti({ particleCount: 180, spread: 100, origin: { y: 0.5 }, colors: ["#ffd700","#ff6b6b","#4ecdc4","#ffe66d","#a8e6cf","#ff8b94"] });
setTimeout(() => window.confetti({ particleCount: 100, angle: 60, spread: 80, origin: { x: 0 }, colors: ["#ffd700","#ff6b6b","#ffe66d"] }), 200);
setTimeout(() => window.confetti({ particleCount: 100, angle: 120, spread: 80, origin: { x: 1 }, colors: ["#4ecdc4","#a8e6cf","#ffd700"] }), 400);
};
document.head.appendChild(script);
}
// ─── Holographic shimmer ──────────────────────────────────────────────────────
const HoloOverlay = ({ mouseX, mouseY, cardRef }) => {
const [pos, setPos] = useState({ x: 50, y: 50 });
useEffect(() => {
if (!cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
const x = ((mouseX - rect.left) / rect.width) * 100;
const y = ((mouseY - rect.top) / rect.height) * 100;
setPos({ x: Math.max(0, Math.min(100, x)), y: Math.max(0, Math.min(100, y)) });
}, [mouseX, mouseY, cardRef]);
return (
);
};
// ─── Pokemon Card ─────────────────────────────────────────────────────────────
const PokemonCard = ({ card, isFlipped, onClick, delay, mouseX, mouseY }) => {
const cardRef = useRef(null);
const motionX = useMotionValue(0);
const motionY = useMotionValue(0);
const rotateX = useTransform(motionY, [-150, 150], [12, -12]);
const rotateY = useTransform(motionX, [-150, 150], [-12, 12]);
const handleMouseMove = (e) => {
if (!isFlipped || !cardRef.current) return;
const rect = cardRef.current.getBoundingClientRect();
motionX.set(e.clientX - rect.left - rect.width / 2);
motionY.set(e.clientY - rect.top - rect.height / 2);
};
const handleMouseLeave = () => {
motionX.set(0);
motionY.set(0);
};
const typeColors = {
normal: { bg: "#f5e6c8", border: "#c8941a", badge: "#e8c56b", text: "#7a4a00" },
shiny: { bg: "#1a1a2e", border: "#ffd700", badge: "#ffd700", text: "#ffd700" },
};
const colors = typeColors[card.rarity];
return (
{/* Back */}
Tap to Reveal
{/* Front */}
{card.rarity === "shiny" && (
<>
>
)}
{/* Header */}
{card.name.toUpperCase()}
{card.rarity === "shiny" ? "✨ SHINY" : "NORMAL"}
{/* Image */}

{card.rarity === "shiny" && (
)}
{/* Type tag */}
🎉 STAG ENERGY TYPE
{/* Stats */}
{card.stats.map((stat) => (
))}
{/* Footer */}
STAG PACK 2025 • #{String(card.id).padStart(3,"0")}/019
);
};
// ─── Pack Tear Animation ──────────────────────────────────────────────────────
const PackOpener = ({ onOpen }) => {
const [tearing, setTearing] = useState(false);
const [done, setDone] = useState(false);
const handleTear = () => {
if (tearing || done) return;
setTearing(true);
setTimeout(() => { setDone(true); setTimeout(onOpen, 300); }, 700);
};
return (
{/* Title */}
STAG PACK
BACHELOR EDITION
{/* Pack */}
{!done && (
<>
{/* Left half */}
{/* Right half */}
{/* Tear line glow */}
>
)}
{/* Pulse ring behind pack */}
{!tearing && (
)}
{/* CTA */}
{!tearing && (
TEAR OPEN
)}
);
};
// ─── Cards Screen ─────────────────────────────────────────────────────────────
const CardsScreen = ({ cards, onReset }) => {
const [flipped, setFlipped] = useState(new Set());
const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
const confettiFired = useRef(false);
const handleMouseMove = useCallback((e) => {
setMousePos({ x: e.clientX, y: e.clientY });
}, []);
// Gyroscope on mobile
useEffect(() => {
const handler = (e) => {
setMousePos({
x: window.innerWidth / 2 + (e.gamma || 0) * 8,
y: window.innerHeight / 2 + (e.beta || 0) * 8,
});
};
window.addEventListener("deviceorientation", handler);
return () => window.removeEventListener("deviceorientation", handler);
}, []);
const handleFlip = (id) => {
const newFlipped = new Set(flipped);
newFlipped.add(id);
setFlipped(newFlipped);
if (newFlipped.size === 5 && !confettiFired.current) {
confettiFired.current = true;
setTimeout(fireConfetti, 300);
}
};
return (
{/* Header */}
YOUR PACK
{flipped.size}/5 REVEALED
{/* Progress bar */}
{/* Cards grid */}
{/* Row 1: 3 cards */}
{cards.slice(0, 3).map((card, i) => (
handleFlip(card.id)}
delay={i * 0.12}
mouseX={mousePos.x}
mouseY={mousePos.y}
/>
))}
{/* Row 2: 2 cards */}
{cards.slice(3, 5).map((card, i) => (
handleFlip(card.id)}
delay={(i + 3) * 0.12}
mouseX={mousePos.x}
mouseY={mousePos.y}
/>
))}
{/* All revealed celebration */}
{flipped.size === 5 && (
🎉 LADS LADS LADS 🎉
)}
);
};
// ─── App ──────────────────────────────────────────────────────────────────────
export default function App() {
const [phase, setPhase] = useState("pack"); // "pack" | "cards"
const [cards, setCards] = useState([]);
const handleOpen = () => {
setCards(getRandCards());
setPhase("cards");
};
const handleReset = () => {
setPhase("pack");
setCards([]);
};
return (
{phase === "pack" ? (
) : (
)}
);
}