Redesign: Modernes SaaS-Design für Joachim Hummel Portfolio

Task #4 — vollständiges Redesign von dunkler Terminal-Ästhetik zu modernem SaaS-Look.

Änderungen:
- index.css: Komplette Neudefinition der CSS-Variablen. Helles Farbschema (weißer Hintergrund),
  Primärfarbe Indigo/Blau (hsl 234 89% 60%), neutrale Grautöne, Google Font Inter eingebunden.
  Utility-Klassen .section-number, .hero-gradient, .dot-pattern, .card-hover hinzugefügt.
  Terminal-spezifische Klassen (.glow-text, .glow-box, .bg-grid-pattern) entfernt.

- navbar.tsx: Sticky Nav mit transparentem Start → weißem Hintergrund + Schatten beim Scrollen.
  Logo "JH" als gefülltes Indigo-Badge + "Joachim Hummel" Text. Pill-Style Navigation Links.
  Primär-CTA Button. Vollständiges Mobile-Menü mit Hamburger/X Toggle.

- hero.tsx: Zentriertes Layout, großer Display-Headline mit Indigo-Akzent auf Nachname.
  "Verfügbar für neue Projekte" Badge mit grünem Puls. Skill-Badges (30+ Jahre, ITIL, etc.).
  Zwei CTAs: gefüllter Primärbutton + Outline-Button. Subtiler Radial-Gradient + Dot-Pattern.

- competencies.tsx: Sechs weiße Karten im Grid mit farbigen Icon-Badges (blau, sky, grün, orange,
  violett, rose). Tech-Icon-Leiste (Linux, Ubuntu, Docker, Grafana, Nginx, Ansible, Prometheus)
  mit Hover-Farbeffekt und Labels.

- strengths.tsx: Drei weiße Karten auf hellgrauem Hintergrund. Nummerierung (01/02/03) als
  dekoratives Element. Highlight-Badge je Karte. Kein Glow, kein Neon.

- projects.tsx: Sechs Projektkarten mit farbigen Icon-Badges je Projektkategorie. Externe Links
  für SafeDocs Portal (safedocsportal.com) und zensend.email als ArrowUpRight-Icon-Buttons.
  Pill-Badges für Tags.

- bio.tsx: Zweispaltiges Layout. Links: Fließtext + Kunden-Badges. Rechts: Weißes Highlights-Panel
  mit CheckCircle-Icons (7 Punkte inkl. ITIL, BSI, Tech-Blogger).

- contact.tsx: Primärfarbige E-Mail-Karte + drei kompakte Link-Cards (Tech-Blog, KI-Blog, Website).
  Footer-Zeile mit Name und Standort.

- home.tsx: Vereinfacht — kein Fixed-Background, kein Grid-Pattern, kein Ambient-Glow.
  max-w-6xl Container für alle Sektionen.
This commit is contained in:
joachimhummel
2026-05-15 15:40:45 +00:00
parent 58234b8621
commit 46358579c4
9 changed files with 526 additions and 246 deletions

View File

