o2o-triple-pick/app/before/page.tsx

252 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"use client";
// ============================================================
// BEFORE — 최초 구축 버전 (다크 네이비 토큰 테마) · 개인 기록용 보존
// 002/003 리디자인 이전 모습. 자체 완결(explicit hex), 현재 globals와 독립.
// ============================================================
import { useEffect, useMemo, useState } from "react";
const C = {
bg: "#0a0e1a",
bg2: "#0d1322",
card: "#141b2d",
green: "#25e07f",
amber: "#ffb23e",
muted: "#8b94a7",
line: "#26304a",
gpt: "#25e07f",
claude: "#c08bff",
gemini: "#4d9bff",
};
const MATCH = {
teamA: { short: "한국", name: "Korea Republic", flag: "🇰🇷" },
teamB: { short: "체코", name: "Czechia", flag: "🇨🇿" },
kickoff: "2026-06-12T11:00:00+09:00",
lockAt: "2026-06-12T10:50:00+09:00",
venue: "Estadio Guadalajara",
};
const AIS = [
{ model: "GPT", color: C.gpt, outcome: "A", score: "2 - 1", conf: 62, reason: "전환 속도와 핵심 공격 자원에서 우위", out: "한국 승" },
{ model: "Claude", color: C.claude, outcome: "D", score: "1 - 1", conf: 41, reason: "체코 수비 조직력과 세트피스가 변수", out: "무승부" },
{ model: "Gemini", color: C.gemini, outcome: "B", score: "0 - 2", conf: 28, reason: "체코의 원정 폼과 역습 효율을 높게 평가", out: "체코 승" },
];
function useCountdown(iso: string) {
const [now, setNow] = useState<number | null>(null);
useEffect(() => {
setNow(Date.now());
const t = setInterval(() => setNow(Date.now()), 1000);
return () => clearInterval(t);
}, []);
if (now === null) return null;
const diff = Math.max(0, new Date(iso).getTime() - now);
return {
d: Math.floor(diff / 86400000),
h: Math.floor((diff % 86400000) / 3600000),
m: Math.floor((diff % 3600000) / 60000),
s: Math.floor((diff % 60000) / 1000),
};
}
const pad = (n: number) => String(n).padStart(2, "0");
export default function Before() {
const cd = useCountdown(MATCH.kickoff);
const [outcome, setOutcome] = useState<string | null>(null);
const [a, setA] = useState(1);
const [b, setB] = useState(0);
const [step, setStep] = useState<"pick" | "form" | "done">("pick");
const [nick, setNick] = useState("");
const [total, setTotal] = useState(1284);
const [dist, setDist] = useState({ A: 48, D: 27, B: 25 });
const matched = useMemo(() => AIS.filter((p) => p.outcome === outcome).map((p) => p.model), [outcome]);
const wrap: React.CSSProperties = { background: C.bg, color: "#fff", minHeight: "100dvh" };
const card: React.CSSProperties = { background: C.card, border: `1px solid ${C.line}`, borderRadius: 12 };
return (
<div style={wrap}>
{/* 기록용 라벨 */}
<div style={{ background: "#1c2640", color: C.green, textAlign: "center", fontSize: 11, fontWeight: 700, padding: "6px" }}>
BEFORE () · <a href="/" style={{ color: "#fff", textDecoration: "underline" }}>/ </a>
</div>
{/* Sticky Top */}
<div style={{ position: "sticky", top: 0, zIndex: 50, background: "rgba(10,14,26,0.9)", borderBottom: `1px solid ${C.line}`, backdropFilter: "blur(6px)" }}>
<div style={{ maxWidth: 480, margin: "0 auto", padding: "0 16px" }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 0" }}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<span style={{ fontSize: 13, fontWeight: 800, color: C.green }}>TriplePick</span>
<span style={{ fontSize: 11, color: C.muted }}>🇰🇷 vs 🇨🇿</span>
</div>
<div style={{ display: "flex", alignItems: "center", gap: 10 }}>
<div style={{ textAlign: "right", lineHeight: 1 }}>
<div style={{ fontSize: 9, color: C.muted }}></div>
<div style={{ fontSize: 12, fontWeight: 700, fontVariantNumeric: "tabular-nums" }}>
{cd ? `${cd.d}${pad(cd.h)}:${pad(cd.m)}:${pad(cd.s)}` : "—"}
</div>
</div>
<button onClick={() => document.getElementById("pick")?.scrollIntoView({ behavior: "smooth" })} style={{ background: C.green, color: "#06210f", fontSize: 12, fontWeight: 700, borderRadius: 6, padding: "6px 12px", border: 0 }}>
</button>
</div>
</div>
<div style={{ fontSize: 10, color: C.muted, paddingBottom: 6 }}> {total.toLocaleString()} </div>
</div>
</div>
<main style={{ maxWidth: 480, margin: "0 auto", padding: "0 16px 64px" }}>
{/* Hero */}
<header style={{ paddingTop: 20, paddingBottom: 12, textAlign: "center" }}>
<div style={{ fontSize: 20, fontWeight: 800 }}>Triple Pick <span style={{ color: C.green }}>2026</span></div>
<div style={{ fontSize: 12, color: C.muted }}>AI Prediction Arena</div>
<h1 style={{ fontSize: 22, fontWeight: 800, marginTop: 16, lineHeight: 1.35 }}>
,<br /><span style={{ color: C.green }}>AI </span>
</h1>
<p style={{ fontSize: 12.5, color: C.muted, marginTop: 8, lineHeight: 1.6 }}>
AI .<br /> , .
</p>
</header>
{/* Match card */}
<section style={{ ...card, padding: 16, marginTop: 12 }}>
<div style={{ display: "flex", justifyContent: "space-between", fontSize: 10, color: C.muted }}>
<span style={{ border: `1px solid ${C.line}`, borderRadius: 999, padding: "2px 8px", color: C.green, fontWeight: 600 }}>Match 01</span>
<span>Matchday Prediction</span>
</div>
<div style={{ display: "grid", gridTemplateColumns: "1fr auto 1fr", alignItems: "center", gap: 8, marginTop: 12 }}>
<Team flag={MATCH.teamA.flag} short={MATCH.teamA.short} name={MATCH.teamA.name} />
<div style={{ fontSize: 18, fontWeight: 800, color: C.muted }}>VS</div>
<Team flag={MATCH.teamB.flag} short={MATCH.teamB.short} name={MATCH.teamB.name} />
</div>
<div style={{ marginTop: 12, textAlign: "center", fontSize: 11, color: C.muted }}>6.12() 11:00 KST · Group A</div>
<div style={{ marginTop: 2, textAlign: "center", fontSize: 10, color: C.muted }}>{MATCH.venue}</div>
</section>
{/* AI predictions */}
<section style={{ marginTop: 20 }}>
<div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 8 }}>
<h2 style={{ fontSize: 14, fontWeight: 800 }}>AI </h2>
<span style={{ fontSize: 10, color: C.muted }}> · </span>
</div>
<div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
{AIS.map((p) => (
<div key={p.model} style={{ ...card, padding: 12 }}>
<div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<span style={{ width: 28, height: 28, display: "grid", placeItems: "center", borderRadius: 6, fontSize: 11, fontWeight: 800, background: `${p.color}2e`, color: p.color }}>{p.model[0]}</span>
<div>
<div style={{ fontSize: 13, fontWeight: 700 }}>{p.model}</div>
<div style={{ fontSize: 10, color: C.muted, marginTop: 2 }}>{p.out} </div>
</div>
</div>
<div style={{ textAlign: "right" }}>
<div style={{ fontSize: 16, fontWeight: 800, fontVariantNumeric: "tabular-nums" }}>{p.score}</div>
<div style={{ fontSize: 10, color: C.muted }}> {p.conf}%</div>
</div>
</div>
<div style={{ marginTop: 8, height: 6, borderRadius: 999, background: C.bg2, overflow: "hidden" }}>
<div style={{ width: `${p.conf}%`, height: "100%", background: p.color, borderRadius: 999 }} />
</div>
<p style={{ marginTop: 8, fontSize: 11, color: C.muted }}>{p.reason} <span style={{ opacity: 0.6 }}>· 2026-06-08</span></p>
</div>
))}
</div>
</section>
{/* Your pick */}
<section id="pick" style={{ marginTop: 20, scrollMarginTop: 80 }}>
<h2 style={{ fontSize: 14, fontWeight: 800, marginBottom: 8 }}> <span style={{ color: C.amber }}>🟠</span></h2>
<div style={{ ...card, padding: 16 }}>
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr 1fr", gap: 6, background: C.bg2, borderRadius: 8, padding: 4 }}>
{([["A", "한국 승"], ["D", "무승부"], ["B", "체코 승"]] as [string, string][]).map(([v, l]) => (
<button key={v} onClick={() => setOutcome(v)} style={{ borderRadius: 6, padding: "8px 0", fontSize: 12.5, fontWeight: 700, border: 0, background: outcome === v ? C.green : "transparent", color: outcome === v ? "#06210f" : C.muted }}>{l}</button>
))}
</div>
<div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 12, marginTop: 16 }}>
<Stepper label="한국" value={a} set={setA} c={C} />
<span style={{ fontSize: 18, fontWeight: 800, color: C.muted, paddingTop: 20 }}>:</span>
<Stepper label="체코" value={b} set={setB} c={C} />
</div>
{step === "pick" && (
<button onClick={() => outcome && setStep("form")} disabled={!outcome} style={{ marginTop: 16, width: "100%", borderRadius: 8, padding: "12px 0", fontSize: 15, fontWeight: 800, border: 0, background: C.green, color: "#06210f", opacity: outcome ? 1 : 0.4 }}>AI </button>
)}
{step === "form" && (
<div style={{ marginTop: 16, display: "flex", flexDirection: "column", gap: 8 }}>
<input value={nick} onChange={(e) => setNick(e.target.value)} placeholder="닉네임 (2~16자)" style={{ width: "100%", borderRadius: 8, border: `1px solid ${C.line}`, background: C.bg2, padding: "10px 12px", fontSize: 13, color: "#fff" }} />
<button onClick={() => { if (nick.trim().length >= 2) { setTotal((t) => t + 1); setStep("done"); } }} disabled={nick.trim().length < 2} style={{ width: "100%", borderRadius: 8, padding: "12px 0", fontSize: 15, fontWeight: 800, border: 0, background: C.green, color: "#06210f", opacity: nick.trim().length >= 2 ? 1 : 0.4 }}> AI </button>
</div>
)}
</div>
{step === "done" && outcome && (
<div style={{ ...card, border: `1px solid ${C.green}80`, padding: 16, marginTop: 12 }}>
<div style={{ fontSize: 11, color: C.muted }}> </div>
<div style={{ fontSize: 20, fontWeight: 800, marginTop: 2 }}> {a}-{b} <span style={{ fontSize: 13, color: C.green }}>{outcome === "A" ? "한국 승" : outcome === "B" ? "체코 승" : "무승부"}</span></div>
<p style={{ fontSize: 12.5, color: "#cfd6e2", marginTop: 8, lineHeight: 1.6 }}>
{matched.length ? <> <b>{matched.join("·")}</b> . AI .</> : <> AI . ! 🔥</>}<br /> .
</p>
<button style={{ marginTop: 12, width: "100%", borderRadius: 8, padding: "10px 0", fontSize: 13, fontWeight: 700, border: 0, background: C.green, color: "#06210f" }}> </button>
</div>
)}
</section>
{/* Crowd (세로 바) */}
<section style={{ marginTop: 20 }}>
<div style={{ display: "flex", justifyContent: "space-between", marginBottom: 8 }}>
<h2 style={{ fontSize: 14, fontWeight: 800 }}>Crowd Pick</h2>
<span style={{ fontSize: 10, color: C.muted }}> {total.toLocaleString()} </span>
</div>
<div style={{ ...card, padding: 16, display: "flex", flexDirection: "column", gap: 10 }}>
{([["한국 승", dist.A], ["무승부", dist.D], ["체코 승", dist.B]] as [string, number][]).map(([l, v]) => (
<div key={l}>
<div style={{ display: "flex", justifyContent: "space-between", fontSize: 11, marginBottom: 4 }}><span style={{ color: C.muted }}>{l}</span><span style={{ fontWeight: 700 }}>{v}%</span></div>
<div style={{ height: 8, borderRadius: 999, background: C.bg2, overflow: "hidden" }}><div style={{ width: `${v}%`, height: "100%", background: C.green, borderRadius: 999 }} /></div>
</div>
))}
</div>
</section>
{/* Prize */}
<section style={{ marginTop: 20 }}>
<div style={{ ...card, padding: 16 }}>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}><span style={{ fontSize: 18 }}>🏆</span><h2 style={{ fontSize: 14, fontWeight: 800 }}> 100 </h2></div>
<p style={{ marginTop: 6, fontSize: 12, color: C.muted, lineHeight: 1.6 }}>AI 100 . , .</p>
<button style={{ marginTop: 12, width: "100%", borderRadius: 8, padding: "10px 0", fontSize: 13, fontWeight: 700, background: `${C.green}1a`, color: C.green, border: `1px solid ${C.green}66` }}>100 </button>
</div>
</section>
{/* Footer */}
<footer style={{ marginTop: 32, borderTop: `1px solid ${C.line}`, paddingTop: 20 }}>
<p style={{ fontSize: 11, color: C.muted, lineHeight: 1.6 }}> AIO2O AI . GPT·Claude·Gemini .</p>
<p style={{ fontSize: 10, color: `${C.muted}cc`, marginTop: 8, lineHeight: 1.6 }}> · · . AI .</p>
<div style={{ marginTop: 12, display: "flex", justifyContent: "space-between", fontSize: 10, color: C.muted }}><span>© 2026 TriplePick · AIO2O</span><span>@triplepick_ai</span></div>
</footer>
</main>
</div>
);
}
function Team({ flag, short, name }: { flag: string; short: string; name: string }) {
return (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 6 }}>
<div style={{ fontSize: 40, lineHeight: 1 }}>{flag}</div>
<div style={{ fontSize: 13, fontWeight: 700 }}>{short}</div>
<div style={{ fontSize: 9, color: "#8b94a7" }}>{name}</div>
</div>
);
}
function Stepper({ label, value, set, c }: { label: string; value: number; set: (n: number) => void; c: typeof C }) {
const btn: React.CSSProperties = { width: 36, height: 36, display: "grid", placeItems: "center", borderRadius: 8, border: `1px solid ${c.line}`, background: c.bg2, color: "#fff", fontSize: 18, fontWeight: 700 };
return (
<div style={{ display: "flex", flexDirection: "column", alignItems: "center" }}>
<div style={{ fontSize: 11, color: c.muted, marginBottom: 4 }}>{label}</div>
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
<button onClick={() => set(Math.max(0, value - 1))} style={btn}></button>
<div style={{ width: 36, textAlign: "center", fontSize: 22, fontWeight: 800 }}>{value}</div>
<button onClick={() => set(Math.min(9, value + 1))} style={btn}>+</button>
</div>
</div>
);
}