eldouma-graphics / src /components /StudioShowcase.tsx
Moeeldouma's picture
Deploy Eldouma Graphics β€” Docker Space, bring-your-own-key demo
39d98a0 verified
Raw
History Blame Contribute Delete
18.6 kB
import { useEffect, useRef, useState } from "react";
// ─────────────────────────────────────────────────────────────────────────────
// StudioShowcase β€” the homepage's living art piece. A self-running reel that
// demonstrates the product's OWN range: kinetic type, documentary lower-thirds,
// Arabic RTL aurora titles, animated data, glassmorphism, 3D space and neural
// agent viz β€” each with its own palette, font and background. Pure CSS/SVG, so
// it always renders (no headless player, no fonts-required fallbacks break it).
// ─────────────────────────────────────────────────────────────────────────────
// shared list, also used by the homepage marquee
export const STYLE_NAMES = [
"Kinetic Type", "Lower-Thirds", "Glassmorphism", "Aurora", "Neural Nets", "3D Space",
"Atlas Maps", "Data Viz", "Sketchbook", "Logo Intros", "Documentary", "Counters",
"Waveforms", "Comic", "Chalkboard", "Spotlight", "Glitch", "Reveal",
];
const GRAIN =
"url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.5'/%3E%3C/svg%3E\")";
export const SHOWCASE_CSS = `
@keyframes mg-in { from { opacity: 0; transform: scale(.985); } to { opacity: 1; transform: none; } }
@keyframes mg-rise { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: none; } }
@keyframes mg-rise-sm { from { opacity: 0; transform: translateY(13px); } to { opacity: 1; transform: none; } }
@keyframes mg-pop { 0% { opacity: 0; transform: scale(.7); } 62% { opacity: 1; transform: scale(1.06); } 100% { transform: scale(1); } }
@keyframes mg-sweep { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes mg-sweep-v { from { transform: scaleY(0); } to { transform: scaleY(1); } }
@keyframes mg-float { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-14px); } }
@keyframes mg-float2 { 0%,100% { transform: translate(0,0); } 50% { transform: translate(12px,-20px); } }
@keyframes mg-grid { from { background-position: 0 0; } to { background-position: 0 42px; } }
@keyframes mg-aurora { 0% { transform: translate(-9%,0) rotate(-7deg); } 50% { transform: translate(9%,5%) rotate(-2deg); } 100% { transform: translate(-9%,0) rotate(-7deg); } }
@keyframes mg-draw { to { stroke-dashoffset: 0; } }
@keyframes mg-pulse { 0%,100% { opacity: .35; transform: translate(-50%,-50%) scale(1); } 50% { opacity: 1; transform: translate(-50%,-50%) scale(1.35); } }
@keyframes mg-spin3d { from { transform: rotateY(-26deg) rotateX(6deg); } to { transform: rotateY(26deg) rotateX(-6deg); } }
@keyframes mg-blink { 0%,100% { opacity: 1; } 50% { opacity: 0; } }
@keyframes mg-mesh { 0% { transform: translate(0,0) scale(1); } 33% { transform: translate(4%,-3%) scale(1.08); } 66% { transform: translate(-3%,4%) scale(1.05); } 100% { transform: translate(0,0) scale(1); } }
@keyframes mg-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes mg-word-in { from { opacity: 0; transform: translateY(16px) rotate(-2deg); } to { opacity: 1; transform: none; } }
@keyframes mg-word-out{ to { opacity: 0; transform: translateY(-12px); } }
@keyframes mg-ring { to { stroke-dashoffset: 0; } }
@keyframes mg-scan { from { transform: translateY(-100%); } to { transform: translateY(220%); } }
`;
// ── Scene 1 Β· Kinetic typography ────────────────────────────────────────────
function KineticScene() {
return (
<div className="absolute inset-0 overflow-hidden" style={{ background: "radial-gradient(120% 100% at 50% 0%, #1c1450, #0a0a16 72%)" }}>
<div className="absolute inset-x-0 bottom-0 h-1/2" style={{ backgroundImage: "linear-gradient(rgba(150,120,255,.20) 1px, transparent 1px), linear-gradient(90deg, rgba(150,120,255,.20) 1px, transparent 1px)", backgroundSize: "42px 42px", transform: "perspective(420px) rotateX(60deg)", transformOrigin: "bottom", animation: "mg-grid 2.4s linear infinite", maskImage: "linear-gradient(to top, #000, transparent)", WebkitMaskImage: "linear-gradient(to top, #000, transparent)" }} />
<div className="absolute inset-0 grid place-items-center">
<div className="text-center">
{["STORIES", "IN", "MOTION"].map((w, i) => (
<div key={w} className="font-extrabold leading-[0.92] tracking-tight" style={{ fontSize: "clamp(34px,7vw,82px)", fontFamily: "Inter, sans-serif", animation: `mg-rise .7s ${0.12 * i + 0.1}s both`, backgroundImage: "linear-gradient(100deg,#a78bfa,#f472b6 55%,#fb923c)", WebkitBackgroundClip: "text", backgroundClip: "text", color: "transparent" }}>{w}</div>
))}
<div className="mx-auto mt-4 h-[3px] w-44 rounded-full" style={{ background: "linear-gradient(90deg,#a78bfa,#fb923c)", transformOrigin: "left", animation: "mg-sweep .8s .55s both" }} />
</div>
</div>
</div>
);
}
// ── Scene 2 Β· Documentary lower-third ───────────────────────────────────────
function DocScene() {
return (
<div className="absolute inset-0 overflow-hidden" style={{ background: "radial-gradient(130% 90% at 72% 18%, #2c1d11, #080706 76%)" }}>
<div className="absolute inset-0" style={{ boxShadow: "inset 0 0 160px 40px rgba(0,0,0,.7)" }} />
<div className="absolute right-5 top-5 flex items-center gap-3 text-[11px] font-medium tracking-wider text-white/60" style={{ fontFamily: "'JetBrains Mono', monospace", animation: "mg-rise-sm .5s .1s both" }}>
<span className="flex items-center gap-1.5"><span className="h-2 w-2 rounded-full bg-red-500" style={{ animation: "mg-blink 1s steps(1) infinite" }} />REC</span>
<span>00:00:14:09</span>
</div>
<div className="absolute inset-x-7 bottom-10">
<div className="flex items-stretch gap-3.5">
<div className="w-1.5 rounded-full" style={{ background: "linear-gradient(#f4934f,#e7723b)", transformOrigin: "top", animation: "mg-sweep-v .5s .15s both" }} />
<div>
<div className="font-semibold leading-tight text-white" style={{ fontSize: "clamp(26px,4.4vw,48px)", fontFamily: "Georgia, 'Times New Roman', serif", animation: "mg-rise .6s .25s both" }}>Dr. Lina Haddad</div>
<div className="relative mt-1.5 inline-block px-0.5">
<span className="relative z-10 text-[12.5px] font-semibold uppercase tracking-[0.18em] text-white" style={{ animation: "mg-rise-sm .6s .45s both" }}>Marine Biologist Β· Red Sea</span>
<span className="absolute inset-y-0 -inset-x-1" style={{ background: "rgba(231,114,59,.92)", transformOrigin: "left", animation: "mg-sweep .55s .62s both" }} />
</div>
</div>
</div>
</div>
</div>
);
}
// ── Scene 3 Β· Arabic Β· Aurora Β· RTL ─────────────────────────────────────────
function ArabicScene() {
return (
<div dir="rtl" className="absolute inset-0 overflow-hidden" style={{ background: "linear-gradient(160deg,#04211e,#06100f)" }}>
<div className="absolute -inset-[20%]">
<div className="absolute left-1/2 top-1/3 h-44 w-[150%] -translate-x-1/2 rounded-full blur-3xl" style={{ background: "linear-gradient(90deg, rgba(54,194,184,0), rgba(54,194,184,.55), rgba(16,185,129,0))", animation: "mg-aurora 9s ease-in-out infinite" }} />
<div className="absolute left-1/2 top-1/2 h-36 w-[140%] -translate-x-1/2 rounded-full blur-3xl" style={{ background: "linear-gradient(90deg, rgba(45,212,191,0), rgba(94,234,212,.4), rgba(45,212,191,0))", animation: "mg-aurora 11s ease-in-out infinite reverse" }} />
</div>
<div className="absolute inset-0 grid place-items-center px-8 text-center">
<div>
<div className="text-[16px] font-semibold tracking-wide text-teal-300/90" style={{ fontFamily: "'El Messiri', sans-serif", animation: "mg-rise-sm .6s .15s both" }}>Ψ₯Ψ¨Ψ―Ψ§ΨΉ بءري</div>
<div className="mt-2 font-bold leading-none text-white" style={{ fontSize: "clamp(44px,10vw,104px)", fontFamily: "'Reem Kufi', sans-serif", animation: "mg-rise .7s .25s both" }}>Ψ¨Ω„Ψ§ حدود</div>
<div className="mx-auto mt-6 h-[3px] w-48 rounded-full" style={{ background: "linear-gradient(90deg,#5eead4,#10b981)", transformOrigin: "right", animation: "mg-sweep .8s .55s both" }} />
</div>
</div>
</div>
);
}
// ── Scene 4 Β· Animated data / counter ───────────────────────────────────────
function CounterScene() {
const [n, setN] = useState(0);
useEffect(() => {
let raf = 0;
const t0 = performance.now();
const tick = (t: number) => {
const p = Math.min(1, (t - t0) / 1500);
const e = 1 - Math.pow(1 - p, 3);
setN(Math.round(e * 340));
if (p < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, []);
return (
<div className="absolute inset-0 overflow-hidden" style={{ background: "radial-gradient(80% 72% at 50% 10%, #2a230c, #070707 72%)" }}>
<div className="absolute left-1/2 top-0 h-full w-[58%] -translate-x-1/2" style={{ background: "linear-gradient(to bottom, rgba(224,179,65,.20), transparent 58%)", filter: "blur(10px)" }} />
<div className="absolute inset-0 grid place-items-center">
<div className="text-center">
<div className="font-extrabold leading-none text-white" style={{ fontSize: "clamp(60px,13vw,132px)", fontFamily: "'JetBrains Mono', monospace", fontVariantNumeric: "tabular-nums", animation: "mg-pop .6s both" }}>
<span style={{ color: "#E0B341" }}>+</span>{n}<span style={{ color: "#E0B341" }}>%</span>
</div>
<div className="mt-2 text-[14px] font-medium uppercase tracking-[0.22em] text-white/70" style={{ animation: "mg-rise-sm .6s .4s both" }}>year-over-year growth</div>
<div className="mx-auto mt-5 flex items-center gap-2.5 text-[12px] text-amber-200/80" style={{ fontFamily: "'JetBrains Mono', monospace", animation: "mg-rise-sm .6s .55s both" }}>
<span>Q4 2026</span><span className="text-white/30">Β·</span><span className="font-bold text-amber-300">record quarter</span>
</div>
</div>
</div>
</div>
);
}
// ── Scene 5 Β· Glassmorphism ─────────────────────────────────────────────────
function GlassScene() {
return (
<div className="absolute inset-0 overflow-hidden" style={{ background: "linear-gradient(150deg,#2a1130,#3b1538 50%,#120a1a)" }}>
<div className="absolute h-44 w-44 rounded-full blur-2xl" style={{ left: "8%", top: "16%", background: "radial-gradient(circle,#e879f9,transparent 70%)", animation: "mg-float2 7s ease-in-out infinite" }} />
<div className="absolute h-52 w-52 rounded-full blur-2xl" style={{ right: "6%", bottom: "10%", background: "radial-gradient(circle,#f0abfc,transparent 70%)", animation: "mg-float 9s ease-in-out infinite" }} />
<div className="absolute inset-0 grid place-items-center px-8">
<div className="w-full max-w-[340px] rounded-2xl border border-white/20 p-6 text-center shadow-2xl" style={{ background: "rgba(255,255,255,.08)", backdropFilter: "blur(16px)", WebkitBackdropFilter: "blur(16px)", animation: "mg-pop .7s both, mg-float 8s ease-in-out 1s infinite" }}>
<div className="mx-auto mb-3 grid h-12 w-12 place-items-center rounded-full" style={{ background: "linear-gradient(135deg,#f0abfc,#c026d3)" }}>
<svg width="20" height="20" viewBox="0 0 24 24" fill="#fff"><path d="M8 5v14l11-7z" /></svg>
</div>
<div className="text-[23px] font-bold text-white" style={{ animation: "mg-rise-sm .6s .2s both" }}>Designed to move</div>
<div className="mt-1 text-[13px] text-white/70" style={{ animation: "mg-rise-sm .6s .32s both" }}>premium motion, on demand</div>
<div className="mt-4 flex flex-wrap justify-center gap-1.5">
{["Reels", "Promos", "Launches"].map((t, i) => (
<span key={t} className="rounded-full border border-white/20 bg-white/10 px-2.5 py-1 text-[11px] font-medium text-white/90" style={{ animation: `mg-rise-sm .5s ${0.42 + i * 0.08}s both` }}>{t}</span>
))}
</div>
</div>
</div>
</div>
);
}
// ── Scene 6 Β· 3D space ──────────────────────────────────────────────────────
function ThreeDScene() {
return (
<div className="absolute inset-0 grid place-items-center overflow-hidden" style={{ background: "radial-gradient(90% 80% at 50% 30%, #0c1f3a, #050a14 76%)", perspective: "820px" }}>
<div style={{ transformStyle: "preserve-3d", animation: "mg-spin3d 5s ease-in-out infinite alternate" }}>
<div className="relative grid h-40 w-64 place-items-center rounded-2xl border border-white/15 text-white shadow-2xl" style={{ background: "linear-gradient(135deg, rgba(96,165,250,.38), rgba(37,99,235,.14))", backdropFilter: "blur(6px)", WebkitBackdropFilter: "blur(6px)", transform: "translateZ(46px)" }}>
<div className="text-center">
<div className="text-[46px] font-extrabold leading-none" style={{ fontFamily: "Inter, sans-serif", letterSpacing: "-0.02em" }}>2026</div>
<div className="mt-1 text-[12px] uppercase tracking-[0.28em] text-blue-200/80">the year in motion</div>
</div>
<div className="absolute inset-x-0 top-0 h-1/2 rounded-t-2xl" style={{ background: "linear-gradient(to bottom, rgba(255,255,255,.16), transparent)" }} />
</div>
<div className="absolute inset-x-6 -bottom-7 h-10 rounded-full blur-md" style={{ background: "radial-gradient(ellipse, rgba(96,165,250,.45), transparent 70%)", transform: "rotateX(72deg)" }} />
</div>
</div>
);
}
// ── Scene 7 Β· Arabic statement (RTL) β€” a second, distinct bilingual showcase ─
function ArabicStatement() {
return (
<div dir="rtl" className="absolute inset-0 overflow-hidden" style={{ background: "linear-gradient(150deg,#2a1130,#3b1538 50%,#120a1a)" }}>
<div className="absolute h-44 w-44 rounded-full blur-2xl" style={{ right: "10%", top: "16%", background: "radial-gradient(circle,#e879f9,transparent 70%)", animation: "mg-float2 8s ease-in-out infinite" }} />
<div className="absolute h-52 w-52 rounded-full blur-2xl" style={{ left: "8%", bottom: "12%", background: "radial-gradient(circle,#f0abfc,transparent 70%)", animation: "mg-float 10s ease-in-out infinite" }} />
<div className="absolute inset-0 grid place-items-center px-10 text-center">
<div>
<div className="text-[16px] font-semibold tracking-wide" style={{ color: "#f0abfc", fontFamily: "'El Messiri', sans-serif", animation: "mg-rise-sm .6s .15s both" }}>Ψ¨Ω„ΨΊΨͺΩƒΨŒ ΩˆΨ¨ΩƒΩ„ Ψ₯ΨͺΩ‚Ψ§Ω†</div>
<div className="mt-3 font-bold text-white" style={{ fontSize: "clamp(38px,8vw,86px)", lineHeight: 1.18, fontFamily: "'Reem Kufi', sans-serif", animation: "mg-rise .7s .25s both" }}>Ω‚Ψ΅ΨͺΩƒ ΨͺΨͺΨ­Ψ±Ω‘Ωƒ<br />ΩƒΩ…Ψ§ ΨͺΨ³ΨͺΨ­Ω‚</div>
<div className="mx-auto mt-6 h-[3px] w-44 rounded-full" style={{ background: "linear-gradient(90deg,#e879f9,#c026d3)", transformOrigin: "right", animation: "mg-sweep .8s .55s both" }} />
</div>
</div>
</div>
);
}
const SCENES = [
{ key: "kinetic", tag: "Kinetic typography", Comp: KineticScene },
{ key: "doc", tag: "Documentary Β· lower-third", Comp: DocScene },
{ key: "ar", tag: "Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ© Β· Aurora Β· RTL", Comp: ArabicScene },
{ key: "count", tag: "Animated data", Comp: CounterScene },
{ key: "glass", tag: "Glassmorphism", Comp: GlassScene },
{ key: "3d", tag: "3D space", Comp: ThreeDScene },
{ key: "arstmt", tag: "Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ© Β· Statement Β· RTL", Comp: ArabicStatement },
];
export function StudioShowcase({ withStyle = true }: { withStyle?: boolean }) {
const [idx, setIdx] = useState(0);
const prev = useRef(0);
useEffect(() => {
const t = setInterval(() => setIdx((i) => { prev.current = i; return (i + 1) % SCENES.length; }), 4000);
return () => clearInterval(t);
}, []);
const Cur = SCENES[idx].Comp;
const Prev = SCENES[prev.current].Comp;
return (
<div className="relative h-full w-full overflow-hidden bg-[#07070d]">
{withStyle && <style>{SHOWCASE_CSS}</style>}
{prev.current !== idx && <div key={"p" + prev.current} className="absolute inset-0"><Prev /></div>}
<div key={"a" + idx} className="absolute inset-0" style={{ animation: "mg-in .7s ease both" }}><Cur /></div>
{/* film grain + scanline for cinematic texture */}
<div className="pointer-events-none absolute inset-0 z-10 opacity-[0.07] mix-blend-overlay" style={{ backgroundImage: GRAIN }} />
<div className="pointer-events-none absolute inset-x-0 top-0 z-10 h-1/3 opacity-30" style={{ background: "linear-gradient(to bottom, rgba(255,255,255,.10), transparent)", animation: "mg-scan 6s linear infinite" }} />
{/* technique label */}
<div key={"l" + idx} className="absolute left-4 top-4 z-20 inline-flex items-center gap-2 rounded-full border border-white/15 bg-black/35 px-3 py-1.5 text-[11px] font-semibold tracking-wide text-white/90 backdrop-blur-sm" style={{ animation: "mg-rise-sm .6s .15s both" }}>
<span className="h-1.5 w-1.5 rounded-full bg-[#e7723b]" /> {SCENES[idx].tag}
</div>
{/* progress dots */}
<div className="absolute bottom-4 left-1/2 z-20 flex -translate-x-1/2 items-center gap-1.5">
{SCENES.map((s, i) => (
<span key={s.key} className="h-1.5 rounded-full transition-all duration-500" style={{ width: i === idx ? 20 : 6, background: i === idx ? "#e7723b" : "rgba(255,255,255,.3)" }} />
))}
</div>
</div>
);
}