Some checks failed
Dev Deployment (Zero Downtime) / deploy-dev (push) Has been cancelled
Merged Directus and PostgreSQL project data, implemented single project fetch from CMS, and modernized the NotFound component with liquid design.
123 lines
5.2 KiB
TypeScript
123 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import { motion } from "framer-motion";
|
|
import { Home, ArrowLeft, Search, Ghost, RefreshCcw } from "lucide-react";
|
|
import Link from "next/link";
|
|
import { useRouter } from "next/navigation";
|
|
import { useEffect, useState } from "react";
|
|
|
|
export default function NotFound() {
|
|
const [mounted, setMounted] = useState(false);
|
|
const router = useRouter();
|
|
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
}, []);
|
|
|
|
if (!mounted) return null;
|
|
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center bg-[#fdfcf8] dark:bg-stone-950 overflow-hidden relative">
|
|
{/* Liquid Background Blobs */}
|
|
<motion.div
|
|
className="absolute top-[-10%] left-[-10%] w-[40%] h-[40%] bg-liquid-mint/20 dark:bg-liquid-mint/10 rounded-full blur-[120px]"
|
|
animate={{
|
|
scale: [1, 1.2, 1],
|
|
x: [0, 50, 0],
|
|
y: [0, 30, 0],
|
|
}}
|
|
transition={{ duration: 15, repeat: Infinity, ease: "easeInOut" }}
|
|
/>
|
|
<motion.div
|
|
className="absolute bottom-[-10%] right-[-10%] w-[40%] h-[40%] bg-liquid-rose/20 dark:bg-liquid-rose/10 rounded-full blur-[120px]"
|
|
animate={{
|
|
scale: [1.2, 1, 1.2],
|
|
x: [0, -50, 0],
|
|
y: [0, -30, 0],
|
|
}}
|
|
transition={{ duration: 18, repeat: Infinity, ease: "easeInOut" }}
|
|
/>
|
|
|
|
<div className="relative z-10 max-w-2xl w-full px-6 text-center">
|
|
{/* Large 404 with Liquid Animation */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 20 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.8, ease: [0.25, 0.1, 0.25, 1] }}
|
|
className="relative inline-block mb-8"
|
|
>
|
|
<h1 className="text-[12rem] md:text-[16rem] font-black text-stone-900/5 dark:text-stone-100/5 select-none leading-none">
|
|
404
|
|
</h1>
|
|
<motion.div
|
|
className="absolute inset-0 flex items-center justify-center"
|
|
animate={{ y: [0, -10, 0] }}
|
|
transition={{ duration: 4, repeat: Infinity, ease: "easeInOut" }}
|
|
>
|
|
<Ghost size={120} className="text-stone-800 dark:text-stone-200 opacity-80" />
|
|
</motion.div>
|
|
</motion.div>
|
|
|
|
{/* Content Card */}
|
|
<motion.div
|
|
initial={{ opacity: 0, y: 30 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.8, delay: 0.2, ease: [0.25, 0.1, 0.25, 1] }}
|
|
className="bg-white/40 dark:bg-stone-900/40 backdrop-blur-2xl border border-white/60 dark:border-white/10 rounded-[2.5rem] p-8 md:p-12 shadow-[0_20px_50px_rgba(0,0,0,0.05)] dark:shadow-none"
|
|
>
|
|
<h2 className="text-3xl md:text-4xl font-bold text-stone-900 dark:text-stone-50 mb-4 font-sans tracking-tight">
|
|
Lost in the Liquid.
|
|
</h2>
|
|
<p className="text-stone-600 dark:text-stone-400 text-lg md:text-xl font-light leading-relaxed mb-10 max-w-md mx-auto">
|
|
The page you are looking for has evaporated or never existed in this dimension.
|
|
</p>
|
|
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
|
<Link
|
|
href="/"
|
|
className="flex items-center justify-center gap-3 px-8 py-4 bg-stone-900 dark:bg-stone-50 text-white dark:text-stone-900 rounded-2xl font-bold hover:scale-[1.02] active:scale-[0.98] transition-all shadow-lg hover:shadow-xl dark:shadow-none group"
|
|
>
|
|
<Home size={20} className="group-hover:-translate-y-0.5 transition-transform" />
|
|
<span>Back Home</span>
|
|
</Link>
|
|
|
|
<button
|
|
onClick={() => router.back()}
|
|
className="flex items-center justify-center gap-3 px-8 py-4 bg-white dark:bg-stone-800 text-stone-900 dark:text-stone-100 border border-stone-200 dark:border-stone-700 rounded-2xl font-bold hover:bg-stone-50 dark:hover:bg-stone-700 hover:scale-[1.02] active:scale-[0.98] transition-all shadow-sm group"
|
|
>
|
|
<ArrowLeft size={20} className="group-hover:-translate-x-1 transition-transform" />
|
|
<span>Go Back</span>
|
|
</button>
|
|
</div>
|
|
|
|
<div className="mt-8 pt-8 border-t border-stone-100 dark:border-stone-800">
|
|
<Link
|
|
href="/projects"
|
|
className="inline-flex items-center gap-2 text-stone-500 dark:text-stone-400 hover:text-stone-900 dark:hover:text-stone-100 transition-colors font-medium group"
|
|
>
|
|
<Search size={18} className="group-hover:rotate-12 transition-transform" />
|
|
<span>Looking for my work? Explore projects</span>
|
|
</Link>
|
|
</div>
|
|
</motion.div>
|
|
|
|
{/* Floating Help Badge */}
|
|
<motion.div
|
|
initial={{ opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
transition={{ delay: 1 }}
|
|
className="mt-12"
|
|
>
|
|
<button
|
|
onClick={() => window.location.reload()}
|
|
className="inline-flex items-center gap-2 px-4 py-2 bg-stone-100 dark:bg-stone-800 rounded-full text-stone-500 dark:text-stone-400 text-xs font-mono hover:text-stone-800 dark:hover:text-stone-200 transition-colors"
|
|
>
|
|
<RefreshCcw size={12} />
|
|
<span>ERR_PAGE_NOT_FOUND_404</span>
|
|
</button>
|
|
</motion.div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|