@@ -1,29 +1,86 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { CheckCircle2 } from "lucide-react";
const highlights = [
"30+ Jahre IT-Betrieb in Behörden & Konzernen",
"ITIL & BSI-Grundschutz Erfahrung",
"Web- & Middleware (Apache, Tomcat, JBoss, Websphere)",
"Docker, Ansible, Gitlab CI/CD",
"Technische Redaktion & Betriebshandbücher",
"KI-Automation seit 2023 (n8n, RAG, Pinecone)",
"Tech-Blogger mit 300+ Fachartikeln",
];
const clients = [
"Landesamt für Statistik Bayern",
"Polizei Hessen",
"Justiz Baden-Württemberg",
"Amt des öff. Rechts Hamburg",
"Finanzdienstleister München (EZB-geprüft)",
];
export function Bio() { export function Bio() {
return ( return (
<div className="py-20 border-t border-border/50"> <div className="py-24 bg-secondary/40 rounded-3xl">
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }} viewport={{ once: true, margin: "-80px" }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
className="max-w-4xl mx-auto text-center" className="px-4 md:px-12"
> >
<h2 className="text-3xl md:text-4xl font-bold mb-8 font-mono text-foreground"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-12 items-center">
<span className="text-primary">04.</span> Über Mich <div>
<p className="section-number mb-3" data-testid="text-section-label-bio">Über mich</p>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-6" data-testid="text-section-title-bio">
IT-Experte mit Tiefe und dem Blick nach vorne
</h2> </h2>
<div className="space-y-4 text-muted-foreground leading-relaxed">
<p>
Mehr als 25 Jahre Erfahrung im Aufbau, Betrieb und Management von IT-Infrastrukturen in Konzernen, Behörden und kritischen Umgebungen. Ich verbinde klassische ITIL-Prozesse und technische Betriebserfahrung mit innovativen Automatisierungslösungen auf Basis von KI und modernen Tools.
</p>
<p>
Mein Ansatz ist bodenständig: Technik muss funktionieren, nachvollziehbar sein und langfristig betrieben werden können. Mit Vibe-Coding bringe ich heute Ideen direkt in produktive Systeme schnell, sauber, betreibbar.
</p>
</div>
<div className="text-lg md:text-xl text-muted-foreground leading-relaxed space-y-6"> <div className="mt-8">
<p> <p className="text-sm font-semibold text-foreground mb-4">Ausgewählte Kunden & Projekte</p>
Joachim Hummel ist Senior IT-Consultant, IT-Systems Engineer und technischer Umsetzer mit rund <span className="text-foreground font-medium">30 Jahren Erfahrung</span> im IT-Betrieb. <div className="flex flex-wrap gap-2">
</p> {clients.map((client) => (
<p> <span
Seine Schwerpunkte liegen auf Linux, Docker, On-Premise-Infrastrukturen, DSGVO-konformem Selfhosting, Mailservern, Monitoring, Automatisierung und KI-gestützter Softwareentwicklung. key={client}
</p> className="text-xs font-medium px-3 py-1.5 bg-white border border-border rounded-full text-foreground/70"
<p> data-testid={`badge-client-${client.replace(/\s/g, '-').toLowerCase()}`}
Mit <span className="text-primary font-mono glow-text">Vibe-Coding</span> entwickelt er aus Ideen lauffähige Anwendungen, Automatisierungen und SaaS-Projekte. >
</p> {client}
</span>
))}
</div>
</div>
</div>
<div>
<div className="bg-white rounded-2xl border border-border p-8">
<p className="text-sm font-semibold text-foreground mb-6">Auf einen Blick</p>
<ul className="space-y-3" data-testid="list-highlights">
{highlights.map((item, index) => (
<motion.li
key={index}
initial={{ opacity: 0, x: 12 }}
whileInView={{ opacity: 1, x: 0 }}
viewport={{ once: true }}
transition={{ duration: 0.3, delay: index * 0.06 }}
className="flex items-start gap-3 text-sm text-muted-foreground"
data-testid={`item-highlight-${index}`}
>
<CheckCircle2 className="w-4 h-4 text-primary mt-0.5 shrink-0" />
{item}
</motion.li>
))}
</ul>
</div>
</div>
</div> </div>
</motion.div> </motion.div>
</div> </div>

View File

@@ -1,79 +1,107 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Server, Box, ShieldCheck, Activity, Cpu } from "lucide-react"; import { Server, Box, ShieldCheck, Activity, Cpu, FileText } from "lucide-react";
import { SiDocker, SiLinux, SiGrafana, SiNginx, SiUbuntu } from "react-icons/si"; import { SiDocker, SiLinux, SiGrafana, SiNginx, SiUbuntu, SiAnsible, SiPrometheus } from "react-icons/si";
const skills = [ const skills = [
{ {
icon: Server, icon: Server,
title: "Linux, Serverbetrieb & Infrastruktur", title: "Linux & Infrastruktur",
desc: "Ubuntu, Debian, Proxmox, Systemd, Shell-Scripting, TLS, Reverse Proxy" desc: "Ubuntu, Debian, AIX, Solaris, Proxmox, Systemd, TLS, Reverse Proxy, Shell-Scripting",
color: "bg-blue-50 text-blue-600 border-blue-100",
}, },
{ {
icon: Box, icon: Box,
title: "Docker & Container-Betrieb", title: "Docker & Container",
desc: "Docker Compose, Netzwerke, Volumes, Persistenz, Hetzner/Contabo" desc: "Docker Compose, Netzwerke, Volumes, Persistenz, Ansible, Deployment auf Hetzner & Contabo",
color: "bg-sky-50 text-sky-600 border-sky-100",
}, },
{ {
icon: ShieldCheck, icon: ShieldCheck,
title: "On-Premise & DSGVO Selfhosting", title: "On-Premise & DSGVO",
desc: "iRedMail/Mailcow, Nextcloud, SOGo, Authentik/OIDC, Zoraxy/Nginx" desc: "iRedMail, Mailcow, Nextcloud, SOGo, Authentik / OIDC, ITIL, BSI-Grundschutz, Compliance",
color: "bg-green-50 text-green-600 border-green-100",
}, },
{ {
icon: Activity, icon: Activity,
title: "Monitoring", title: "Monitoring & Betrieb",
desc: "Grafana, Prometheus, Loki, Alloy" desc: "Grafana, Prometheus, Loki, Alloy, Nagios, JMX-Monitoring, Metriken & Dashboards",
color: "bg-orange-50 text-orange-600 border-orange-100",
}, },
{ {
icon: Cpu, icon: Cpu,
title: "KI, Automatisierung & Vibe-Coding", title: "KI & Vibe-Coding",
desc: "Claude Code, ChatGPT, n8n, RAG-Systeme, Pinecone" desc: "n8n, Claude Code, ChatGPT, RAG-Systeme, Pinecone, OpenAI, Ollama, API-Automation",
} color: "bg-violet-50 text-violet-600 border-violet-100",
},
{
icon: FileText,
title: "Dokumentation & Service",
desc: "Betriebshandbücher, ITIL-Prozesse, BSI-Grundschutz, Confluence, Jira, draw.io",
color: "bg-rose-50 text-rose-600 border-rose-100",
},
];
const techIcons = [
{ Icon: SiLinux, label: "Linux", color: "#000" },
{ Icon: SiUbuntu, label: "Ubuntu", color: "#E95420" },
{ Icon: SiDocker, label: "Docker", color: "#2496ED" },
{ Icon: SiGrafana, label: "Grafana", color: "#F46800" },
{ Icon: SiNginx, label: "Nginx", color: "#009639" },
{ Icon: SiAnsible, label: "Ansible", color: "#EE0000" },
{ Icon: SiPrometheus, label: "Prometheus", color: "#E6522C" },
]; ];
export function Competencies() { export function Competencies() {
return ( return (
<div className="py-20 border-t border-border/50"> <div className="py-24">
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }} viewport={{ once: true, margin: "-80px" }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
> >
<h2 className="text-3xl md:text-4xl font-bold mb-4 font-mono text-foreground flex items-center gap-4"> <div className="text-center max-w-2xl mx-auto mb-16">
<span className="text-primary">01.</span> Kernkompetenzen <p className="section-number mb-3" data-testid="text-section-label-competencies">Kernkompetenzen</p>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4" data-testid="text-section-title-competencies">
30 Jahre Erfahrung breit und tief
</h2> </h2>
<p className="text-muted-foreground max-w-2xl mb-12"> <p className="text-muted-foreground text-lg">
Über 30 Jahre Erfahrung im IT-Betrieb, destilliert in hochspezialisierte Fähigkeiten. Von klassischem Serverbetrieb bis zur KI-gestützten Automatisierung praxisbewährt in Behörden, Konzernen und eigenen Projekten.
</p> </p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-5">
{skills.map((skill, index) => ( {skills.map((skill, index) => (
<motion.div <motion.div
key={index} key={index}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.4, delay: index * 0.08 }}
className="p-6 rounded-lg bg-card border border-border hover:border-primary/50 transition-colors group relative overflow-hidden" className="p-6 rounded-2xl bg-white border border-border card-hover"
data-testid={`card-competency-${index}`}
> >
<div className="absolute top-0 right-0 p-4 opacity-5 group-hover:opacity-10 transition-opacity"> <div className={`w-11 h-11 rounded-xl border flex items-center justify-center mb-4 ${skill.color}`}>
<skill.icon className="w-24 h-24 text-primary" /> <skill.icon className="w-5 h-5" />
</div> </div>
<skill.icon className="w-8 h-8 text-primary mb-4" /> <h3 className="text-base font-semibold text-foreground mb-2">{skill.title}</h3>
<h3 className="text-xl font-bold mb-3">{skill.title}</h3> <p className="text-sm text-muted-foreground leading-relaxed">{skill.desc}</p>
<p className="text-muted-foreground font-mono text-sm leading-relaxed">
{skill.desc}
</p>
</motion.div> </motion.div>
))} ))}
</div> </div>
<div className="mt-16 flex flex-wrap justify-center gap-8 text-muted-foreground opacity-50 grayscale"> <div className="mt-16 flex flex-wrap justify-center items-center gap-8">
<SiLinux className="w-12 h-12 hover:grayscale-0 hover:text-primary transition-all cursor-help" title="Linux" /> {techIcons.map(({ Icon, label, color }) => (
<SiUbuntu className="w-12 h-12 hover:grayscale-0 hover:text-[#E95420] transition-all cursor-help" title="Ubuntu" /> <div key={label} className="flex flex-col items-center gap-1.5 group" title={label}>
<SiDocker className="w-12 h-12 hover:grayscale-0 hover:text-[#2496ED] transition-all cursor-help" title="Docker" /> <Icon
<SiGrafana className="w-12 h-12 hover:grayscale-0 hover:text-[#F46800] transition-all cursor-help" title="Grafana" /> className="w-8 h-8 text-muted-foreground/40 group-hover:text-current transition-all duration-200"
<SiNginx className="w-12 h-12 hover:grayscale-0 hover:text-[#009639] transition-all cursor-help" title="Nginx" /> style={{ "--tw-text-opacity": 1 } as React.CSSProperties}
onMouseEnter={(e) => { (e.currentTarget as SVGElement).style.color = color; }}
onMouseLeave={(e) => { (e.currentTarget as SVGElement).style.color = ""; }}
/>
<span className="text-xs text-muted-foreground/60 group-hover:text-muted-foreground transition-colors">{label}</span>
</div>
))}
</div> </div>
</motion.div> </motion.div>
</div> </div>

View File

@@ -1,40 +1,88 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Mail, Github, Linkedin, Terminal } from "lucide-react"; import { Mail, ExternalLink, ArrowRight } from "lucide-react";
const links = [
{
label: "Tech-Blog",
url: "https://blog.unixweb.de",
desc: "300+ Fachartikel rund um Linux, Server & IT",
},
{
label: "KI-Blog",
url: "https://ki-blog.unixweb.de",
desc: "KI, Automation, Vibe-Coding & n8n",
},
{
label: "Website",
url: "https://joachimhummel.de",
desc: "joachimhummel.de",
},
];
export function Contact() { export function Contact() {
return ( return (
<div className="py-32 border-t border-border/50"> <div className="py-24">
<motion.div <motion.div
initial={{ opacity: 0, scale: 0.95 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, scale: 1 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }} viewport={{ once: true, margin: "-80px" }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
className="max-w-2xl mx-auto text-center p-12 rounded-2xl bg-card border border-border glow-box relative overflow-hidden"
> >
<div className="absolute top-0 left-0 right-0 h-1 bg-gradient-to-r from-transparent via-primary to-transparent" /> <div className="max-w-4xl mx-auto">
<div className="text-center mb-12">
<Terminal className="w-12 h-12 text-primary mx-auto mb-6" /> <p className="section-number mb-3" data-testid="text-section-label-contact">Kontakt</p>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4" data-testid="text-section-title-contact">
<h2 className="text-3xl md:text-4xl font-bold mb-4">Bereit für das nächste Projekt?</h2> Bereit für das nächste Projekt?
<p className="text-muted-foreground mb-8 text-lg"> </h2>
Lassen Sie uns darüber sprechen, wie wir Ihre Infrastruktur modernisieren oder Ihre nächste Anwendung bauen können. <p className="text-muted-foreground text-lg max-w-xl mx-auto">
Ob Infrastruktur-Modernisierung, KI-Automation oder technische Dokumentation ich freue mich auf Ihre Anfrage.
</p> </p>
</div>
<a <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
href="mailto:hello@example.com" <motion.a
className="inline-flex items-center gap-2 px-8 py-4 bg-primary text-primary-foreground font-bold rounded hover:bg-primary/90 transition-all hover:scale-105" href="mailto:jh@unixweb.de"
whileHover={{ scale: 1.01 }}
className="flex items-center gap-5 p-6 rounded-2xl bg-primary text-white border border-primary card-hover group"
data-testid="link-contact-email"
> >
<Mail className="w-5 h-5" /> <div className="w-12 h-12 rounded-xl bg-white/20 flex items-center justify-center shrink-0">
Kontakt aufnehmen <Mail className="w-6 h-6" />
</a> </div>
<div className="flex-grow">
<p className="font-semibold text-white">E-Mail schreiben</p>
<p className="text-sm text-white/70">jh@unixweb.de</p>
</div>
<ArrowRight className="w-5 h-5 text-white/60 group-hover:translate-x-1 transition-transform" />
</motion.a>
<div className="mt-12 flex justify-center gap-6"> <div className="flex flex-col gap-3">
<a href="#" className="text-muted-foreground hover:text-primary transition-colors"> {links.map((link) => (
<Github className="w-6 h-6" /> <motion.a
</a> key={link.label}
<a href="#" className="text-muted-foreground hover:text-primary transition-colors"> href={link.url}
<Linkedin className="w-6 h-6" /> target="_blank"
</a> rel="noopener noreferrer"
whileHover={{ scale: 1.01 }}
className="flex items-center gap-4 px-5 py-4 rounded-xl bg-white border border-border card-hover group"
data-testid={`link-contact-${link.label.toLowerCase().replace(/\s/g, '-')}`}
>
<div className="w-9 h-9 rounded-lg bg-secondary flex items-center justify-center shrink-0">
<ExternalLink className="w-4 h-4 text-muted-foreground group-hover:text-primary transition-colors" />
</div>
<div className="flex-grow min-w-0">
<p className="text-sm font-semibold text-foreground">{link.label}</p>
<p className="text-xs text-muted-foreground truncate">{link.desc}</p>
</div>
<ArrowRight className="w-4 h-4 text-muted-foreground/40 group-hover:text-primary group-hover:translate-x-1 transition-all shrink-0" />
</motion.a>
))}
</div>
</div>
<div className="mt-12 pt-8 border-t border-border text-center text-sm text-muted-foreground">
Joachim Hummel &mdash; Senior IT-Consultant &middot; München
</div>
</div> </div>
</motion.div> </motion.div>
</div> </div>

View File

@@ -1,61 +1,92 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { Terminal, ChevronDown } from "lucide-react"; import { ArrowRight, ChevronDown } from "lucide-react";
const badges = [
"30+ Jahre IT-Erfahrung",
"ITIL & BSI-Grundschutz",
"Vibe-Coding mit KI",
"On-Premise & DSGVO",
];
export function Hero() { export function Hero() {
return ( return (
<div className="min-h-[85vh] flex flex-col justify-center relative"> <div className="min-h-[92vh] flex flex-col justify-center items-center text-center relative pt-20">
<motion.div <div className="absolute inset-0 hero-gradient pointer-events-none" />
initial={{ opacity: 0, y: 20 }} <div className="absolute inset-0 dot-pattern opacity-40 pointer-events-none" />
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.8, ease: "easeOut" }}
className="max-w-4xl"
>
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-secondary border border-border mb-8">
<Terminal className="w-4 h-4 text-primary" />
<span className="text-sm font-mono text-muted-foreground">System.init()</span>
</div>
<h1 className="text-5xl md:text-7xl font-bold tracking-tight mb-6"> <motion.div
<span className="block text-muted-foreground">Joachim</span> initial={{ opacity: 0, y: 24 }}
<span className="block text-foreground">Hummel</span> animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.7, ease: "easeOut" }}
className="relative z-10 max-w-4xl mx-auto px-4"
>
<motion.div
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5, delay: 0.1 }}
className="inline-flex items-center gap-2 px-4 py-1.5 rounded-full bg-accent text-accent-foreground text-sm font-medium mb-8 border border-primary/20"
data-testid="badge-availability"
>
<span className="w-2 h-2 rounded-full bg-green-500 animate-pulse" />
Verfügbar für neue Projekte
</motion.div>
<h1 className="text-5xl md:text-7xl font-extrabold tracking-tight text-foreground mb-6 leading-tight" data-testid="text-hero-name">
Joachim{" "}
<span className="text-primary">Hummel</span>
</h1> </h1>
<div className="text-xl md:text-2xl font-mono text-primary mb-8 glow-text"> <p className="text-lg md:text-xl font-medium text-muted-foreground mb-4 tracking-wide" data-testid="text-hero-title">
Senior IT-Consultant · IT-Systems Engineer · Vibe-Coder Senior IT-Consultant &middot; Service Manager &middot; Technical Writer &middot; Vibe-Coder
</div>
<p className="text-lg md:text-xl text-muted-foreground max-w-2xl leading-relaxed">
Zwei Welten, eine Lösung: Tiefe, praxisbewährte Infrastruktur-Expertise
trifft auf moderne KI-gestützte Softwareentwicklung. Ich baue Systeme, die
in Produktion laufen präzise, sicher und zukunftsfähig.
</p> </p>
<div className="mt-12 flex flex-wrap gap-4"> <p className="text-base md:text-lg text-muted-foreground max-w-2xl mx-auto leading-relaxed mb-10" data-testid="text-hero-description">
Über 30 Jahre IT-Betrieb in Behörden und Konzernen. Heute verbinde ich klassische
Infrastruktur-Expertise mit KI-gestützter Softwareentwicklung von der Idee bis zum
produktiven System.
</p>
<div className="flex flex-wrap justify-center gap-3 mb-12">
{badges.map((badge) => (
<span
key={badge}
className="px-3 py-1 text-sm font-medium bg-secondary text-secondary-foreground rounded-full border border-border"
data-testid={`badge-skill-${badge.replace(/\s/g, '-').toLowerCase()}`}
>
{badge}
</span>
))}
</div>
<div className="flex flex-wrap justify-center gap-4">
<a <a
href="#contact" href="#contact"
className="px-6 py-3 bg-primary text-primary-foreground font-medium rounded hover:bg-primary/90 transition-colors flex items-center gap-2" className="inline-flex items-center gap-2 px-6 py-3 bg-primary text-white font-semibold rounded-xl hover:bg-primary/90 transition-all shadow-lg shadow-primary/20 hover:shadow-primary/30"
data-testid="button-hero-contact"
> >
Kontakt aufnehmen Kontakt aufnehmen
<ArrowRight className="w-4 h-4" />
</a> </a>
<a <a
href="#projects" href="#projects"
className="px-6 py-3 bg-secondary text-secondary-foreground border border-border font-medium rounded hover:bg-secondary/80 transition-colors" className="inline-flex items-center gap-2 px-6 py-3 bg-white text-foreground font-semibold rounded-xl border border-border hover:bg-secondary transition-all"
data-testid="button-hero-projects"
> >
Projekte ansehen Projekte ansehen
</a> </a>
</div> </div>
</motion.div> </motion.div>
<motion.div <motion.a
href="#competencies"
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
transition={{ delay: 1, duration: 1 }} transition={{ delay: 1.2, duration: 0.6 }}
className="absolute bottom-10 left-0 right-0 flex justify-center" className="absolute bottom-10 text-muted-foreground hover:text-primary transition-colors"
data-testid="link-scroll-down"
> >
<a href="#competencies" className="text-muted-foreground hover:text-primary transition-colors animate-bounce"> <ChevronDown className="w-6 h-6 animate-bounce" />
<ChevronDown className="w-8 h-8" /> </motion.a>
</a>
</motion.div>
</div> </div>
); );
} }

View File

@@ -1,18 +1,21 @@
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { motion, useScroll, useMotionValueEvent } from "framer-motion"; import { motion, useScroll, useMotionValueEvent } from "framer-motion";
import { Menu, X } from "lucide-react";
export function Navbar() { export function Navbar() {
const [isScrolled, setIsScrolled] = useState(false); const [isScrolled, setIsScrolled] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const { scrollY } = useScroll(); const { scrollY } = useScroll();
useMotionValueEvent(scrollY, "change", (latest) => { useMotionValueEvent(scrollY, "change", (latest) => {
setIsScrolled(latest > 50); setIsScrolled(latest > 20);
}); });
const navLinks = [ const navLinks = [
{ name: "Kompetenzen", href: "#competencies" }, { name: "Kompetenzen", href: "#competencies" },
{ name: "3 Welten", href: "#strengths" }, { name: "Stärken", href: "#strengths" },
{ name: "Projekte", href: "#projects" }, { name: "Projekte", href: "#projects" },
{ name: "Über mich", href: "#bio" },
{ name: "Kontakt", href: "#contact" }, { name: "Kontakt", href: "#contact" },
]; ];
@@ -20,37 +23,82 @@ export function Navbar() {
<motion.header <motion.header
className={`fixed top-0 w-full z-50 transition-all duration-300 ${ className={`fixed top-0 w-full z-50 transition-all duration-300 ${
isScrolled isScrolled
? "bg-background/80 backdrop-blur-md border-b border-border/50 py-4" ? "bg-white/95 backdrop-blur-md border-b border-border shadow-sm py-3"
: "bg-transparent py-6" : "bg-transparent py-5"
}`} }`}
initial={{ y: -100 }} initial={{ y: -100 }}
animate={{ y: 0 }} animate={{ y: 0 }}
transition={{ duration: 0.5 }} transition={{ duration: 0.4, ease: "easeOut" }}
> >
<div className="container mx-auto px-4 md:px-8 flex items-center justify-between"> <div className="max-w-6xl mx-auto px-4 md:px-8 flex items-center justify-between">
<a href="#hero" className="text-xl font-bold font-mono text-primary glow-text"> <a
JH<span className="text-muted-foreground">.</span> href="#hero"
className="flex items-center gap-2 text-foreground font-bold text-lg tracking-tight"
data-testid="link-logo"
>
<span className="w-8 h-8 rounded-lg bg-primary flex items-center justify-center text-white text-sm font-bold">
JH
</span>
<span className="hidden sm:block text-foreground">Joachim Hummel</span>
</a> </a>
<nav className="hidden md:flex gap-8"> <nav className="hidden md:flex items-center gap-1">
{navLinks.map((link) => ( {navLinks.map((link) => (
<a <a
key={link.name} key={link.name}
href={link.href} href={link.href}
className="text-sm font-mono text-muted-foreground hover:text-primary transition-colors" className="px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-secondary rounded-lg transition-all"
data-testid={`link-nav-${link.name.toLowerCase().replace(/\s/g, '-')}`}
> >
{link.name} {link.name}
</a> </a>
))} ))}
</nav> </nav>
<div className="hidden md:block">
<a <a
href="#contact" href="#contact"
className="md:hidden px-4 py-2 border border-border rounded text-sm font-mono text-foreground hover:border-primary transition-colors" className="px-4 py-2 bg-primary text-white text-sm font-semibold rounded-lg hover:bg-primary/90 transition-colors"
data-testid="button-nav-contact"
> >
Kontakt Kontakt
</a> </a>
</div> </div>
<button
className="md:hidden p-2 rounded-lg text-muted-foreground hover:bg-secondary transition-colors"
onClick={() => setMobileOpen(!mobileOpen)}
data-testid="button-mobile-menu"
>
{mobileOpen ? <X className="w-5 h-5" /> : <Menu className="w-5 h-5" />}
</button>
</div>
{mobileOpen && (
<motion.div
initial={{ opacity: 0, y: -8 }}
animate={{ opacity: 1, y: 0 }}
className="md:hidden bg-white border-t border-border px-4 py-4 flex flex-col gap-1"
>
{navLinks.map((link) => (
<a
key={link.name}
href={link.href}
onClick={() => setMobileOpen(false)}
className="px-4 py-3 text-sm font-medium text-muted-foreground hover:text-foreground hover:bg-secondary rounded-lg transition-all"
>
{link.name}
</a>
))}
<a
href="#contact"
onClick={() => setMobileOpen(false)}
className="mt-2 px-4 py-3 bg-primary text-white text-sm font-semibold rounded-lg text-center"
>
Kontakt aufnehmen
</a>
</motion.div>
)}
</motion.header> </motion.header>
); );
} }

