NicholaiVogel 633ef418d1 feat: implement complete Biohazard VFX website with all pages and components
- Add all shadcn/ui components (button, card, form, dialog, navigation-menu, etc.)
- Create main layout with navigation header and footer
- Implement homepage with hero, showreel, featured projects, and capabilities
- Build about page with studio origins, values, and capabilities
- Create services page with detailed service offerings
- Implement portfolio page with masonry grid for varying aspect ratios
- Build contact page with 4-step multistep form
- Add project and service data structures with placeholder content
- Configure SEO metadata, canonical links, and JSON-LD schema
- Add font preloading and image lazy-loading for performance
- Configure Next.js Image for Unsplash remote patterns
- Fix Navigation component to use modern Link pattern (remove legacyBehavior)
- Add comprehensive README with project documentation
2025-10-12 03:47:12 -06:00

89 lines
2.8 KiB
TypeScript

"use client";
import { useState } from "react";
import Image from "next/image";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Play } from "lucide-react";
import type { Project } from "@/data/projects";
interface ProjectCardProps {
project: Project;
}
export function ProjectCard({ project }: ProjectCardProps) {
const [isOpen, setIsOpen] = useState(false);
return (
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogTrigger asChild>
<Card className="group cursor-pointer overflow-hidden transition-all hover:shadow-lg">
<div className="relative overflow-hidden">
<Image
src={project.thumbnailUrl}
alt={project.title}
width={800}
height={800 / project.aspectRatio}
className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
loading="lazy"
/>
{project.videoUrl && (
<div className="absolute inset-0 flex items-center justify-center bg-black/0 transition-all group-hover:bg-black/40">
<Play className="h-12 w-12 text-white opacity-0 transition-opacity group-hover:opacity-100" />
</div>
)}
</div>
<CardHeader>
<div className="flex items-start justify-between gap-2">
<CardTitle className="line-clamp-1">{project.title}</CardTitle>
<Badge variant="secondary">{project.category}</Badge>
</div>
<CardDescription className="line-clamp-2">
{project.description}
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex flex-wrap gap-2">
{project.tags.map((tag) => (
<Badge key={tag} variant="outline">
{tag}
</Badge>
))}
</div>
</CardContent>
</Card>
</DialogTrigger>
{project.videoUrl && (
<DialogContent className="max-w-5xl p-0">
<DialogHeader className="sr-only">
<DialogTitle>{project.title}</DialogTitle>
</DialogHeader>
<div className="aspect-video w-full">
<iframe
src={project.videoUrl}
title={project.title}
className="h-full w-full"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
/>
</div>
</DialogContent>
)}
</Dialog>
);
}