mirror of
https://github.com/Since-3/since3-website.git
synced 2026-10-12 19:51:16 +00:00
105 lines
3.5 KiB
TypeScript
105 lines
3.5 KiB
TypeScript
import Image from "next/image"
|
|
|
|
import type { Project } from "@/modules/landingPage/data/content"
|
|
|
|
/**
|
|
* Abstract stand-in visuals until real screenshots exist.
|
|
* Each project gets its own composition so the track does not feel repetitive.
|
|
*/
|
|
export function ProjectVisual({ project, index }: { project: Project; index: number }) {
|
|
if (project.image) {
|
|
return (
|
|
<Image
|
|
src={project.image.src}
|
|
alt={project.image.alt}
|
|
fill
|
|
sizes="(min-width: 768px) 60vw, 90vw"
|
|
className="object-cover"
|
|
/>
|
|
)
|
|
}
|
|
|
|
const variant = index % 3
|
|
|
|
if (variant === 0) {
|
|
// Typography poster, sized so the longest word always fits the card
|
|
const words = project.title.split(" ")
|
|
const longest = Math.max(...words.map((word) => word.length))
|
|
const fontSize = Math.min(14, 70 / (longest * 0.5))
|
|
return (
|
|
<div className="flex size-full flex-col justify-center bg-ink-100 p-[6cqi] text-black">
|
|
{words.map((word, i) => (
|
|
<span
|
|
key={i}
|
|
className="block leading-[0.85] type-poster"
|
|
style={{ fontSize: `${fontSize}cqi`, paddingLeft: `${i * 6}cqi` }}
|
|
>
|
|
{word}
|
|
</span>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (variant === 1) {
|
|
// Interface skeleton
|
|
return (
|
|
<div className="grid size-full grid-cols-[22%_1fr] gap-3 bg-ink-900 p-5 md:p-8">
|
|
<div className="flex flex-col gap-2 rounded-xl bg-ink-800 p-3">
|
|
{Array.from({ length: 6 }).map((_, i) => (
|
|
<div key={i} className="h-2 rounded-full bg-ink-700" style={{ width: `${90 - i * 9}%` }} />
|
|
))}
|
|
</div>
|
|
<div className="grid grid-rows-[auto_1fr] gap-3">
|
|
<div className="grid grid-cols-3 gap-3">
|
|
{["128", "4.2k", "99%"].map((value) => (
|
|
<div key={value} className="rounded-xl bg-ink-800 p-3 md:p-4">
|
|
<div className="h-1.5 w-1/2 rounded-full bg-ink-700" />
|
|
<p className="mt-3 text-2xl text-white type-poster md:text-4xl">{value}</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
<div className="relative overflow-hidden rounded-xl bg-ink-800">
|
|
<svg viewBox="0 0 300 100" preserveAspectRatio="none" className="absolute inset-0 size-full">
|
|
<path
|
|
d="M0 85 C 30 80, 50 60, 80 62 S 130 30, 160 40 S 220 10, 250 18 S 290 5, 300 4"
|
|
fill="none"
|
|
stroke="white"
|
|
strokeWidth="2"
|
|
vectorEffect="non-scaling-stroke"
|
|
/>
|
|
<path
|
|
d="M0 85 C 30 80, 50 60, 80 62 S 130 30, 160 40 S 220 10, 250 18 S 290 5, 300 4 V 100 H 0 Z"
|
|
fill="white"
|
|
fillOpacity="0.06"
|
|
/>
|
|
</svg>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Orbit: automation as moving parts
|
|
return (
|
|
<div className="relative grid size-full place-items-center overflow-hidden rounded-glass border border-line bg-ink-950">
|
|
{[1, 2, 3, 4].map((ring) => (
|
|
<div
|
|
key={ring}
|
|
className="absolute aspect-square rounded-full border border-white/15"
|
|
style={{ width: `${ring * 22}%` }}
|
|
/>
|
|
))}
|
|
{[0, 60, 150, 230, 300].map((angle, i) => (
|
|
<div
|
|
key={angle}
|
|
className="absolute size-2.5 rounded-full bg-white"
|
|
style={{
|
|
transform: `rotate(${angle}deg) translateX(${(i % 4) * 11 + 11}cqi)`,
|
|
}}
|
|
/>
|
|
))}
|
|
<p className="relative font-mono text-label text-white">input → ki → output</p>
|
|
</div>
|
|
)
|
|
}
|