Free cookie consent management tool by TermsFeed Generator
import React, { useEffect, useMemo, useRef, useState } from "react"; import { motion, useReducedMotion } from "framer-motion"; import { Camera, Wand2, Code2, Clapperboard, Rocket, Volume2, VolumeX, ArrowUpRight, Smartphone, TabletSmartphone, Monitor, PencilLine, Upload, Download } from "lucide-react"; // === THEME === const BELITZ_YELLOW = "#FFD400"; // Primärgelb const BELITZ_YELLOW_SOFT = "rgba(255,212,0,0.15)"; const BG = "#0A0A0A"; // Tiefschwarz // === Simple content store with localStorage (plain JS) === const DEFAULT_CONTENT = { heroTitle: "Cutting‑Edge Creative &\nHigh‑Impact Editing", heroCopy: "Belitzmedia baut dir die Show, die hängen bleibt: kompromissloses Editing, starke Markeninszenierung, schnelles Storytelling. Schwarz‑Gelb. Direkt. Unübersehbar.", contactEmail: "hello@belitz.media", colorPrimary: BELITZ_YELLOW, bg: BG, useSVGLogo: false, svgString: "", logoText: "BELITZMEDIA", }; function useContent() { const [content, setContent] = useState(() => { if (typeof window !== "undefined") { const raw = localStorage.getItem("belitzLandingContent"); if (raw) return { ...DEFAULT_CONTENT, ...JSON.parse(raw) }; } return DEFAULT_CONTENT; }); useEffect(() => { if (typeof window !== "undefined") { localStorage.setItem("belitzLandingContent", JSON.stringify(content)); } }, [content]); return { content, setContent }; } export default function BelitzmediaLanding() { const prefersReduced = useReducedMotion(); const { content, setContent } = useContent(); const [mouse, setMouse] = useState({ x: 0, y: 0 }); const [audioOn, setAudioOn] = useState(false); const [editorOpen, setEditorOpen] = useState(false); const [device, setDevice] = useState("desktop"); // 'desktop' | 'tablet' | 'mobile' // Spotlight follows cursor useEffect(() => { const handleMove = (e) => setMouse({ x: e.clientX, y: e.clientY }); window.addEventListener("mousemove", handleMove); return () => window.removeEventListener("mousemove", handleMove); }, []); // Ambient sound (safe, only on click) const audioRef = useRef(null); const toggleAudio = async () => { try { if (!audioOn) { const ctx = new (window.AudioContext || window.webkitAudioContext)(); audioRef.current = ctx; const osc = ctx.createOscillator(); const gain = ctx.createGain(); const lfo = ctx.createOscillator(); const lfoGain = ctx.createGain(); osc.type = "sine"; osc.frequency.value = 72; gain.gain.value = 0.0001; lfo.frequency.value = 0.03; lfoGain.gain.value = 18; lfo.connect(lfoGain).connect(osc.frequency); osc.connect(gain).connect(ctx.destination); osc.start(); lfo.start(); gain.gain.exponentialRampToValueAtTime(0.02, ctx.currentTime + 2); setAudioOn(true); } else if (audioRef.current) { const ctx = audioRef.current; const dest = ctx.destination; // quick suspend for simplicity ctx.suspend(); setAudioOn(false); } } catch (_) { setAudioOn(false); } }; const deviceWidth = device === "mobile" ? 390 : device === "tablet" ? 768 : undefined; return (
{/* HUD: Editor & Device toggles */}
{/* Cursor Spotlight */}
{/* Canvas-like Background (2D Particles) */}
{/* NAV */}
{/* CONTENT WIDTH WRAPPER for device preview */}
{/* HERO */}
{/* 3D‑LOOK LOGO (WebGL‑freie Variante) */}
Interactive Logo (No‑WebGL)
{content.heroTitle.split("\n").map((line, i) => ( {line} ))} {content.heroCopy} Showreel ansehen Projekt anfragen
{/* CAPABILITIES */}
{capabilities.map((c, i) => ( ))}
{/* WORK GRID */}

Selected Work

Alle Projekte
{projects.map((p, idx) => ( ))}
{/* PROCESS */}

