120 lines
5.3 KiB
TypeScript
120 lines
5.3 KiB
TypeScript
import { Metadata } from "next";
|
||
import { getAllServices, SERVICE_CATEGORIES, type ServiceCategoryId } from "@/lib/services";
|
||
import ServiceCard from "@/components/ServiceCard";
|
||
import Link from "next/link";
|
||
|
||
export const revalidate = 86400;
|
||
|
||
export const metadata: Metadata = {
|
||
title: "Services — Van Hunen IT",
|
||
description:
|
||
"Explore Van Hunen IT's fixed-scope sprints and managed solutions — from VPS hardening and Docker orchestration to Kubernetes, Cloudflare, Core Web Vitals, and Minecraft operations.",
|
||
alternates: { canonical: "/services" },
|
||
};
|
||
|
||
export default function ServicesPage() {
|
||
const services = getAllServices();
|
||
const categories: ServiceCategoryId[] = Object.keys(SERVICE_CATEGORIES) as ServiceCategoryId[];
|
||
|
||
return (
|
||
<main className="relative isolate min-h-screen bg-gradient-to-b from-neutral-50 via-white to-neutral-100 dark:from-neutral-950 dark:via-neutral-900 dark:to-neutral-950">
|
||
{/* --- Hero Header --- */}
|
||
<section className="relative overflow-hidden border-b border-neutral-200 dark:border-neutral-800">
|
||
<div className="container mx-auto max-w-6xl px-4 py-20 text-center">
|
||
<h1 className="text-4xl font-bold tracking-tight sm:text-5xl bg-gradient-to-r from-blue-600 via-purple-600 to-blue-600 bg-clip-text text-transparent">
|
||
Professional IT Services
|
||
</h1>
|
||
<p className="mt-5 text-lg text-neutral-700 dark:text-neutral-300 max-w-2xl mx-auto">
|
||
Fixed-scope sprints and managed plans for{" "}
|
||
<strong>VPS</strong>, <strong>Docker</strong>, <strong>Kubernetes</strong>,{" "}
|
||
<strong>Cloudflare</strong>, <strong>Core Web Vitals</strong>, and{" "}
|
||
<strong>Minecraft</strong>. Clear outcomes, flat pricing, and proof you can keep.
|
||
</p>
|
||
<div className="mt-8 flex flex-wrap justify-center gap-3">
|
||
{categories.map((id) => (
|
||
<a
|
||
key={id}
|
||
href={`#${SERVICE_CATEGORIES[id].anchor}`}
|
||
className="rounded-full border border-neutral-300 dark:border-neutral-700 px-4 py-1.5 text-sm font-medium text-neutral-700 dark:text-neutral-300 hover:border-blue-500 hover:text-blue-600 dark:hover:text-blue-400 transition"
|
||
>
|
||
{SERVICE_CATEGORIES[id].label}
|
||
</a>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* --- Service Categories --- */}
|
||
<div className="container mx-auto max-w-6xl px-4 py-16">
|
||
{categories.map((id, index) => {
|
||
const list = services.filter((s) => s.category === id);
|
||
if (!list.length) return null;
|
||
|
||
return (
|
||
<section
|
||
key={id}
|
||
id={SERVICE_CATEGORIES[id].anchor}
|
||
aria-labelledby={`${id}-heading`}
|
||
className={`relative py-16 scroll-mt-24 ${
|
||
index % 2 === 0
|
||
? "bg-white dark:bg-neutral-900/50"
|
||
: "bg-neutral-50 dark:bg-neutral-900/30"
|
||
} rounded-2xl mb-12 shadow-sm`}
|
||
>
|
||
<div className="absolute inset-0 -z-10 bg-gradient-to-br from-blue-50/10 via-transparent to-purple-50/10 dark:from-blue-950/10 dark:to-purple-950/10 rounded-2xl" />
|
||
|
||
<div className="px-4 sm:px-8">
|
||
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between mb-8">
|
||
<h2
|
||
id={`${id}-heading`}
|
||
className="text-2xl sm:text-3xl font-bold tracking-tight bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent"
|
||
>
|
||
{SERVICE_CATEGORIES[id].label}
|
||
</h2>
|
||
|
||
<Link
|
||
href={`/services#${SERVICE_CATEGORIES[id].anchor}`}
|
||
className="mt-3 sm:mt-0 inline-flex items-center text-sm font-medium text-blue-600 hover:underline dark:text-blue-400"
|
||
>
|
||
Jump to top ↑
|
||
</Link>
|
||
</div>
|
||
|
||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
|
||
{list.map((svc, i) => (
|
||
<div
|
||
key={svc.slug}
|
||
className="animate-[fadeInUp_0.6s_ease_forwards]"
|
||
style={{ animationDelay: `${i * 80}ms` }}
|
||
>
|
||
<ServiceCard svc={svc} />
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</section>
|
||
);
|
||
})}
|
||
</div>
|
||
|
||
{/* --- CTA footer --- */}
|
||
<section className="relative py-24 text-center bg-gradient-to-r from-blue-600 via-purple-600 to-blue-600 text-white">
|
||
<div className="container mx-auto max-w-4xl px-6">
|
||
<h2 className="text-3xl sm:text-4xl font-bold tracking-tight">
|
||
Ready to optimize your IT infrastructure?
|
||
</h2>
|
||
<p className="mt-4 text-lg text-blue-100">
|
||
Get in touch to discuss your goals — we’ll help you choose the right sprint or managed plan.
|
||
</p>
|
||
<Link
|
||
href="/contact"
|
||
className="mt-8 inline-flex items-center rounded-lg bg-white text-blue-700 font-semibold px-6 py-3 hover:bg-blue-50 transition"
|
||
>
|
||
Contact Us
|
||
</Link>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
);
|
||
}
|