// =============================================================
// CARD COMPONENT — beautiful Pokémon TCG card
// Used everywhere: pack reveal, collection grid, battle field
// =============================================================
const { useState, useRef, useEffect } = React;
// Energy chip — small circle showing energy type
function EnergyChip({ type, size = 18 }) {
const info = TYPE_INFO[type] || TYPE_INFO.normal;
return (
{info.icon}
);
}
// Type ribbon at top of card
function TypeRibbon({ types }) {
return (
{types.map((t,i) => )}
);
}
// Rarity badge bottom-right
function RarityMark({ rarity }) {
const r = RARITY[rarity];
return (
{r.symbol}
{r.name}
);
}
// Holographic foil overlay for rare cards
function HoloFoil({ rarity, hover }) {
if (rarity !== "holo" && rarity !== "legendary") return null;
const isLeg = rarity === "legendary";
return (
);
}
// Main PokemonCard
function PokemonCard({ pokemon, size = "md", showBack = false, onClick, glow = false, dim = false, count = 0 }) {
const [hover, setHover] = useState(false);
const [tilt, setTilt] = useState({ rx: 0, ry: 0 });
const ref = useRef(null);
const dims = {
xs: { w: 110, h: 154, scale: 0.45 },
sm: { w: 160, h: 224, scale: 0.65 },
md: { w: 220, h: 308, scale: 0.9 },
lg: { w: 280, h: 392, scale: 1.15 },
xl: { w: 340, h: 476, scale: 1.4 },
}[size];
const primaryType = pokemon.types[0];
const info = TYPE_INFO[primaryType];
function onMove(e) {
if (!ref.current) return;
const rect = ref.current.getBoundingClientRect();
const x = (e.clientX - rect.left) / rect.width;
const y = (e.clientY - rect.top) / rect.height;
setTilt({ ry: (x - 0.5) * 14, rx: -(y - 0.5) * 14 });
}
function onLeave() { setHover(false); setTilt({ rx:0, ry:0 }); }
if (showBack) {
return (
);
}
const isRareGlow = pokemon.rarity === "holo" || pokemon.rarity === "legendary";
return (
setHover(true)}
onMouseLeave={onLeave}
onClick={onClick}
style={{
width: dims.w, height: dims.h,
position:"relative",
cursor: onClick ? "pointer" : "default",
transform: `perspective(1000px) rotateX(${tilt.rx}deg) rotateY(${tilt.ry}deg) ${hover ? "translateY(-6px)" : ""}`,
transformStyle: "preserve-3d",
transition: "transform 220ms cubic-bezier(0.16,1,0.3,1)",
filter: dim ? "grayscale(1) brightness(0.5)" : "none",
opacity: dim ? 0.65 : 1,
}}
>
{/* Outer frame */}
{/* Inner card body */}
{/* Header row: name + HP */}
{pokemon.name}
#{String(pokemon.id).padStart(3,"0")} · GEN {pokemon.id <= 151 ? "I" : "II"}
{/* Image well */}
})
{ e.target.style.display='none'; }}
/>
{/* Hologram scanlines */}
{/* Attacks */}
{size !== "xs" && (
{pokemon.attacks.map((atk, i) => (
{atk.cost.map((c,j) => )}
{atk.name}
0 ? "#fff" : "var(--fg-3)",
}}>{atk.dmg > 0 ? atk.dmg : "—"}
))}
)}
{/* Footer: weakness / retreat / rarity */}
{size === "md" || size === "lg" || size === "xl" ? (
DÉBIL
RETIR
{Array.from({length: pokemon.retreat}).map((_,i)=>(
))}
{pokemon.retreat === 0 && —}
) : null}
{/* Count badge */}
{count > 1 && (
×{count}
)}
);
}
window.PokemonCard = PokemonCard;
window.EnergyChip = EnergyChip;
window.TypeRibbon = TypeRibbon;
window.RarityMark = RarityMark;