import { useState } from "react";
const CATEGORIES = [
{
name: "MINDFULNESS",
color: "#00B4D8",
questions: [
{ value: 100, question: "This state blends emotional and rational thinking into balanced wisdom.", answer: "What is Wise Mind?" },
{ value: 200, question: "This mindfulness skill means watching your thoughts and feelings without reacting — like clouds passing in the sky.", answer: "What is Observe?" },
{ value: 300, question: "The opposite of judging — this mindfulness stance means accepting what IS without labeling it good or bad.", answer: "What is Non-judgmental stance?" },
{ value: 400, question: "Doing one thing at a time with full attention is this mindfulness 'how' skill.", answer: "What is One-mindfully?" },
{ value: 500, question: "This mindfulness skill involves putting experiences into words without adding extra meaning or judgment.", answer: "What is Describe?" },
]
},
{
name: "DISTRESS TOLERANCE",
color: "#E63946",
questions: [
{ value: 100, question: "The 'T' in TIPP stands for this — holding ice or splashing cold water on your face to quickly calm down.", answer: "What is Temperature?" },
{ value: 200, question: "In ACCEPTS, the 'C' stands for helping others to get out of your own head — this skill.", answer: "What is Contributing?" },
{ value: 300, question: "This ACCEPTS skill involves temporarily imagining putting your pain in a box on a shelf.", answer: "What is Pushing Away?" },
{ value: 400, question: "This crisis survival skill uses the acronym IMPROVE, where the 'R' stands for finding meaning in suffering.", answer: "What is Relaxation? (Meaning/purpose)" },
{ value: 500, question: "Radical _______ means fully accepting reality as it is, not as you wish it were — without approval.", answer: "What is Acceptance?" },
]
},
{
name: "EMOTION REGULATION",
color: "#2DC653",
questions: [
{ value: 100, question: "PLEASE skills target this — the physical foundations that affect how emotionally reactive you are.", answer: "What is emotional vulnerability?" },
{ value: 200, question: "Acting opposite to your urge — approaching instead of avoiding when afraid — is this skill.", answer: "What is Opposite Action?" },
{ value: 300, question: "Before choosing a coping strategy, DBT recommends first doing this — identifying what emotion you're actually feeling.", answer: "What is checking the facts (or identifying the emotion)?" },
{ value: 400, question: "This emotion regulation concept says emotions have a beginning, middle, and end — and 'surfing' them rather than suppressing prevents them from lasting longer.", answer: "What is riding the wave (or emotion surfing)?" },
{ value: 500, question: "Building Mastery and accumulating Positive Experiences are part of this ABC PLEASE sub-skill that builds resilience in advance.", answer: "What is ABC (Accumulate positives, Build mastery, Cope ahead)?" },
]
},
{
name: "INTERPERSONAL",
color: "#F4A261",
questions: [
{ value: 100, question: "DEAR MAN's first letter — you begin an assertive request by doing this to the situation.", answer: "What is Describe?" },
{ value: 200, question: "The GIVE skill where you acknowledge the other person's feelings as understandable.", answer: "What is Validate?" },
{ value: 300, question: "FAST skills protect this — your own values and self-respect — even while being effective with others.", answer: "What is self-respect?" },
{ value: 400, question: "The 'N' in DEAR MAN — offering this shows flexibility and increases the chance of getting what you need.", answer: "What is Negotiate?" },
{ value: 500, question: "This FAST skill means staying true to your values and not apologizing for existing or having needs.", answer: "What is being Truthful / no Apologies (Stick to values)?" },
]
},
{
name: "ACRONYMS",
color: "#C77DFF",
questions: [
{ value: 100, question: "This acronym's letters stand for: Describe, Express, Assert, Reinforce, Mindful, Appear confident, Negotiate.", answer: "What is DEAR MAN?" },
{ value: 200, question: "This acronym covers: Gentle, Interested, Validate, Easy manner — used to protect relationships.", answer: "What is GIVE?" },
{ value: 300, question: "Temperature, Intense exercise, Paced breathing, Progressive relaxation — rapid nervous system reset.", answer: "What is TIPP?" },
{ value: 400, question: "Physical illness, Eating balanced, Avoid mood-altering substances, Sleep, Exercise — physical self-care for emotion regulation.", answer: "What is PLEASE?" },
{ value: 500, question: "Activities, Contributing, Comparisons, opposite Emotions, Pushing away, Thoughts, Sensations — distraction toolkit.", answer: "What is ACCEPTS?" },
]
},
{
name: "DBT POT POURRI",
color: "#FFB703",
questions: [
{ value: 100, question: "DBT was developed in the late 1980s by this psychologist.", answer: "Who is Marsha Linehan?" },
{ value: 200, question: "DBT stands for these three words.", answer: "What is Dialectical Behavior Therapy?" },
{ value: 300, question: "The core dialectic in DBT — accepting yourself as you are while also working to change.", answer: "What is acceptance AND change?" },
{ value: 400, question: "DBT was originally developed to treat this specific disorder, characterized by emotional dysregulation and unstable relationships.", answer: "What is Borderline Personality Disorder (BPD)?" },
{ value: 500, question: "This DBT concept means that two seemingly opposite things can both be true at the same time — the foundation of the whole approach.", answer: "What is dialectics (or dialectical thinking)?" },
]
}
];
const DAILY_DOUBLE_CELLS = [
{ cat: 2, q: 3 }, { cat: 4, q: 2 }
];
function isDailyDouble(catIdx, qIdx) {
return DAILY_DOUBLE_CELLS.some(d => d.cat === catIdx && d.q === qIdx);
}
export default function DBTJeopardy() {
const [score, setScore] = useState(0);
const [answered, setAnswered] = useState({});
const [activeCell, setActiveCell] = useState(null);
const [phase, setPhase] = useState("board"); // board | question | answer | daily
const [ddWager, setDdWager] = useState("");
const [ddWagerSet, setDdWagerSet] = useState(false);
const [showAnswer, setShowAnswer] = useState(false);
const [flashScore, setFlashScore] = useState(null);
const totalPossible = CATEGORIES.reduce((s, c) => s + c.questions.reduce((a, q) => a + q.value, 0), 0);
const answeredCount = Object.keys(answered).length;
const totalQuestions = CATEGORIES.reduce((s, c) => s + c.questions.length, 0);
const gameOver = answeredCount === totalQuestions;
function openCell(catIdx, qIdx) {
if (answered[`${catIdx}-${qIdx}`]) return;
setActiveCell({ catIdx, qIdx });
setShowAnswer(false);
if (isDailyDouble(catIdx, qIdx)) {
setDdWager("");
setDdWagerSet(false);
setPhase("daily");
} else {
setPhase("question");
}
}
function handleScore(correct) {
const q = CATEGORIES[activeCell.catIdx].questions[activeCell.qIdx];
const pts = phase === "daily" ? parseInt(ddWager) || 0 : q.value;
const delta = correct ? pts : -pts;
setScore(prev => prev + delta);
setFlashScore(delta);
setTimeout(() => setFlashScore(null), 1200);
setAnswered(prev => ({ ...prev, [`${activeCell.catIdx}-${activeCell.qIdx}`]: true }));
setActiveCell(null);
setPhase("board");
setShowAnswer(false);
}
function closeModal() {
setActiveCell(null);
setPhase("board");
setShowAnswer(false);
}
const activeQ = activeCell ? CATEGORIES[activeCell.catIdx].questions[activeCell.qIdx] : null;
const activeCat = activeCell ? CATEGORIES[activeCell.catIdx] : null;
return (
<div style={{
minHeight: "100vh",
background: "#060818",
fontFamily: "'Georgia', 'Times New Roman', serif",
color: "#fff",
overflow: "hidden",
position: "relative"
}}>
<style>{`
@import url('https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Crimson+Text:ital,wght@0,400;0,600;1,400&display=swap');
* { box-sizing: border-box; margin: 0; padding: 0; }
@keyframes fadeIn { from { opacity:0; transform:scale(0.95); } to { opacity:1; transform:scale(1); } }
@keyframes slideUp { from { opacity:0; transform:translateY(30px); } to { opacity:1; transform:translateY(0); } }
@keyframes glow { 0%,100% { box-shadow: 0 0 20px rgba(255,183,3,0.3); } 50% { box-shadow: 0 0 40px rgba(255,183,3,0.7); } }
@keyframes scoreFlash { 0% { opacity:0; transform:translateY(0) scale(0.8); } 30% { opacity:1; transform:translateY(-10px) scale(1.2); } 100% { opacity:0; transform:translateY(-40px) scale(1); } }
@keyframes scanline { 0% { transform: translateY(-100%); } 100% { transform: translateY(100vh); } }
@keyframes pulse { 0%,100%{opacity:1;} 50%{opacity:0.6;} }
.board-cell {
background: linear-gradient(145deg, #0a1628, #061020);
border: 1px solid rgba(255,183,3,0.2);
border-radius: 4px;
cursor: pointer;
transition: all 0.15s ease;
position: relative;
overflow: hidden;
}
.board-cell::before {
content: '';
position: absolute; inset: 0;
background: linear-gradient(135deg, rgba(255,255,255,0.04) 0%, transparent 50%);
pointer-events: none;
}
.board-cell:hover:not(.used) {
background: linear-gradient(145deg, #0e2040, #0a1830);
border-color: rgba(255,183,3,0.6);
transform: scale(1.03);
z-index: 2;
box-shadow: 0 0 20px rgba(255,183,3,0.25), inset 0 0 20px rgba(255,183,3,0.05);
}
.board-cell.used {
background: linear-gradient(145deg, #050c18, #030810);
cursor: default;
border-color: rgba(255,255,255,0.04);
}
.board-cell.daily-double { animation: glow 2s infinite; }
.modal-overlay {
position: fixed; inset: 0;
background: rgba(2,5,20,0.92);
backdrop-filter: blur(8px);
display: flex; align-items: center; justify-content: center;
z-index: 50;
animation: fadeIn 0.25s ease;
}
.modal-card {
background: linear-gradient(160deg, #0a1628 0%, #060f20 100%);
border: 1px solid rgba(255,183,3,0.4);
border-radius: 12px;
padding: 40px;
max-width: 680px;
width: 90%;
box-shadow: 0 0 60px rgba(255,183,3,0.15), 0 40px 80px rgba(0,0,0,0.8);
animation: slideUp 0.3s ease;
position: relative;
overflow: hidden;
}
.modal-card::after {
content: '';
position: absolute; top: 0; left: 0; right: 0; height: 2px;
background: linear-gradient(90deg, transparent, rgba(255,183,3,0.8), transparent);
}
.btn {
font-family: 'Bebas Neue', sans-serif;
letter-spacing: 2px;
font-size: 18px;
border: none;
border-radius: 6px;
padding: 12px 28px;
cursor: pointer;
transition: all 0.15s ease;
}
.btn:hover { transform: translateY(-2px); filter: brightness(1.15); }
.btn:active { transform: translateY(0); }
.btn-correct { background: linear-gradient(135deg, #1a8c4e, #14693b); color: #fff; box-shadow: 0 4px 20px rgba(26,140,78,0.4); }
.btn-wrong { background: linear-gradient(135deg, #8c1a1a, #691414); color: #fff; box-shadow: 0 4px 20px rgba(140,26,26,0.4); }
.btn-reveal { background: linear-gradient(135deg, #FFB703, #e09c00); color: #060818; box-shadow: 0 4px 20px rgba(255,183,3,0.4); }
.btn-close { background: rgba(255,255,255,0.08); color: #888; font-size: 14px; padding: 8px 18px; }
input.wager-input {
background: rgba(255,255,255,0.07);
border: 1px solid rgba(255,183,3,0.4);
border-radius: 6px;
color: #FFB703;
font-family: 'Bebas Neue', sans-serif;
font-size: 32px;
letter-spacing: 3px;
padding: 12px 20px;
width: 200px;
text-align: center;
outline: none;
}
input.wager-input:focus { border-color: #FFB703; box-shadow: 0 0 20px rgba(255,183,3,0.3); }
`}</style>
{/* Scanline effect */}
<div style={{ position: "fixed", inset: 0, pointerEvents: "none", zIndex: 0, overflow: "hidden", opacity: 0.025 }}>
<div style={{ position: "absolute", width: "100%", height: 2, background: "rgba(255,255,255,0.8)", animation: "scanline 8s linear infinite" }} />
</div>
{/* Score flash */}
{flashScore !== null && (
<div style={{
position: "fixed", top: "20%", left: "50%", transform: "translateX(-50%)",
fontSize: 48, fontFamily: "'Bebas Neue', sans-serif", letterSpacing: 3,
color: flashScore > 0 ? "#2DC653" : "#E63946",
animation: "scoreFlash 1.2s ease forwards",
zIndex: 200, pointerEvents: "none",
textShadow: `0 0 30px ${flashScore > 0 ? "#2DC653" : "#E63946"}`
}}>
{flashScore > 0 ? `+$${flashScore}` : `-$${Math.abs(flashScore)}`}
</div>
)}
<div style={{ position: "relative", zIndex: 1, padding: "16px 12px", maxWidth: 1100, margin: "0 auto" }}>
{/* Header */}
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 20 }}>
<div>
<div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 36, letterSpacing: 6, color: "#FFB703", lineHeight: 1, textShadow: "0 0 30px rgba(255,183,3,0.5)" }}>
DBT JEOPARDY
</div>
<div style={{ fontSize: 11, letterSpacing: 3, color: "#334", textTransform: "uppercase", marginTop: 2 }}>
Dialectical Behavior Therapy
</div>
</div>
<div style={{ textAlign: "right" }}>
<div style={{ fontSize: 11, letterSpacing: 2, color: "#445", marginBottom: 2 }}>SCORE</div>
<div style={{
fontFamily: "'Bebas Neue', sans-serif", fontSize: 42, letterSpacing: 4,
color: score >= 0 ? "#FFB703" : "#E63946",
textShadow: `0 0 20px ${score >= 0 ? "rgba(255,183,3,0.5)" : "rgba(230,57,70,0.5)"}`
}}>
${score.toLocaleString()}
</div>
<div style={{ fontSize: 10, color: "#334" }}>{answeredCount}/{totalQuestions} answered</div>
</div>
</div>
{/* Board */}
<div style={{ display: "grid", gridTemplateColumns: `repeat(${CATEGORIES.length}, 1fr)`, gap: 6 }}>
{/* Category headers */}
{CATEGORIES.map((cat, ci) => (
<div key={ci} style={{
background: `linear-gradient(160deg, ${cat.color}22, ${cat.color}0a)`,
border: `1px solid ${cat.color}44`,
borderRadius: 6,
padding: "12px 8px",
textAlign: "center",
fontFamily: "'Bebas Neue', sans-serif",
fontSize: 14,
letterSpacing: 2,
color: cat.color,
lineHeight: 1.3,
minHeight: 60,
display: "flex", alignItems: "center", justifyContent: "center"
}}>
{cat.name}
</div>
))}
{/* Question cells */}
{[0, 1, 2, 3, 4].map(qi => (
CATEGORIES.map((cat, ci) => {
const q = cat.questions[qi];
const key = `${ci}-${qi}`;
const used = !!answered[key];
const dd = isDailyDouble(ci, qi);
return (
<div
key={key}
className={`board-cell${used ? " used" : ""}${dd && !used ? " daily-double" : ""}`}
onClick={() => !used && openCell(ci, qi)}
style={{
height: 72,
display: "flex", alignItems: "center", justifyContent: "center",
}}
>
{used ? (
<div style={{ width: 20, height: 2, background: "rgba(255,255,255,0.08)", borderRadius: 1 }} />
) : (
<div style={{
fontFamily: "'Bebas Neue', sans-serif",
fontSize: 26,
letterSpacing: 2,
color: "#FFB703",
textShadow: "0 0 12px rgba(255,183,3,0.4)"
}}>
${q.value}
</div>
)}
</div>
);
})
))}
</div>
{/* Footer */}
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: 14, padding: "0 4px" }}>
<div style={{ fontSize: 11, color: "#223", letterSpacing: 1 }}>
⭐ = Daily Double &nbsp;|&nbsp; Answer in the form of a question
</div>
<div style={{ fontSize: 11, color: "#223" }}>
Max possible: ${totalPossible.toLocaleString()}
</div>
</div>
</div>
{/* DAILY DOUBLE MODAL */}
{phase === "daily" && activeCell && (
<div className="modal-overlay">
<div className="modal-card" style={{ textAlign: "center" }}>
<div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 56, letterSpacing: 6, color: "#FFB703", textShadow: "0 0 40px rgba(255,183,3,0.8)", marginBottom: 8, animation: "glow 1s infinite" }}>
DAILY DOUBLE!
</div>
<div style={{ color: activeCat.color, fontFamily: "'Bebas Neue', sans-serif", fontSize: 18, letterSpacing: 3, marginBottom: 24 }}>
{activeCat.name}
</div>
{!ddWagerSet ? (
<>
<div style={{ color: "#aaa", fontSize: 15, marginBottom: 20, fontFamily: "'Crimson Text', serif" }}>
Current score: <span style={{ color: "#FFB703" }}>${score.toLocaleString()}</span>
<br />Enter your wager (up to ${Math.max(score, activeQ.value * 2).toLocaleString()})
</div>
<input
className="wager-input"
type="number"
placeholder="0"
value={ddWager}
onChange={e => setDdWager(e.target.value)}
min={0}
max={Math.max(score, activeQ.value * 2)}
/>
<div style={{ marginTop: 20, display: "flex", gap: 12, justifyContent: "center" }}>
<button className="btn btn-close" onClick={closeModal}>Cancel</button>
<button className="btn btn-reveal" onClick={() => ddWager && setDdWagerSet(true)}>
Lock In Wager
</button>
</div>
</>
) : (
<>
<div style={{ color: "#aaa", fontSize: 13, marginBottom: 6, letterSpacing: 1 }}>WAGER</div>
<div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 40, color: "#FFB703", marginBottom: 24 }}>${parseInt(ddWager).toLocaleString()}</div>
<div style={{
background: "rgba(255,255,255,0.04)", borderRadius: 10,
border: "1px solid rgba(255,255,255,0.08)",
padding: "24px 28px", marginBottom: 28,
fontFamily: "'Crimson Text', serif", fontSize: 20, lineHeight: 1.7, color: "#E8E0D0"
}}>
{activeQ.question}
</div>
{!showAnswer ? (
<button className="btn btn-reveal" onClick={() => setShowAnswer(true)}>Reveal Answer</button>
) : (
<>
<div style={{ background: "rgba(255,183,3,0.08)", border: "1px solid rgba(255,183,3,0.25)", borderRadius: 8, padding: "16px 20px", marginBottom: 24, fontFamily: "'Crimson Text', serif", fontSize: 18, color: "#FFD166", fontStyle: "italic" }}>
{activeQ.answer}
</div>
<div style={{ display: "flex", gap: 14, justifyContent: "center" }}>
<button className="btn btn-wrong" onClick={() => handleScore(false)}>✗ Wrong</button>
<button className="btn btn-correct" onClick={() => handleScore(true)}>✓ Correct</button>
</div>
</>
)}
</>
)}
</div>
</div>
)}
{/* QUESTION MODAL */}
{phase === "question" && activeCell && (
<div className="modal-overlay">
<div className="modal-card">
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", marginBottom: 24 }}>
<div>
<div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 13, letterSpacing: 3, color: activeCat.color }}>{activeCat.name}</div>
<div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 36, color: "#FFB703", letterSpacing: 3 }}>${activeQ.value}</div>
</div>
<button className="btn btn-close" onClick={closeModal}>✕ Skip</button>
</div>
<div style={{
background: "rgba(255,255,255,0.03)", border: "1px solid rgba(255,255,255,0.07)",
borderRadius: 10, padding: "28px 32px", marginBottom: 28,
fontFamily: "'Crimson Text', serif", fontSize: 22, lineHeight: 1.75, color: "#EEE8D8",
textAlign: "center", minHeight: 100, display: "flex", alignItems: "center", justifyContent: "center"
}}>
{activeQ.question}
</div>
{!showAnswer ? (
<div style={{ textAlign: "center" }}>
<button className="btn btn-reveal" onClick={() => setShowAnswer(true)}>
Reveal Answer
</button>
</div>
) : (
<>
<div style={{
background: "rgba(255,183,3,0.08)", border: "1px solid rgba(255,183,3,0.3)",
borderRadius: 8, padding: "16px 22px", marginBottom: 24,
fontFamily: "'Crimson Text', serif", fontSize: 19, color: "#FFD166",
fontStyle: "italic", textAlign: "center", lineHeight: 1.5
}}>
{activeQ.answer}
</div>
<div style={{ display: "flex", gap: 14, justifyContent: "center" }}>
<button className="btn btn-wrong" onClick={() => handleScore(false)}>✗ Wrong</button>
<button className="btn btn-correct" onClick={() => handleScore(true)}>✓ Correct</button>
</div>
</>
)}
</div>
</div>
)}
{/* GAME OVER */}
{gameOver && (
<div className="modal-overlay">
<div className="modal-card" style={{ textAlign: "center" }}>
<div style={{ fontSize: 52, marginBottom: 12 }}>🏆</div>
<div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 48, letterSpacing: 5, color: "#FFB703", marginBottom: 6 }}>FINAL SCORE</div>
<div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 72, color: score >= 0 ? "#2DC653" : "#E63946", letterSpacing: 4, textShadow: `0 0 40px ${score >= 0 ? "#2DC653" : "#E63946"}` }}>
${score.toLocaleString()}
</div>
<div style={{ color: "#556", fontSize: 14, marginTop: 8, marginBottom: 28 }}>
out of ${totalPossible.toLocaleString()} possible
</div>
<div style={{ fontFamily: "'Crimson Text', serif", color: "#aaa", fontSize: 16, lineHeight: 1.7, maxWidth: 380, margin: "0 auto 32px" }}>
{score >= 2000 ? "Outstanding! You clearly know your DBT skills." :
score >= 1000 ? "Great job! Keep practicing those trickier concepts." :
score >= 0 ? "Good effort! Review the skills and try again." :
"Keep studying — DBT takes practice. You've got this!"}
</div>
<button className="btn btn-reveal" onClick={() => { setScore(0); setAnswered({}); setActiveCell(null); setPhase("board"); }}>
Play Again
</button>
</div>
</div>
)}
</div>
);
}