enchanted-tarot / index.html
Tingchenliang's picture
Add 2 files
511cbad verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Enchanted Pixel Tarot</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Cinzel+Decorative:wght@400;700&family=MedievalSharp&display=swap');
:root {
--lavender: #D9B4E6;
--gold: #E8D10B;
--ruby: #C34B36;
--wine: #4C2226;
--sky: #7CBCE3;
--bg: #1A0B1E;
}
body {
font-family: 'MedievalSharp', cursive;
background-color: var(--bg);
color: white;
min-height: 100vh;
overflow-x: hidden;
}
.magic-font {
font-family: 'Cinzel Decorative', cursive;
}
.tarot-card {
transition: all 0.4s ease;
transform-style: preserve-3d;
position: relative;
cursor: pointer;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
border: 3px solid var(--gold);
border-radius: 8px;
background: linear-gradient(145deg, var(--wine), var(--ruby));
perspective: 1000px;
}
.tarot-card:hover {
transform: translateY(-10px) rotate(2deg);
box-shadow: 0 12px 24px rgba(195, 75, 54, 0.4);
border-color: var(--lavender);
}
.tarot-card.selected {
transform: translateY(-20px) scale(1.05);
box-shadow: 0 0 30px var(--lavender), 0 0 60px rgba(217, 180, 230, 0.4);
border-color: var(--sky);
animation: pulse-glow 2s infinite alternate;
}
.card-front, .card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 12px;
}
.card-back {
background: linear-gradient(135deg, var(--wine), var(--ruby));
background-size: 200% 200%;
animation: gradient-shift 8s ease infinite;
}
.card-front {
transform: rotateY(180deg);
/* background: linear-gradient(145deg, var(--bg), var(--wine)); */
text-align: center;
}
.flipped {
transform: rotateY(180deg);
}
.magic-border {
border: 3px solid var(--gold);
border-radius: 8px;
box-shadow: 0 0 15px var(--lavender);
}
.magic-border-thick {
border: 5px solid var(--gold);
border-radius: 12px;
box-shadow: 0 0 25px var(--lavender);
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
.page {
animation: fadeIn 0.8s ease-out;
}
.magic-text {
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.7), 0 0 10px var(--lavender);
}
.magic-glow {
box-shadow: 0 0 15px var(--lavender);
}
.flip-animation {
animation: flip 1s cubic-bezier(0.455, 0.03, 0.515, 0.955) both;
}
@keyframes flip {
0% { transform: rotateY(0); }
100% { transform: rotateY(180deg); }
}
.fade-in {
animation: fadeIn 1s ease-in;
}
.magic-btn {
position: relative;
border: none;
background: linear-gradient(to right, var(--ruby), var(--wine));
color: var(--gold);
padding: 14px 28px;
font-size: 1.2rem;
font-family: 'Cinzel Decorative', cursive;
text-transform: uppercase;
cursor: pointer;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3), 0 0 15px var(--lavender);
transition: all 0.3s;
overflow: hidden;
}
.magic-btn:hover {
transform: translateY(-3px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.4), 0 0 25px var(--lavender);
}
.magic-btn:active {
transform: translateY(1px);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3), 0 0 10px var(--lavender);
}
.magic-btn.gold {
background: linear-gradient(to right, var(--gold), #F0D84D);
color: var(--wine);
text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.3);
}
.magic-btn::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
to bottom right,
rgba(217, 180, 230, 0) 0%,
rgba(217, 180, 230, 0.3) 50%,
rgba(217, 180, 230, 0) 100%
);
transform: rotate(30deg);
transition: all 0.5s;
}
.magic-btn:hover::before {
left: 100%;
top: 100%;
}
@keyframes gradient-shift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
@keyframes pulse-glow {
0% { box-shadow: 0 0 15px var(--lavender); }
100% { box-shadow: 0 0 30px var(--lavender), 0 0 60px rgba(217, 180, 230, 0.6); }
}
.magic-star {
width: 24px;
height: 24px;
background: var(--gold);
clip-path: polygon(
50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%,
50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%
);
box-shadow: 0 0 10px var(--gold);
}
.pixel-pattern {
background-image:
linear-gradient(45deg, var(--lavender) 25%, transparent 25%),
linear-gradient(-45deg, var(--lavender) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, var(--lavender) 75%),
linear-gradient(-45deg, transparent 75%, var(--lavender) 75%);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
opacity: 0.2;
}
.floating {
animation: floating 3s ease-in-out infinite;
}
@keyframes floating {
0% { transform: translateY(0px); }
50% { transform: translateY(-15px); }
100% { transform: translateY(0px); }
}
.sparkle {
position: absolute;
width: 6px;
height: 6px;
background: var(--gold);
border-radius: 50%;
pointer-events: none;
opacity: 0;
}
.twinkle {
animation: twinkle 1.5s ease-out;
}
@keyframes twinkle {
0% { transform: scale(0); opacity: 0; }
50% { opacity: 1; }
100% { transform: scale(1.5); opacity: 0; }
}
.glow-text {
text-shadow: 0 0 8px var(--lavender), 0 0 16px var(--lavender);
}
.crystal-ball {
width: 120px;
height: 120px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, var(--sky), var(--lavender));
box-shadow: 0 0 30px var(--sky), inset 0 0 20px white;
position: relative;
animation: float 4s ease-in-out infinite;
}
.crystal-ball::after {
content: '';
position: absolute;
top: 10%;
left: 10%;
width: 20%;
height: 20%;
border-radius: 50%;
background: rgba(255, 255, 255, 0.8);
filter: blur(5px);
}
.mini-crystal-ball {
width: 40px;
height: 40px;
border-radius: 50%;
background: radial-gradient(circle at 30% 30%, var(--sky), var(--lavender));
box-shadow: 0 0 10px var(--sky), inset 0 0 8px white;
position: relative;
margin: 0 auto;
}
.mini-crystal-ball::after {
content: '';
position: absolute;
top: 15%;
left: 15%;
width: 20%;
height: 20%;
border-radius: 50%;
background: rgba(255, 255, 255, 0.8);
filter: blur(2px);
}
.hide-after::after {
display: none !important; /* Hide the pseudo-element */
}
@keyframes float {
0% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-20px) rotate(5deg); }
100% { transform: translateY(0) rotate(0deg); }
}
.magic-dust {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: none;
overflow: hidden;
z-index: -1;
}
.dust-particle {
position: absolute;
background: var(--lavender);
border-radius: 50%;
opacity: 0.6;
filter: blur(1px);
animation: float-up linear infinite;
}
@keyframes float-up {
0% { transform: translateY(100vh) translateX(0); opacity: 0; }
10% { opacity: 0.6; }
90% { opacity: 0.6; }
100% { transform: translateY(-100px) translateX(20px); opacity: 0; }
}
.lucky-color-label {
font-size: 14px;
color: var(--gold);
text-align: center;
margin-top: 8px;
font-family: 'Cinzel Decorative', cursive;
}
/* Export modal styles */
.export-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
pointer-events: none;
transition: opacity 0.3s ease;
}
.export-modal.active {
opacity: 1;
pointer-events: all;
}
.export-modal-content {
background: linear-gradient(135deg, var(--wine), var(--bg));
border: 3px solid var(--gold);
border-radius: 12px;
width: 90%;
max-width: 500px;
padding: 30px;
position: relative;
box-shadow: 0 0 30px var(--lavender);
transform: scale(0.9);
transition: transform 0.3s ease;
text-align: center;
}
.export-modal.active .export-modal-content {
transform: scale(1);
}
.export-modal-close {
position: absolute;
top: 15px;
right: 15px;
background: var(--ruby);
border: none;
color: white;
width: 30px;
height: 30px;
border-radius: 50%;
cursor: pointer;
font-size: 16px;
display: flex;
justify-content: center;
align-items: center;
}
.export-modal-download {
background: var(--gold);
color: var(--wine);
border: none;
padding: 10px 20px;
border-radius: 8px;
font-family: 'Cinzel Decorative', cursive;
cursor: pointer;
display: flex;
align-items: center;
gap: 8px;
margin: 20px auto 0;
font-weight: bold;
}
.export-modal-download:hover {
background: #f0d84d;
}
/* Styles for the overlapping card stack */
#cards-stack-container {
perspective: 1500px; /* Add perspective for potential 3D effects */
overflow-x: auto; /* Allow horizontal scrolling if stack is too wide */
overflow-y: visible;
padding-top: 100px;
padding-bottom: 50px; /* Add space below for lifted cards */
-webkit-overflow-scrolling: touch; /* Smooth scrolling on iOS */
}
#cards-stack-container .tarot-card {
position: relative; /* Needed for z-index stacking */
flex-shrink: 0; /* Prevent cards from shrinking */
transition: transform 0.4s cubic-bezier(0.25, 0.8, 0.25, 1),
margin-left 0.4s cubic-bezier(0.25, 0.8, 0.25, 1); /* Smooth transition for transform and margin */
/* Calculate overlap: card width (md:w-32 = 8rem = 128px) * overlap amount (e.g., 70%) */
/* Adjust -90px based on desired overlap and card size */
margin-left: -95px; /* Overlap cards */
z-index: 1; /* Base z-index */
}
/* No negative margin for the first card */
#cards-stack-container .tarot-card:first-child {
margin-left: 0;
}
/* Hover effect: lift the card up */
#cards-stack-container .tarot-card:hover {
transform: translateY(-40px) rotate(1deg); /* Lift and slightly rotate */
cursor: pointer;
/* Keep existing hover border/shadow from general .tarot-card:hover */
}
/* Keep existing selected styles, ensure it's above hover */
#cards-stack-container .tarot-card.selected {
transform: translateY(-50px) scale(1.05) rotate(1deg); /* Lift higher, scale */
/* Keep existing selected border/shadow */
/* Override negative margin to slightly separate selected card if needed */
/* margin-left: -85px; */ /* Optional: Slightly adjust margin if needed */
}
/* Ensure base .tarot-card styles don't conflict */
.tarot-card {
/* Keep existing styles like width, height, border, etc. */
/* Ensure transition includes transform if not already */
transition: all 0.4s ease; /* Ensure transform is part of transition */
/* Remove conflicting transforms if any were applied directly */
transform-style: preserve-3d; /* Keep this */
perspective: 1000px; /* Keep this */
}
/* Remove original hover transform if it conflicts */
.tarot-card:hover {
/* transform: translateY(-10px) rotate(2deg); REMOVE OR ADJUST */
box-shadow: 0 12px 24px rgba(195, 75, 54, 0.4);
border-color: var(--lavender);
}
/* Remove original selected transform if it conflicts */
.tarot-card.selected {
/* transform: translateY(-20px) scale(1.05); REMOVE OR ADJUST */
box-shadow: 0 0 30px var(--lavender), 0 0 60px rgba(217, 180, 230, 0.4);
border-color: var(--sky);
animation: pulse-glow 2s infinite alternate;
}
#results-page .tarot-card:hover {
transform: scale(1.2); /* Expand slightly */
box-shadow: 0 0 15px var(--lavender), 0 0 30px rgba(217, 180, 230, 0.5);
</style>
</head>
<body class="relative">
<!-- Magic dust particles -->
<div class="magic-dust" id="magic-dust"></div>
<!-- Export Modal (hidden by default) -->
<div class="export-modal" id="export-modal">
<div class="export-modal-content">
<button class="export-modal-close" id="export-modal-close"><i class="fas fa-times"></i></button>
<h2 class="text-2xl font-bold text-gold mb-4 magic-font">READING EXPORTED</h2>
<p class="text-lavender mb-6">Your tarot reading has been prepared for download.</p>
<button class="export-modal-download" id="export-download-btn">
<i class="fas fa-download"></i> DOWNLOAD IMAGE
</button>
</div>
</div>
<!-- Landing Page -->
<div id="landing-page" class="page min-h-screen flex flex-col items-center justify-center p-4 text-center relative">
<div class="max-w-2xl mx-auto relative">
<div class="crystal-ball mx-auto mb-8 floating"></div>
<h1 class="text-5xl md:text-7xl font-bold mb-6 magic-text text-lavender magic-font">
<span class="text-gold">ENCHANTED</span> TAROT
</h1>
<p class="text-2xl md:text-3xl mb-8 text-sky glow-text">
REVEAL YOUR DESTINY THROUGH THE CARDS
</p>
<div class="mb-12">
<button id="start-btn", class="magic-btn gold mb-4">
BEGIN READING <i class="fas fa-crystal ml-2"></i>
</button>
</div>
<div class="flex justify-center space-x-4">
<div class="w-24 h-36 md:w-32 md:h-48 tarot-card floating" style="animation-delay: 0.2s;">
<div class="card-back w-full h-full">
<div class="magic-star mx-auto mt-8"></div>
</div>
</div>
<div class="w-24 h-36 md:w-32 md:h-48 tarot-card floating" style="animation-delay: 0.4s;">
<div class="card-back w-full h-full">
<div class="magic-star mx-auto mt-8"></div>
</div>
</div>
<div class="w-24 h-36 md:w-32 md:h-48 tarot-card floating" style="animation-delay: 0.6s;">
<div class="card-back w-full h-full">
<div class="magic-star mx-auto mt-8"></div>
</div>
</div>
</div>
</div>
<div class="absolute bottom-8 text-lavender text-xl magic-text">
<!-- <p>TOUCH THE CRYSTAL TO BEGIN...</p> -->
</div>
</div>
<!-- Card Selection Page -->
<div id="selection-page" class="page hidden min-h-screen flex flex-col items-center justify-center p-4">
<div class="text-center mb-8">
<h2 class="text-3xl md:text-5xl font-bold mb-4 text-lavender magic-text magic-font">CHOOSE YOUR CARDS</h2>
<p class="text-xl md:text-2xl text-sky glow-text">SELECT 3 CARDS TO REVEAL YOUR FATE</p>
<div class="mt-4 text-gold text-xl magic-text">
<span id="selected-count">0</span>/3 CARDS SELECTED
</div>
</div>
<!-- CHANGE THIS DIV: Remove grid classes, add ID -->
<div id="cards-stack-container" class="flex justify-center items-center p-4 mt-8 min-h-[250px] md:min-h-[300px] w-full relative">
<!-- Cards will be generated here by JavaScript into this container -->
</div>
<button id="reveal-btn", class="mt-12 magic-btn gold hidden">
REVEAL FATE <i class="fas fa-hand-sparkles ml-2"></i>
</button>
</div>
<!-- Results Page -->
<div id="results-page" class="page hidden min-h-screen py-12 flex flex-col items-center">
<div class="text-center mb-8">
<h2 class="text-3xl md:text-5xl font-bold mb-4 text-lavender magic-text magic-font">YOUR TAROT READING</h2>
<p class="text-xl md:text-2xl text-sky glow-text">THE CARDS HAVE SPOKEN...</p>
</div>
<div class="flex flex-wrap justify-center gap-8 mb-12">
<!-- Selected cards will appear here -->
</div>
<div class="max-w-4xl mx-auto px-4 mb-12">
<!-- Card interpretations will appear here -->
</div>
<!-- This is the block we'll export as an image -->
<div class="max-w-3xl mx-auto bg-wine bg-opacity-80 p-6 md:p-8 magic-border-thick mb-12 backdrop-blur-sm" id="destiny-block">
<h3 class="text-2xl font-bold text-gold mb-4 magic-font glow-text">YOUR DESTINY AWAITS</h3>
<div id="summary" class="text-xl text-lavender">
<!-- Summary will be generated here -->
</div>
<!-- Lucky color crystal ball -->
<div class="mt-8">
<div class="mini-crystal-ball" id="lucky-color-ball"></div>
<div class="lucky-color-label" id="lucky-color-label">YOUR LUCKY COLOR TODAY</div>
</div>
</div>
<div class="flex flex-wrap justify-center gap-4 mb-8">
<button id="new-reading-btn", class="magic-btn">
NEW READING <i class="fas fa-redo ml-2"></i>
</button>
<!-- <button id="export-btn", class="magic-btn gold">
EXPORT READING <i class="fas fa-scroll ml-2"></i>
</button> -->
</div>
</div>
<script>
// Tarot card data
const tarotDeck = [
{ name: "THE FOOL", image: "fool.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A128bc320-c9f7-4510-8581-a658d0f87183%3Athe_fool.jpeg?table=block&id=1cfb51d5-463a-80f8-803b-ecf3763bc277&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=480&userId=&cache=v2",
meanings: {
work: "A new beginning or leap of faith in your career. Don't be afraid to take risks.",
love: "Approach relationships with an open heart and mind. New romantic adventures await.",
health: "Listen to your instincts regarding your wellbeing. A fresh start is possible."
}},
{ name: "THE MAGICIAN", image: "magician.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A3b5b6303-fbcb-46c4-808e-638ceff5cf3c%3Athe_magician.png?table=block&id=1cfb51d5-463a-800e-b91f-f2392d366ac6&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "You have all the tools you need for success. Manifest your career goals.",
love: "Communication and intention will improve your relationships. Be authentic.",
health: "You have the power to transform your health. Focus on positive habits."
}},
{ name: "HIGH PRIESTESS", image: "priestess.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A65ca1734-ab68-4ac4-915b-1d6befac1532%3Athe_high_priestess.png?table=block&id=1cfb51d5-463a-80ca-9705-d193588b16b9&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Trust your intuition about work decisions. Hidden factors may be at play.",
love: "Mystery and depth in relationships. Pay attention to unspoken feelings.",
health: "Listen to your body's subtle signals. The answer lies within."
}},
{ name: "THE EMPRESS", image: "empress.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A5f775c9c-6117-46b8-9ed4-9cca1f840427%3Athe_empress.png?table=block&id=1cfb51d5-463a-80d0-839b-de77bc2c43ee&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Creativity and abundance in your career. Nurture your projects like they're your children.",
love: "Fertility, sensuality and nurturing in relationships. Deep emotional connections.",
health: "Focus on self-care and nourishment. Your body is a temple."
}},
{ name: "THE EMPEROR", image: "emperor.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A40307339-d7d9-448a-aad0-a695772b57d9%3Athe_emperor.png?table=block&id=1cfb51d5-463a-80de-a0a6-f387b154a5a3&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Take control and establish structure. Leadership opportunities are coming.",
love: "Stability and protection in relationships. Traditional values may be important.",
health: "Establish routines and discipline for better health. Structure brings results."
}},
{ name: "THE HIEROPHANT", image: "hierophant.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A9e085089-12ac-4a7d-b186-b87bc9bd136b%3Athe_hierohant.png?table=block&id=1cfb51d5-463a-80b4-b8aa-de3f907ac6d1&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2", // Note: Name in URL is "hierohant"
meanings: {
work: "Seek mentorship or follow established systems. Conventional wisdom applies.",
love: "Commitment and traditional values in relationships. Spiritual connections.",
health: "Consider conventional treatments or seeking expert advice."
}},
{ name: "THE LOVERS", image: "lovers.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3Ae5483385-0eb6-4fc8-9afd-7a4692d1e549%3Athe_lovers.png?table=block&id=1cfb51d5-463a-80f8-bea4-ee659a891d76&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Important choices about your career path. Align work with your values.",
love: "Deep connections and meaningful choices in relationships. Soulmate energy.",
health: "Balance and harmony in your wellbeing. Mind-body-spirit alignment needed."
}},
{ name: "THE CHARIOT", image: "chariot.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3Af9035ecc-340f-4a7b-9bad-d01132ea4183%3Athe_chariot.png?table=block&id=1cfb51d5-463a-8089-9e07-ebc272d54da3&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Victory through determination. Control and willpower will lead to success.",
love: "Passion and intensity in relationships. Need to find balance between opposing forces.",
health: "Willpower can overcome health challenges. Stay the course with treatments."
}},
{ name: "STRENGTH", image: "strength.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A6bfaac2a-0011-417d-9c1c-e25c6c4a2f8b%3Astrength2.png?table=block&id=1cfb51d5-463a-8020-82dc-e9df9b7bf0f7&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=290&userId=&cache=v2",
meanings: {
work: "Inner strength will help you overcome work challenges. Compassionative leadership.",
love: "Relationships require patience and gentle strength. Deep bonds through vulnerability.",
health: "Inner resilience will aid healing. Emotional strength affects physical health."
}},
{ name: "THE HERMIT", image: "hermit.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3Aefb99da9-c44c-45d5-b942-76ba01841ec8%3Athe_hermit.png?table=block&id=1cfb51d5-463a-80e3-a6ac-c26980018fcc&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Time for introspection about your career path. Seek wisdom within.",
love: "Need for space or solitude in relationships. Inner reflection before commitment.",
health: "Listen to your inner voice about health matters. Solitude may bring healing."
}},
{ name: "WHEEL OF FORTUNE", image: "wheel.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A498d6be6-02a2-47df-870c-d208e67568da%3Awheel_of_fortune.png?table=block&id=1cfb51d5-463a-806c-8494-ca31c5c64601&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Changes and cycles in your career. Luck is on your side - embrace change.",
love: "Relationships may go through ups and downs. Go with the flow of change.",
health: "Your health situation may improve unexpectedly. Cycles of wellbeing."
}},
{ name: "JUSTICE", image: "justice.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3Adeb94cb3-5223-4922-98f9-420bc389fc21%3Ajustice.png?table=block&id=1cfb51d5-463a-8078-a9f8-e2ceca9f1ee4&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=290&userId=&cache=v2",
meanings: {
work: "Fairness and balance in career matters. Karma at work - you'll get what you deserve.",
love: "Relationships require honesty and fairness. Truth will come to light.",
health: "Your lifestyle choices are affecting your health. Seek balance."
}},
{ name: "THE HANGED MAN", image: "hanged.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3Abb8dace8-0361-42dd-9ec1-f961a873777b%3Athe_hanged_man.jpeg?table=block&id=1cfb51d5-463a-8001-a905-cd0a1ce134c1&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "New perspectives needed in your career. Sometimes surrender brings insight.",
love: "Sacrifice may be needed in relationships. See things from a different angle.",
health: "Alternative approaches to health may help. Change your perspective."
}},
{ name: "DEATH", image: "death.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A5ebb5f2b-29ef-4bcc-be11-b6526642d705%3Adeath.png?table=block&id=1cfb51d5-463a-8089-96cf-effa9cd1d35b&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=260&userId=&cache=v2",
meanings: {
work: "Endings making way for new beginnings in your career. Transformation is coming.",
love: "Relationships may transform or end. Necessary changes for growth.",
health: "Significant changes in your health approach needed. Out with the old."
}},
{ name: "TEMPERANCE", image: "temperance.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3Ae52af800-05ae-47b8-bd78-57d6125f19c3%3Atemperance.png?table=block&id=1cfb51d5-463a-80d3-8458-db177a3d6687&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=580&userId=&cache=v2",
meanings: {
work: "Balance and moderation in your career. Blend different approaches for success.",
love: "Relationships need compromise and patience. Finding the middle path.",
health: "Moderation is key to health. Balance different aspects of your wellbeing."
}},
{ name: "THE DEVIL", image: "devil.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A36a256df-92e6-4c35-92fa-dc0959d4209f%3Aimage.png?table=block&id=1cfb51d5-463a-8030-a6fb-e5db5dc388d8&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=2000&userId=&cache=v2",
meanings: {
work: "Feeling trapped in your job or by material concerns. Examine unhealthy attachments.",
love: "Unhealthy attachments or toxic patterns in relationships. Break free.",
health: "Addictions or unhealthy habits may be affecting your wellbeing. Time to let go."
}},
{ name: "THE TOWER", image: "tower.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A1b5c401d-218a-4204-a1c6-0a68d276237d%3Athe_tower.jpeg?table=block&id=1cfb51d5-463a-80c2-ae33-fcb21f069e3b&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Sudden changes or revelations in your career. Destruction leads to rebuilding.",
love: "Shocking truths in relationships. Foundations may be shaken.",
health: "Sudden health issues may arise. Necessary breakdown before breakthrough."
}},
{ name: "THE STAR", image: "star.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A5e3ddb0a-7783-40b1-a706-ee10dd0e35c9%3Athe_star.jpeg?table=block&id=1cfb51d5-463a-806c-ab7f-cce08a169b9a&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Hope and inspiration in your career. Follow your true calling.",
love: "Optimism and spiritual connection in relationships. Soulful bonds.",
health: "Renewed hope for healing. Divine guidance in your health journey."
}},
{ name: "THE MOON", image: "moon.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A06a1db90-0a35-45e1-97d0-854a63455d0e%3Athe_moon.jpeg?table=block&id=1cfb51d5-463a-80ee-b5b1-dd4db3e16a5f&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Uncertainty and illusion in your career. Trust your intuition over appearances.",
love: "Confusion or hidden aspects in relationships. Things aren't as they seem.",
health: "Listen to dreams and intuition about health. Subconscious messages."
}},
{ name: "THE SUN", image: "sun.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A79499ecd-b773-4cfc-9677-0b7d691081b0%3Athe_sun.jpeg?table=block&id=1cfb51d5-463a-809c-8415-e9f73cce1a8d&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Success, vitality and joy in your career. Recognition and achievement.",
love: "Warmth, happiness and positivity in relationships. Childlike joy.",
health: "Excellent vitality and wellbeing. Positive energy for healing."
}},
{ name: "JUDGEMENT", image: "judgement.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A0507b672-7e8e-4ef7-b12c-197212b08315%3AJudgement.jpeg?table=block&id=1cfb51d5-463a-8089-8dfe-d211a148894e&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=260&userId=&cache=v2",
meanings: {
work: "Time for evaluation and rebirth in your career. Answer your calling.",
love: "Relationships may reach a turning point. Forgiveness and renewal.",
health: "Time to evaluate your health choices. Awakening to new approaches."
}},
{ name: "THE WORLD", image: "world.jpg",
imageUrl: "https://sturdy-hamster-1b6.notion.site/image/attachment%3A2dcce03e-835d-4ef1-8758-ab94175e9a52%3Athe_world.jpeg?table=block&id=1cfb51d5-463a-80fb-86d6-d3225d044045&spaceId=c65cb6b9-517a-4d69-b577-6ea190b40fc6&width=1420&userId=&cache=v2",
meanings: {
work: "Completion and success in your career. A cycle comes to satisfying conclusion.",
love: "Wholeness and fulfillment in relationships. Soul connections.",
health: "Excellent overall wellbeing. Completion of a healing journey."
}}
];
// Example entry - add 'solidColor' to ALL entries
const luckyColors = [
{ name: "Moonlight Silver", gradient: "radial-gradient(circle at 30% 30%, #E0E0E0, #B0B0B0)", solidColor: "#E0E0E0" },
{ name: "Sunset Gold", gradient: "radial-gradient(circle at 30% 30%, #FFD700, #E6B800)", solidColor: "#FFD700" },
{ name: "Mystic Purple", gradient: "radial-gradient(circle at 30% 30%, #9B59B6, #8E44AD)", solidColor: "#9B59B6" },
{ name: "Ocean Blue", gradient: "radial-gradient(circle at 30% 30%, #3498DB, #2980B9)", solidColor: "#3498DB" },
{ name: "Emerald Green", gradient: "radial-gradient(circle at 30% 30%, #2ECC71, #27AE60)", solidColor: "#2ECC71" },
{ name: "Ruby Red", gradient: "radial-gradient(circle at 30% 30%, #E74C3C, #C0392B)", solidColor: "#E74C3C" },
{ name: "Amber Orange", gradient: "radial-gradient(circle at 30% 30%, #F39C12, #D35400)", solidColor: "#F39C12" },
{ name: "Rose Quartz", gradient: "radial-gradient(circle at 30% 30%, #F7CAC9, #F58F84)", solidColor: "#F7CAC9" }
// ... make sure all have a solidColor
];
// DOM elements
const landingPage = document.getElementById('landing-page');
const selectionPage = document.getElementById('selection-page');
const resultsPage = document.getElementById('results-page');
const startBtn = document.getElementById('start-btn');
const revealBtn = document.getElementById('reveal-btn');
const newReadingBtn = document.getElementById('new-reading-btn');
const exportBtn = document.getElementById('export-btn');
const selectedCount = document.getElementById('selected-count');
const cardsContainer = document.getElementById('cards-stack-container'); const resultsContainer = resultsPage.querySelector('.flex-wrap');
const interpretationsContainer = resultsPage.querySelector('.max-w-4xl');
const summaryContainer = document.getElementById('summary');
const magicDust = document.getElementById('magic-dust');
const luckyColorBall = document.getElementById('lucky-color-ball');
const luckyColorLabel = document.getElementById('lucky-color-label');
const destinyBlock = document.getElementById('destiny-block');
// Export modal elements
const exportModal = document.getElementById('export-modal');
const exportModalClose = document.getElementById('export-modal-close');
const exportDownloadBtn = document.getElementById('export-download-btn');
// App state
let selectedCards = [];
let shuffledDeck = [];
let currentReading = null;
let currentLuckyColor = null;
let exportedImageUrl = null;
// Initialize the app
function init() {
createMagicDust();
setupEventListeners();
setupSparkleEffects();
}
// Create floating magic dust particles
function createMagicDust() {
for (let i = 0; i < 30; i++) {
const particle = document.createElement('div');
particle.classList.add('dust-particle');
// Random properties
const size = Math.random() * 5 + 2;
const left = Math.random() * 100;
const animationDuration = Math.random() * 10 + 10;
const delay = Math.random() * 10;
particle.style.width = `${size}px`;
particle.style.height = `${size}px`;
particle.style.left = `${left}%`;
particle.style.animationDuration = `${animationDuration}s`;
particle.style.animationDelay = `${delay}s`;
// Random color from our palette
const colors = ['var(--lavender)', 'var(--gold)', 'var(--sky)'];
particle.style.background = colors[Math.floor(Math.random() * colors.length)];
magicDust.appendChild(particle);
}
}
// Set up sparkle effects on hover
function setupSparkleEffects() {
const sparkleElements = [startBtn, revealBtn, newReadingBtn, exportBtn];
sparkleElements.forEach(element => {
element.addEventListener('mouseenter', (e) => {
createSparkles(e.target);
});
});
}
// Create sparkle effect
function createSparkles(element) {
const rect = element.getBoundingClientRect();
for (let i = 0; i < 8; i++) {
const sparkle = document.createElement('div');
sparkle.classList.add('sparkle', 'twinkle');
// Position randomly around the element
const x = Math.random() * rect.width;
const y = Math.random() * rect.height;
sparkle.style.left = `${x}px`;
sparkle.style.top = `${y}px`;
// Random size and animation delay
const size = Math.random() * 4 + 2;
sparkle.style.width = `${size}px`;
sparkle.style.height = `${size}px`;
sparkle.style.animationDelay = `${Math.random() * 0.5}s`;
element.appendChild(sparkle);
// Remove after animation
setTimeout(() => {
sparkle.remove();
}, 1500);
}
}
// Set up event listeners
function setupEventListeners() {
startBtn.addEventListener('click', startReading);
revealBtn.addEventListener('click', showResults);
newReadingBtn.addEventListener('click', resetApp);
exportBtn.addEventListener('click', exportReading);
// Export modal events
exportModalClose.addEventListener('click', () => {
exportModal.classList.remove('active');
});
exportDownloadBtn.addEventListener('click', () => {
if (exportedImageUrl) {
const link = document.createElement('a');
link.download = 'enchanted-tarot-reading.png';
link.href = exportedImageUrl;
link.click();
}
});
}
// Start the reading - shuffle deck and show selection page
function startReading() {
// Create sparkle effect
createSparkles(startBtn);
// Shuffle the deck
shuffledDeck = [...tarotDeck].sort(() => Math.random() - 0.5);
// Create card elements
cardsContainer.innerHTML = '';
shuffledDeck.forEach((card, index) => {
const cardElement = document.createElement('div');
cardElement.className = 'tarot-card w-24 h-36 md:w-32 md:h-48';
cardElement.dataset.index = index;
// Use the imageUrl for the card front background
cardElement.innerHTML = `
<div class="card-back w-full h-full pixel-pattern">
<div class="magic-star mx-auto mt-8"></div>
</div>
<div class="card-front w-full h-full"
style="background-image: url('${card.imageUrl}'); background-size: cover; background-position: center center; background-repeat: no-repeat;">
<!-- Text removed, background image set via style -->
</div>
`;
cardElement.addEventListener('click', () => selectCard(cardElement, index));
cardsContainer.appendChild(cardElement);
});
// Transition to selection page with animation
landingPage.style.animation = 'fadeIn 0.8s reverse forwards';
setTimeout(() => {
landingPage.classList.add('hidden');
landingPage.style.animation = '';
selectionPage.classList.remove('hidden');
}, 800);
}
// Get card suit for display (simple categorization)
function getCardSuit(index) {
if (index < 7) return "MAJOR ARCANA";
if (index < 14) return "CUPS";
if (index < 21) return "SWORDS";
return "PENTACLES";
}
// Select a card
function selectCard(cardElement, index) {
const cardIndex = selectedCards.indexOf(index);
if (cardIndex === -1) {
// Select the card if we have less than 3 selected
if (selectedCards.length < 3) {
selectedCards.push(index);
cardElement.classList.add('selected');
// Create sparkle effect
createSparkles(cardElement);
}
} else {
// Deselect the card
selectedCards.splice(cardIndex, 1);
cardElement.classList.remove('selected');
}
// Update selected count
selectedCount.textContent = selectedCards.length;
// Show/hide reveal button
if (selectedCards.length === 3) {
revealBtn.classList.remove('hidden');
createSparkles(revealBtn);
} else {
revealBtn.classList.add('hidden');
}
}
// Show the results of the reading
function showResults() {
// Create big sparkle effect
createSparkles(revealBtn);
// Get the selected cards
const readingCards = selectedCards.map(index => shuffledDeck[index]);
currentReading = readingCards; // Store for sharing
// Select a random lucky color
currentLuckyColor = luckyColors[Math.floor(Math.random() * luckyColors.length)];
// Update lucky color display
if (luckyColorBall) {
luckyColorBall.style.background = currentLuckyColor.gradient;
}
if (luckyColorLabel) {
luckyColorLabel.textContent = `YOUR LUCKY COLOR: ${currentLuckyColor.name}`;
}
// Create results display
resultsContainer.innerHTML = '';
interpretationsContainer.innerHTML = '';
readingCards.forEach((card, i) => {
// Create card display
const cardElement = document.createElement('div');
cardElement.className = 'tarot-card w-32 h-48 md:w-40 md:h-60 relative';
cardElement.innerHTML = `
<div class="card-back w-full h-full pixel-pattern">
<div class="magic-star mx-auto mt-8"></div>
</div>
<div class="card-front w-full h-full"
style="background-image: url('${card.imageUrl}'); background-size: cover; background-position: center center; background-repeat: no-repeat;">
<!-- Content removed -->
</div>
`;
// Add flip animation with delay
setTimeout(() => {
cardElement.classList.add('flip-animation');
createSparkles(cardElement);
}, i * 400);
resultsContainer.appendChild(cardElement);
// Create interpretation
const interpretationElement = document.createElement('div');
interpretationElement.className = 'mb-8 fade-in bg-wine bg-opacity-70 p-6 magic-border backdrop-blur-sm';
interpretationElement.style.animationDelay = `${i * 400 + 1200}ms`;
interpretationElement.innerHTML = `
<h3 class="text-xl md:text-2xl font-bold text-gold mb-2 magic-font glow-text">${card.name}</h3>
<div>
<h4 class="font-bold text-sky mb-1">WORK:</h4>
<p class="mb-3 text-lavender">${card.meanings.work}</p>
<h4 class="font-bold text-sky mb-1">LOVE:</h4>
<p class="mb-3 text-lavender">${card.meanings.love}</p>
<h4 class="font-bold text-sky mb-1">HEALTH:</h4>
<p class="text-lavender">${card.meanings.health}</p>
</div>
`;
interpretationsContainer.appendChild(interpretationElement);
});
// Generate summary
generateSummary(readingCards);
// Transition to results page with animation
selectionPage.style.animation = 'fadeIn 0.8s reverse forwards';
setTimeout(() => {
selectionPage.classList.add('hidden');
selectionPage.style.animation = '';
resultsPage.classList.remove('hidden');
}, 800);
// Scroll to top
window.scrollTo(0, 0);
}
// Generate a summary of the reading
function generateSummary(cards) {
summaryContainer.innerHTML = '';
// Create short summaries for each category
const workSummary = createShortSummary(cards.map(card => card.meanings.work));
const loveSummary = createShortSummary(cards.map(card => card.meanings.love));
const healthSummary = createShortSummary(cards.map(card => card.meanings.health));
summaryContainer.innerHTML = `
<div class="mb-4">
<h4 class="font-bold text-sky mb-1 magic-font glow-text">WORK SITUATION:</h4>
<p class="text-lavender">${workSummary}</p>
</div>
<div class="mb-4">
<h4 class="font-bold text-sky mb-1 magic-font glow-text">LOVE SITUATION:</h4>
<p class="text-lavender">${loveSummary}</p>
</div>
<div>
<h4 class="font-bold text-sky mb-1 magic-font glow-text">HEALTH SITUATION:</h4>
<p class="text-lavender">${healthSummary}</p>
</div>
`;
}
// Create a short summary from multiple sentences
function createShortSummary(sentences) {
// Extract key phrases from each sentence
const keyPhrases = sentences.map(sentence => {
// Simple way to get the first meaningful part of the sentence
const firstClause = sentence.split(/[.,;]/)[0];
return firstClause.trim();
});
// Join with meaningful connectors
return keyPhrases.join(". ").replace(/\.\./g, '.') + ".";
}
// Export the reading as an image
async function exportReading() {
if (!currentReading || !currentLuckyColor) return;
createSparkles(exportBtn);
const originalBallBackground = luckyColorBall.style.background;
const solidExportColor = currentLuckyColor.solidColor;
try {
// Temporarily set the SOLID color
luckyColorBall.style.background = solidExportColor;
// Temporarily HIDE the ::after element
luckyColorBall.classList.add('hide-after'); // <-- ADD THIS
// Create canvas
const canvas = await html2canvas(destinyBlock, {
scale: 2,
useCORS: true,
backgroundColor: '#4C2226',
logging: false,
});
exportedImageUrl = canvas.toDataURL('image/png');
exportModal.classList.add('active');
} catch (error) {
console.error("Error generating export image:", error);
alert("Sorry, couldn't generate the export image.");
} finally {
// Restore original background AND remove the hiding class
luckyColorBall.style.background = originalBallBackground;
luckyColorBall.classList.remove('hide-after'); // <-- ADD THIS
}
}
// Reset the app for a new reading
function resetApp() {
// Reset selections
selectedCards = [];
shuffledDeck = [];
currentReading = null;
currentLuckyColor = null;
exportedImageUrl = null;
// Reset UI elements
selectedCount.textContent = '0';
revealBtn.classList.add('hidden');
resultsContainer.innerHTML = '';
interpretationsContainer.innerHTML = '';
summaryContainer.innerHTML = '';
// Reset lucky color display
luckyColorBall.style.background = 'radial-gradient(circle at 30% 30%, var(--sky), var(--lavender))';
luckyColorLabel.textContent = 'YOUR LUCKY COLOR TODAY';
// Close export modal if open
exportModal.classList.remove('active');
// Transition to landing page with animation
resultsPage.style.animation = 'fadeIn 0.8s reverse forwards';
setTimeout(() => {
resultsPage.classList.add('hidden');
resultsPage.style.animation = '';
landingPage.classList.remove('hidden');
landingPage.style.animation = 'fadeIn 0.8s ease-out';
window.scrollTo(0, 0);
}, 800);
}
// Initialize the app when the page loads
window.addEventListener('DOMContentLoaded', init);
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Tingchenliang/enchanted-tarot" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>