Prozess

    {processSteps.map((s, i) => (
    0{i + 1}
    {s.title}

    {s.copy}

    ))}
{/* CTA */}

Bereit, aufzudrehen?

Ein Call, ein Plan, ein Schnitt – und deine Marke bekommt den Punch. Lass uns kurz abstimmen, was du brauchst.

{content.contactEmail}
{/* Footer */}
{/* EDITOR PANEL */} {editorOpen && ( )} {/* util styles */}
); } // === 3D-LOOK (Fallback ohne WebGL) === function LogoStageFallback({ color, useSVG, svgString, text }) { const ref = useRef(null); const [tilt, setTilt] = useState({ x: 0, y: 0 }); const onMove = (e) => { const el = ref.current; if (!el) return; const rect = el.getBoundingClientRect(); const px = (e.clientX - rect.left) / rect.width; // 0..1 const py = (e.clientY - rect.top) / rect.height; // 0..1 const ry = (px - 0.5) * 18; // rotateY const rx = (0.5 - py) * 12; // rotateX setTilt({ x: rx, y: ry }); }; const onLeave = () => setTilt({ x: 0, y: 0 }); return (
{/* Glow */}
{/* Content */}
{useSVG ? (
) : (
{text}
Signature Cut • Black & Yellow
)}
{/* Fake depth layers */}
); } // === Capability Card === function CapabilityCard({ title, copy, icon: Icon, index, color }) { return (
{title}

