Najjar\NajjarV02 729ab71c2c
Some checks are pending
CI/CD / test-and-build (push) Waiting to run
CI/CD / deploy (push) Blocked by required conditions
Refactor code structure for improved readability and maintainability; removed redundant code blocks and optimized function calls.
2026-05-20 18:03:42 +04:00

126 lines
4.8 KiB
TypeScript

"use client";
/**
* 4-image certificate gallery each tile links to a PDF in a new tab.
*
* Expected data shape:
* export type CertPage = {
* src: string; // /images/certifications/page.jpg
* alt: string;
* title: string; // ISCC Certificate 2025
* page: string; // Page 1 · Annex
* document: string; // ISCC Certificate
* href: string; // /documents/certifications/iscc.pdf OR upstream URL
* };
* export const certificatePages: CertPage[] = [ ... ];
*/
import Image from "next/image";
import { ArrowUpRight, FileText } from "lucide-react";
import { motion, type Variants } from "framer-motion";
export type CertPage = {
src: string;
alt: string;
title: string;
page: string;
document: string;
href: string;
};
const grid: Variants = {
hidden: {},
visible: { transition: { staggerChildren: 0.08, delayChildren: 0.15 } },
};
const cell: Variants = {
hidden: { opacity: 0, y: 24, filter: "blur(6px)" },
visible: {
opacity: 1,
y: 0,
filter: "blur(0px)",
transition: { duration: 0.8, ease: [0.16, 1, 0.3, 1] },
},
};
export function CertificateGallery({ pages }: { pages: CertPage[] }) {
return (
<motion.div
variants={grid}
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.1 }}
className="grid gap-5 sm:grid-cols-2 lg:grid-cols-4 lg:gap-6"
>
{pages.map((p) => (
<motion.div key={p.src} variants={cell}>
<CertCard {...p} />
</motion.div>
))}
</motion.div>
);
}
function CertCard({ src, alt, title, page, document, href }: CertPage) {
return (
<a
href={href}
target="_blank"
rel="noopener noreferrer"
aria-label={`${title} ${page}. Opens PDF in a new tab.`}
className="group relative isolate flex h-full flex-col overflow-hidden rounded-2xl border border-bone/10 bg-graphite/40 backdrop-blur-sm transition-all duration-500 hover:-translate-y-1 hover:border-gold/35 hover:bg-graphite/60 focus:outline-none focus-visible:border-gold/60 focus-visible:ring-2 focus-visible:ring-gold/40"
>
<div className="relative aspect-[3/4] w-full overflow-hidden bg-obsidian">
<Image
src={src}
alt={alt}
fill
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 25vw"
className="object-cover object-top transition-transform duration-[1200ms] ease-[cubic-bezier(0.16,1,0.3,1)] group-hover:scale-[1.04]"
/>
<div
aria-hidden
className="pointer-events-none absolute inset-x-0 top-0 h-20 bg-gradient-to-b from-obsidian/70 via-obsidian/20 to-transparent"
/>
<div
aria-hidden
className="pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-obsidian/80 via-obsidian/30 to-transparent"
/>
<span className="absolute left-3 top-3 inline-flex items-center gap-2 rounded-full border border-gold/30 bg-obsidian/70 px-3 py-1 font-mono text-[10px] uppercase tracking-[0.22em] text-gold-light backdrop-blur">
<span className="size-1 rounded-full bg-gold-light shadow-[0_0_8px_rgba(242,194,91,0.8)]" />
{page}
</span>
<span className="absolute right-3 top-3 grid size-9 place-items-center rounded-full border border-bone/20 bg-obsidian/70 text-mist backdrop-blur transition-all duration-500 group-hover:-translate-y-0.5 group-hover:border-gold/50 group-hover:text-gold-light">
<ArrowUpRight className="size-4" />
</span>
<div
aria-hidden
className="pointer-events-none absolute -bottom-16 -right-16 size-48 rounded-full opacity-0 blur-3xl transition-opacity duration-700 group-hover:opacity-100"
style={{
background:
"radial-gradient(circle, rgba(212,164,55,0.32), transparent 70%)",
}}
/>
<span
aria-hidden
className="pointer-events-none absolute inset-0 rounded-t-2xl border border-transparent transition-colors duration-500 group-hover:border-gold/40"
/>
</div>
<div className="relative flex flex-col gap-2 p-5 md:p-6">
<span className="font-mono text-[10px] uppercase tracking-[0.22em] text-gold-light">
{document}
</span>
<h4 className="font-display text-lg text-bone text-balance transition-colors duration-500 group-hover:text-gold-light md:text-xl">
{title}
</h4>
<div className="mt-1 inline-flex items-center gap-2 text-xs text-mist">
<FileText className="size-3.5 text-gold-light" />
<span>Open PDF</span>
</div>
</div>
<span
aria-hidden
className="pointer-events-none absolute bottom-0 left-0 h-px w-0 bg-gradient-to-r from-gold via-gold-light to-transparent transition-all duration-700 group-hover:w-full"
/>
</a>
);
}