portfolio/app/page.tsx
GeorgeWebberley 14173ff9ad
All checks were successful
ci/woodpecker/release/woodpecker Pipeline was successful
Fixed favicon and updated title from the architect
2026-02-03 15:26:42 +01:00

259 lines
9.1 KiB
TypeScript

"use client";
import Link from "next/link";
import { motion, Variants } from "framer-motion";
import { Globe, Smartphone, Server, Gamepad2, Hammer } from "lucide-react";
import { useState } from "react";
import MonitorCard from "@/components/MonitorCard";
import PageLayout from "@/components/PageLayout";
import { CategoryCardProps } from "@/types/index";
const containerVariants: Variants = {
hidden: { opacity: 0 },
visible: {
opacity: 1,
transition: {
staggerChildren: 0.1,
delayChildren: 0.2,
},
},
};
const itemVariants: Variants = {
hidden: { opacity: 0, y: 20 },
visible: {
opacity: 1,
y: 0,
transition: { duration: 0.6, ease: "easeOut" },
},
};
export default function Home() {
const [isHoveringMonitors, setIsHoveringMonitors] = useState(false);
return (
<PageLayout maxWidth="7xl">
<motion.div
variants={containerVariants}
initial="hidden"
animate="visible"
className="flex flex-col gap-12"
>
{/* Header Section */}
<motion.header variants={itemVariants}>
<h1 className="text-4xl font-bold tracking-tight">George W.</h1>
<p className="text-neutral-400 mt-2">
Senior Full Stack Engineer & Tech Lead
</p>
<div className="flex gap-6 text-[10px] font-mono tracking-[0.2em] uppercase mt-4">
<a
href="https://git.georgew.dev"
className="text-neutral-500 hover:text-white transition-colors"
>
Git
</a>
<a
href="https://linkedin.com/in/georgew"
className="text-neutral-500 hover:text-white transition-colors"
>
LinkedIn
</a>
</div>
</motion.header>
{/* Main Bento Grid */}
<div className="grid grid-cols-1 md:grid-cols-6 gap-6">
{/* Top Row Left: The Architect */}
<motion.div
variants={itemVariants}
className="md:col-span-4 p-8 rounded-3xl bg-neutral-900/50 border border-neutral-800 flex flex-col md:flex-row gap-8 min-h-[300px] overflow-hidden relative"
>
<div className="flex-[1.5] flex flex-col justify-between relative z-10">
<div>
<h2 className="text-3xl font-bold mb-4 tracking-tight">
Technical Focus
</h2>
<p className="text-base text-neutral-400 leading-relaxed max-w-lg">
Bridging the gap between rigid regulatory requirements and
fluid user experiences. I specialize in designing{" "}
<span className="text-white">distributed systems</span> and
<span className="text-white">
{" "}
cross-platform mobile apps
</span>{" "}
with a focus on automated delivery and high-integrity code.
</p>
</div>
<div className="flex flex-wrap gap-2 mt-8">
{[
"#Architecture",
"#Regulatory Compliance",
"#Agile Leadership",
"#DevOps",
].map((tag) => (
<span
key={tag}
className="text-[10px] font-mono text-neutral-500 border border-neutral-800 px-2 py-1 rounded"
>
{tag}
</span>
))}
</div>
</div>
<div className="hidden md:block w-px bg-neutral-800/50 self-stretch" />
<div className="flex-1 flex flex-col justify-around py-2 relative z-10">
<div className="space-y-6">
<TechnicalFocus
label="Leadership"
color="text-blue-500"
text="Tech Lead & Scrum Master. Orchestrating sprint cycles and system design."
/>
<TechnicalFocus
label="Integrity"
color="text-purple-500"
text="Medical/Regulatory environments, QMS, and Cyber Essentials."
/>
<TechnicalFocus
label="Infrastructure"
color="text-green-500"
text="Kubernetes, GCP, and automated CI/CD pipelines."
/>
</div>
</div>
</motion.div>
{/* Top Row Right: The Service Registry */}
<Link href="/lab" className="md:col-span-2 flex flex-col group">
<motion.div
variants={itemVariants}
whileHover={{ y: -5 }}
onMouseEnter={() => setIsHoveringMonitors(true)}
onMouseLeave={() => setIsHoveringMonitors(false)}
className="flex-1 p-6 rounded-3xl bg-neutral-900 border border-neutral-800 flex flex-col justify-center relative overflow-hidden min-h-[180px] hover:border-blue-500/30"
>
<MonitorCard isHovered={isHoveringMonitors} />
</motion.div>
</Link>
{/* Project Category Cards */}
<CategoryCard
href="/projects/web"
icon={<Globe className="text-blue-400 w-6 h-6 mb-4" />}
title="Web Systems"
description="Architecting distributed platforms with a focus on high-availability."
tech={["Next.js", "Python", "Node.js", "Caddy", "PostgreSQL"]}
hoverColor="hover:border-blue-500/30"
activeTechColor="group-hover:text-blue-400"
/>
<CategoryCard
href="/projects/mobile"
icon={<Smartphone className="text-purple-400 w-6 h-6 mb-4" />}
title="Mobile Apps"
description="Building fluid, cross-platform experiences using reactive state."
tech={["Android", "iOS", "Flutter", "Riverpod", "Stores"]}
hoverColor="hover:border-purple-500/30"
activeTechColor="group-hover:text-purple-400"
/>
<CategoryCard
href="/infrastructure"
icon={<Server className="text-green-400 w-6 h-6 mb-4" />}
title="Infrastructure"
description="Resilient cloud environments with automated IaC and multi-region orchestration."
tech={["Kubernetes", "Terraform", "GCP", "CI/CD", "Security"]}
hoverColor="hover:border-green-500/30"
activeTechColor="group-hover:text-green-400"
/>
{/* Bottom Row: The Forge */}
<Link href="/forge" className="md:col-span-6">
<motion.div
variants={itemVariants}
className="p-8 rounded-3xl bg-neutral-900/50 border border-neutral-800 flex items-center gap-6 group hover:border-orange-500/30 transition-colors cursor-pointer"
>
<div className="p-4 bg-orange-500/10 rounded-2xl border border-orange-500/20 group-hover:border-orange-500/40 transition-colors">
<Hammer className="text-orange-500 w-8 h-8 group-hover:rotate-12 transition-transform" />
</div>
<div>
<div className="flex items-center gap-2 mb-0.5">
<h3 className="font-bold text-xl tracking-tight">
The Forge
</h3>
<span className="w-1.5 h-1.5 rounded-full bg-orange-500 animate-pulse" />
</div>
<p className="text-sm text-neutral-500 leading-tight">
A space where I demonstrate what I am currently working on and
any future projects.
</p>
</div>
</motion.div>
</Link>
</div>
</motion.div>
</PageLayout>
);
}
function TechnicalFocus({
label,
color,
text,
}: {
label: string;
color: string;
text: string;
}) {
return (
<section>
<h4
className={`text-[12px] font-mono ${color} uppercase tracking-[0.2em] mb-2`}
>
{label}
</h4>
<p className="text-xs text-neutral-300 leading-tight">{text}</p>
</section>
);
}
function CategoryCard({
href,
icon,
title,
description,
tech,
hoverColor,
activeTechColor,
}: CategoryCardProps) {
return (
<Link href={href} className="group md:col-span-2">
<motion.div
variants={itemVariants}
whileHover={{ y: -5 }}
className={`p-6 rounded-3xl bg-neutral-900 border border-neutral-800 h-full flex flex-col justify-between min-h-[260px] relative transition-colors ${hoverColor}`}
>
<div>
{icon}
<h3 className="font-bold text-xl mb-2">{title}</h3>
<p className="text-sm text-neutral-500 leading-relaxed">
{description}
</p>
</div>
<div className="flex flex-wrap gap-2 mt-6">
{tech.map((t: string) => (
<span
key={t}
className={`text-[9px] font-mono text-neutral-600 border border-neutral-800 px-2 py-1 rounded-md uppercase ${activeTechColor} group-hover:border-current/20 transition-all`}
>
{t}
</span>
))}
</div>
</motion.div>
</Link>
);
}