View File

@@ -1,74 +1,126 @@
import { motion } from "framer-motion"; import { motion } from "framer-motion";
import { FolderGit2, ArrowRight } from "lucide-react"; import { ArrowUpRight, Lock, Mail, Zap, Database, BarChart2, Server } from "lucide-react";
const projects = [ const projects = [
{ {
icon: Lock,
color: "from-blue-500 to-indigo-600",
iconBg: "bg-blue-100 text-blue-600",
title: "SafeDocs Portal", title: "SafeDocs Portal",
desc: "Sichere Upload-Plattform mit AES-256-GCM-Verschlüsselung, JWT-Auth, Passwort-Reset, Rate Limiting, Docker-Deployment", desc: "Sichere Upload-Plattform mit AES-256-GCM-Verschlüsselung, JWT-Auth, Passwort-Reset per E-Mail, Rate Limiting und Docker-Deployment.",
tags: ["Docker", "Security", "AES-256"] tags: ["AES-256-GCM", "JWT", "Docker", "Security"],
url: "https://safedocsportal.com",
type: "Vibe-Coding Projekt",
}, },
{ {
icon: Mail,
color: "from-violet-500 to-purple-600",
iconBg: "bg-violet-100 text-violet-600",
title: "zensend.email", title: "zensend.email",
desc: "Newsletter & E-Mail-Marketing SaaS: Double-Opt-In, DSGVO, SPF/DKIM/DMARC, Zahlungsmodelle, Onboarding", desc: "Newsletter & E-Mail-Marketing SaaS mit Double-Opt-In, DSGVO-Konformität, SPF/DKIM/DMARC, Zahlungsmodellen und Onboarding-Prozessen.",
tags: ["SaaS", "Mail", "DSGVO"] tags: ["SaaS", "DSGVO", "SMTP", "Newsletter"],
url: "https://zensend.email",
type: "Vibe-Coding Projekt",
}, },
{ {
icon: Zap,
color: "from-amber-500 to-orange-500",
iconBg: "bg-amber-100 text-amber-600",
title: "KI-Automation mit n8n", title: "KI-Automation mit n8n",
desc: "Automatisierte Bildgenerierung, Lead-Prozesse, KI-gestützte Bildprüfung, API/Webhook-Workflows", desc: "Automatisierte Bildgenerierung, Lead-Prozesse, KI-gestützte Bildprüfung und API/Webhook-Workflows für echte Geschäftsprozesse.",
tags: ["n8n", "AI", "API"] tags: ["n8n", "OpenAI", "Webhooks", "Automation"],
type: "KI-Projekt",
}, },
{ {
icon: Database,
color: "from-emerald-500 to-teal-500",
iconBg: "bg-emerald-100 text-emerald-600",
title: "Eigene KI- & RAG-Systeme", title: "Eigene KI- & RAG-Systeme",
desc: "PDF-Verarbeitung, Vektorisierung, Pinecone, eigene Wissensdatenbanken", desc: "PDF-Verarbeitung, Vektorisierung eigener Dokumente, Pinecone-Integration und eigene Wissensdatenbanken für KI-gestützte Recherche.",
tags: ["RAG", "Pinecone", "LLMs"] tags: ["RAG", "Pinecone", "Vektoren", "LLMs"],
type: "KI-Projekt",
}, },
{ {
icon: BarChart2,
color: "from-orange-500 to-red-500",
iconBg: "bg-orange-100 text-orange-600",
title: "On-Premise Monitoring", title: "On-Premise Monitoring",
desc: "Grafana, Prometheus, Loki, Alloy Stacks auf Docker-Basis", desc: "Vollständige Monitoring-Stacks mit Grafana, Prometheus, Loki und Alloy auf Docker-Basis — transparent, nachvollziehbar, betreibbar.",
tags: ["Grafana", "Prometheus", "Docker"] tags: ["Grafana", "Prometheus", "Loki", "Docker"],
type: "Infrastruktur",
}, },
{ {
icon: Server,
color: "from-slate-500 to-gray-600",
iconBg: "bg-slate-100 text-slate-600",
title: "Mailserver & Groupware", title: "Mailserver & Groupware",
desc: "iRedMail, Mailcow, SOGo, DNS, SPF, DKIM, DMARC, Zustellbarkeit", desc: "Betrieb eigener Mailinfrastruktur mit iRedMail, Mailcow, SOGo inklusive vollständiger DNS-Konfiguration, SPF/DKIM/DMARC und Zustellbarkeitsanalyse.",
tags: ["Mailcow", "DNS", "Security"] tags: ["Mailcow", "SPF/DKIM", "DNS", "IMAP"],
} type: "Infrastruktur",
},
]; ];
export function Projects() { export function Projects() {
return ( return (
<div className="py-20 border-t border-border/50"> <div className="py-24">
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }} viewport={{ once: true, margin: "-80px" }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
> >
<h2 className="text-3xl md:text-4xl font-bold mb-4 font-mono text-foreground flex items-center gap-4"> <div className="text-center max-w-2xl mx-auto mb-16">
<span className="text-primary">03.</span> Ausgewählte Projekte <p className="section-number mb-3" data-testid="text-section-label-projects">Ausgewählte Projekte</p>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4" data-testid="text-section-title-projects">
Gebaut, deployed, betrieben
</h2> </h2>
<p className="text-muted-foreground max-w-2xl mb-12"> <p className="text-muted-foreground text-lg">
Von der sicheren Infrastruktur bis zur modernen SaaS-Lösung in der Praxis bewährt. Reale Systeme von der sicheren Upload-Plattform bis zur Newsletter-SaaS und eigenen KI-Infrastruktur.
</p> </p>
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-5">
{projects.map((project, index) => ( {projects.map((project, index) => (
<motion.div <motion.div
key={index} key={index}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.4, delay: index * 0.07 }}
className="group flex flex-col p-6 rounded-lg bg-card border border-border hover:border-primary/30 transition-all hover:shadow-[0_0_20px_rgba(0,255,255,0.05)]" className="group flex flex-col p-6 rounded-2xl bg-white border border-border card-hover overflow-hidden relative"
data-testid={`card-project-${index}`}
> >
<div className="flex justify-between items-start mb-4"> <div className="flex items-start justify-between mb-4">
<FolderGit2 className="w-8 h-8 text-primary" /> <div className="flex items-center gap-3">
<ArrowRight className="w-5 h-5 text-muted-foreground opacity-0 group-hover:opacity-100 group-hover:translate-x-1 transition-all" /> <div className={`w-10 h-10 rounded-xl flex items-center justify-center ${project.iconBg}`}>
<project.icon className="w-5 h-5" />
</div> </div>
<h3 className="text-xl font-bold mb-2 group-hover:text-primary transition-colors">{project.title}</h3> <span className="text-xs font-semibold text-muted-foreground uppercase tracking-wider">
<p className="text-muted-foreground mb-6 flex-grow">{project.desc}</p> {project.type}
<div className="flex flex-wrap gap-2 mt-auto"> </span>
</div>
{project.url && (
<a
href={project.url}
target="_blank"
rel="noopener noreferrer"
className="w-8 h-8 rounded-lg bg-secondary flex items-center justify-center text-muted-foreground hover:bg-primary hover:text-white transition-all"
data-testid={`link-project-${index}`}
>
<ArrowUpRight className="w-4 h-4" />
</a>
)}
</div>
<h3 className="text-lg font-bold text-foreground mb-2">{project.title}</h3>
<p className="text-sm text-muted-foreground leading-relaxed mb-5 flex-grow">{project.desc}</p>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag, tIndex) => ( {project.tags.map((tag, tIndex) => (
<span key={tIndex} className="text-xs font-mono text-primary/80 bg-primary/10 px-2 py-1 rounded"> <span
key={tIndex}
className="text-xs font-medium text-foreground/70 bg-secondary px-2.5 py-1 rounded-full border border-border"
>
{tag} {tag}
</span> </span>
))} ))}

View File

@@ -3,58 +3,70 @@ import { TerminalSquare, BrainCircuit, Wrench } from "lucide-react";
const strengths = [ const strengths = [
{ {
number: "01",
icon: TerminalSquare, icon: TerminalSquare,
title: "Klassischer IT-Betrieb", title: "Klassischer IT-Betrieb",
desc: "Server, Netzwerke, Logs, Zertifikate, Mailserver aus 30 Jahren Praxis. Fundiertes Wissen, das die Basis für jedes zuverlässige System bildet." desc: "Server, Netzwerke, Logs, Zertifikate, Mailserver aus über 30 Jahren produktivem Betrieb in Konzernen und Behörden. Ein Fundament, das hält.",
highlight: "Behörden & Konzernprojekte",
}, },
{ {
number: "02",
icon: BrainCircuit, icon: BrainCircuit,
title: "KI-gestützte Entwicklung", title: "KI-gestützte Entwicklung",
desc: "Vibe-Coding mit Claude Code & ChatGPT. Der direkte Weg von der Idee zur lauffähigen Anwendung, unterstützt durch modernste AI-Tools." desc: "Vibe-Coding mit Claude Code & ChatGPT: Von der Idee zur lauffähigen Anwendung. KI als präzises Entwicklungswerkzeug — nicht als Buzzword.",
highlight: "Idee → Produktion",
}, },
{ {
number: "03",
icon: Wrench, icon: Wrench,
title: "Praxisnahe Umsetzung", title: "Praxisnahe Umsetzung",
desc: "Baut, testet, betreibt, dokumentiert und verbessert reale Systeme. Kein Elfenbeinturm-Engineering, sondern Lösungen, die in Produktion funktionieren." desc: "Bauen, testen, betreiben, dokumentieren — und verbessern. Keine Konzepte ohne Ausführung. Kein Deployment ohne Betrieb.",
} highlight: "Nachvollziehbar & betreibbar",
},
]; ];
export function Strengths() { export function Strengths() {
return ( return (
<div className="py-20 border-t border-border/50"> <div className="py-24 bg-secondary/40 rounded-3xl">
<motion.div <motion.div
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-100px" }} viewport={{ once: true, margin: "-80px" }}
transition={{ duration: 0.6 }} transition={{ duration: 0.6 }}
className="px-4 md:px-12"
> >
<h2 className="text-3xl md:text-4xl font-bold mb-4 font-mono text-foreground flex items-center gap-4"> <div className="text-center max-w-2xl mx-auto mb-16">
<span className="text-primary">02.</span> 3 Welten <p className="section-number mb-3" data-testid="text-section-label-strengths">Besondere Stärke</p>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4" data-testid="text-section-title-strengths">
Drei Welten eine Person
</h2> </h2>
<p className="text-muted-foreground max-w-2xl mb-12"> <p className="text-muted-foreground text-lg">
Die besondere Stärke liegt in der Kombination aus klassischem Betrieb, moderner KI-Entwicklung und handfester Umsetzung. Die Kombination macht den Unterschied: klassisches IT-Handwerk trifft auf modernste KI-Werkzeuge und konsequente Umsetzungsstärke.
</p> </p>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-8"> <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{strengths.map((item, index) => ( {strengths.map((item, index) => (
<motion.div <motion.div
key={index} key={index}
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 16 }}
whileInView={{ opacity: 1, y: 0 }} whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }} viewport={{ once: true }}
transition={{ duration: 0.5, delay: index * 0.1 }} transition={{ duration: 0.4, delay: index * 0.1 }}
className="relative p-8 rounded-lg bg-gradient-to-b from-card to-background border border-border" className="relative p-8 rounded-2xl bg-white border border-border card-hover flex flex-col"
data-testid={`card-strength-${index}`}
> >
<div className="w-12 h-12 rounded bg-primary/10 flex items-center justify-center mb-6"> <div className="flex items-start justify-between mb-6">
<div className="w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center">
<item.icon className="w-6 h-6 text-primary" /> <item.icon className="w-6 h-6 text-primary" />
</div> </div>
<h3 className="text-xl font-bold mb-4">{item.title}</h3> <span className="text-4xl font-black text-border">{item.number}</span>
<p className="text-muted-foreground leading-relaxed"> </div>
{item.desc} <h3 className="text-lg font-bold text-foreground mb-3">{item.title}</h3>
</p> <p className="text-sm text-muted-foreground leading-relaxed mb-6 flex-grow">{item.desc}</p>
<span className="inline-block px-3 py-1 text-xs font-semibold bg-accent text-accent-foreground rounded-full self-start">
{/* Decorative line */} {item.highlight}
<div className="absolute bottom-0 left-8 right-8 h-[1px] bg-gradient-to-r from-transparent via-primary/30 to-transparent" /> </span>
</motion.div> </motion.div>
))} ))}
</div> </div>

