import { useEffect, useMemo, useRef, useState } from "react"; const START_COLOR = { r: 26, g: 47, b: 107 }; // #1A2F6B const END_COLOR = { r: 128, g: 0, b: 32 }; // #800020 const TOTAL_DAYS = 300; const START_DATE = new Date(2026, 4, 8); // TEMPORAL VISIBILITY WINDOW const FADED_START = new Date(2026, 4, 28); const FADED_END = new Date(2027, 2, 3); type Language = "ar" | "en" | "kmh" | "ckb" | "es" | "ca"; interface LanguageOption { code: Language; label: string; title: string; subtitle: string; position: "left" | "right"; } const LANGUAGES: LanguageOption[] = [ { code: "en", label: "English", title: "Spectral Timeline Interface", subtitle: "Temporal residues accumulate inside color", position: "left", }, { code: "es", label: "Español", title: "Interfaz de línea de tiempo espectral", subtitle: "Los residuos temporales se acumulan dentro del color", position: "left", }, { code: "kmh", label: "Kurmancî", title: "Rûbera Xeta Demê ya Spektral", subtitle: "Şopên demê di nav rengan de kom dibin", position: "left", }, { code: "ca", label: "Català", title: "Interfície de línia temporal espectral", subtitle: "Els residus temporals s'acumulen dins del color", position: "left", }, { code: "ar", label: "العربية", title: "الخط الزمني", subtitle: "تراكم البقايا الزمنية", position: "right", }, { code: "ckb", label: "سۆرانی", title: "ڕووکاری هێڵی کاتی ڕەنگاوڕەنگ", subtitle: "شوێنەواری کات لە ناو ڕەنگەکاندا کۆدەبنەوە", position: "right", }, ]; function interpolateColor(t: number) { const r = Math.round(START_COLOR.r + (END_COLOR.r - START_COLOR.r) * t); const g = Math.round(START_COLOR.g + (END_COLOR.g - START_COLOR.g) * t); const b = Math.round(START_COLOR.b + (END_COLOR.b - START_COLOR.b) * t); return `rgb(${r}, ${g}, ${b})`; } function formatDate(date: Date) { const d = date.getDate().toString().padStart(2, "0"); const m = (date.getMonth() + 1).toString().padStart(2, "0"); const y = date.getFullYear(); return `${d}/${m}/${y}`; } export default function App() { const [selectedLanguage, setSelectedLanguage] = useState(null); const [hoveredLangIndex, setHoveredLangIndex] = useState(0); const [hoverTimer, setHoverTimer] = useState(null); const [selectedDay, setSelectedDay] = useState(null); const [hoveredDay, setHoveredDay] = useState(null); const [input, setInput] = useState(""); const [predictions, setPredictions] = useState>({}); const [activeDay, setActiveDay] = useState(150); const timelineRef = useRef(null); const days = useMemo(() => { return Array.from({ length: TOTAL_DAYS }, (_, i) => { const dayNumber = i + 1; const t = i / (TOTAL_DAYS - 1); const color = interpolateColor(t); const currentDate = new Date(START_DATE); currentDate.setDate(currentDate.getDate() + i); const isFaded = currentDate >= FADED_START && currentDate <= FADED_END; return { index: dayNumber, color, visibleColor: color, isFaded, position: (i / (TOTAL_DAYS - 1)) * 100, }; }); }, []); const currentDate = useMemo(() => { if (selectedDay === null) return null; const d = new Date(START_DATE); d.setDate(d.getDate() + (selectedDay - 1)); return d; }, [selectedDay]); useEffect(() => { const timelineElement = timelineRef.current; if (!timelineElement) return; let isThrottled = false; const handleWheel = (e: WheelEvent) => { if (selectedLanguage !== null) { if (isThrottled) return; if (Math.abs(e.deltaY) < 2) return; isThrottled = true; setTimeout(() => { isThrottled = false; }, 45); if (e.deltaY > 0) { setActiveDay((prev) => Math.max(prev - 1, 1)); } if (e.deltaY < 0) { setActiveDay((prev) => Math.min(prev + 1, TOTAL_DAYS)); } } else { // Language selection mode - rotate through languages if (isThrottled) return; if (Math.abs(e.deltaY) < 2) return; isThrottled = true; setTimeout(() => { isThrottled = false; }, 100); if (e.deltaY > 0) { setHoveredLangIndex((prev) => (prev - 1 + LANGUAGES.length) % LANGUAGES.length); } if (e.deltaY < 0) { setHoveredLangIndex((prev) => (prev + 1) % LANGUAGES.length); } } }; timelineElement.addEventListener("wheel", handleWheel, { passive: true }); const handleKeyPress = (e: KeyboardEvent) => { if (selectedLanguage !== null && (e.key === "Enter" || e.key === " ")) { setSelectedDay(activeDay); } }; window.addEventListener("keydown", handleKeyPress); return () => { timelineElement.removeEventListener("wheel", handleWheel); window.removeEventListener("keydown", handleKeyPress); }; }, [activeDay, selectedLanguage]); function addPrediction(dayIndex: number) { if (!input.trim()) return; setPredictions((prev) => ({ ...prev, [dayIndex]: [...(prev[dayIndex] || []), input.trim()], })); setInput(""); } const handleLanguageHover = (index: number) => { if (hoverTimer) clearTimeout(hoverTimer); setHoveredLangIndex(index); const timer = setTimeout(() => { setSelectedLanguage(LANGUAGES[index].code); }, 2000); setHoverTimer(timer); }; const handleLanguageLeave = () => { if (hoverTimer) { clearTimeout(hoverTimer); setHoverTimer(null); } }; return (
{/* LANGUAGE OVERLAY */} {selectedLanguage === null && (
{LANGUAGES.map((lang, idx) => ( ))}
)} {/* HEADER TEXT */} {selectedLanguage !== null && (
l.code === selectedLanguage)?.position === "right" ? "right-8" : "left-8" }`} >

{LANGUAGES.find((l) => l.code === selectedLanguage)?.title}

{LANGUAGES.find((l) => l.code === selectedLanguage)?.subtitle}

)} {/* TIMELINE */}
{ if (timelineRef.current && selectedLanguage !== null) { timelineRef.current.scrollLeft += e.deltaY; } }} >
{days.map((d) => { const hoverDate = new Date(START_DATE); hoverDate.setDate(hoverDate.getDate() + (d.index - 1)); return (
{ if (selectedLanguage !== null) { setActiveDay(d.index); setSelectedDay(d.index); } }} onMouseEnter={() => { if (selectedLanguage !== null) { setHoveredDay(d.index); } }} onMouseLeave={() => { if (selectedLanguage !== null) { setHoveredDay(null); } }} style={{ background: d.visibleColor, width: selectedLanguage === null ? "2.64cm" : "8.8cm", minWidth: selectedLanguage === null ? "2.64cm" : "8.8cm", height: "100%", cursor: selectedLanguage !== null ? "pointer" : "default", position: "relative", transition: "all 0.18s ease", borderRight: "1px solid rgba(255,255,255,0.12)", borderLeft: "1px solid rgba(0,0,0,0.04)", ...(d.index <= 150 ? { borderTopLeftRadius: "10px", borderBottomLeftRadius: "10px", borderTopRightRadius: "0px", borderBottomRightRadius: "0px", } : { borderTopLeftRadius: "0px", borderBottomLeftRadius: "0px", borderTopRightRadius: "10px", borderBottomRightRadius: "10px", }), filter: selectedLanguage !== null ? d.index < 100 ? hoveredDay === d.index || activeDay === d.index ? "brightness(1.12) saturate(1.05) opacity(0.72)" : "brightness(1.08) saturate(1.02) opacity(0.58)" : hoveredDay === d.index || activeDay === d.index ? "brightness(1.05)" : "brightness(1)" : "brightness(1)", }} className="relative overflow-hidden" > {selectedLanguage !== null && activeDay === d.index && (
)} {selectedLanguage !== null && (hoveredDay === d.index || activeDay === d.index) && (
Day {d.index} • {formatDate(hoverDate)}
)}
); })}
{/* MODAL */} {selectedDay !== null && selectedLanguage !== null && (
setSelectedDay(null)} >
e.stopPropagation()} >

Day {selectedDay}

{currentDate ? formatDate(currentDate) : ""}

{(predictions[selectedDay] || []).length > 0 && ( <>

Public expectations

{(predictions[selectedDay] || []).map((p, i) => (
{p}
))}
)}