- Introduced RobotProductCard component for displaying robot details. - Added WhyUs component highlighting key reasons for choosing our robotics solutions. - Implemented CursorSpotlight for enhanced user interaction. - Created GlassPanel for a stylish UI element. - Developed MotionSection for animated section visibility. - Added PremiumButton for versatile button options. - Established data structures for industries and robots, including detailed specifications and use cases. - Included utility functions for retrieving robots by slug and category.
126 lines
4.8 KiB
TypeScript
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>
|
|
);
|
|
}
|