View File

@@ -1,3 +1,4 @@
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap');
@import "tailwindcss"; @import "tailwindcss";
@import "tw-animate-css"; @import "tw-animate-css";
@plugin "@tailwindcss/typography"; @plugin "@tailwindcss/typography";
@@ -34,7 +35,7 @@
--color-destructive: hsl(var(--destructive)); --color-destructive: hsl(var(--destructive));
--color-destructive-foreground: hsl(var(--destructive-foreground)); --color-destructive-foreground: hsl(var(--destructive-foreground));
--font-sans: 'Space Grotesk', 'Inter', sans-serif; --font-sans: 'Inter', system-ui, sans-serif;
--font-mono: 'JetBrains Mono', 'Menlo', monospace; --font-mono: 'JetBrains Mono', 'Menlo', monospace;
--radius-sm: calc(var(--radius) - 4px); --radius-sm: calc(var(--radius) - 4px);
@@ -44,38 +45,37 @@
} }
:root { :root {
/* Dark technical industrial theme as default */ --background: 0 0% 100%;
--background: 224 25% 6%; --foreground: 222 47% 11%;
--foreground: 210 40% 98%;
--card: 224 25% 8%; --card: 0 0% 100%;
--card-foreground: 210 40% 98%; --card-foreground: 222 47% 11%;
--card-border: 224 25% 12%; --card-border: 220 13% 91%;
--popover: 224 25% 6%; --popover: 0 0% 100%;
--popover-foreground: 210 40% 98%; --popover-foreground: 222 47% 11%;
--popover-border: 224 25% 12%; --popover-border: 220 13% 91%;
--primary: 186 100% 42%; /* Cyan */ --primary: 234 89% 60%;
--primary-foreground: 224 25% 6%; --primary-foreground: 0 0% 100%;
--secondary: 224 25% 12%; --secondary: 220 14% 96%;
--secondary-foreground: 210 40% 98%; --secondary-foreground: 222 47% 11%;
--muted: 224 25% 14%; --muted: 220 14% 96%;
--muted-foreground: 215 20% 65%; --muted-foreground: 220 9% 46%;
--accent: 224 25% 14%; --accent: 234 89% 97%;
--accent-foreground: 210 40% 98%; --accent-foreground: 234 89% 40%;
--destructive: 0 84% 60%; --destructive: 0 84% 60%;
--destructive-foreground: 210 40% 98%; --destructive-foreground: 0 0% 100%;
--border: 224 25% 15%; --border: 220 13% 91%;
--input: 224 25% 15%; --input: 220 13% 91%;
--ring: 186 100% 42%; --ring: 234 89% 60%;
--radius: 0.25rem; --radius: 0.625rem;
} }
@layer base { @layer base {
@@ -83,23 +83,34 @@
@apply border-border; @apply border-border;
} }
html {
scroll-behavior: smooth;
}
body { body {
@apply font-sans antialiased bg-background text-foreground selection:bg-primary/30; @apply font-sans antialiased bg-background text-foreground;
} }
} }
.glow-text { .section-number {
text-shadow: 0 0 20px hsl(var(--primary) / 0.5); @apply text-sm font-semibold tracking-widest uppercase text-primary;
} }
.glow-box { .hero-gradient {
box-shadow: 0 0 30px hsl(var(--primary) / 0.1); background: radial-gradient(ellipse 80% 60% at 50% -10%, hsl(234 89% 60% / 0.12) 0%, transparent 70%);
} }
/* Grid background pattern */ .dot-pattern {
.bg-grid-pattern { background-image: radial-gradient(circle, hsl(220 13% 91%) 1px, transparent 1px);
background-size: 40px 40px; background-size: 28px 28px;
background-image: }
linear-gradient(to right, hsl(var(--border) / 0.3) 1px, transparent 1px),
linear-gradient(to bottom, hsl(var(--border) / 0.3) 1px, transparent 1px); .card-hover {
transition: box-shadow 0.2s ease, transform 0.2s ease, border-color 0.2s ease;
}
.card-hover:hover {
box-shadow: 0 8px 30px hsl(234 89% 60% / 0.1);
transform: translateY(-2px);
border-color: hsl(234 89% 60% / 0.3);
} }