{copy}

); } const capabilities = [ { title: "High‑End Editing", copy: "Tempo, Timing, Taktgefühl – wir schneiden so, dass die Message trifft und hängen bleibt.", icon: Clapperboard, }, { title: "Motion & FX", copy: "Titel, Transitions, 2.5D/3D – stilvoll und on‑brand. Dezent, wenn's reicht. Wild, wenn's wirkt.", icon: Wand2, }, { title: "Creative Direction", copy: "Konzept bis Kampagne: Story, Look, Sound. Schwarz‑Gelb als Signatur.", icon: Rocket, }, { title: "Web & Interaktion", copy: "Landingpages mit Punch: schnelle, reaktive Erlebnisse, die konvertieren.", icon: Code2, }, { title: "Brand Film", copy: "Cinematic Snippets, die wie Trailer wirken – für TikTok, Reels & YouTube.", icon: Camera, }, { title: "Packaging Content", copy: "Produkt sexy erzählen: Macro Shots, Sounddesign, Micro‑Animationen.", icon: Wand2, }, ]; // === Projects (placeholder thumbs) === const projects = [ { title: "Mall of Berlin – TikTok Hype", tag: "Campaign Editing" }, { title: "SugarGang – Product Shorts", tag: "Social Spots" }, { title: "Nicflex – YT Cuts", tag: "YouTube Editing" }, { title: "Wonderwaffel – Sizzle", tag: "Hero Reel" }, { title: "Rennesaince Wear – Drop", tag: "Brand Film" }, { title: "VegaHappy – Explainer", tag: "Motion Design" }, ]; function ProjectCard({ title, tag, color }) { return ( {/* Thumb */}
{/* Overlay */}
{tag}
{title}
); } // === Process === const processSteps = [ { title: "Kickoff", copy: "Kurzer Call, klare Ziele. Wir definieren Look, Tonalität und KPIs." }, { title: "Prototyp", copy: "Styleframes, Snippets, Intro – schnell greifbare Vorschau deiner Marke." }, { title: "Delivery", copy: "Iterationen in Tempo, Master‑Export, Formate für alle Plattformen." }, ]; // === Particle Canvas (2D) === function ParticleCanvas({ color = BELITZ_YELLOW, density = 1, reduceMotion = false }) { const canvasRef = useRef(null); const mouse = useRef({ x: 0, y: 0 }); const particles = useRef([]); const raf = useRef(null); const config = useMemo(() => ({ COUNT: Math.floor(140 * density), MAX_V: reduceMotion ? 0.08 : 0.25, R_MIN: 0.6, R_MAX: 1.8, LINK_DIST: 110, MOUSE_PULL: reduceMotion ? 0.05 : 0.12, }), [density, reduceMotion]); useEffect(() => { const canvas = canvasRef.current; const ctx = canvas.getContext("2d"); const resize = () => { const dpr = Math.min(window.devicePixelRatio || 1, 2); canvas.width = Math.floor(window.innerWidth * dpr); canvas.height = Math.floor(window.innerHeight * dpr); canvas.style.width = window.innerWidth + "px"; canvas.style.height = window.innerHeight + "px"; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); }; resize(); window.addEventListener("resize", resize); // init particles particles.current = Array.from({ length: config.COUNT }, () => ({ x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight, vx: (Math.random() - 0.5) * config.MAX_V, vy: (Math.random() - 0.5) * config.MAX_V, r: config.R_MIN + Math.random() * (config.R_MAX - config.R_MIN), })); const onMove = (e) => { mouse.current.x = e.clientX; mouse.current.y = e.clientY; }; window.addEventListener("mousemove", onMove); const draw = () => { ctx.clearRect(0, 0, canvas.width, canvas.height); // subtle vignette const grad = ctx.createRadialGradient(canvas.width / 2, canvas.height / 2, 0, canvas.width / 2, canvas.height / 2, Math.max(canvas.width, canvas.height) * 0.6); grad.addColorStop(0, "rgba(255,255,255,0.02)"); grad.addColorStop(1, "rgba(0,0,0,0.35)"); ctx.fillStyle = grad; ctx.fillRect(0, 0, canvas.width, canvas.height); // update & draw particles ctx.globalCompositeOperation = "lighter"; particles.current.forEach((p) => { const dx = mouse.current.x - p.x; const dy = mouse.current.y - p.y; const dist = Math.hypot(dx, dy) || 1; const pull = Math.min(config.MOUSE_PULL / dist, 0.02); p.vx += dx * pull; p.vy += dy * pull; const sp = Math.hypot(p.vx, p.vy); const MAX_V = config.MAX_V; if (sp > MAX_V) { p.vx = (p.vx / sp) * MAX_V; p.vy = (p.vy / sp) * MAX_V; } p.x += p.vx; p.y += p.vy; if (p.x < -10) p.x = window.innerWidth + 10; if (p.x > window.innerWidth + 10) p.x = -10; if (p.y < -10) p.y = window.innerHeight + 10; if (p.y > window.innerHeight + 10) p.y = -10; ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); ctx.fillStyle = color; ctx.globalAlpha = 0.12; ctx.fill(); }); ctx.globalAlpha = 0.06; for (let i = 0; i < particles.current.length; i++) { const a = particles.current[i]; for (let j = i + 1; j < particles.current.length; j++) { const b = particles.current[j]; const dx = a.x - b.x; const dy = a.y - b.y; const d = dx * dx + dy * dy; if (d < 110 * 110) { ctx.beginPath(); ctx.moveTo(a.x, a.y); ctx.lineTo(b.x, b.y); ctx.strokeStyle = color; ctx.lineWidth = 1; ctx.stroke(); } } } ctx.globalAlpha = 1; raf.current = requestAnimationFrame(draw); }; draw(); return () => { window.removeEventListener("resize", resize); window.removeEventListener("mousemove", onMove); if (raf.current) cancelAnimationFrame(raf.current); }; }, [color, config]); return ; } // === EDITOR PANEL === function EditorPanel({ content, setContent }) { const onSVGUpload = async (e) => { const file = e.target.files?.[0]; if (!file) return; const text = await file.text(); setContent({ ...content, svgString: text, useSVGLogo: true }); }; const downloadJSON = () => { const blob = new Blob([JSON.stringify(content, null, 2)], { type: "application/json" }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "belitz-landing.json"; a.click(); URL.revokeObjectURL(url); }; const onJSONUpload = async (e) => { const file = e.target.files?.[0]; if (!file) return; const text = await file.text(); try { const data = JSON.parse(text); setContent({ ...DEFAULT_CONTENT, ...data }); } catch (err) { alert("Ungültige JSON-Datei"); } }; return (
Editor
setContent({ ...content, colorPrimary: e.target.value })} className="h-9 w-full rounded-md border border-white/20 bg-black/40" />
setContent({ ...content, bg: e.target.value })} className="h-9 w-full rounded-md border border-white/20 bg-black/40" />