setHov(true)}
onMouseLeave={() => setHov(false)}
style={{
borderRadius: 14,
overflow: "hidden",
cursor: "pointer",
transition: "transform .3s,box-shadow .3s",
transform: hov ? "translateY(-8px) scale(1.03)" : "none",
boxShadow: hov ? `0 24px 60px ${color}33,0 4px 20px #0005` : "0 4px 16px #0003",
background: bg,
border: `1px solid ${border}`,
flex: "0 0 auto",
width: 158
}}
>
{hov && (
e.stopPropagation()}
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%,-60%)",
background: "#000b",
border: "2px solid #fff4",
borderRadius: "50%",
width: 44,
height: 44,
display: "flex",
alignItems: "center",
justifyContent: "center"
}}
>
)}
{item.title}
{item.genre} · {item.year}
{item.tags.slice(0, 2).map(t => )}
);
}
// ─── AD BANNER ────────────────────────────────────────────────────────────────
function AdBanner({ color = "#ff2d78", dark = true, slot = "horizontal" }) {
return (
e.stopPropagation()}
>
👑
{t.premiumTitle.toUpperCase()}
3,99€ / Monat
);
}
// ─── VISION UPLOADER ──────────────────────────────────────────────────────────
function VisionSearch({ cat, onResult, dark, t }) {
const [loading, setLoading] = useState(false);
const [preview, setPreview] = useState(null);
const fileRef = useRef(null);
const handleFile = async (e) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = async (ev) => {
const base64Data = ev.target.result.split(",")[1];
setPreview(ev.target.result);
setLoading(true);
try {
if (!API_CONFIG.apiKey) {
onResult(t.apiKeyMissing);
setLoading(false);
return;
}
const response = await fetch(API_CONFIG.baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": API_CONFIG.apiKey,
"anthropic-version": "2023-06-01",
},
body: JSON.stringify({
model: API_CONFIG.model,
max_tokens: 800,
system: `Du bist ein Entertainment-Experte. Erkenne in diesem Bild: Welche Serie, welcher Film, welcher Anime oder welche Comic-Szene ist zu sehen? Nenne Titel, Plattform, und einen YouTube-Trailer-Link wenn möglich. Antworte auf Deutsch, kurz und enthusiastisch.`,
messages: [{
role: "user",
content: [
{ type: "image", source: { type: "base64", media_type: file.type, data: base64Data } },
{ type: "text", text: "Was siehst du hier? Welche Serie oder welcher Film ist das?" }
]
}]
})
});
const data = await response.json();
onResult(data.content?.map(b => b.text || "").join("") || "Konnte das Bild nicht erkennen.");
} catch (error) {
onResult("Fehler bei der Bildanalyse 😅");
}
setLoading(false);
};
reader.readAsDataURL(file);
};
return (
{/* Header */}
{memory?.enabled && (
{t.memoryOn}
)}
{/* Messages */}
{msgs.map((m, i) => (
{m.role === "ai" && (
)}
{m.text.replace(/\*\*(.*?)\*\*/g, "$1")}
))}
{typing && (
{[0, 1, 2].map(j => (
))}
)}
{/* Quick suggestions */}
{quickSuggestions.map(s => (
))}
{/* Premium tools row */}
{isPremium
?
: (
)
}
{!isPremium && (
{t.premiumOnly}
)}
{/* Input */}
setInput(e.target.value)}
onKeyDown={e => e.key === "Enter" && send()}
placeholder={`${t.typeHere} ${botName}…`}
style={{
flex: 1,
background: inputBg,
border: `1px solid ${cat.color}22`,
borderRadius: 10,
color: dark ? "#ccc" : "#333",
fontSize: 12,
padding: "9px 12px",
outline: "none",
fontFamily: "inherit"
}}
/>
{showMemory && (
{ onMemoryUpdate(m); setShowMemory(false); }}
onClose={() => setShowMemory(false)}
dark={dark}
t={t}
/>
)}
);
}
// ─── AUTH MODAL ───────────────────────────────────────────────────────────────
function AuthModal({ onClose, onLogin, dark, t }) {
const [mode, setMode] = useState("login");
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [pw, setPw] = useState("");
const bg = dark ? "#0d0d1a" : "#fff";
const inputStyle = {
width: "100%",
background: dark ? "#080810" : "#f5f5f5",
border: `1px solid ${dark ? "#252540" : "#ddd"}`,
borderRadius: 10,
color: dark ? "#ddd" : "#333",
fontSize: 13,
padding: "11px 14px",
outline: "none",
fontFamily: "inherit",
marginBottom: 10,
boxSizing: "border-box"
};
const handleSubmit = () => {
if (!email) return;
onLogin({ name: name || email.split("@")[0], email });
onClose();
};
return (
e.stopPropagation()}
>
{["login", "register"].map(m => (
))}
{mode === "register" && (
setName(e.target.value)} placeholder="Name" style={inputStyle}/>
)}
setEmail(e.target.value)} placeholder="E-Mail" style={inputStyle}/>
setPw(e.target.value)}
type="password"
placeholder="Passwort"
style={{ ...inputStyle, marginBottom: 18 }}
/>
— oder —
{["Google", "Apple"].map(p => (
))}
);
}
// ─── WATCHLIST PANEL ──────────────────────────────────────────────────────────
function WatchlistPanel({ items, onRemove, onClose, dark, t }) {
const bg = dark ? "#0a0a15" : "#fff";
return (