Hero decode effect, static terrain, and selimaj.dev rebrand

- 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]>
This commit is contained in:
2026-10-03 21:04:01 +02:00
co-authored by claude
parent 85dbdb3db4
commit fd72e1082a
20 changed files with 406 additions and 227 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

+3 -3
View File
@@ -1,7 +1,7 @@
import type { Metadata } from "next"; import type { Metadata } from "next";
import Link from "next/link"; import Link from "next/link";
import { dossier } from "@/content/dossier"; import { dossier } from "@/content/dossier";
import { orus, profile } from "@/content/profile"; import { profile } from "@/content/profile";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Dossier", title: "Dossier",
@@ -25,8 +25,8 @@ export default function AboutPage() {
<dl className="bracket grid min-w-[280px] grid-cols-[110px_minmax(0,1fr)] gap-y-2 border border-white/6 bg-surface px-[22px] py-[18px] text-[11px] [--bc:rgb(242_244_245/0.4)]"> <dl className="bracket grid min-w-[280px] grid-cols-[110px_minmax(0,1fr)] gap-y-2 border border-white/6 bg-surface px-[22px] py-[18px] text-[11px] [--bc:rgb(242_244_245/0.4)]">
<dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">SUBJECT</dt> <dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">SUBJECT</dt>
<dd>{profile.displayName}</dd> <dd>{profile.displayName}</dd>
<dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">ROOT</dt> <dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">DOMAIN</dt>
<dd>{orus.name}</dd> <dd>selimaj.dev</dd>
<dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">FILES</dt> <dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">FILES</dt>
<dd>{String(dossier.length).padStart(2, "0")}</dd> <dd>{String(dossier.length).padStart(2, "0")}</dd>
<dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">ACCESS</dt> <dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">ACCESS</dt>
-9
View File
@@ -97,10 +97,6 @@ html[data-boot] .seq {
animation-fill-mode: both; animation-fill-mode: both;
} }
.drift {
animation: drift 80s ease-in-out infinite alternate;
will-change: transform;
}
.spin { .spin {
animation: spin 16s linear infinite; animation: spin 16s linear infinite;
transform-origin: 60px 60px; transform-origin: 60px 60px;
@@ -185,11 +181,6 @@ html[data-boot] .seq {
} }
} }
@keyframes drift {
to {
transform: translate3d(-28px, 14px, 0);
}
}
@keyframes spin { @keyframes spin {
to { to {
transform: rotate(360deg); transform: rotate(360deg);
+2 -2
View File
@@ -1,5 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<rect width="24" height="24" fill="#050607"/> <rect width="24" height="24" fill="#050607"/>
<circle cx="12" cy="12" r="8.5" stroke="#f2f4f5" stroke-width="1.6" stroke-dasharray="44 9"/> <path d="M3,8.5 V3 H8.5 M15.5,3 H21 V8.5 M21,15.5 V21 H15.5 M8.5,21 H3 V15.5" stroke="#f2f4f5" stroke-width="1.8"/>
<circle cx="12" cy="12" r="2.6" fill="#4dff95"/> <rect x="9.25" y="9.25" width="5.5" height="5.5" fill="#4dff95"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 314 B

+1 -1
View File
@@ -17,7 +17,7 @@ const jetbrains = JetBrains_Mono({
export const metadata: Metadata = { export const metadata: Metadata = {
title: { default: "Selimaj · Entrepreneur & Builder", template: "%s · Selimaj" }, title: { default: "Selimaj · Entrepreneur & Builder", template: "%s · Selimaj" },
description: "Selimaj, entrepreneur and builder. Founder of Orus. I build the layer underneath.", description: "Selimaj, entrepreneur and builder. I build the layer underneath.",
metadataBase: new URL("https://selimaj.dev"), metadataBase: new URL("https://selimaj.dev"),
}; };
+2 -2
View File
@@ -4,14 +4,14 @@ import { projects } from "@/content/projects";
export const metadata: Metadata = { export const metadata: Metadata = {
title: "Projects", title: "Projects",
description: "Everything built and conceived under Orus, from Saturn and Enclave to cyberdecks and RC planes.", description: "Everything I’ve built and conceived, from Enclave and Osias to cyberdecks and RC planes.",
}; };
export default function ProjectsPage() { export default function ProjectsPage() {
return ( return (
<main className="pt-[68px] md:pt-[85px]"> <main className="pt-[68px] md:pt-[85px]">
<div className="mx-auto max-w-[1440px] px-4 pt-10 pb-20 md:px-16 md:pt-16 md:pb-24"> <div className="mx-auto max-w-[1440px] px-4 pt-10 pb-20 md:px-16 md:pt-16 md:pb-24">
<div className="label tracking-[0.3em]">Node index · Root Orus · {projects.length} nodes</div> <div className="label tracking-[0.3em]">Node index · Root selimaj.dev · {projects.length} nodes</div>
<h1 className="mt-3 font-display text-[52px] leading-[0.95] font-semibold uppercase md:text-7xl">Projects</h1> <h1 className="mt-3 font-display text-[52px] leading-[0.95] font-semibold uppercase md:text-7xl">Projects</h1>
<p className="mt-[18px] max-w-[560px] text-[13px] leading-[1.7] text-ink-2"> <p className="mt-[18px] max-w-[560px] text-[13px] leading-[1.7] text-ink-2">
Most of what I&apos;ve built and conceived. Concepts are labelled as concepts. Most of what I&apos;ve built and conceived. Concepts are labelled as concepts.
+7
View File
@@ -1,4 +1,11 @@
"use client";
import { usePathname } from "next/navigation";
// Re-mounts on every navigation, so each page resolves in from wireframe. // Re-mounts on every navigation, so each page resolves in from wireframe.
// Home is skipped: the hero runs its own sequence and the terrain stays still.
export default function Template({ children }: { children: React.ReactNode }) { export default function Template({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
if (pathname === "/") return <>{children}</>;
return <div className="page-resolve seq">{children}</div>; return <div className="page-resolve seq">{children}</div>;
} }
+4 -1
View File
@@ -27,7 +27,10 @@ export function BootSequence() {
sessionStorage.setItem("boot", "1"); sessionStorage.setItem("boot", "1");
} catch {} } catch {}
setLeaving(true); setLeaving(true);
outTimer = window.setTimeout(() => root.removeAttribute("data-boot"), 250); outTimer = window.setTimeout(() => {
root.removeAttribute("data-boot");
window.dispatchEvent(new Event("boot:done"));
}, 250);
}; };
const timer = window.setTimeout(finish, DURATION); const timer = window.setTimeout(finish, DURATION);
window.addEventListener("keydown", finish); window.addEventListener("keydown", finish);
+35 -23
View File
@@ -2,11 +2,9 @@
import Link from "next/link"; import Link from "next/link";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { featured, projectById, projects } from "@/content/projects"; import { brandOf, featured, featuredRoot, projectById, projects, root } from "@/content/projects";
import { Segment } from "./Segment"; import { Segment } from "./Segment";
const ROOT = featured[0];
export function FeaturedNodes() { export function FeaturedNodes() {
const [active, setActive] = useState<string | null>(null); const [active, setActive] = useState<string | null>(null);
const nodes = featured.map((f) => ({ ...f, project: projectById(f.id)! })); const nodes = featured.map((f) => ({ ...f, project: projectById(f.id)! }));
@@ -30,11 +28,11 @@ export function FeaturedNodes() {
> >
{size && ( {size && (
<div aria-hidden="true" className="hidden md:block"> <div aria-hidden="true" className="hidden md:block">
{nodes.slice(1).map((n) => ( {nodes.map((n) => (
<Segment <Segment
key={`${n.id}-${active === n.id}`} key={`${n.id}-${active === n.id}`}
x1={(ROOT.x / 100) * size.w} x1={(featuredRoot.x / 100) * size.w}
y1={(ROOT.y / 100) * size.h} y1={(featuredRoot.y / 100) * size.h}
x2={(n.x / 100) * size.w} x2={(n.x / 100) * size.w}
y2={(n.y / 100) * size.h} y2={(n.y / 100) * size.h}
on={active === n.id} on={active === n.id}
@@ -45,9 +43,29 @@ export function FeaturedNodes() {
</div> </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) => { {nodes.map((n) => {
const on = active === n.id; const on = active === n.id;
const root = n.project.root; const brand = brandOf(n.project);
const meta = n.id === "orus" ? "BRAND" : brand ? `${n.project.status} · ${brand.toUpperCase()}` : n.project.status;
return ( return (
<Link <Link
key={n.id} key={n.id}
@@ -60,33 +78,27 @@ export function FeaturedNodes() {
> >
<span <span
aria-hidden="true" aria-hidden="true"
className={`absolute box-border border ${ className={`absolute top-4 left-2 box-border size-3 border md:-top-1.5 md:-left-1.5 ${n.id === "orus" ? "rotate-45" : ""} ${
root on ? "border-accent bg-accent shadow-[0_0_14px_var(--color-accent)]" : "border-ink/60 bg-night"
? "top-2.5 left-0 size-7 rounded-full md:-top-3.5 md:-left-3.5" }`}
: "top-4 left-2 size-3 md:-top-1.5 md:-left-1.5" />
} ${on ? "border-accent shadow-[0_0_14px_var(--color-accent)]" : "border-ink/60"} ${on && !root ? "bg-accent" : "bg-night"}`}
>
{root && <span className={`absolute top-[9px] left-[9px] size-2 rounded-full ${on ? "bg-accent" : "bg-ink"}`} />}
</span>
<span <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:ml-0 ${ 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] ${
root ? "md:right-6 md:w-max md:text-right" : "md:left-6 md:w-[280px]"
} ${
on ? "[--bc:var(--color-accent)]" : "[--bc:transparent]" on ? "[--bc:var(--color-accent)]" : "[--bc:transparent]"
}`} }`}
> >
<span className={`flex items-baseline gap-3 ${root ? "md:flex-row-reverse" : ""}`}> <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="font-display text-lg font-semibold tracking-[0.08em] uppercase">{n.project.name}</span>
<span className="text-[9px] tracking-[0.22em] text-mute">{root ? "ROOT" : n.project.status}</span> <span className="text-[9px] tracking-[0.22em] text-mute">{meta}</span>
</span> </span>
<span className={`mt-1.5 block text-[11px] leading-[1.55] text-ink-2 ${on && !root ? "" : "md:sr-only"}`}>{n.project.desc}</span> <span className={`mt-1.5 block text-[11px] leading-[1.55] text-ink-2 ${on ? "" : "md:sr-only"}`}>{n.project.desc}</span>
</span> </span>
</Link> </Link>
); );
})} })}
<div aria-hidden="true" className="absolute right-4 bottom-3 hidden text-[9px] tracking-[0.2em] text-mute/70 md:block"> <div aria-hidden="true" className="absolute right-4 bottom-3 hidden text-[9px] tracking-[0.2em] text-mute/70 uppercase md:block">
ROOT ORUS · {featured.length} OF {projects.length} NODES Root {root.sub} · {featured.length} of {projects.length} nodes
</div> </div>
</div> </div>
); );
+57 -48
View File
@@ -1,39 +1,47 @@
import Link from "next/link"; import Link from "next/link";
import { dossierFields, lock, orus, profile, type DossierField } from "@/content/profile"; import { dossierFields, lock, profile, type DossierField } from "@/content/profile";
import { Bust } from "./Bust"; import { Bust } from "./Bust";
import { Scramble } from "./Scramble";
import { LocalTime, SessionStamp } from "./Telemetry"; import { LocalTime, SessionStamp } from "./Telemetry";
import { TerrainLayer } from "./TerrainLayer"; import { TerrainLayer } from "./TerrainLayer";
import { LockReticle, TiranaMap } from "./TiranaMap"; import { LockReticle, TiranaMap } from "./TiranaMap";
const d = (s: number) => ({ "--d": `${s}s` }) as React.CSSProperties; const d = (s: number) => ({ "--d": `${s}s` }) as React.CSSProperties;
/** Dossier values decode one after another, starting once the card has built. */
const DOSSIER_START = 1.35;
const DOSSIER_STAGGER = 0.14;
function DossierList({ fields, className = "" }: { fields: DossierField[]; className?: string }) { function DossierList({ fields, className = "" }: { fields: DossierField[]; className?: string }) {
return ( return (
<dl className={`grid grid-cols-[124px_minmax(0,1fr)] gap-y-2.5 border-t border-white/8 pt-[18px] text-xs leading-[1.45] xl:grid-cols-[116px_minmax(0,1fr)] ${className}`}> <dl className={`grid grid-cols-[124px_minmax(0,1fr)] gap-y-2.5 border-t border-white/8 pt-[18px] text-xs leading-[1.45] xl:grid-cols-[116px_minmax(0,1fr)] ${className}`}>
{fields.map((f) => ( {fields.map((f, i) => {
<div key={f.label} className="contents"> const delay = DOSSIER_START + i * DOSSIER_STAGGER;
<dt className="pt-[3px] text-[9px] uppercase tracking-[0.22em] text-mute">{f.label}</dt> return (
<dd className="flex flex-wrap gap-x-3.5"> <div key={f.label} className="contents">
{f.links <dt className="pt-[3px] text-[9px] uppercase tracking-[0.22em] text-mute">{f.label}</dt>
? f.links.map((l) => ( <dd className="flex flex-wrap gap-x-3.5">
<a key={l.href} href={l.href} className="border-b border-white/25 hover:border-accent"> {f.links
{l.label} ? f.links.map((l, j) => (
</a> <a key={l.href} href={l.href} className="border-b border-white/25 hover:border-accent">
)) <Scramble text={l.label} delay={delay + j * 0.2} step={30} />
: f.value} </a>
</dd> ))
</div> : <Scramble text={f.value} delay={delay} step={30} />}
))} </dd>
</div>
);
})}
</dl> </dl>
); );
} }
export function Hero() { export function Hero() {
return ( return (
<section aria-labelledby="hero-title" className="relative overflow-hidden border-b border-line"> <section aria-labelledby="hero-title" className="relative overflow-hidden border-b border-line xl:flex xl:min-h-dvh xl:items-center">
<TerrainLayer /> <TerrainLayer />
<div className="relative z-10 mx-auto max-w-[1440px] pt-[68px] md:pt-[84px] xl:h-[864px]"> <div className="relative z-10 mx-auto w-full max-w-[1440px] pt-[68px] md:pt-[84px] xl:h-[864px]">
{/* Lock assembly: a strip on small screens, the full stage on desktop. */} {/* Lock assembly: a strip on small screens, the full stage on desktop. */}
<div <div
aria-hidden="true" aria-hidden="true"
@@ -44,15 +52,16 @@ export function Hero() {
className="reticle seq absolute top-[calc(50%-60px)] left-[calc(var(--lx)-60px)] max-xl:scale-75 xl:top-[174px]" className="reticle seq absolute top-[calc(50%-60px)] left-[calc(var(--lx)-60px)] max-xl:scale-75 xl:top-[174px]"
style={d(0.25)} style={d(0.25)}
/> />
<div <div className="absolute bottom-3 left-4 text-[9px] leading-[1.7] tracking-[0.14em] text-mute xl:bottom-auto xl:left-[calc(var(--lx)-60px)] xl:top-[306px] xl:text-[10px]">
className="resolve seq absolute bottom-3 left-4 text-[9px] leading-[1.7] tracking-[0.14em] text-mute xl:bottom-auto xl:left-[calc(var(--lx)-60px)] xl:top-[306px] xl:text-[10px]" <div className="text-accent">
style={d(0.85)} <Scramble text="LOCK ACQUIRED" delay={0.85} step={35} nowrap />
> </div>
<div className="text-accent">LOCK ACQUIRED</div>
<div> <div>
{lock.lat} · {lock.lon} <Scramble text={`${lock.lat} · ${lock.lon}`} delay={0.95} step={30} nowrap />
</div>
<div className="uppercase">
<Scramble text={lock.place} delay={1.05} step={30} nowrap />
</div> </div>
<div className="uppercase">{lock.place}</div>
</div> </div>
<div <div
className="trace seq absolute top-[233px] right-[510px] left-[calc(var(--lx)+66px)] hidden h-px bg-accent/55 xl:block" className="trace seq absolute top-[233px] right-[510px] left-[calc(var(--lx)+66px)] hidden h-px bg-accent/55 xl:block"
@@ -66,20 +75,19 @@ export function Hero() {
<div className="flex flex-col gap-9 px-4 pt-6 pb-16 md:px-16 xl:block xl:p-0"> <div className="flex flex-col gap-9 px-4 pt-6 pb-16 md:px-16 xl:block xl:p-0">
{/* Headline */} {/* Headline */}
<div className="xl:absolute xl:top-[364px] xl:left-16 xl:w-[620px]"> <div className="xl:absolute xl:top-[364px] xl:left-16 xl:w-[620px]">
<p className="resolve seq mb-3 text-[9px] tracking-[0.28em] text-mute xl:mb-5 xl:text-[10px] xl:tracking-[0.3em]" style={d(1.1)}> <p className="mb-3 text-[9px] tracking-[0.28em] text-mute xl:mb-5 xl:text-[10px] xl:tracking-[0.3em]">
TARGET RESOLVED · SUBJECT 0001 <Scramble text="TARGET RESOLVED · SUBJECT 0001" delay={0.2} step={15} />
</p> </p>
<h1 <h1
id="hero-title" id="hero-title"
className="resolve seq font-display text-[46px] leading-[0.96] font-semibold uppercase sm:text-[64px] xl:text-[84px] xl:leading-[0.94] xl:tracking-[0.01em]" className="font-display text-[46px] leading-[0.96] font-semibold uppercase sm:text-[64px] xl:text-[84px] xl:leading-[0.94] xl:tracking-[0.01em]"
style={d(1.2)}
> >
Entrepreneur <Scramble text="ENTREPRENEUR" delay={0.3} step={35} nowrap />
<br /> <br />
&amp; Builder <Scramble text="& BUILDER" delay={0.5} step={35} nowrap />
</h1> </h1>
<p className="resolve seq mt-4 text-[15px] leading-normal text-ink-2 xl:mt-6 xl:text-[17px]" style={d(1.3)}> <p className="mt-4 text-[15px] leading-normal text-ink-2 xl:mt-6 xl:text-[17px]">
{profile.stance} <Scramble text={profile.stance} delay={0.7} step={15} />
</p> </p>
<div className="resolve seq mt-7 flex flex-col gap-2.5 sm:flex-row sm:flex-wrap sm:gap-3 xl:mt-10" style={d(1.4)}> <div className="resolve seq mt-7 flex flex-col gap-2.5 sm:flex-row sm:flex-wrap sm:gap-3 xl:mt-10" style={d(1.4)}>
<Link <Link
@@ -95,10 +103,6 @@ export function Hero() {
OPEN DOSSIER <span aria-hidden="true">↗</span> OPEN DOSSIER <span aria-hidden="true">↗</span>
</Link> </Link>
</div> </div>
<div className="resolve seq mt-10 hidden max-w-[580px] items-start gap-3.5 xl:flex" style={d(1.5)}>
<span className="pt-[3px] text-[9px] whitespace-nowrap tracking-[0.24em] text-mute">ROOT ─ ORUS</span>
<span className="text-xs leading-[1.6] text-mute">{orus.oneLiner}</span>
</div>
<DossierList fields={dossierFields} className="mt-9 xl:hidden" /> <DossierList fields={dossierFields} className="mt-9 xl:hidden" />
</div> </div>
@@ -122,7 +126,7 @@ export function Hero() {
<div className="min-w-0 xl:pt-1"> <div className="min-w-0 xl:pt-1">
<div className="text-[9px] tracking-[0.24em] text-mute">ALIAS</div> <div className="text-[9px] tracking-[0.24em] text-mute">ALIAS</div>
<h2 id="profile-name" className="mt-1.5 font-display text-[28px] leading-none font-semibold tracking-[0.06em] uppercase xl:text-4xl"> <h2 id="profile-name" className="mt-1.5 font-display text-[28px] leading-none font-semibold tracking-[0.06em] uppercase xl:text-4xl">
{profile.displayName} <Scramble text={profile.displayName.toUpperCase()} delay={1.2} step={90} nowrap />
</h2> </h2>
<div className="mt-2.5 text-[9px] leading-[1.9] tracking-[0.16em] text-mute xl:mt-[22px]"> <div className="mt-2.5 text-[9px] leading-[1.9] tracking-[0.16em] text-mute xl:mt-[22px]">
<div className="max-xl:hidden">MESH 2.5D · RESOLVED</div> <div className="max-xl:hidden">MESH 2.5D · RESOLVED</div>
@@ -135,18 +139,23 @@ export function Hero() {
</article> </article>
</div> </div>
{/* Corner telemetry */} </div>
<div aria-hidden="true" className="appear seq absolute bottom-[22px] left-16 hidden text-[9px] leading-[1.8] tracking-[0.16em] text-mute/70 xl:block" style={d(0.2)}>
<SessionStamp /> <div className="pointer-events-none absolute inset-x-0 bottom-0 z-10 hidden xl:block">
</div> <div className="relative mx-auto h-20 max-w-[1440px]">
<div aria-hidden="true" className="absolute bottom-[22px] left-1/2 hidden -translate-x-1/2 text-[9px] tracking-[0.3em] text-mute xl:block"> {/* Corner telemetry, pinned to the bottom of the screen-tall hero */}
SCROLL ▾ NODES <div aria-hidden="true" className="appear seq absolute bottom-[22px] left-16 hidden text-[9px] leading-[1.8] tracking-[0.16em] text-mute/70 xl:block" style={d(0.2)}>
</div> <SessionStamp />
<div aria-hidden="true" className="appear seq absolute right-16 bottom-[22px] hidden text-right text-[9px] leading-[1.8] tracking-[0.16em] xl:block" style={d(0.2)}> </div>
<div className="text-accent"> <div aria-hidden="true" className="absolute bottom-[22px] left-1/2 hidden -translate-x-1/2 text-[9px] tracking-[0.3em] text-mute xl:block">
<LocalTime /> SCROLL ▾ NODES
</div>
<div aria-hidden="true" className="appear seq absolute right-16 bottom-[22px] hidden text-right text-[9px] leading-[1.8] tracking-[0.16em] xl:block" style={d(0.2)}>
<div className="text-accent">
<LocalTime />
</div>
<div className="text-mute/70">VISITOR · UNRESOLVED</div>
</div> </div>
<div className="text-mute/70">VISITOR · UNRESOLVED</div>
</div> </div>
</div> </div>
</section> </section>
+9
View File
@@ -0,0 +1,9 @@
/** selimaj.dev mark: a lock bracket around a point. */
export function Mark({ size = 24, className }: { size?: number; className?: string }) {
return (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}>
<path d="M2.5,8 V2.5 H8 M16,2.5 H21.5 V8 M21.5,16 V21.5 H16 M8,21.5 H2.5 V16" stroke="currentColor" strokeWidth="1.3" />
<rect x="9.5" y="9.5" width="5" height="5" fill="currentColor" />
</svg>
);
}
-8
View File
@@ -1,8 +0,0 @@
export function OrusMark({ size = 24, className }: { size?: number; className?: string }) {
return (
<svg width={size} height={size} viewBox="0 0 24 24" fill="none" aria-hidden="true" className={className}>
<circle cx="12" cy="12" r="9.5" stroke="currentColor" strokeWidth="1.2" strokeDasharray="50 10" />
<circle cx="12" cy="12" r="2.6" fill="currentColor" />
</svg>
);
}
+94 -64
View File
@@ -1,37 +1,40 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { groups, projects, type GroupId, type Project } from "@/content/projects"; import { brandOf, groups, projects, root, type GroupId, type Project } from "@/content/projects";
import { Segment } from "./Segment"; import { Segment } from "./Segment";
import { StatusTag } from "./StatusTag"; import { StatusTag } from "./StatusTag";
const W = 1200; const W = 1200;
const H = 720; const H = 720;
const root = projects.find((p) => p.root)!;
const groupIds = Object.keys(groups) as GroupId[]; const groupIds = Object.keys(groups) as GroupId[];
const isHub = (p: Project) => groups[p.group].node === p.id;
function Marker({ p, on }: { p: Project; on: boolean }) { function Marker({ p, on }: { p: Project; on: boolean }) {
const size = p.root ? 30 : 14; const hub = isHub(p);
const size = hub ? 22 : 14;
const rest = const rest =
p.status === "CONCEPT" ? "border border-dashed border-ink/60" : p.status === "ARCHIVED" ? "border border-ink/30" : "border border-ink/65"; p.status === "CONCEPT" ? "border border-dashed border-ink/60" : p.status === "ARCHIVED" ? "border border-ink/30" : "border border-ink/65";
return ( return (
<span <span
aria-hidden="true" aria-hidden="true"
className={`relative box-border flex-none ${p.root ? "rounded-full" : ""} ${ className={`relative box-border flex-none ${hub ? "rotate-45" : ""} ${
on ? (p.root ? "border border-accent shadow-[0_0_14px_var(--color-accent)]" : "bg-accent shadow-[0_0_12px_var(--color-accent)]") : `${rest} bg-surface` on ? "bg-accent shadow-[0_0_12px_var(--color-accent)]" : `${rest} bg-surface`
}`} }`}
style={{ width: size, height: size }} style={{ width: size, height: size }}
> />
{p.root && <span className={`absolute top-[10px] left-[10px] size-2 rounded-full ${on ? "bg-accent" : "bg-ink"}`} />}
</span>
); );
} }
function ProjectCard({ p }: { p: Project }) { function ProjectCard({ p }: { p: Project }) {
const brand = brandOf(p);
const body = ( const body = (
<> <>
<span className="flex items-center justify-between"> <span className="flex items-center justify-between gap-3">
<StatusTag status={p.status} /> <span className="flex items-center gap-3">
<StatusTag status={p.status} />
{brand && <span className="text-[9px] tracking-[0.22em] text-mute uppercase">{brand}</span>}
</span>
<span aria-hidden="true" className="text-mute transition-colors group-hover:text-accent group-focus-visible:text-accent"> <span aria-hidden="true" className="text-mute transition-colors group-hover:text-accent group-focus-visible:text-accent">
{p.href ? "↗" : "→"} {p.href ? "↗" : "→"}
</span> </span>
@@ -62,7 +65,7 @@ function ProjectCard({ p }: { p: Project }) {
export function ProjectIndex() { export function ProjectIndex() {
const [view, setView] = useState<"map" | "list">("map"); const [view, setView] = useState<"map" | "list">("map");
const [active, setActive] = useState<string>("orus"); const [active, setActive] = useState<string | null>(null);
// Deep links (/projects/#enclave) select that node; small screens start on the list. // Deep links (/projects/#enclave) select that node; small screens start on the list.
useEffect(() => { useEffect(() => {
@@ -75,7 +78,7 @@ export function ProjectIndex() {
} }
}, []); }, []);
const sel = projects.find((p) => p.id === active) ?? root; const sel = projects.find((p) => p.id === active) ?? null;
const count = (s: Project["status"]) => String(projects.filter((p) => p.status === s).length).padStart(2, "0"); const count = (s: Project["status"]) => String(projects.filter((p) => p.status === s).length).padStart(2, "0");
return ( return (
@@ -112,10 +115,10 @@ export function ProjectIndex() {
<div className="mt-6 overflow-x-auto border border-white/6"> <div className="mt-6 overflow-x-auto border border-white/6">
<div className="grid-bg relative mx-auto bg-surface [background-size:40px_40px]" style={{ width: W, height: H }}> <div className="grid-bg relative mx-auto bg-surface [background-size:40px_40px]" style={{ width: W, height: H }}>
{groupIds.map((k) => ( {groupIds.map((k) => (
<Segment key={k} x1={root.map.x} y1={root.map.y} x2={groups[k].x} y2={groups[k].y} on={!sel.root && sel.group === k} strong /> <Segment key={k} x1={root.map.x} y1={root.map.y} x2={groups[k].x} y2={groups[k].y} on={!!sel && sel.group === k} strong />
))} ))}
{projects {projects
.filter((p) => !p.root) .filter((p) => !isHub(p))
.map((p) => ( .map((p) => (
<Segment <Segment
key={`${p.id}-${active === p.id}`} key={`${p.id}-${active === p.id}`}
@@ -127,25 +130,41 @@ export function ProjectIndex() {
dashed={p.status === "CONCEPT"} dashed={p.status === "CONCEPT"}
/> />
))} ))}
{groupIds.map((k) => {
const g = groups[k]; {/* Plain hubs (groups without a project of their own). */}
return ( {groupIds
<div key={k} aria-hidden="true"> .filter((k) => !groups[k].node)
<div .map((k) => {
className="absolute text-[9px] whitespace-nowrap tracking-[0.24em] text-mute uppercase" const g = groups[k];
style={{ top: g.y - 7, ...(g.side === "l" ? { right: W - g.x + 14 } : { left: g.x + 14 }) }} return (
> <div key={k} aria-hidden="true">
{g.label} <div
className="absolute text-[9px] whitespace-nowrap tracking-[0.24em] text-mute uppercase"
style={{ top: g.y - 7, ...(g.side === "l" ? { right: W - g.x + 14 } : { left: g.x + 14 }) }}
>
{g.label}
</div>
<div className="absolute box-border size-1.5 rotate-45 border border-ink/40" style={{ left: g.x - 3, top: g.y - 3 }} />
</div> </div>
<div className="absolute box-border size-1.5 rotate-45 border border-ink/40" style={{ left: g.x - 3, top: g.y - 3 }} /> );
</div> })}
);
})} {/* Root: the site itself. */}
<div className="absolute flex items-center gap-3 whitespace-nowrap" style={{ left: root.map.x - 15, top: root.map.y - 15 }}>
<span aria-hidden="true" className="relative box-border size-[30px] flex-none rounded-full border border-ink/70 bg-surface">
<span className="absolute top-[10px] left-[10px] size-2 rounded-full bg-ink" />
</span>
<span className="flex flex-col gap-[3px]">
<span className="font-display text-xl font-semibold tracking-[0.08em] uppercase">{root.name}</span>
<span className="text-[8px] tracking-[0.22em] text-mute uppercase">Root · {root.sub}</span>
</span>
</div>
{projects.map((p) => { {projects.map((p) => {
const on = active === p.id; const on = active === p.id;
const g = groups[p.group]; const g = groups[p.group];
const labelLeft = !p.root && p.map.x < g.x; const labelLeft = (p.label ?? (p.map.x < g.x ? "l" : "r")) === "l";
const m = p.root ? 30 : 14; const m = isHub(p) ? 22 : 14;
const pad = 8; const pad = 8;
return ( return (
<button <button
@@ -165,15 +184,16 @@ export function ProjectIndex() {
}} }}
> >
<Marker p={p} on={on} /> <Marker p={p} on={on} />
<span className="flex flex-col gap-[3px]"> {/* A hub's label sits a little high so its root line passes underneath. */}
<span className={`flex flex-col gap-[3px] ${isHub(p) ? "-translate-y-4" : ""}`}>
<span <span
className={`font-display font-semibold tracking-[0.08em] uppercase ${p.root ? "text-xl" : "text-[13px]"} ${ className={`font-display font-semibold tracking-[0.08em] uppercase ${isHub(p) ? "text-base" : "text-[13px]"} ${
p.status === "ARCHIVED" && !on ? "text-ink/70" : "text-ink" p.status === "ARCHIVED" && !on ? "text-ink/70" : "text-ink"
}`} }`}
> >
{p.name} {p.name}
</span> </span>
<span className="text-[8px] tracking-[0.22em] text-mute">{p.root ? "ROOT" : p.status}</span> <span className="text-[8px] tracking-[0.22em] text-mute">{isHub(p) ? "BRAND" : p.status}</span>
</span> </span>
</button> </button>
); );
@@ -181,8 +201,8 @@ export function ProjectIndex() {
<div aria-hidden="true" className="absolute bottom-3 left-4 text-[9px] tracking-[0.2em] text-mute/70"> <div aria-hidden="true" className="absolute bottom-3 left-4 text-[9px] tracking-[0.2em] text-mute/70">
GRID 40 · ROOT {root.map.x},{root.map.y} GRID 40 · ROOT {root.map.x},{root.map.y}
</div> </div>
<div aria-hidden="true" className="absolute right-4 bottom-3 text-[9px] tracking-[0.2em] text-accent uppercase"> <div aria-hidden="true" className="absolute right-4 bottom-3 text-[9px] tracking-[0.2em] uppercase">
TRACKING ▸ {sel.name} {sel ? <span className="text-accent">TRACKING ▸ {sel.name}</span> : <span className="text-mute/70">AWAITING TARGET</span>}
</div> </div>
</div> </div>
</div> </div>
@@ -190,38 +210,48 @@ export function ProjectIndex() {
<section <section
aria-live="polite" aria-live="polite"
aria-label="Selected node" aria-label="Selected node"
className="bracket mt-4 grid gap-6 border border-white/6 bg-surface px-7 py-6 [--bc:var(--color-accent)] md:grid-cols-[minmax(0,1fr)_auto] md:items-center md:gap-x-12" className={`bracket mt-4 grid min-h-[176px] gap-6 border border-white/6 bg-surface px-7 py-6 md:grid-cols-[minmax(0,1fr)_auto] md:items-center md:gap-x-12 ${
sel ? "[--bc:var(--color-accent)]" : "[--bc:rgb(242_244_245/0.3)]"
}`}
> >
<div> {sel ? (
<div className="flex flex-wrap items-center gap-3.5"> <>
<StatusTag status={sel.status} /> <div>
<span className="text-[9px] tracking-[0.24em] text-mute uppercase">{sel.root ? "Root" : groups[sel.group].label}</span> <div className="flex flex-wrap items-center gap-3.5">
</div> <StatusTag status={sel.status} />
<h2 className="mt-3 font-display text-3xl font-semibold tracking-[0.04em] uppercase">{sel.name}</h2> <span className="text-[9px] tracking-[0.24em] text-mute uppercase">
<p className="mt-2 max-w-[720px] text-[13px] leading-[1.7] text-ink-2">{sel.desc}</p> {isHub(sel) ? "Brand" : (brandOf(sel) ?? groups[sel.group].label)}
<div className="mt-3.5 flex flex-wrap gap-2"> </span>
{sel.tags.map((t) => ( </div>
<span key={t} className="bg-surface-2 px-2 py-1 text-[10px] tracking-[0.1em] text-mute"> <h2 className="mt-3 font-display text-3xl font-semibold tracking-[0.04em] uppercase">{sel.name}</h2>
{t} <p className="mt-2 max-w-[720px] text-[13px] leading-[1.7] text-ink-2">{sel.desc}</p>
</span> <div className="mt-3.5 flex flex-wrap gap-2">
))} {sel.tags.map((t) => (
</div> <span key={t} className="bg-surface-2 px-2 py-1 text-[10px] tracking-[0.1em] text-mute">
</div> {t}
{sel.href ? ( </span>
<a href={sel.href} className="inline-flex min-h-12 items-center gap-3 justify-self-start border border-white/14 px-5 text-[11px] tracking-[0.2em] hover:border-accent"> ))}
VISIT <span aria-hidden="true">↗</span> </div>
</a> </div>
{sel.href ? (
<a href={sel.href} className="inline-flex min-h-12 items-center gap-3 justify-self-start border border-white/14 px-5 text-[11px] tracking-[0.2em] hover:border-accent">
VISIT <span aria-hidden="true">↗</span>
</a>
) : (
<button
type="button"
onClick={() => {
setView("list");
requestAnimationFrame(() => document.getElementById(sel.id)?.scrollIntoView({ block: "center" }));
}}
className="inline-flex min-h-12 items-center gap-3 justify-self-start border border-white/14 px-5 text-[11px] tracking-[0.2em] hover:border-accent hover:text-accent"
>
VIEW IN LIST <span aria-hidden="true">→</span>
</button>
)}
</>
) : ( ) : (
<button <p className="text-[11px] tracking-[0.2em] text-mute">HOVER OR SELECT A NODE TO TRACK IT.</p>
type="button"
onClick={() => {
setView("list");
requestAnimationFrame(() => document.getElementById(sel.id)?.scrollIntoView({ block: "center" }));
}}
className="inline-flex min-h-12 items-center gap-3 justify-self-start border border-white/14 px-5 text-[11px] tracking-[0.2em] hover:border-accent hover:text-accent"
>
VIEW IN LIST <span aria-hidden="true">→</span>
</button>
)} )}
</section> </section>
</div> </div>
+110
View File
@@ -0,0 +1,110 @@
"use client";
import { useEffect, useState } from "react";
const UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#%&@$*+=<>/";
const LOWER = "abcdefghijklmnopqrstuvwxyz0123456789#%&@$*+=<>/";
const DIGIT = "0123456789#%&@$*";
/** Characters that stay put while everything around them is brute-forced. */
const FIXED = /[\s.,·()\-/]/;
const FRAME_MS = 45;
function noise(ch: string) {
if (FIXED.test(ch)) return ch;
const pool = /[0-9]/.test(ch) ? DIGIT : ch === ch.toLowerCase() && ch !== ch.toUpperCase() ? LOWER : UPPER;
return pool[Math.floor(Math.random() * pool.length)];
}
type Frame = { done: string; head: string; rest: string };
/**
* Text that brute-forces itself: every character cycles through random
* glyphs, then the real value locks in from left to right.
*
* Server and no-JS output is the plain text. With reduced motion it never
* animates. While the boot screen is up it waits for `boot:done`.
*/
export function Scramble({
text,
delay = 0,
step = 45,
nowrap = false,
}: {
text: string;
/** Seconds before characters start locking in (noise runs from the start). */
delay?: number;
/** Milliseconds per character as the value locks in. */
step?: number;
nowrap?: boolean;
}) {
const [frame, setFrame] = useState<Frame | null>(null);
useEffect(() => {
if (matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const chars = Array.from(text);
let raf = 0;
let start = 0;
let last = -Infinity;
const build = (locked: number): Frame => ({
done: chars.slice(0, locked).join(""),
head: locked < chars.length ? chars[locked] : "",
rest: chars
.slice(locked + 1)
.map(noise)
.join(""),
});
const loop = (t: number) => {
if (!start) start = t;
const elapsed = t - start - delay * 1000;
const locked = elapsed < 0 ? 0 : Math.floor(elapsed / step);
if (locked >= chars.length) {
setFrame(null);
return;
}
if (t - last >= FRAME_MS) {
last = t;
// Before the reveal starts, the first character is noise too.
setFrame(elapsed < 0 ? { done: "", head: noise(chars[0]), rest: build(0).rest } : build(locked));
}
raf = requestAnimationFrame(loop);
};
const begin = () => {
raf = requestAnimationFrame(loop);
};
if (document.documentElement.hasAttribute("data-boot")) {
// Hide the real value under the boot screen, then start when it lifts.
raf = requestAnimationFrame(() => setFrame({ done: "", head: "", rest: chars.map(noise).join("") }));
window.addEventListener("boot:done", begin, { once: true });
} else {
begin();
}
return () => {
cancelAnimationFrame(raf);
window.removeEventListener("boot:done", begin);
};
}, [text, delay, step]);
if (!frame) return <>{text}</>;
return (
<span className="relative inline-block max-w-full align-top">
<span className="sr-only">{text}</span>
{/* Invisible copy keeps the box exactly the size of the real text. */}
<span aria-hidden="true" className={`invisible ${nowrap ? "whitespace-nowrap" : ""}`}>
{text}
</span>
<span aria-hidden="true" className={`absolute inset-0 ${nowrap ? "whitespace-nowrap" : ""}`}>
{frame.done}
<span className="text-accent">{frame.head}</span>
<span className="opacity-50">{frame.rest}</span>
</span>
</span>
);
}
+1 -1
View File
@@ -4,7 +4,7 @@ export function SiteFooter() {
return ( return (
<footer className="border-t border-line"> <footer className="border-t border-line">
<div className="mx-auto flex max-w-[1440px] flex-wrap justify-between gap-6 px-4 pt-7 pb-10 text-[10px] tracking-[0.18em] text-mute md:px-16"> <div className="mx-auto flex max-w-[1440px] flex-wrap justify-between gap-6 px-4 pt-7 pb-10 text-[10px] tracking-[0.18em] text-mute md:px-16">
<span>{profile.displayName.toUpperCase()} · BUILT UNDER ORUS</span> <span>SELIMAJ.DEV · TIRANA, AL</span>
<span className="flex gap-6"> <span className="flex gap-6">
<a href={profile.github} className="hover:text-accent">GITHUB</a> <a href={profile.github} className="hover:text-accent">GITHUB</a>
<a href={profile.git} className="hover:text-accent">GIT.SELIMAJ.DEV</a> <a href={profile.git} className="hover:text-accent">GIT.SELIMAJ.DEV</a>
+6 -5
View File
@@ -3,7 +3,7 @@
import Link from "next/link"; import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { useState } from "react"; import { useState } from "react";
import { OrusMark } from "./OrusMark"; import { Mark } from "./Mark";
import { SessionClock } from "./Telemetry"; import { SessionClock } from "./Telemetry";
const links = [ const links = [
@@ -25,10 +25,11 @@ export function SiteHeader() {
className={`absolute inset-x-0 top-0 z-30 ${isHome ? "" : "border-b border-line"}`} className={`absolute inset-x-0 top-0 z-30 ${isHome ? "" : "border-b border-line"}`}
> >
<div className="mx-auto flex max-w-[1440px] items-center justify-between gap-6 px-4 py-3 md:px-16 md:py-[22px]"> <div className="mx-auto flex max-w-[1440px] items-center justify-between gap-6 px-4 py-3 md:px-16 md:py-[22px]">
<Link href="/" className="flex min-h-11 items-center gap-3" aria-label="Orus · selimaj.dev home"> <Link href="/" className="flex min-h-11 items-center gap-3" aria-label="selimaj.dev home">
<OrusMark /> <Mark />
<span className="font-display text-sm font-semibold tracking-[0.24em]">ORUS</span> <span className="font-display text-sm font-semibold tracking-[0.24em]">
<span className="hidden text-[10px] tracking-[0.18em] text-mute sm:inline">/ SELIMAJ.DEV</span> SELIMAJ<span className="text-mute">.DEV</span>
</span>
</Link> </Link>
<button <button
+2 -2
View File
@@ -1,6 +1,6 @@
/** /**
* Hero background, bottom to top: terrain contours (real Dajt elevation data), * Hero background, bottom to top: terrain contours (real Dajt elevation data),
* grid, scanlines, vignette. Purely decorative. * grid, scanlines, vignette. Purely decorative and deliberately static.
*/ */
export function TerrainLayer({ className = "" }: { className?: string }) { export function TerrainLayer({ className = "" }: { className?: string }) {
return ( return (
@@ -9,7 +9,7 @@ export function TerrainLayer({ className = "" }: { className?: string }) {
<img <img
src="/terrain.svg" src="/terrain.svg"
alt="" alt=""
className="drift absolute -inset-10 h-[calc(100%+80px)] w-[calc(100%+80px)] max-w-none object-cover opacity-20 max-xl:object-[60%_50%]" className="absolute -inset-10 h-[calc(100%+80px)] w-[calc(100%+80px)] max-w-none object-cover opacity-20 max-xl:object-[60%_50%]"
/> />
<div className="grid-bg absolute inset-0" /> <div className="grid-bg absolute inset-0" />
<div className="scan-bg absolute inset-0" /> <div className="scan-bg absolute inset-0" />
+5 -5
View File
@@ -34,7 +34,7 @@ export const dossier: DossierFile[] = [
proof: [ proof: [
{ {
label: "Orus", label: "Orus",
note: "A community that has grown around the work", note: "My open-source brand for privacy and opsec tools",
href: "/projects/#orus", href: "/projects/#orus",
status: "LIVE", status: "LIVE",
}, },
@@ -44,16 +44,16 @@ export const dossier: DossierFile[] = [
slug: "entrepreneurship", slug: "entrepreneurship",
no: "02", no: "02",
title: "Entrepreneurship", title: "Entrepreneurship",
summary: "Orus as the umbrella. Systems where incentives and behavior matter.", summary: "Brands and ventures. Systems where incentives and behavior matter.",
proofSummary: "Osias · Orus · EquitySMP", proofSummary: "Osias · Orus · EquitySMP",
lead: "Orus is the umbrella for everything serious I make.", lead: "I build brands, and ventures that stand on their own.",
paragraphs: [ paragraphs: [
"Beyond it I design systems where incentives and behavior matter.", "Orus is one of the brands: open-source software for privacy, independence, and opsec. Outside it I design systems where incentives and behavior matter.",
"Osias is a blockchain-powered AI agent for trading emotions and behavior. Alongside it sits a Minecraft economy concept with jurisdictions, taxes, and IPOs.", "Osias is a blockchain-powered AI agent for trading emotions and behavior. Alongside it sits a Minecraft economy concept with jurisdictions, taxes, and IPOs.",
], ],
proof: [ proof: [
{ label: "Osias", note: "osias.trade", href: "https://osias.trade", status: "BUILDING" }, { label: "Osias", note: "osias.trade", href: "https://osias.trade", status: "BUILDING" },
{ label: "Orus", note: "The umbrella org", href: "/projects/#orus", status: "LIVE" }, { label: "Orus", note: "Open-source privacy and opsec brand", href: "/projects/#orus", status: "LIVE" },
{ label: "EquitySMP", note: "Real-world-style server economy", href: "/projects/#equitysmp", status: "BUILDING" }, { label: "EquitySMP", note: "Real-world-style server economy", href: "/projects/#equitysmp", status: "BUILDING" },
], ],
}, },
+1 -7
View File
@@ -15,12 +15,6 @@ export const profile = {
git: "https://git.selimaj.dev", git: "https://git.selimaj.dev",
} as const; } as const;
export const orus = {
name: "Orus",
oneLiner:
"A community and toolset for privacy, independence, and opsec, plus the dev tools built under it.",
} as const;
/** /**
* Map lock. Privacy rule: city centre only (Skanderbeg Square, a public * Map lock. Privacy rule: city centre only (Skanderbeg Square, a public
* landmark). Never put anything finer than city scale here. * landmark). Never put anything finer than city scale here.
@@ -39,7 +33,7 @@ export type DossierField = {
export const dossierFields: DossierField[] = [ export const dossierFields: DossierField[] = [
{ label: "Based", value: "Tirana, Albania" }, { label: "Based", value: "Tirana, Albania" },
{ label: "Affiliation", value: "Orus (founder)" }, { label: "Brands", value: "Orus" },
// PLACEHOLDER decision: remove this entry to stop stating it publicly. // PLACEHOLDER decision: remove this entry to stop stating it publicly.
{ label: "Building since", value: "12" }, { label: "Building since", value: "12" },
{ label: "Focus", value: "Privacy, independence, opsec, dev tooling" }, { label: "Focus", value: "Privacy, independence, opsec, dev tooling" },
+67 -46
View File
@@ -1,6 +1,6 @@
export type Status = "LIVE" | "BUILDING" | "CONCEPT" | "ARCHIVED"; export type Status = "LIVE" | "BUILDING" | "CONCEPT" | "ARCHIVED";
export type GroupId = "core" | "ventures" | "infra" | "hardware"; export type GroupId = "orus" | "ventures" | "infra" | "hardware";
export type Project = { export type Project = {
id: string; id: string;
@@ -10,57 +10,91 @@ export type Project = {
desc: string; desc: string;
tags: string[]; tags: string[];
href?: string; href?: string;
root?: boolean;
/** Position on the 1200×720 node map. */ /** Position on the 1200×720 node map. */
map: { x: number; y: number }; map: { x: number; y: number };
/** Which side of the marker the label sits on (defaults to away from its hub). */
label?: "l" | "r";
}; };
export const groups: Record<GroupId, { label: string; x: number; y: number; side: "l" | "r" }> = { /** The map's root: the site itself. Not a project. */
core: { label: "Core", x: 360, y: 220, side: "l" }, export const root = {
name: "Selimaj",
sub: "selimaj.dev",
map: { x: 600, y: 360 },
} as const;
/**
* Groups branch off the root. A group with `node` uses that project as its hub
* (Orus is a brand with its own projects); the rest get a plain labelled hub.
*/
export const groups: Record<
GroupId,
{ label: string; x: number; y: number; side: "l" | "r"; node?: string }
> = {
orus: { label: "Orus", x: 340, y: 240, side: "r", node: "orus" },
ventures: { label: "Ventures & systems", x: 840, y: 220, side: "r" }, ventures: { label: "Ventures & systems", x: 840, y: 220, side: "r" },
infra: { label: "Infrastructure", x: 360, y: 520, side: "l" }, infra: { label: "Infrastructure", x: 360, y: 520, side: "l" },
hardware: { label: "Hardware", x: 840, y: 520, side: "r" }, hardware: { label: "Hardware", x: 840, y: 520, side: "r" },
}; };
// PLACEHOLDER: statuses for everything except Orus and the self-hosted // PLACEHOLDER: statuses other than Orus and the self-hosted infrastructure are
// infrastructure are best guesses. Links are filled in where known. // best guesses. Links are filled in where known.
export const projects: Project[] = [ export const projects: Project[] = [
{ {
id: "orus", id: "orus",
name: "Orus", name: "Orus",
group: "core", group: "orus",
root: true,
status: "LIVE", status: "LIVE",
desc: "Community and org for privacy, independence, opsec, and dev tools. Root of everything below.", desc: "One of my brands: open-source software for privacy, independence and opsec. Home to Enclave, Saturn and the cyberdecks.",
tags: ["community", "opsec", "org"], tags: ["brand", "oss", "privacy"],
map: { x: 600, y: 360 }, map: { x: 340, y: 240 },
label: "r",
}, },
{ {
id: "saturn", id: "saturn",
name: "Saturn", name: "Saturn",
group: "core", group: "orus",
status: "ARCHIVED", status: "BUILDING",
desc: "Minecraft client. Did most of what I set out to do with it.", desc: "Highly advanced and customizable Minecraft client with free emotes and cosmetics",
tags: ["minecraft", "client"], tags: ["minecraft", "client"],
map: { x: 200, y: 90 }, map: { x: 190, y: 100 },
}, },
{ {
id: "enclave", id: "enclave",
name: "Enclave", name: "Enclave",
group: "core", group: "orus",
status: "BUILDING", status: "BUILDING",
desc: "Discord-like chat platform, running on a custom transport protocol.", desc: "Discord-like chat platform, running on a custom transport protocol.",
tags: ["chat", "realtime"], tags: ["chat", "realtime"],
map: { x: 420, y: 70 }, map: { x: 430, y: 80 },
label: "r",
}, },
{ {
id: "enclave-protocol", id: "enclave-protocol",
name: "Enclave protocol", name: "Enclave protocol",
group: "core", group: "orus",
status: "CONCEPT", status: "CONCEPT",
desc: "Transport protocol replacing HTTP and WSS; Enclave’s chat protocol runs on top of it. Concept and design notes.", desc: "Transport protocol replacing HTTP and WSS; Enclave’s chat protocol runs on top of it. Concept and design notes.",
tags: ["networking", "protocol design"], tags: ["networking", "protocol design"],
map: { x: 190, y: 300 }, map: { x: 200, y: 330 },
},
{
id: "briefcase-deck",
name: "Briefcase deck",
group: "orus",
status: "CONCEPT",
desc: "Briefcase cyberdeck: the most versatile and doable build.",
tags: ["hardware", "cyberdeck"],
map: { x: 160, y: 215 },
},
{
id: "compact-deck",
name: "Compact deck",
group: "orus",
status: "CONCEPT",
desc: "Compact premium cyberdeck: a Flipper Zero, but better.",
tags: ["hardware", "cyberdeck"],
map: { x: 290, y: 400 },
}, },
{ {
id: "osias", id: "osias",
@@ -94,7 +128,7 @@ export const projects: Project[] = [
id: "algo-trading", id: "algo-trading",
name: "Algo trading", name: "Algo trading",
group: "ventures", group: "ventures",
status: "BUILDING", status: "ARCHIVED",
desc: "Several repos exploring automated crypto trading strategies.", desc: "Several repos exploring automated crypto trading strategies.",
tags: ["trading", "crypto"], tags: ["trading", "crypto"],
map: { x: 980, y: 400 }, map: { x: 980, y: 400 },
@@ -107,7 +141,7 @@ export const projects: Project[] = [
desc: "Gitea at git.selimaj.dev, on a Docker and Coolify homelab server behind Cloudflare.", desc: "Gitea at git.selimaj.dev, on a Docker and Coolify homelab server behind Cloudflare.",
tags: ["gitea", "docker", "coolify"], tags: ["gitea", "docker", "coolify"],
href: "https://git.selimaj.dev", href: "https://git.selimaj.dev",
map: { x: 190, y: 450 }, map: { x: 190, y: 470 },
}, },
{ {
id: "homelab", id: "homelab",
@@ -118,41 +152,28 @@ export const projects: Project[] = [
tags: ["infra", "docker"], tags: ["infra", "docker"],
map: { x: 300, y: 660 }, map: { x: 300, y: 660 },
}, },
{
id: "briefcase-deck",
name: "Briefcase deck",
group: "hardware",
status: "BUILDING",
desc: "Briefcase cyberdeck: the most versatile and doable build.",
tags: ["hardware", "cyberdeck"],
map: { x: 1020, y: 470 },
},
{
id: "compact-deck",
name: "Compact deck",
group: "hardware",
status: "CONCEPT",
desc: "Compact premium cyberdeck: a Flipper Zero, but better.",
tags: ["hardware", "cyberdeck"],
map: { x: 1000, y: 640 },
},
{ {
id: "rc-planes", id: "rc-planes",
name: "RC planes", name: "RC planes",
group: "hardware", group: "hardware",
status: "BUILDING", status: "CONCEPT",
desc: "Custom long-range, PC and 4G-controlled planes pushing speed and efficiency, built around a custom watercooled powertrain.", desc: "Custom long-range, PC and 4G-controlled planes pushing speed and efficiency, built around a custom watercooled powertrain.",
tags: ["hardware", "rc", "4g"], tags: ["hardware", "rc", "4g"],
map: { x: 720, y: 660 }, map: { x: 1000, y: 620 },
}, },
]; ];
export const projectById = (id: string) => projects.find((p) => p.id === id); export const projectById = (id: string) => projects.find((p) => p.id === id);
/** Home page featured strip: positions are percentages of the stage. */ /** The brand a project is published under, if any. */
export const brandOf = (p: Project) =>
p.group === "orus" && p.id !== "orus" ? "Orus" : undefined;
/** Home page featured strip, branching from the root. Positions are % of the stage. */
export const featuredRoot = { x: 16, y: 50 };
export const featured = [ export const featured = [
{ id: "orus", x: 18, y: 50 }, { id: "orus", x: 42, y: 16 },
{ id: "saturn", x: 46, y: 22 }, { id: "enclave", x: 66, y: 36 },
{ id: "enclave", x: 68, y: 52 }, { id: "saturn", x: 62, y: 64 },
{ id: "equitysmp", x: 44, y: 80 }, { id: "osias", x: 40, y: 84 },
] as const; ] as const;