다시 만나고 싶은 그 사람을 떠올리며, 진지한 마음으로 카드를 선택해 주세요.
선택: 0/1
카드를 클릭해 선택/해제하세요.
`;
const spreadSel = root.querySelector("#spread");
const revChk = root.querySelector("#rev");
const deckEl = root.querySelector("#deck");
const selCntEl = root.querySelector("#selCnt");
const needEl = root.querySelector("#need");
const revealBtn = root.querySelector("#reveal");
const intentionEl = root.querySelector(".intention");
const summaryEl = root.querySelector("#summary");
const storyEl = root.querySelector("#story");
const resultsEl = root.querySelector("#results");
const tiles = [...root.querySelectorAll(".tile")];
let order = [...Array(78).keys()];
let selected = [];
let isRevealing = false;
const shuffle = (a) => { for(let i=a.length-1;i>0;i--){const j=Math.floor(Math.random()*(i+1)); [a[i],a[j]]=[a[j],a[i]];} return a; };
function updateUIState() {
const spreadKey = spreadSel.value;
const need = SPREADS[spreadKey].size;
needEl.textContent = need;
selCntEl.textContent = selected.length;
revealBtn.disabled = (selected.length !== need);
tiles.forEach(t => {
const on = (t.dataset.spread === spreadKey);
t.classList.toggle("active", on);
t.setAttribute("aria-selected", String(on));
});
const prompts = { one: "
핵심 조언이 필요하신가요? 가장 마음이 끌리는 카드 1장을 골라주세요.", three: "
과거-현재-미래의 흐름이 궁금하신가요? 순서대로 3장의 카드를 골라주세요.", reunion5: "
재회에 대해 심층적으로 분석하고 싶다면, 신중하게 5장의 카드를 골라주세요.", cross6: "
두 사람의 관계를 다각도에서 보고 싶다면, 6장의 카드를 골라주세요." };
intentionEl.innerHTML = `조용히 심호흡하고, 그 사람을 떠올리며... ${prompts[spreadKey]}`;
}
function resetSelection() {
selected = [];
summaryEl.hidden = true; storyEl.hidden = true; resultsEl.innerHTML = "";
root.querySelectorAll('.back.selected').forEach(b => b.classList.remove('selected'));
updateUIState();
}
function refreshDeck() {
deckEl.innerHTML = "";
order.forEach(i => {
const b = document.createElement("button");
b.type="button"; b.className="back"; b.dataset.idx=String(i);
b.innerHTML = `

`;
b.addEventListener("click", () => {
if (isRevealing) return;
const idx = Number(b.dataset.idx), need = SPREADS[spreadSel.value].size, pos = selected.indexOf(idx);
if (pos >= 0) { selected.splice(pos, 1); b.classList.remove("selected"); }
else { if (selected.length >= need) return; selected.push(idx); b.classList.add("selected"); }
updateUIState();
});
deckEl.appendChild(b);
});
resetSelection();
}
function playConfetti() {
const confettiContainer = root.querySelector('.confetti');
if(!confettiContainer) return;
for (let i = 0; i < 50; i++) {
const piece = document.createElement('div');
piece.className = 'confetti-piece';
piece.style.left = `${Math.random() * 100}%`;
piece.style.animation = `fall ${2 + Math.random() * 3}s ${Math.random() * 2}s linear forwards`;
piece.style.transform = `rotate(${Math.random() * 360}deg)`;
confettiContainer.appendChild(piece);
}
setTimeout(() => confettiContainer.innerHTML = '', 5000);
}
if (!document.querySelector('#confetti-keyframes')) {
const style = document.createElement('style');
style.id = 'confetti-keyframes';
style.innerHTML = `@keyframes fall { to { transform: translateY(100vh) rotate(720deg); opacity: 1; } }`;
document.head.appendChild(style);
}
function compute() {
if (isRevealing) return;
isRevealing = true;
root.querySelector('.loading-overlay').classList.add('active');
setTimeout(() => {
const key = spreadSel.value;
const positions = SPREADS[key].positions;
const picks = selected.map(idx => {
const card = DECK[idx];
const reversed = revChk.checked ? Math.random() < 0.5 : false;
const face = reversed ? card.meaning.rev : card.meaning.up;
return { ...face, title:card.title, image:card.image, reversed };
});
const tot = totalScore(picks, key);
summaryEl.hidden = false;
root.querySelector("#score").textContent = tot;
const tag = scoreTag(tot);
const tagEl = root.querySelector("#tag");
tagEl.textContent = tag.t; tagEl.className = "tag " + tag.c;
root.querySelector("#advc").textContent = friendlyIntro(tot);
const barFill = root.querySelector("#barfill");
barFill.style.width = tot + "%";
barFill.style.background = tot>=85 ? "var(--good)" : tot>=65 ? "var(--good)" : tot>=45 ? "var(--warn)" : "var(--bad)";
resultsEl.innerHTML = "";
picks.forEach((c,i) => {
const wrap = document.createElement("div");
wrap.className = `card-wrapper ${key === 'one' ? 'is-single' : ''}`;
wrap.innerHTML = `
${positions[i]}
${c.title} ${c.reversed?'역':'정'}
${c.kw ? `
🗝️ 키워드: ${c.kw}
` : ''}
${c.text || c.story}
`;
resultsEl.appendChild(wrap);
});
const tips = advicePack(tot);
storyEl.hidden = false;
storyEl.innerHTML = `
총운 및 조언 📝
${friendlyIntro(tot)}
지금 시도해볼 만한 Action Plan
첫 메시지: ${tips.first}
타이밍: ${tips.timing}
주의할 점: ${tips.caution}
AI 타로로 오늘의 운세 확인하기
- 연애·금전·직장·학업 한 번에
- 설치 없이 즉시 결과
🔮 무료로 카드 뽑기
AI 타로로 오늘의 운세 확인하기
- 연애·금전·직장·학업 한 번에
- 설치 없이 즉시 결과
🔮 무료로 카드 뽑기
※ 본 타로는 즐거움을 위한 콘텐츠이며, 중요한 결정은 자신의 판단과 전문가의 조언을 참고하세요.
`;
root.querySelector('.loading-overlay').classList.remove('active');
isRevealing = false;
root.querySelector('#result-anchor').scrollIntoView({ behavior: 'smooth', block: 'start' });
if (tot >= 90) playConfetti();
root.querySelectorAll('.rcard').forEach((rc, i) => {
setTimeout(() => rc.classList.add('is-flipped'), 200 * (i + 1));
});
}, 1500);
}
tiles.forEach(t => t.addEventListener("click", () => { spreadSel.value = t.dataset.spread; resetSelection(); }));
root.querySelector("#shuffle").addEventListener("click", () => { order=shuffle(order); refreshDeck(); });
root.querySelector("#clear").addEventListener("click", resetSelection);
revealBtn.addEventListener("click", compute);
order = shuffle(order);
refreshDeck();
}
render();
})();