View File

@@ -1,4 +1,3 @@
import { motion } from "framer-motion";
import { Hero } from "@/components/hero"; import { Hero } from "@/components/hero";
import { Competencies } from "@/components/competencies"; import { Competencies } from "@/components/competencies";
import { Projects } from "@/components/projects"; import { Projects } from "@/components/projects";
@@ -9,37 +8,31 @@ import { Navbar } from "@/components/navbar";
export default function Home() { export default function Home() {
return ( return (
<div className="relative min-h-screen bg-background text-foreground overflow-hidden"> <div className="min-h-screen bg-background text-foreground">
{/* Background grid */}
<div className="fixed inset-0 pointer-events-none bg-grid-pattern opacity-20 z-0" />
{/* Subtle ambient glow */}
<div className="fixed top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-primary/10 blur-[120px] rounded-full pointer-events-none z-0" />
<Navbar /> <Navbar />
<main className="relative z-10 container mx-auto px-4 md:px-8 pt-24 pb-32 flex flex-col gap-32"> <main className="max-w-6xl mx-auto px-4 md:px-8 pb-24">
<section id="hero"> <section id="hero">
<Hero /> <Hero />
</section> </section>
<section id="competencies"> <section id="competencies" className="mt-8">
<Competencies /> <Competencies />
</section> </section>
<section id="strengths"> <section id="strengths" className="mt-8">
<Strengths /> <Strengths />
</section> </section>
<section id="projects"> <section id="projects" className="mt-8">
<Projects /> <Projects />
</section> </section>
<section id="bio"> <section id="bio" className="mt-8">
<Bio /> <Bio />
</section> </section>
<section id="contact"> <section id="contact" className="mt-8">
<Contact /> <Contact />
</section> </section>
</main> </main>