it/web/app/services/page.tsx
2025-10-26 02:05:16 +02:00

160 lines
6.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// app/services/page.tsx
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 — Website Reliability for SMBs | Van Hunen IT",
description:
"Fixed-scope sprints and care plans for SMB website reliability: email deliverability (DMARC), Cloudflare edge security & speed, tested backups, and uptime watch.",
alternates: { canonical: "/services" },
};
export default function ServicesPage() {
const services = getAllServices();
// Emphasize core Reliability first; keep the rest discoverable.
const categories: ServiceCategoryId[] = [
"web-performance",
"infrastructure-devops",
"dev-platforms",
"migrations",
"web-dev",
"minecraft",
];
return (
<main
id="top"
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">
Services that make websites reliable
</h1>
<p className="mt-5 text-lg text-neutral-700 dark:text-neutral-300 max-w-2xl mx-auto">
Fixed-scope sprints and care plans for{" "}
<strong>email deliverability</strong>, <strong>Cloudflare edge security &amp; speed</strong>,{" "}
<strong>backups with restore tests</strong>, and <strong>uptime watch</strong>. Clear outcomes, flat pricing,
and before/after 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 className="mt-8 flex items-center justify-center gap-3">
<Link
href="/pricing"
className="inline-flex items-center rounded-lg bg-blue-600 px-5 py-2.5 text-white font-semibold hover:bg-blue-500 transition"
>
See pricing
</Link>
<Link
href="/contact"
className="inline-flex items-center rounded-lg border border-neutral-300 dark:border-neutral-700 px-5 py-2.5 font-semibold text-neutral-800 dark:text-neutral-200 hover:bg-neutral-50 dark:hover:bg-neutral-900/40 transition"
>
Book a 15-min Fit Call
</Link>
</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;
const headingId = `${id}-heading`;
const sectionClasses = `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`;
return (
<section
key={id}
id={SERVICE_CATEGORIES[id].anchor}
aria-labelledby={headingId}
className={sectionClasses}
>
<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={headingId}
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="#top"
className="mt-3 sm:mt-0 inline-flex items-center text-sm font-medium text-blue-600 hover:underline dark:text-blue-400"
aria-label="Jump to top"
>
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 improve reliability and deliverability?
</h2>
<p className="mt-4 text-lg text-blue-100">
Tell us about your site. Well recommend the right Fix Sprint or Care plan and show you the expected
before/after.
</p>
<div className="mt-8 flex items-center justify-center gap-3">
<Link
href="/pricing"
className="inline-flex items-center rounded-lg bg-white text-blue-700 font-semibold px-6 py-3 hover:bg-blue-50 transition"
>
Compare plans
</Link>
<Link
href="/contact"
className="inline-flex items-center rounded-lg border border-white/80 text-white font-semibold px-6 py-3 hover:bg-white/10 transition"
>
Contact us
</Link>
</div>
</div>
</section>
</main>
);
}