180 lines
6.8 KiB
TypeScript
180 lines
6.8 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
import { Reveal } from "@/components/reveal";
|
|
|
|
export type ArchitectureLayer = {
|
|
title: string;
|
|
subtitle?: string;
|
|
bullets: string[];
|
|
};
|
|
|
|
export type ArchitectureInterface = {
|
|
label: string;
|
|
detail: string;
|
|
};
|
|
|
|
export type ArchitectureBreakdownData = {
|
|
title?: string;
|
|
subtitle?: string;
|
|
layers: ArchitectureLayer[];
|
|
flow: Array<{ from: string; to: string; note?: string }>;
|
|
interfaces: ArchitectureInterface[];
|
|
};
|
|
|
|
export function ArchitectureBreakdown({
|
|
title = "Architecture",
|
|
subtitle = "How the system is structured and how state moves through it.",
|
|
data,
|
|
}: {
|
|
title?: string;
|
|
subtitle?: string;
|
|
data: ArchitectureBreakdownData;
|
|
}) {
|
|
return (
|
|
<section className="relative overflow-hidden">
|
|
<div className="glass-strong noise-overlay rounded-3xl p-6 sm:p-8 shadow-sm relative overflow-hidden">
|
|
<div className="flex flex-col gap-2">
|
|
<h2 className="text-xl sm:text-2xl font-semibold tracking-tight">
|
|
{title}
|
|
</h2>
|
|
<p className="text-sm sm:text-[15px] text-black/60 max-w-3xl">
|
|
{subtitle}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Layers */}
|
|
<div className="mt-6 sm:mt-8 grid gap-4 lg:grid-cols-3">
|
|
{data.layers.map((layer, idx) => (
|
|
<Reveal key={`${layer.title}-${idx}`}>
|
|
<div className="glass rounded-3xl p-5 sm:p-6 h-full">
|
|
<div className="flex items-start justify-between gap-3">
|
|
<div className="min-w-0">
|
|
<div className="text-[11px] uppercase tracking-[0.18em] text-black/45">
|
|
Layer {String(idx + 1).padStart(2, "0")}
|
|
</div>
|
|
<h3 className="mt-2 text-base sm:text-lg font-semibold tracking-tight">
|
|
{layer.title}
|
|
</h3>
|
|
{layer.subtitle ? (
|
|
<p className="mt-1 text-sm text-black/60">
|
|
{layer.subtitle}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
|
|
<div className="shrink-0">
|
|
<span className="glass px-2.5 py-1 rounded-full text-[11px] text-black/55 tabular-nums">
|
|
{String(idx + 1).padStart(2, "0")}/03
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
<ul className="mt-4 grid gap-2 text-sm text-black/70">
|
|
{layer.bullets.map((b, bi) => (
|
|
<li key={`${layer.title}-b-${bi}`} className="flex gap-2">
|
|
<span className="mt-[7px] h-1.5 w-1.5 rounded-full bg-black/25" />
|
|
<span className="min-w-0">{b}</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</Reveal>
|
|
))}
|
|
</div>
|
|
|
|
{/* Flow */}
|
|
<div className="mt-6 sm:mt-8">
|
|
<Reveal>
|
|
<div className="glass rounded-3xl p-5 sm:p-6">
|
|
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-2">
|
|
<div>
|
|
<div className="text-[11px] uppercase tracking-[0.18em] text-black/45">
|
|
Data flow
|
|
</div>
|
|
<h3 className="mt-2 text-base sm:text-lg font-semibold tracking-tight">
|
|
State moves as a pipeline
|
|
</h3>
|
|
<p className="mt-1 text-sm text-black/60 max-w-3xl">
|
|
Configuration is authored on desktop, executed deterministically, then mirrored back as
|
|
LED feedback so the device stays readable at a glance.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-5 grid gap-3">
|
|
{data.flow.map((step, i) => (
|
|
<div
|
|
key={`${step.from}-${step.to}-${i}`}
|
|
className="glass rounded-2xl px-4 py-3"
|
|
>
|
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-2">
|
|
<div className="min-w-0">
|
|
<div className="text-sm text-black/75">
|
|
<span className="font-semibold">{step.from}</span>
|
|
<span className="mx-2 text-black/30">→</span>
|
|
<span className="font-semibold">{step.to}</span>
|
|
</div>
|
|
{step.note ? (
|
|
<div className="mt-1 text-[13px] text-black/60">
|
|
{step.note}
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
<div className="shrink-0 text-[12px] text-black/45 tabular-nums">
|
|
Step {String(i + 1).padStart(2, "0")}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
|
|
{/* Interfaces */}
|
|
<div className="mt-6 sm:mt-8">
|
|
<Reveal>
|
|
<div className="glass rounded-3xl p-5 sm:p-6">
|
|
<div className="text-[11px] uppercase tracking-[0.18em] text-black/45">
|
|
Interfaces
|
|
</div>
|
|
<h3 className="mt-2 text-base sm:text-lg font-semibold tracking-tight">
|
|
Stable contracts between modules
|
|
</h3>
|
|
<p className="mt-1 text-sm text-black/60 max-w-3xl">
|
|
The system stays maintainable because boundaries are explicit and small.
|
|
</p>
|
|
|
|
<div className="mt-5 grid gap-3 sm:grid-cols-2">
|
|
{data.interfaces.map((it, idx) => (
|
|
<div
|
|
key={`${it.label}-${idx}`}
|
|
className="glass rounded-2xl p-4"
|
|
>
|
|
<div className="flex items-start justify-between gap-3">
|
|
<div className="min-w-0">
|
|
<div className="text-sm font-semibold text-black/75">
|
|
{it.label}
|
|
</div>
|
|
<div className="mt-1 text-sm text-black/60">
|
|
{it.detail}
|
|
</div>
|
|
</div>
|
|
<span className="glass px-2.5 py-1 rounded-full text-[11px] text-black/50 tabular-nums">
|
|
{String(idx + 1).padStart(2, "0")}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Reveal>
|
|
</div>
|
|
|
|
{/* Specular highlight */}
|
|
<div className="pointer-events-none specular absolute -top-24 -right-24 h-64 w-64 rounded-full opacity-50 blur-2xl" />
|
|
</div>
|
|
</section>
|
|
);
|
|
} |