Rebuild portfolio in Next.js and Tailwind

Static-export Next.js 16 app with the tactical-map hero, Dajt terrain
background, dossier pages and the projects node index.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-03 20:40:10 +02:00
co-authored by claude
parent 3ea4946164
commit 85dbdb3db4
37 changed files with 9042 additions and 16 deletions
+38 -16
View File
@@ -1,24 +1,46 @@
# Logs # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
logs
*.log # dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log* npm-debug.log*
yarn-debug.log* yarn-debug.log*
yarn-error.log* yarn-error.log*
pnpm-debug.log* .pnpm-debug.log*
lerna-debug.log*
node_modules # env files (can opt-in for committing if needed)
dist .env*
dist-ssr
*.local
# Editor directories and files # vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
# editors
.vscode/* .vscode/*
!.vscode/extensions.json !.vscode/extensions.json
.idea .idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+9
View File
@@ -0,0 +1,9 @@
<!-- BEGIN:nextjs-agent-rules -->
# This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
<!-- END:nextjs-agent-rules -->
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+28
View File
@@ -0,0 +1,28 @@
# selimaj.dev
Portfolio site. Next.js (App Router, static export) and Tailwind CSS v4.
```bash
npm install
npm run dev # http://localhost:3000
npm run build # static site in out/
npm run lint
```
`npm run build` writes plain HTML/CSS/JS to `out/`, which any static host can serve.
## Where things live
- `src/content/`: all copy and data, including every placeholder (marked `PLACEHOLDER`).
- `profile.ts`: name, title, stance, dossier fields, links, map lock.
- `dossier.ts`: the five About files.
- `projects.ts`: projects, statuses, node-map positions, featured nodes.
- `public/profile/pfp.svg`: placeholder profile picture. Replace this file (or point `profile.pfp` at a new one).
- `public/terrain.svg`: hero terrain contours (Dajt elevation data).
- `src/app/globals.css`: colour tokens, bracket/grid utilities, and all motion.
## Rules worth keeping
- Green (`accent`) means active or detected: hover, focus, current page, live telemetry, the lock. Nothing at rest is green.
- The map shows Tirana city centre only. Never add anything finer than city scale.
- All motion sits behind `prefers-reduced-motion: no-preference`. Text renders without JS.
+18
View File
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);
export default eslintConfig;
+10
View File
@@ -0,0 +1,10 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
// Static-first: `next build` emits plain HTML/CSS/JS into `out/`.
output: "export",
trailingSlash: true,
images: { unoptimized: true },
};
export default nextConfig;
+6892
View File
File diff suppressed because it is too large Load Diff
+26
View File
@@ -0,0 +1,26 @@
{
"name": "selimaj-dev",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"next": "16.3.8",
"react": "19.2.8",
"react-dom": "19.2.8"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.3.8",
"tailwindcss": "^4",
"typescript": "^5"
}
}
+7
View File
@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
+11
View File
@@ -0,0 +1,11 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 168 200">
<!-- PLACEHOLDER profile picture: replace this file with the real one. -->
<defs>
<linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#3a4247"/>
<stop offset="1" stop-color="#101315"/>
</linearGradient>
</defs>
<path fill="url(#g)" d="M84,28 C108,28 122,46 122,72 C122,96 112,114 100,122 L102,136 C130,142 156,156 164,182 L166,200 L2,200 L4,182 C12,156 38,142 66,136 L68,122 C56,114 46,96 46,72 C46,46 60,28 84,28Z"/>
<path fill="none" stroke="#f2f4f5" stroke-opacity=".3" d="M84,28 C108,28 122,46 122,72 C122,96 112,114 100,122 L102,136 C130,142 156,156 164,182 L166,200 L2,200 L4,182 C12,156 38,142 66,136 L68,122 C56,114 46,96 46,72 C46,46 60,28 84,28Z"/>
</svg>

After

Width:  |  Height:  |  Size: 788 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 304 KiB

+131
View File
@@ -0,0 +1,131 @@
import type { Metadata } from "next";
import Link from "next/link";
import { notFound } from "next/navigation";
import { StatusTag } from "@/components/StatusTag";
import { dossier, fileBySlug, type Proof } from "@/content/dossier";
export const dynamicParams = false;
export function generateStaticParams() {
return dossier.map((f) => ({ slug: f.slug }));
}
export async function generateMetadata({ params }: PageProps<"/about/[slug]">): Promise<Metadata> {
const { slug } = await params;
const f = fileBySlug(slug);
return f ? { title: f.title, description: f.summary } : {};
}
function ProofRow({ p }: { p: Proof }) {
const inner = (
<>
<span>
<span className="font-display text-[17px] font-semibold tracking-[0.06em] uppercase">{p.label}</span>
<span className="mt-0.5 block text-[11px] text-mute">{p.note}</span>
</span>
{p.status ? <StatusTag status={p.status} /> : <span />}
<span aria-hidden="true" className="text-mute group-hover:text-accent group-focus-visible:text-accent">
{p.href ? (p.href.startsWith("http") ? "↗" : "→") : ""}
</span>
</>
);
const cls = "group bracket grid min-h-14 grid-cols-[minmax(0,1fr)_auto_16px] items-center gap-5 px-3 [--bc:transparent]";
if (!p.href) return <div className={cls}>{inner}</div>;
const interactive = `${cls} hover:[--bc:var(--color-accent)] focus-visible:outline-none focus-visible:[--bc:var(--color-accent)]`;
return p.href.startsWith("http") ? (
<a href={p.href} className={interactive}>
{inner}
</a>
) : (
<Link href={p.href} className={interactive}>
{inner}
</Link>
);
}
export default async function DossierFilePage({ params }: PageProps<"/about/[slug]">) {
const { slug } = await params;
const f = fileBySlug(slug);
if (!f) notFound();
const i = dossier.indexOf(f);
const prev = dossier[i - 1];
const next = dossier[i + 1];
return (
<main className="pt-[68px] md:pt-[85px]">
<div className="mx-auto flex max-w-[1240px] flex-col gap-8 px-4 pt-8 pb-20 md:px-16 md:pt-[72px] md:pb-24 lg:flex-row lg:items-start lg:gap-24">
<aside aria-label="Dossier files" className="lg:sticky lg:top-8 lg:w-[220px] lg:flex-none">
<Link href="/about/" className="inline-flex min-h-11 items-center gap-2 text-[10px] tracking-[0.22em] text-mute hover:text-accent">
<span aria-hidden="true">←</span> DOSSIER INDEX
</Link>
<ol className="mt-4 flex flex-wrap gap-x-4 text-[11px] tracking-[0.16em] lg:block">
{dossier.map((d) => {
const current = d.slug === f.slug;
return (
<li key={d.slug}>
<Link
href={`/about/${d.slug}/`}
aria-current={current ? "page" : undefined}
className={`flex min-h-10 items-center gap-3 uppercase ${current ? "text-ink" : "text-mute hover:text-ink"}`}
>
<span className={`w-6 ${current ? "text-accent" : ""}`}>{d.no}</span>
{d.title}
{current && <span aria-hidden="true" className="size-1.5 bg-accent" />}
</Link>
</li>
);
})}
</ol>
</aside>
<article className="min-w-0 max-w-[720px] flex-1">
<div className="flex flex-wrap gap-5 text-[10px] tracking-[0.24em] text-mute">
<span>
FILE {f.no} / {String(dossier.length).padStart(2, "0")}
</span>
<span>CLASS · OPEN</span>
</div>
<h1 className="mt-3.5 font-display text-[52px] leading-[0.95] font-semibold uppercase md:text-[80px]">{f.title}</h1>
<div aria-hidden="true" className="mt-8 h-px bg-white/10" />
<p className="mt-10 text-xl leading-[1.55] md:text-[22px]">{f.lead}</p>
{f.paragraphs.map((t) => (
<p key={t} className="mt-5 text-[15px] leading-[1.8] text-ink-2">
{t}
</p>
))}
<section aria-labelledby="proof-title" className="bracket mt-14 border border-white/6 bg-surface p-6 [--bc:rgb(242_244_245/0.45)]">
<h2 id="proof-title" className="text-[10px] font-normal tracking-[0.3em] text-mute">
PROOF
</h2>
<ul className="mt-4 flex flex-col gap-1">
{f.proof.map((p) => (
<li key={p.label}>
<ProofRow p={p} />
</li>
))}
</ul>
</section>
<nav aria-label="Adjacent files" className="mt-16 flex flex-col justify-between gap-4 border-t border-white/8 pt-6 sm:flex-row">
{prev ? (
<Link href={`/about/${prev.slug}/`} className="group flex min-h-11 flex-col gap-1.5">
<span className="text-[9px] tracking-[0.24em] text-mute">← FILE {prev.no}</span>
<span className="font-display text-lg font-semibold tracking-[0.06em] uppercase group-hover:text-accent">{prev.title}</span>
</Link>
) : (
<span />
)}
{next && (
<Link href={`/about/${next.slug}/`} className="group flex min-h-11 flex-col gap-1.5 sm:text-right">
<span className="text-[9px] tracking-[0.24em] text-mute">FILE {next.no} →</span>
<span className="font-display text-lg font-semibold tracking-[0.06em] uppercase group-hover:text-accent">{next.title}</span>
</Link>
)}
</nav>
</article>
</div>
</main>
);
}
+68
View File
@@ -0,0 +1,68 @@
import type { Metadata } from "next";
import Link from "next/link";
import { dossier } from "@/content/dossier";
import { orus, profile } from "@/content/profile";
export const metadata: Metadata = {
title: "Dossier",
description: "Five short files: leadership, entrepreneurship, building, discipline, passions.",
};
const cols = "md:grid-cols-[96px_240px_minmax(0,1fr)_200px_32px] xl:grid-cols-[96px_260px_minmax(0,1fr)_240px_32px]";
export default function AboutPage() {
return (
<main className="grid-bg min-h-full pt-[68px] md:pt-[85px]">
<div className="mx-auto max-w-[1440px] px-4 pt-10 pb-20 md:px-16 md:pt-[72px] md:pb-24">
<div className="flex flex-col justify-between gap-10 md:flex-row md:items-end md:gap-16">
<div>
<div className="label tracking-[0.3em]">Case file · Subject 0001</div>
<h1 className="mt-3 font-display text-[52px] leading-[0.95] font-semibold uppercase md:text-7xl">Dossier</h1>
<p className="mt-5 max-w-[520px] text-sm leading-[1.7] text-ink-2">
Five files. Each one is short: two or three paragraphs and one piece of proof.
</p>
</div>
<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>
<dd>{profile.displayName}</dd>
<dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">ROOT</dt>
<dd>{orus.name}</dd>
<dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">FILES</dt>
<dd>{String(dossier.length).padStart(2, "0")}</dd>
<dt className="pt-0.5 text-[9px] tracking-[0.22em] text-mute">ACCESS</dt>
<dd>Open</dd>
</dl>
</div>
<div aria-hidden="true" className={`mt-16 hidden gap-6 border-b border-white/10 px-5 pb-3 text-[9px] tracking-[0.24em] text-mute md:grid ${cols}`}>
<span>ID</span>
<span>FILE</span>
<span>SUMMARY</span>
<span>PROOF</span>
<span />
</div>
<ol className="mt-8 border-t border-white/7 md:mt-0 md:border-t-0">
{dossier.map((f) => (
<li key={f.slug} className="border-b border-white/7">
<Link
href={`/about/${f.slug}/`}
className={`group bracket grid min-h-[88px] grid-cols-[72px_minmax(0,1fr)_24px] items-center gap-6 px-3 transition-colors [--bc:transparent] hover:bg-white/2 hover:[--bc:var(--color-accent)] focus-visible:outline-none focus-visible:[--bc:var(--color-accent)] md:px-5 ${cols}`}
>
<span className="text-[11px] tracking-[0.2em] text-mute group-hover:text-accent group-focus-visible:text-accent">FILE {f.no}</span>
<span className="font-display text-lg font-semibold tracking-[0.06em] uppercase md:text-[22px]">{f.title}</span>
<span className="hidden text-xs leading-[1.6] text-ink-2 md:block">{f.summary}</span>
<span className="hidden text-[10px] tracking-[0.18em] text-mute uppercase md:block">{f.proofSummary}</span>
<span aria-hidden="true" className="text-mute transition-transform group-hover:translate-x-1 group-hover:text-accent">
→
</span>
</Link>
</li>
))}
</ol>
<p aria-hidden="true" className="mt-5 text-[9px] tracking-[0.22em] text-mute/70">
{String(dossier.length).padStart(2, "0")} FILES · END OF INDEX
</p>
</div>
</main>
);
}
+351
View File
@@ -0,0 +1,351 @@
@import "tailwindcss";
/* `js:` applies only once the head script has marked <html class="js">. */
@custom-variant js (&:where(.js *));
@theme {
--color-night: #050607;
--color-surface: #0b0d0e;
--color-surface-2: #121517;
--color-ink: #f2f4f5;
--color-ink-2: #c4cacd;
--color-mute: #8b9296;
/* Green means active or detected. Never decoration. */
--color-accent: #4dff95;
--color-line: rgb(255 255 255 / 0.07);
--color-line-2: rgb(255 255 255 / 0.12);
}
@theme inline {
--font-display: var(--font-chakra), ui-sans-serif, system-ui, sans-serif;
--font-mono: var(--font-jetbrains), ui-monospace, SFMono-Regular, monospace;
}
@layer base {
html {
background: var(--color-night);
color: var(--color-ink);
color-scheme: dark;
}
body {
font-family: var(--font-mono);
-webkit-font-smoothing: antialiased;
}
:focus-visible {
outline: 1px solid var(--color-accent);
outline-offset: 3px;
}
::selection {
background: rgb(77 255 149 / 0.25);
}
}
/* Corner brackets. Set --bc for colour, --bl for arm length. Uses only the
background-image longhands so it composes with bg-* colour utilities. */
@utility bracket {
--bc: rgb(242 244 245 / 0.28);
--bl: 10px;
background-image:
linear-gradient(var(--bc), var(--bc)), linear-gradient(var(--bc), var(--bc)),
linear-gradient(var(--bc), var(--bc)), linear-gradient(var(--bc), var(--bc)),
linear-gradient(var(--bc), var(--bc)), linear-gradient(var(--bc), var(--bc)),
linear-gradient(var(--bc), var(--bc)), linear-gradient(var(--bc), var(--bc));
background-repeat: no-repeat;
background-position: 0 0, 0 0, 100% 0, 100% 0, 0 100%, 0 100%, 100% 100%, 100% 100%;
background-size:
var(--bl) 1px, 1px var(--bl), var(--bl) 1px, 1px var(--bl),
var(--bl) 1px, 1px var(--bl), var(--bl) 1px, 1px var(--bl);
}
@utility grid-bg {
background-image:
linear-gradient(rgb(255 255 255 / 0.03) 1px, transparent 1px),
linear-gradient(90deg, rgb(255 255 255 / 0.03) 1px, transparent 1px);
background-size: 48px 48px;
}
@utility scan-bg {
background-image: repeating-linear-gradient(0deg, rgb(255 255 255 / 0.016) 0 1px, transparent 1px 3px);
}
@utility label {
font-size: 10px;
letter-spacing: 0.24em;
text-transform: uppercase;
color: var(--color-mute);
}
/* ---------- Boot overlay (only ever shown when the head script sets data-boot) ---------- */
.boot {
display: none;
}
html[data-boot] .boot {
display: flex;
}
html[data-boot] body {
overflow: hidden;
}
/* Hold the hero sequence until boot finishes; removing data-boot restarts it. */
html[data-boot] .seq {
animation: none !important;
}
/* ---------- Motion (all of it opt-in; reduced motion gets the final state) ---------- */
@media (prefers-reduced-motion: no-preference) {
.seq {
animation-delay: var(--d, 0s);
animation-fill-mode: both;
}
.drift {
animation: drift 80s ease-in-out infinite alternate;
will-change: transform;
}
.spin {
animation: spin 16s linear infinite;
transform-origin: 60px 60px;
}
.blink {
animation: blink 1.1s steps(2) infinite;
}
/* Text resolves from outline to solid with a short glitch. */
.resolve {
animation-name: resolve;
animation-duration: 0.55s;
animation-timing-function: linear;
}
/* Panels build as a dashed wireframe, then fill. */
.panel {
animation-name: panel;
animation-duration: 0.6s;
animation-timing-function: linear;
}
/* Map strokes draw in (paths need pathLength="1"). */
.draw path,
.draw circle,
.draw rect {
stroke-dasharray: 1;
animation: draw 0.9s ease-out both;
animation-delay: var(--d, 0s);
}
html[data-boot] .draw path,
html[data-boot] .draw circle,
html[data-boot] .draw rect {
animation: none;
}
.reticle {
animation-name: reticle;
animation-duration: 0.7s;
animation-timing-function: cubic-bezier(0.6, 0, 0.2, 1);
}
.trace {
animation-name: trace;
animation-duration: 0.3s;
animation-timing-function: ease-out;
transform-origin: 0 50%;
}
.appear {
animation-name: appear;
animation-duration: 0.01s;
}
.bust-solid {
animation-name: bust-solid;
animation-duration: 0.8s;
animation-timing-function: linear;
}
.bust-scan {
animation-name: bust-scan;
animation-duration: 0.8s;
animation-timing-function: linear;
}
.page-resolve {
animation: page-resolve 0.45s linear both;
}
.type-line {
overflow: hidden;
white-space: nowrap;
animation: type 0.3s steps(24) both;
animation-delay: var(--d, 0s);
}
.boot-out {
animation: boot-out 0.25s linear both;
}
.seg-on {
animation: seg-grow 0.45s ease-out both;
}
}
@media (prefers-reduced-motion: reduce) {
.page-resolve {
animation: fade 0.2s ease-out both;
}
.bust-scan {
display: none;
}
}
@keyframes drift {
to {
transform: translate3d(-28px, 14px, 0);
}
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes blink {
50% {
opacity: 0;
}
}
@keyframes fade {
from {
opacity: 0;
}
}
@keyframes appear {
from {
visibility: hidden;
}
}
@keyframes resolve {
0% {
color: transparent;
-webkit-text-stroke: 1px rgb(242 244 245 / 0.45);
clip-path: inset(0 100% 0 0);
}
40% {
color: transparent;
-webkit-text-stroke: 1px rgb(242 244 245 / 0.45);
clip-path: inset(0 0 0 0);
}
55% {
transform: translateX(2px);
text-shadow: -2px 0 rgb(255 70 90 / 0.45), 2px 0 rgb(70 150 255 / 0.45);
}
62% {
transform: translateX(-1px);
}
70% {
transform: none;
text-shadow: none;
}
}
@keyframes panel {
0% {
clip-path: inset(0 0 100% 0);
background-color: transparent;
outline: 1px dashed rgb(242 244 245 / 0.35);
}
45% {
clip-path: inset(0 0 0 0);
background-color: transparent;
outline: 1px dashed rgb(242 244 245 / 0.35);
}
58% {
transform: translateX(2px);
filter: drop-shadow(-1px 0 rgb(255 70 90 / 0.5)) drop-shadow(1px 0 rgb(70 150 255 / 0.5));
}
66% {
transform: none;
filter: none;
outline: 0;
}
}
@keyframes draw {
from {
stroke-dashoffset: 1;
}
to {
stroke-dashoffset: 0;
}
}
@keyframes reticle {
0% {
opacity: 0;
transform: translate(-90px, 50px) scale(1.4);
}
10% {
opacity: 1;
}
45% {
transform: translate(40px, -24px) scale(1.4);
}
80% {
transform: translate(0, 0) scale(1.4);
}
100% {
transform: translate(0, 0) scale(1);
}
}
@keyframes trace {
from {
transform: scaleX(0);
}
}
@keyframes bust-solid {
from {
clip-path: inset(0 100% 0 0);
}
to {
clip-path: inset(0 0 0 0);
}
}
@keyframes bust-scan {
0% {
left: 0;
opacity: 1;
}
95% {
left: 100%;
opacity: 1;
}
100% {
left: 100%;
opacity: 0;
}
}
@keyframes page-resolve {
0% {
clip-path: inset(0 0 100% 0);
filter: drop-shadow(-2px 0 rgb(255 70 90 / 0.5)) drop-shadow(2px 0 rgb(70 150 255 / 0.5));
}
60% {
clip-path: inset(0 0 0 0);
transform: translateX(2px);
}
75% {
transform: translateX(-1px);
}
100% {
transform: none;
filter: none;
}
}
@keyframes type {
from {
max-width: 0;
}
to {
max-width: 760px;
}
}
@keyframes boot-out {
40% {
transform: translateX(3px);
filter: drop-shadow(-2px 0 rgb(255 70 90 / 0.6)) drop-shadow(2px 0 rgb(70 150 255 / 0.6));
}
100% {
clip-path: inset(50% 0 50% 0);
}
}
@keyframes seg-grow {
from {
clip-path: inset(0 100% 0 0);
}
to {
clip-path: inset(0 0 0 0);
}
}
+5
View File
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<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"/>
<circle cx="12" cy="12" r="2.6" fill="#4dff95"/>
</svg>

After

Width:  |  Height:  |  Size: 275 B

+41
View File
@@ -0,0 +1,41 @@
import type { Metadata } from "next";
import { Chakra_Petch, JetBrains_Mono } from "next/font/google";
import { SiteHeader } from "@/components/SiteHeader";
import { SiteFooter } from "@/components/SiteFooter";
import "./globals.css";
const chakra = Chakra_Petch({
variable: "--font-chakra",
subsets: ["latin"],
weight: ["400", "500", "600", "700"],
});
const jetbrains = JetBrains_Mono({
variable: "--font-jetbrains",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: { default: "Selimaj · Entrepreneur & Builder", template: "%s · Selimaj" },
description: "Selimaj, entrepreneur and builder. Founder of Orus. I build the layer underneath.",
metadataBase: new URL("https://selimaj.dev"),
};
// Runs before paint: marks JS as available, and arms the boot sequence once
// per session on the home page (never with reduced motion).
const headScript = `(function(){var d=document.documentElement;d.classList.add('js');try{if(location.pathname==='/'&&!sessionStorage.getItem('boot')&&!matchMedia('(prefers-reduced-motion: reduce)').matches){d.setAttribute('data-boot','')}}catch(e){}})();`;
export default function RootLayout({ children }: LayoutProps<"/">) {
return (
<html lang="en" suppressHydrationWarning className={`${chakra.variable} ${jetbrains.variable}`}>
<head>
<script dangerouslySetInnerHTML={{ __html: headScript }} />
</head>
<body className="flex min-h-dvh flex-col overflow-x-hidden bg-night text-ink">
<SiteHeader />
<div className="flex-1">{children}</div>
<SiteFooter />
</body>
</html>
);
}
+16
View File
@@ -0,0 +1,16 @@
import Link from "next/link";
export default function NotFound() {
return (
<main className="grid-bg flex min-h-[70dvh] items-center pt-[85px]">
<div className="mx-auto w-full max-w-[1440px] px-4 md:px-16">
<div className="label tracking-[0.3em]">404 · Signal lost</div>
<h1 className="mt-3 font-display text-[52px] leading-[0.95] font-semibold uppercase md:text-7xl">No trace</h1>
<p className="mt-5 text-sm text-ink-2">Nothing resolves at this address.</p>
<Link href="/" className="mt-8 inline-flex min-h-12 items-center gap-3 bg-ink px-[22px] text-xs tracking-[0.2em] text-night hover:bg-accent">
RETURN TO BASE <span aria-hidden="true">→</span>
</Link>
</div>
</main>
);
}
+29
View File
@@ -0,0 +1,29 @@
import Link from "next/link";
import { BootSequence } from "@/components/BootSequence";
import { FeaturedNodes } from "@/components/FeaturedNodes";
import { Hero } from "@/components/Hero";
export default function Home() {
return (
<>
<BootSequence />
<main>
<Hero />
<section aria-labelledby="nodes-title" className="mx-auto max-w-[1440px] px-4 pt-16 pb-16 md:px-16 md:pt-24 md:pb-20">
<div className="flex flex-wrap items-end justify-between gap-6">
<div>
<div className="label tracking-[0.3em]">02 / Nodes</div>
<h2 id="nodes-title" className="mt-2.5 font-display text-[28px] font-semibold tracking-[0.04em] uppercase md:text-[40px]">
Featured
</h2>
</div>
<Link href="/projects/" className="inline-flex min-h-11 items-center gap-2.5 text-[11px] tracking-[0.2em] text-mute hover:text-accent">
ALL PROJECTS <span aria-hidden="true">→</span>
</Link>
</div>
<FeaturedNodes />
</section>
</main>
</>
);
}
+23
View File
@@ -0,0 +1,23 @@
import type { Metadata } from "next";
import { ProjectIndex } from "@/components/ProjectIndex";
import { projects } from "@/content/projects";
export const metadata: Metadata = {
title: "Projects",
description: "Everything built and conceived under Orus, from Saturn and Enclave to cyberdecks and RC planes.",
};
export default function ProjectsPage() {
return (
<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="label tracking-[0.3em]">Node index · Root Orus · {projects.length} nodes</div>
<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">
Most of what I&apos;ve built and conceived. Concepts are labelled as concepts.
</p>
<ProjectIndex />
</div>
</main>
);
}
+4
View File
@@ -0,0 +1,4 @@
// Re-mounts on every navigation, so each page resolves in from wireframe.
export default function Template({ children }: { children: React.ReactNode }) {
return <div className="page-resolve seq">{children}</div>;
}
+82
View File
@@ -0,0 +1,82 @@
"use client";
import { useEffect, useState } from "react";
import { useSession } from "./Telemetry";
const DURATION = 1900;
/**
* Once-per-session boot screen on the home page. It only shows when the head
* script has set <html data-boot>, so without JS (or with reduced motion) it
* never appears. Click, tap or any key skips it.
*/
export function BootSequence() {
const s = useSession();
const [leaving, setLeaving] = useState(false);
useEffect(() => {
const root = document.documentElement;
if (!root.hasAttribute("data-boot")) return;
let done = false;
let outTimer: number | undefined;
const finish = () => {
if (done) return;
done = true;
try {
sessionStorage.setItem("boot", "1");
} catch {}
setLeaving(true);
outTimer = window.setTimeout(() => root.removeAttribute("data-boot"), 250);
};
const timer = window.setTimeout(finish, DURATION);
window.addEventListener("keydown", finish);
window.addEventListener("pointerdown", finish);
return () => {
clearTimeout(timer);
clearTimeout(outTimer);
window.removeEventListener("keydown", finish);
window.removeEventListener("pointerdown", finish);
};
}, []);
const stamp = s ? new Date(s.start).toISOString() : "";
const lines = [
{ text: "SESSION OPEN", tail: "[ OK ]" },
{ text: stamp },
{ text: s ? `HASH ${s.hash}` : "HASH" },
{ text: "SIGNAL ACQUIRED · TRACE ▸ TIRANA, AL" },
];
return (
<div
role="status"
aria-label="Loading"
className={`boot fixed inset-0 z-50 flex-col justify-end bg-night ${leaving ? "boot-out" : ""}`}
>
<div aria-hidden="true" className="grid-bg absolute inset-0" />
<div aria-hidden="true" className="scan-bg absolute inset-0" />
<div className="relative px-6 pb-[18vh] text-[13px] leading-[2.1] tracking-[0.08em] md:px-24 md:text-sm">
{lines.map((l, i) => (
<div key={i} className="type-line flex gap-[18px] text-ink-2 first:text-ink" style={{ "--d": `${0.1 + i * 0.3}s` } as React.CSSProperties}>
<span className="text-mute">&gt;</span>
{l.text}
{l.tail && <span className="text-mute">{l.tail}</span>}
</div>
))}
<div className="type-line flex gap-[18px] text-accent" style={{ "--d": "1.3s" } as React.CSSProperties}>
<span className="text-mute">&gt;</span>LOCKING<span className="blink">▌</span>
</div>
<div className="mt-10 w-full max-w-[420px]">
<div className="flex justify-between text-[9px] tracking-[0.22em] text-mute">
<span>TRACE</span>
</div>
<div className="mt-2 h-px bg-white/12">
<div className="h-px origin-left bg-accent shadow-[0_0_8px_var(--color-accent)] [animation:trace_1.8s_ease-out_both]" />
</div>
</div>
</div>
<p className="absolute right-6 bottom-8 text-[10px] tracking-[0.24em] text-mute md:right-16">CLICK OR PRESS ANY KEY TO SKIP</p>
</div>
);
}
+65
View File
@@ -0,0 +1,65 @@
"use client";
import Image from "next/image";
import { useRef } from "react";
/**
* 2.5D identity reveal. The picture first shows as a high-contrast scanline
* "wireframe", a scan sweeps across and resolves it to solid, and on desktop
* the layers tilt apart slightly with the pointer. No 3D library needed.
*/
export function Bust({ src, delay = "0s", className = "" }: { src: string; delay?: string; className?: string }) {
const tiltRef = useRef<HTMLDivElement>(null);
const meshRef = useRef<HTMLDivElement>(null);
function onMove(e: React.PointerEvent<HTMLDivElement>) {
if (e.pointerType !== "mouse" || matchMedia("(prefers-reduced-motion: reduce)").matches) return;
const r = e.currentTarget.getBoundingClientRect();
const x = (e.clientX - r.left) / r.width - 0.5;
const y = (e.clientY - r.top) / r.height - 0.5;
if (tiltRef.current) tiltRef.current.style.transform = `rotateX(${(-y * 8).toFixed(2)}deg) rotateY(${(x * 10).toFixed(2)}deg)`;
if (meshRef.current) meshRef.current.style.transform = `translate(${(x * -6).toFixed(1)}px, ${(y * -6).toFixed(1)}px)`;
}
function onLeave() {
if (tiltRef.current) tiltRef.current.style.transform = "";
if (meshRef.current) meshRef.current.style.transform = "";
}
return (
<div
className={`relative aspect-[168/200] [perspective:600px] ${className}`}
onPointerMove={onMove}
onPointerLeave={onLeave}
>
<div ref={tiltRef} className="absolute inset-0 transition-transform duration-200 ease-out [transform-style:preserve-3d]">
{/* wireframe pass */}
<div className="absolute inset-0 [mask-image:repeating-linear-gradient(0deg,#000_0_1px,transparent_1px_5px)]">
<Image
src={src}
alt=""
fill
sizes="168px"
className="object-cover opacity-80 [filter:grayscale(1)_contrast(2.4)_brightness(1.6)_drop-shadow(1px_0_0_rgb(255_70_90/0.35))_drop-shadow(-1px_0_0_rgb(70_150_255/0.35))]"
/>
</div>
{/* solid pass */}
<div className="bust-solid seq absolute inset-0" style={{ "--d": delay } as React.CSSProperties}>
<Image src={src} alt="Profile picture" fill sizes="168px" className="object-cover" />
</div>
{/* residual mesh that floats a few px above the photo */}
<div
ref={meshRef}
aria-hidden="true"
className="absolute inset-0 opacity-[0.12] transition-transform duration-200 ease-out [background-image:repeating-linear-gradient(0deg,#f2f4f5_0_1px,transparent_1px_8px)] [mask-image:linear-gradient(90deg,#000,transparent_70%)]"
/>
{/* scan line */}
<div
aria-hidden="true"
className="bust-scan seq absolute top-0 bottom-0 left-full w-px bg-accent opacity-0 shadow-[0_0_8px_var(--color-accent)]"
style={{ "--d": delay } as React.CSSProperties}
/>
</div>
</div>
);
}
+93
View File
@@ -0,0 +1,93 @@
"use client";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { featured, projectById, projects } from "@/content/projects";
import { Segment } from "./Segment";
const ROOT = featured[0];
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.slice(1).map((n) => (
<Segment
key={`${n.id}-${active === n.id}`}
x1={(ROOT.x / 100) * size.w}
y1={(ROOT.y / 100) * size.h}
x2={(n.x / 100) * size.w}
y2={(n.y / 100) * size.h}
on={active === n.id}
dashed
strong
/>
))}
</div>
)}
{nodes.map((n) => {
const on = active === n.id;
const root = n.project.root;
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 box-border border ${
root
? "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
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 ${
root ? "md:right-6 md:w-max md:text-right" : "md:left-6 md:w-[280px]"
} ${
on ? "[--bc:var(--color-accent)]" : "[--bc:transparent]"
}`}
>
<span className={`flex items-baseline gap-3 ${root ? "md:flex-row-reverse" : ""}`}>
<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>
<span className={`mt-1.5 block text-[11px] leading-[1.55] text-ink-2 ${on && !root ? "" : "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 md:block">
ROOT ORUS · {featured.length} OF {projects.length} NODES
</div>
</div>
);
}
+154
View File
@@ -0,0 +1,154 @@
import Link from "next/link";
import { dossierFields, lock, orus, profile, type DossierField } from "@/content/profile";
import { Bust } from "./Bust";
import { LocalTime, SessionStamp } from "./Telemetry";
import { TerrainLayer } from "./TerrainLayer";
import { LockReticle, TiranaMap } from "./TiranaMap";
const d = (s: number) => ({ "--d": `${s}s` }) as React.CSSProperties;
function DossierList({ fields, className = "" }: { fields: DossierField[]; className?: string }) {
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}`}>
{fields.map((f) => (
<div key={f.label} className="contents">
<dt className="pt-[3px] text-[9px] uppercase tracking-[0.22em] text-mute">{f.label}</dt>
<dd className="flex flex-wrap gap-x-3.5">
{f.links
? f.links.map((l) => (
<a key={l.href} href={l.href} className="border-b border-white/25 hover:border-accent">
{l.label}
</a>
))
: f.value}
</dd>
</div>
))}
</dl>
);
}
export function Hero() {
return (
<section aria-labelledby="hero-title" className="relative overflow-hidden border-b border-line">
<TerrainLayer />
<div className="relative z-10 mx-auto max-w-[1440px] pt-[68px] md:pt-[84px] xl:h-[864px]">
{/* Lock assembly: a strip on small screens, the full stage on desktop. */}
<div
aria-hidden="true"
className="relative h-[240px] overflow-hidden border-b border-line [--lx:50%] xl:pointer-events-none xl:[--lx:45%] 2xl:[--lx:50%] xl:absolute xl:inset-0 xl:h-auto xl:overflow-visible xl:border-0"
>
<TiranaMap className="absolute top-[calc(50%-320px)] left-[calc(var(--lx)-320px)] size-[640px] xl:top-[-86px]" />
<LockReticle
className="reticle seq absolute top-[calc(50%-60px)] left-[calc(var(--lx)-60px)] max-xl:scale-75 xl:top-[174px]"
style={d(0.25)}
/>
<div
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]"
style={d(0.85)}
>
<div className="text-accent">LOCK ACQUIRED</div>
<div>
{lock.lat} · {lock.lon}
</div>
<div className="uppercase">{lock.place}</div>
</div>
<div
className="trace seq absolute top-[233px] right-[510px] left-[calc(var(--lx)+66px)] hidden h-px bg-accent/55 xl:block"
style={d(0.9)}
>
<span className="absolute -top-4 left-0 text-[9px] whitespace-nowrap tracking-[0.2em] text-accent">TRACE ▸ 0001</span>
<span className="absolute -top-[3px] -right-[3px] size-[7px] bg-accent" />
</div>
</div>
<div className="flex flex-col gap-9 px-4 pt-6 pb-16 md:px-16 xl:block xl:p-0">
{/* Headline */}
<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)}>
TARGET RESOLVED · SUBJECT 0001
</p>
<h1
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]"
style={d(1.2)}
>
Entrepreneur
<br />
&amp; Builder
</h1>
<p className="resolve seq mt-4 text-[15px] leading-normal text-ink-2 xl:mt-6 xl:text-[17px]" style={d(1.3)}>
{profile.stance}
</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)}>
<Link
href="/projects/"
className="inline-flex min-h-12 items-center justify-between gap-3 bg-ink px-[22px] text-xs font-medium tracking-[0.2em] text-night transition-colors hover:bg-accent hover:text-night"
>
VIEW PROJECTS <span aria-hidden="true">→</span>
</Link>
<Link
href="/about/"
className="bracket inline-flex min-h-12 items-center justify-between gap-3 border border-white/12 px-[22px] text-xs tracking-[0.2em] transition-colors [--bc:rgb(242_244_245/0.6)] hover:border-transparent hover:text-accent hover:[--bc:var(--color-accent)]"
>
OPEN DOSSIER <span aria-hidden="true">↗</span>
</Link>
</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" />
</div>
{/* Profile card */}
<article
aria-labelledby="profile-name"
className="bracket panel seq order-first border border-white/7 bg-surface/90 p-4 [--bc:rgb(242_244_245/0.55)] xl:absolute xl:top-[124px] xl:right-16 xl:order-none xl:w-[446px] xl:p-[22px]"
style={d(1.0)}
>
<div className="flex justify-between text-[9px] tracking-[0.2em] text-mute xl:text-[10px]">
<span>PROFILE / 0001</span>
<span className="text-accent">● MATCH CONFIRMED</span>
</div>
<div className="mt-3.5 flex items-end gap-4 xl:mt-[18px] xl:items-start xl:gap-[22px]">
<figure className="bracket w-[112px] flex-none [--bc:rgb(242_244_245/0.4)] xl:w-[168px]">
<Bust src={profile.pfp} delay="1.2s" />
{profile.pfpIsPlaceholder && (
<figcaption className="pt-2 pb-0.5 text-[9px] tracking-[0.2em] text-mute max-xl:hidden">PFP · PLACEHOLDER</figcaption>
)}
</figure>
<div className="min-w-0 xl:pt-1">
<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">
{profile.displayName}
</h2>
<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">CLEARANCE · PUBLIC</div>
{profile.pfpIsPlaceholder && <div className="xl:hidden">PFP · PLACEHOLDER</div>}
</div>
</div>
</div>
<DossierList fields={dossierFields} className="mt-[22px] hidden xl:grid" />
</article>
</div>
{/* Corner telemetry */}
<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>
<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">
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>
</section>
);
}
+8
View File
@@ -0,0 +1,8 @@
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>
);
}
+252
View File
@@ -0,0 +1,252 @@
"use client";
import { useEffect, useState } from "react";
import { groups, projects, type GroupId, type Project } from "@/content/projects";
import { Segment } from "./Segment";
import { StatusTag } from "./StatusTag";
const W = 1200;
const H = 720;
const root = projects.find((p) => p.root)!;
const groupIds = Object.keys(groups) as GroupId[];
function Marker({ p, on }: { p: Project; on: boolean }) {
const size = p.root ? 30 : 14;
const rest =
p.status === "CONCEPT" ? "border border-dashed border-ink/60" : p.status === "ARCHIVED" ? "border border-ink/30" : "border border-ink/65";
return (
<span
aria-hidden="true"
className={`relative box-border flex-none ${p.root ? "rounded-full" : ""} ${
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`
}`}
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 }) {
const body = (
<>
<span className="flex items-center justify-between">
<StatusTag status={p.status} />
<span aria-hidden="true" className="text-mute transition-colors group-hover:text-accent group-focus-visible:text-accent">
{p.href ? "↗" : "→"}
</span>
</span>
<span className="font-display text-[22px] font-semibold tracking-[0.04em] uppercase">{p.name}</span>
<span className="flex-1 text-xs leading-[1.65] text-ink-2">{p.desc}</span>
<span className="flex flex-wrap gap-1.5">
{p.tags.map((t) => (
<span key={t} className="bg-surface-2 px-[7px] py-[3px] text-[10px] text-mute">
{t}
</span>
))}
</span>
</>
);
const cls =
"group bracket flex min-h-[200px] scroll-mt-24 flex-col gap-3 border border-white/6 bg-surface p-[22px] [--bc:rgb(242_244_245/0.3)] hover:[--bc:var(--color-accent)] focus-visible:[--bc:var(--color-accent)]";
return p.href ? (
<a id={p.id} href={p.href} className={cls}>
{body}
</a>
) : (
<article id={p.id} className={cls}>
{body}
</article>
);
}
export function ProjectIndex() {
const [view, setView] = useState<"map" | "list">("map");
const [active, setActive] = useState<string>("orus");
// Deep links (/projects/#enclave) select that node; small screens start on the list.
useEffect(() => {
const id = decodeURIComponent(location.hash.slice(1));
const small = matchMedia("(max-width: 767px)").matches;
if (projects.some((p) => p.id === id) || small) {
// eslint-disable-next-line react-hooks/set-state-in-effect -- syncing with browser-only state once on mount
if (projects.some((p) => p.id === id)) setActive(id);
if (small) setView("list");
}
}, []);
const sel = projects.find((p) => p.id === active) ?? root;
const count = (s: Project["status"]) => String(projects.filter((p) => p.status === s).length).padStart(2, "0");
return (
<>
<div className="mt-8 flex flex-wrap items-end justify-between gap-6">
<ul aria-label="Status legend" className="flex flex-wrap gap-[18px] text-[9px] tracking-[0.2em] text-mute">
<li className="flex items-center gap-1.5">
<span aria-hidden="true" className="size-[5px] rounded-full bg-accent" />
LIVE {count("LIVE")}
</li>
<li>BUILDING {count("BUILDING")}</li>
<li>CONCEPT {count("CONCEPT")}</li>
<li>ARCHIVED {count("ARCHIVED")}</li>
</ul>
<div role="group" aria-label="View" className="hidden border border-white/12 js:flex">
{(["map", "list"] as const).map((v) => (
<button
key={v}
type="button"
aria-pressed={view === v}
onClick={() => setView(v)}
className={`min-h-11 min-w-[76px] border-b px-4 text-[11px] tracking-[0.22em] uppercase ${
view === v ? "border-accent bg-surface-2 text-ink" : "border-transparent text-mute hover:text-ink"
}`}
>
{v}
</button>
))}
</div>
</div>
{/* MAP: needs JS to be useful, so it is hidden without it. */}
<div className={`hidden ${view === "map" ? "js:block" : ""}`}>
<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 }}>
{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 />
))}
{projects
.filter((p) => !p.root)
.map((p) => (
<Segment
key={`${p.id}-${active === p.id}`}
x1={groups[p.group].x}
y1={groups[p.group].y}
x2={p.map.x}
y2={p.map.y}
on={active === p.id}
dashed={p.status === "CONCEPT"}
/>
))}
{groupIds.map((k) => {
const g = groups[k];
return (
<div key={k} aria-hidden="true">
<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>
);
})}
{projects.map((p) => {
const on = active === p.id;
const g = groups[p.group];
const labelLeft = !p.root && p.map.x < g.x;
const m = p.root ? 30 : 14;
const pad = 8;
return (
<button
key={p.id}
type="button"
aria-pressed={on}
onClick={() => setActive(p.id)}
onMouseEnter={() => setActive(p.id)}
onFocus={() => setActive(p.id)}
className={`bracket absolute flex min-h-12 items-center gap-2.5 whitespace-nowrap focus-visible:outline-none ${
on ? "[--bc:var(--color-accent)]" : "[--bc:transparent] hover:[--bc:var(--color-accent)]"
} ${labelLeft ? "flex-row-reverse text-right" : ""}`}
style={{
top: p.map.y - 24,
padding: `0 ${pad}px`,
...(labelLeft ? { right: W - p.map.x - m / 2 - pad } : { left: p.map.x - m / 2 - pad }),
}}
>
<Marker p={p} on={on} />
<span className="flex flex-col gap-[3px]">
<span
className={`font-display font-semibold tracking-[0.08em] uppercase ${p.root ? "text-xl" : "text-[13px]"} ${
p.status === "ARCHIVED" && !on ? "text-ink/70" : "text-ink"
}`}
>
{p.name}
</span>
<span className="text-[8px] tracking-[0.22em] text-mute">{p.root ? "ROOT" : p.status}</span>
</span>
</button>
);
})}
<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}
</div>
<div aria-hidden="true" className="absolute right-4 bottom-3 text-[9px] tracking-[0.2em] text-accent uppercase">
TRACKING ▸ {sel.name}
</div>
</div>
</div>
<section
aria-live="polite"
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"
>
<div>
<div className="flex flex-wrap items-center gap-3.5">
<StatusTag status={sel.status} />
<span className="text-[9px] tracking-[0.24em] text-mute uppercase">{sel.root ? "Root" : groups[sel.group].label}</span>
</div>
<h2 className="mt-3 font-display text-3xl font-semibold tracking-[0.04em] uppercase">{sel.name}</h2>
<p className="mt-2 max-w-[720px] text-[13px] leading-[1.7] text-ink-2">{sel.desc}</p>
<div className="mt-3.5 flex flex-wrap gap-2">
{sel.tags.map((t) => (
<span key={t} className="bg-surface-2 px-2 py-1 text-[10px] tracking-[0.1em] text-mute">
{t}
</span>
))}
</div>
</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>
)}
</section>
</div>
{/* LIST: always rendered; hidden only when JS is on and the map is chosen. */}
<div className={`mt-10 flex flex-col gap-14 ${view === "map" ? "js:hidden" : ""}`}>
{groupIds.map((k) => {
const items = projects.filter((p) => p.group === k);
return (
<section key={k} aria-labelledby={`g-${k}`}>
<div className="flex items-baseline gap-4 border-b border-white/8 pb-3.5">
<h2 id={`g-${k}`} className="font-display text-xl font-semibold tracking-[0.14em] uppercase">
{groups[k].label}
</h2>
<span className="text-[9px] tracking-[0.22em] text-mute">{String(items.length).padStart(2, "0")}</span>
</div>
<div className="mt-5 grid grid-cols-[repeat(auto-fill,minmax(280px,1fr))] gap-4">
{items.map((p) => (
<ProjectCard key={p.id} p={p} />
))}
</div>
</section>
);
})}
</div>
</>
);
}
+35
View File
@@ -0,0 +1,35 @@
/** A 1px tracking line between two points, in px, inside a positioned parent. */
export function Segment({
x1,
y1,
x2,
y2,
on,
dashed,
strong,
}: {
x1: number;
y1: number;
x2: number;
y2: number;
on: boolean;
dashed?: boolean;
strong?: boolean;
}) {
const len = Math.hypot(x2 - x1, y2 - y1);
const ang = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI;
const c = on ? "var(--color-accent)" : strong ? "rgb(242 244 245 / 0.2)" : "rgb(242 244 245 / 0.11)";
return (
<div
aria-hidden="true"
className={`pointer-events-none absolute h-px origin-[0_50%] ${on ? "seg-on shadow-[0_0_6px_var(--color-accent)]" : ""}`}
style={{
left: x1,
top: y1,
width: len,
transform: `rotate(${ang}deg)`,
background: dashed && !on ? `repeating-linear-gradient(90deg, ${c} 0 4px, transparent 4px 9px)` : c,
}}
/>
);
}
+18
View File
@@ -0,0 +1,18 @@
import { profile } from "@/content/profile";
export function SiteFooter() {
return (
<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">
<span>{profile.displayName.toUpperCase()} · BUILT UNDER ORUS</span>
<span className="flex gap-6">
<a href={profile.github} className="hover:text-accent">GITHUB</a>
<a href={profile.git} className="hover:text-accent">GIT.SELIMAJ.DEV</a>
</span>
<span aria-hidden="true">
41.33 N · 19.82 E · <span className="blink text-accent">▌</span>
</span>
</div>
</footer>
);
}
+78
View File
@@ -0,0 +1,78 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState } from "react";
import { OrusMark } from "./OrusMark";
import { SessionClock } from "./Telemetry";
const links = [
{ href: "/", label: "Home" },
{ href: "/about/", label: "About" },
{ href: "/projects/", label: "Projects" },
];
const isCurrent = (pathname: string, href: string) =>
href === "/" ? pathname === "/" : pathname.startsWith(href.replace(/\/$/, ""));
export function SiteHeader() {
const pathname = usePathname();
const [open, setOpen] = useState(false);
const isHome = pathname === "/";
return (
<header
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]">
<Link href="/" className="flex min-h-11 items-center gap-3" aria-label="Orus · selimaj.dev home">
<OrusMark />
<span className="font-display text-sm font-semibold tracking-[0.24em]">ORUS</span>
<span className="hidden text-[10px] tracking-[0.18em] text-mute sm:inline">/ SELIMAJ.DEV</span>
</Link>
<button
type="button"
className="flex min-h-11 items-center gap-2.5 px-1 text-[10px] tracking-[0.22em] md:hidden"
aria-expanded={open}
aria-controls="site-nav"
onClick={() => setOpen((o) => !o)}
>
{open ? "CLOSE" : "MENU"}
<svg width="18" height="12" viewBox="0 0 18 12" fill="none" stroke="currentColor" aria-hidden="true">
<path d="M0,1 H18 M6,6 H18 M0,11 H18" />
</svg>
</button>
<nav
id="site-nav"
aria-label="Primary"
className={`${open ? "block" : "hidden"} absolute inset-x-0 top-full border-y border-line bg-night/95 px-4 backdrop-blur md:static md:block md:border-0 md:bg-transparent md:p-0 md:backdrop-blur-none`}
>
<ul className="flex flex-col text-[11px] tracking-[0.2em] md:flex-row md:gap-9">
{links.map((l) => {
const current = isCurrent(pathname, l.href);
return (
<li key={l.href}>
<Link
href={l.href}
aria-current={current ? "page" : undefined}
onClick={() => setOpen(false)}
className={`flex min-h-11 items-center gap-2 uppercase md:py-3.5 ${current ? "text-ink" : "text-mute hover:text-accent"}`}
>
{current && <span aria-hidden="true" className="size-1.5 bg-accent shadow-[0_0_8px_var(--color-accent)]" />}
{l.label}
</Link>
</li>
);
})}
</ul>
</nav>
<div aria-hidden="true" className="hidden text-right text-[10px] leading-relaxed tracking-[0.14em] text-mute lg:block">
<SessionClock />
</div>
</div>
</header>
);
}
+17
View File
@@ -0,0 +1,17 @@
import type { Status } from "@/content/projects";
const styles: Record<Status, string> = {
LIVE: "text-ink-2 border border-white/30",
BUILDING: "text-ink-2 border border-white/30",
CONCEPT: "text-ink-2 border border-dashed border-white/40",
ARCHIVED: "text-mute border border-white/12",
};
export function StatusTag({ status }: { status: Status }) {
return (
<span className={`inline-flex items-center gap-1.5 px-2 py-1 text-[9px] tracking-[0.2em] ${styles[status]}`}>
{status === "LIVE" && <span aria-hidden="true" className="size-[5px] rounded-full bg-accent" />}
{status}
</span>
);
}
+90
View File
@@ -0,0 +1,90 @@
"use client";
import { useSyncExternalStore } from "react";
// Real data: when this session opened, a random per-session hash, and the
// visitor's local clock. All of it is client-only and never leaves the page.
type Session = { start: number; hash: string };
let session: Session | null = null;
function getSession(): Session {
if (session) return session;
try {
const raw = sessionStorage.getItem("session");
if (raw) return (session = JSON.parse(raw) as Session);
} catch {}
const bytes = crypto.getRandomValues(new Uint8Array(8));
const hash = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
session = { start: Date.now(), hash };
try {
sessionStorage.setItem("session", JSON.stringify(session));
} catch {}
return session;
}
const noop = () => () => {};
const tick = (cb: () => void) => {
const id = setInterval(cb, 1000);
return () => clearInterval(id);
};
const nowSeconds = () => Math.floor(Date.now() / 1000);
export function useSession() {
return useSyncExternalStore(noop, getSession, () => null);
}
export function useNow() {
const s = useSyncExternalStore(tick, nowSeconds, () => null);
return s === null ? null : new Date(s * 1000);
}
const p = (n: number) => String(n).padStart(2, "0");
export function formatTime(d: Date) {
return `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`;
}
export function formatOffset(d: Date) {
const off = -d.getTimezoneOffset();
const a = Math.abs(off);
return `UTC${off >= 0 ? "+" : "-"}${p(Math.floor(a / 60))}:${p(a % 60)}`;
}
function formatElapsed(ms: number) {
const s = Math.max(0, Math.floor(ms / 1000));
return `${p(Math.floor(s / 60))}:${p(s % 60)}`;
}
export function SessionClock() {
const s = useSession();
const now = useNow();
const id = s ? `${s.hash.slice(0, 4)}·${s.hash.slice(4, 8)}`.toUpperCase() : "····";
return (
<>
<div>SESSION {id}</div>
<div>
<span className="text-accent">● LIVE</span> T+{s && now ? formatElapsed(now.getTime() - s.start) : "--:--"}
</div>
</>
);
}
export function SessionStamp() {
const s = useSession();
return (
<>
<div>SESSION OPEN · {s ? new Date(s.start).toISOString().slice(0, 19) + "Z" : "…"}</div>
<div>HASH {s ? `${s.hash.slice(0, 8)}·${s.hash.slice(8)}` : "…"} · RES CITY-SCALE</div>
</>
);
}
export function LocalTime({ prefix = "LOCAL" }: { prefix?: string }) {
const now = useNow();
return (
<>
{prefix} {now ? `${formatTime(now)} ${formatOffset(now)}` : "--:--:--"}
</>
);
}
+19
View File
@@ -0,0 +1,19 @@
/**
* Hero background, bottom to top: terrain contours (real Dajt elevation data),
* grid, scanlines, vignette. Purely decorative.
*/
export function TerrainLayer({ className = "" }: { className?: string }) {
return (
<div aria-hidden="true" className={`pointer-events-none absolute inset-0 overflow-hidden ${className}`}>
{/* eslint-disable-next-line @next/next/no-img-element -- decorative SVG, no optimisation needed */}
<img
src="/terrain.svg"
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%]"
/>
<div className="grid-bg absolute inset-0" />
<div className="scan-bg absolute inset-0" />
<div className="absolute inset-0 bg-[radial-gradient(ellipse_at_55%_35%,transparent_35%,rgb(0_0_0/0.75)_100%)]" />
</div>
);
}
+59
View File
@@ -0,0 +1,59 @@
/**
* Stylised Tirana city centre, drawn by hand at city scale only.
* Centred on Skanderbeg Square. No street-level detail beyond that.
*/
export function TiranaMap({ className = "" }: { className?: string }) {
return (
<svg viewBox="0 0 640 640" aria-hidden="true" className={className}>
<defs>
<radialGradient id="map-fade">
<stop offset="0.45" stopColor="#fff" />
<stop offset="1" stopColor="#000" />
</radialGradient>
<mask id="map-mask">
<rect width="640" height="640" fill="url(#map-fade)" />
</mask>
</defs>
<g mask="url(#map-mask)" fill="none" stroke="#fff" strokeWidth="1" className="draw">
<circle pathLength={1} cx="320" cy="320" r="300" strokeOpacity="0.08" style={{ "--d": "0.1s" } as React.CSSProperties} />
<path pathLength={1} strokeOpacity="0.14" d="M150,250 C170,170 250,130 330,135 C420,140 495,190 505,280 C515,370 470,450 380,480 C290,505 190,480 155,400 C135,350 138,300 150,250Z" />
<path pathLength={1} strokeOpacity="0.18" d="M320,640 L320,354" />
<path pathLength={1} strokeOpacity="0.18" d="M320,286 L310,0" />
<path pathLength={1} strokeOpacity="0.13" d="M286,316 L0,262" />
<path pathLength={1} strokeOpacity="0.13" d="M354,322 L640,352" />
<path pathLength={1} strokeOpacity="0.08" d="M296,296 L120,120" style={{ "--d": "0.2s" } as React.CSSProperties} />
<path pathLength={1} strokeOpacity="0.08" d="M346,344 L560,560" style={{ "--d": "0.2s" } as React.CSSProperties} />
<path pathLength={1} strokeOpacity="0.08" d="M346,296 L540,110" style={{ "--d": "0.2s" } as React.CSSProperties} />
<path pathLength={1} strokeOpacity="0.08" d="M294,346 L90,560" style={{ "--d": "0.2s" } as React.CSSProperties} />
<path pathLength={1} strokeOpacity="0.14" d="M0,470 C110,452 200,492 320,482 C440,472 530,500 640,490" />
<path pathLength={1} strokeOpacity="0.14" d="M0,478 C110,460 200,500 320,490 C440,480 530,508 640,498" />
<g strokeOpacity="0.07" style={{ "--d": "0.35s" } as React.CSSProperties}>
{[
[360, 200, 60, 44], [432, 212, 46, 56], [360, 380, 70, 40], [208, 372, 64, 50], [200, 212, 56, 40],
[442, 380, 44, 60], [232, 150, 40, 34], [380, 150, 54, 30], [236, 268, 40, 30], [364, 262, 34, 34],
].map(([x, y, w, h]) => (
<rect key={`${x}-${y}`} pathLength={1} x={x} y={y} width={w} height={h} style={{ "--d": "0.35s" } as React.CSSProperties} />
))}
</g>
<circle pathLength={1} cx="320" cy="320" r="34" strokeOpacity="0.24" style={{ "--d": "0.4s" } as React.CSSProperties} />
<circle pathLength={1} cx="320" cy="320" r="16" strokeOpacity="0.12" style={{ "--d": "0.45s" } as React.CSSProperties} />
</g>
<text x="362" y="352" fill="#8b9296" fillOpacity="0.7" fontSize="9" letterSpacing="1.6" fontFamily="var(--font-mono)">
SKANDERBEG SQ.
</text>
</svg>
);
}
export function LockReticle({ className = "", style }: { className?: string; style?: React.CSSProperties }) {
return (
<div aria-hidden="true" className={`bracket size-[120px] [--bc:var(--color-accent)] [--bl:16px] ${className}`} style={style}>
<svg viewBox="0 0 120 120" width="120" height="120" fill="none" stroke="var(--color-accent)">
<circle className="spin" cx="60" cy="60" r="34" strokeDasharray="3 5" strokeOpacity="0.8" />
<circle cx="60" cy="60" r="20" strokeOpacity="0.35" />
<path d="M60,14 L60,30 M60,90 L60,106 M14,60 L30,60 M90,60 L106,60" />
<circle cx="60" cy="60" r="2.5" fill="var(--color-accent)" />
</svg>
</div>
);
}
+111
View File
@@ -0,0 +1,111 @@
import type { Status } from "./projects";
export type Proof = {
label: string;
note: string;
href?: string;
status?: Status;
};
export type DossierFile = {
slug: string;
no: string;
title: string;
summary: string;
proofSummary: string;
lead: string;
paragraphs: string[];
proof: Proof[];
};
// PLACEHOLDER: all copy below is a first draft from the spec. Edit freely.
export const dossier: DossierFile[] = [
{
slug: "leadership",
no: "01",
title: "Leadership",
summary: "Leading groups since I was ten. Clans first, then Orus.",
proofSummary: "Orus",
lead: "Leading groups and getting people to follow, since I was ten.",
paragraphs: [
"It started with gaming clans at 10 and 11, and the content around them.",
"The same instinct became Orus at 12 and 13.",
],
proof: [
{
label: "Orus",
note: "A community that has grown around the work",
href: "/projects/#orus",
status: "LIVE",
},
],
},
{
slug: "entrepreneurship",
no: "02",
title: "Entrepreneurship",
summary: "Orus as the umbrella. Systems where incentives and behavior matter.",
proofSummary: "Osias · Orus · EquitySMP",
lead: "Orus is the umbrella for everything serious I make.",
paragraphs: [
"Beyond 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.",
],
proof: [
{ label: "Osias", note: "osias.trade", href: "https://osias.trade", status: "BUILDING" },
{ label: "Orus", note: "The umbrella org", href: "/projects/#orus", status: "LIVE" },
{ label: "EquitySMP", note: "Real-world-style server economy", href: "/projects/#equitysmp", status: "BUILDING" },
],
},
{
slug: "builder",
no: "03",
title: "Builder",
summary: "The interesting problems sit one layer lower than where most people stop.",
proofSummary: "Saturn · Enclave · Gitea",
lead: "I don’t accept the layer underneath as a given.",
paragraphs: [
"I wrote my own client for Minecraft (Saturn). I’m designing a transport protocol to replace HTTP and WebSockets for Enclave’s chat. I host my own Git.",
"The interesting problems are usually one layer lower than where most people stop.",
],
proof: [
{ label: "Saturn", note: "Minecraft client, written from scratch", href: "/projects/#saturn", status: "ARCHIVED" },
{ label: "Enclave + transport protocol", note: "Chat platform, and the protocol it runs on", href: "/projects/#enclave", status: "BUILDING" },
{ label: "git.selimaj.dev", note: "Gitea on a Docker and Coolify homelab", href: "https://git.selimaj.dev", status: "LIVE" },
],
},
{
slug: "discipline",
no: "04",
title: "Discipline",
summary: "Formerly #1 on Discipline in Albania. Consistency, tracked in public.",
proofSummary: "Contribution graph · rank",
lead: "Formerly ranked #1 on Discipline in Albania.",
paragraphs: [
"I track consistency through contribution graphs.",
"A long project list reads as sustained output, not abandoned experiments.",
],
proof: [
{ label: "Contribution graph", note: "github.com/selimaj-dev", href: "https://github.com/selimaj-dev" },
{ label: "Ranking", note: "Formerly #1 on Discipline, Albania" },
],
},
{
slug: "passions",
no: "05",
title: "Passions",
summary: "Cyberdecks, long-range RC planes, algorithmic trading, Minecraft with friends.",
proofSummary: "Build logs",
lead: "The things I do because I can’t not.",
paragraphs: [
"Custom hardware: a briefcase cyberdeck, and a compact one that aims to be a Flipper Zero but better.",
"Long-range PC and 4G-controlled RC planes built around a custom watercooled powertrain. Algorithmic trading. Minecraft with friends.",
],
proof: [
{ label: "Cyberdecks", note: "Build photos and logs to come", href: "/projects/#briefcase-deck", status: "BUILDING" },
{ label: "RC planes", note: "Build photos and logs to come", href: "/projects/#rc-planes", status: "BUILDING" },
],
},
];
export const fileBySlug = (slug: string) => dossier.find((f) => f.slug === slug);
+56
View File
@@ -0,0 +1,56 @@
/**
* Everything personal or placeholder lives in src/content/.
* Items marked PLACEHOLDER are meant to be swapped later.
*/
export const profile = {
// PLACEHOLDER: final display form of the name is still TBD.
displayName: "Selimaj",
title: "Entrepreneur & Builder",
stance: "I build the layer underneath.",
// PLACEHOLDER: replace this one file to swap the profile picture.
pfp: "/profile/pfp.svg",
pfpIsPlaceholder: true,
github: "https://github.com/selimaj-dev",
git: "https://git.selimaj.dev",
} 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
* landmark). Never put anything finer than city scale here.
*/
export const lock = {
lat: "41.3275 N",
lon: "19.8187 E",
place: "Tirana · City centre",
} as const;
export type DossierField = {
label: string;
value: string;
links?: { label: string; href: string }[];
};
export const dossierFields: DossierField[] = [
{ label: "Based", value: "Tirana, Albania" },
{ label: "Affiliation", value: "Orus (founder)" },
// PLACEHOLDER decision: remove this entry to stop stating it publicly.
{ label: "Building since", value: "12" },
{ label: "Focus", value: "Privacy, independence, opsec, dev tooling" },
{ label: "Known for", value: "Saturn, Enclave" },
{ label: "Record", value: "Formerly #1 on Discipline, Albania" },
{
label: "Links",
value: "",
links: [
{ label: "GitHub", href: profile.github },
{ label: "git.selimaj.dev", href: profile.git },
],
},
];
+158
View File
@@ -0,0 +1,158 @@
export type Status = "LIVE" | "BUILDING" | "CONCEPT" | "ARCHIVED";
export type GroupId = "core" | "ventures" | "infra" | "hardware";
export type Project = {
id: string;
name: string;
group: GroupId;
status: Status;
desc: string;
tags: string[];
href?: string;
root?: boolean;
/** Position on the 1200×720 node map. */
map: { x: number; y: number };
};
export const groups: Record<GroupId, { label: string; x: number; y: number; side: "l" | "r" }> = {
core: { label: "Core", x: 360, y: 220, side: "l" },
ventures: { label: "Ventures & systems", x: 840, y: 220, side: "r" },
infra: { label: "Infrastructure", x: 360, y: 520, side: "l" },
hardware: { label: "Hardware", x: 840, y: 520, side: "r" },
};
// PLACEHOLDER: statuses for everything except Orus and the self-hosted
// infrastructure are best guesses. Links are filled in where known.
export const projects: Project[] = [
{
id: "orus",
name: "Orus",
group: "core",
root: true,
status: "LIVE",
desc: "Community and org for privacy, independence, opsec, and dev tools. Root of everything below.",
tags: ["community", "opsec", "org"],
map: { x: 600, y: 360 },
},
{
id: "saturn",
name: "Saturn",
group: "core",
status: "ARCHIVED",
desc: "Minecraft client. Did most of what I set out to do with it.",
tags: ["minecraft", "client"],
map: { x: 200, y: 90 },
},
{
id: "enclave",
name: "Enclave",
group: "core",
status: "BUILDING",
desc: "Discord-like chat platform, running on a custom transport protocol.",
tags: ["chat", "realtime"],
map: { x: 420, y: 70 },
},
{
id: "enclave-protocol",
name: "Enclave protocol",
group: "core",
status: "CONCEPT",
desc: "Transport protocol replacing HTTP and WSS; Enclave’s chat protocol runs on top of it. Concept and design notes.",
tags: ["networking", "protocol design"],
map: { x: 190, y: 300 },
},
{
id: "osias",
name: "Osias",
group: "ventures",
status: "BUILDING",
desc: "Blockchain-powered AI agent for trading emotions and behavior.",
tags: ["ai", "blockchain"],
href: "https://osias.trade",
map: { x: 700, y: 90 },
},
{
id: "equitysmp",
name: "EquitySMP",
group: "ventures",
status: "BUILDING",
desc: "Minecraft server built around an advanced real-world-style economy.",
tags: ["minecraft", "economy"],
map: { x: 990, y: 90 },
},
{
id: "mc-economy",
name: "MC economy",
group: "ventures",
status: "CONCEPT",
desc: "Jurisdictions and taxes, world-healing tied to liquidity, trading and IPOs, sandbox GTA-style elements.",
tags: ["systems design", "economy"],
map: { x: 1020, y: 280 },
},
{
id: "algo-trading",
name: "Algo trading",
group: "ventures",
status: "BUILDING",
desc: "Several repos exploring automated crypto trading strategies.",
tags: ["trading", "crypto"],
map: { x: 980, y: 400 },
},
{
id: "git",
name: "Self-hosted Git",
group: "infra",
status: "LIVE",
desc: "Gitea at git.selimaj.dev, on a Docker and Coolify homelab server behind Cloudflare.",
tags: ["gitea", "docker", "coolify"],
href: "https://git.selimaj.dev",
map: { x: 190, y: 450 },
},
{
id: "homelab",
name: "HomeLab",
group: "infra",
status: "LIVE",
desc: "The server environment everything self-hosted runs on.",
tags: ["infra", "docker"],
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",
name: "RC planes",
group: "hardware",
status: "BUILDING",
desc: "Custom long-range, PC and 4G-controlled planes pushing speed and efficiency, built around a custom watercooled powertrain.",
tags: ["hardware", "rc", "4g"],
map: { x: 720, y: 660 },
},
];
export const projectById = (id: string) => projects.find((p) => p.id === id);
/** Home page featured strip: positions are percentages of the stage. */
export const featured = [
{ id: "orus", x: 18, y: 50 },
{ id: "saturn", x: 46, y: 22 },
{ id: "enclave", x: 68, y: 52 },
{ id: "equitysmp", x: 44, y: 80 },
] as const;
+34
View File
@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./src/*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}