- Brute-force scramble that decodes hero values left to right - Full-screen hero on desktop with telemetry pinned to the bottom - Terrain no longer drifts; home skips the page transition - selimaj.dev is the root brand; Orus becomes one brand with its own projects, and independent projects branch from the root Co-Authored-By: Claude Opus 5.5 <[email protected]>
106 lines
4.5 KiB
TypeScript
106 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { useEffect, useRef, useState } from "react";
|
|
import { brandOf, featured, featuredRoot, projectById, projects, root } from "@/content/projects";
|
|
import { Segment } from "./Segment";
|
|
|
|
export function FeaturedNodes() {
|
|
const [active, setActive] = useState<string | null>(null);
|
|
const nodes = featured.map((f) => ({ ...f, project: projectById(f.id)! }));
|
|
const stageRef = useRef<HTMLDivElement>(null);
|
|
const [size, setSize] = useState<{ w: number; h: number } | null>(null);
|
|
|
|
// Lines are drawn in px so they stay 1px crisp at any stage size.
|
|
useEffect(() => {
|
|
const el = stageRef.current;
|
|
if (!el) return;
|
|
const ro = new ResizeObserver(([e]) => setSize({ w: e.contentRect.width, h: e.contentRect.height }));
|
|
ro.observe(el);
|
|
return () => ro.disconnect();
|
|
}, []);
|
|
|
|
return (
|
|
<div
|
|
ref={stageRef}
|
|
className="bracket grid-bg relative mt-8 flex flex-col gap-1 border border-white/6 bg-surface p-6 [--bc:rgb(242_244_245/0.35)] md:block md:h-[460px] md:p-0"
|
|
onMouseLeave={() => setActive(null)}
|
|
>
|
|
{size && (
|
|
<div aria-hidden="true" className="hidden md:block">
|
|
{nodes.map((n) => (
|
|
<Segment
|
|
key={`${n.id}-${active === n.id}`}
|
|
x1={(featuredRoot.x / 100) * size.w}
|
|
y1={(featuredRoot.y / 100) * size.h}
|
|
x2={(n.x / 100) * size.w}
|
|
y2={(n.y / 100) * size.h}
|
|
on={active === n.id}
|
|
dashed
|
|
strong
|
|
/>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Root: the site itself, labelled to the left so no line crosses it. */}
|
|
<div
|
|
className="relative min-h-12 md:absolute md:top-(--y) md:left-(--x) md:min-h-0"
|
|
style={{ "--x": `${featuredRoot.x}%`, "--y": `${featuredRoot.y}%` } as React.CSSProperties}
|
|
>
|
|
<span
|
|
aria-hidden="true"
|
|
className="absolute top-2.5 left-0 box-border size-7 rounded-full border border-ink/70 bg-night md:-top-3.5 md:-left-3.5"
|
|
>
|
|
<span className="absolute top-[9px] left-[9px] size-2 rounded-full bg-ink" />
|
|
</span>
|
|
<span className="ml-10 block px-3 py-2.5 md:absolute md:-top-[22px] md:right-6 md:ml-0 md:w-max md:text-right">
|
|
<span className="flex items-baseline gap-3 md:flex-row-reverse">
|
|
<span className="font-display text-lg font-semibold tracking-[0.08em] uppercase">{root.name}</span>
|
|
<span className="text-[9px] tracking-[0.22em] text-mute uppercase">Root</span>
|
|
</span>
|
|
</span>
|
|
</div>
|
|
|
|
{nodes.map((n) => {
|
|
const on = active === n.id;
|
|
const brand = brandOf(n.project);
|
|
const meta = n.id === "orus" ? "BRAND" : brand ? `${n.project.status} · ${brand.toUpperCase()}` : n.project.status;
|
|
return (
|
|
<Link
|
|
key={n.id}
|
|
href={`/projects/#${n.id}`}
|
|
onMouseEnter={() => setActive(n.id)}
|
|
onFocus={() => setActive(n.id)}
|
|
onBlur={() => setActive(null)}
|
|
className="group relative block min-h-12 outline-none md:absolute md:top-(--y) md:left-(--x) md:min-h-0"
|
|
style={{ "--x": `${n.x}%`, "--y": `${n.y}%` } as React.CSSProperties}
|
|
>
|
|
<span
|
|
aria-hidden="true"
|
|
className={`absolute top-4 left-2 box-border size-3 border md:-top-1.5 md:-left-1.5 ${n.id === "orus" ? "rotate-45" : ""} ${
|
|
on ? "border-accent bg-accent shadow-[0_0_14px_var(--color-accent)]" : "border-ink/60 bg-night"
|
|
}`}
|
|
/>
|
|
<span
|
|
className={`bracket ml-10 block min-h-11 px-3 py-2.5 group-focus-visible:outline group-focus-visible:outline-accent md:absolute md:-top-[22px] md:left-6 md:ml-0 md:w-[300px] ${
|
|
on ? "[--bc:var(--color-accent)]" : "[--bc:transparent]"
|
|
}`}
|
|
>
|
|
<span className="flex items-baseline gap-3">
|
|
<span className="font-display text-lg font-semibold tracking-[0.08em] uppercase">{n.project.name}</span>
|
|
<span className="text-[9px] tracking-[0.22em] text-mute">{meta}</span>
|
|
</span>
|
|
<span className={`mt-1.5 block text-[11px] leading-[1.55] text-ink-2 ${on ? "" : "md:sr-only"}`}>{n.project.desc}</span>
|
|
</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
|
|
<div aria-hidden="true" className="absolute right-4 bottom-3 hidden text-[9px] tracking-[0.2em] text-mute/70 uppercase md:block">
|
|
Root {root.sub} · {featured.length} of {projects.length} nodes
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|