revamp projects/hardware as grouped index lists, drop emoji tiles, add Freezmo feature section

This commit is contained in:
Martin Dvořák 2026-10-11 23:22:30 +02:00
commit 95dd0128b8
10 changed files with 730 additions and 398 deletions

View file

@ -0,0 +1,140 @@
import type { CSSProperties, ReactNode } from "react";
import {
codingProjects,
techProjects,
type CodingProject,
type CodingProjectGroup,
type ProjectStatus,
type TechProject,
type TechProjectGroup,
} from "../data";
const STATUS_LABEL: Record<ProjectStatus, string> = {
finished: "Finished",
live: "Live",
wip: "WIP",
unfinished: "Unfinished",
discontinued: "Discontinued",
forgotten: "Forgotten",
};
const PROJECT_GROUPS: { id: CodingProjectGroup; title: string }[] = [
{ id: "games", title: "Games & game jams" },
{ id: "linux", title: "Linux & desktop" },
{ id: "bots", title: "Bots, servers & plugins" },
{ id: "apps", title: "Apps & experiments" },
];
const HARDWARE_GROUPS: { id: TechProjectGroup; title: string }[] = [
{ id: "desktops", title: "Desktops & servers" },
{ id: "laptops", title: "Laptops" },
{ id: "phones", title: "Phones" },
{ id: "consoles", title: "Consoles & handhelds" },
];
/**
* Grouped, repo-listing-style index that replaced the old card gallery.
* Coding rows: language-coloured dot — title (+ status pill) over the
* description — host/language tag. Hardware rows: plain rows with the year
* on the right. Coding rows are links; hardware rows are static.
*/
function Group({
title,
count,
children,
}: {
title: string;
count: number;
children: ReactNode;
}) {
return (
<section className="index-group">
<h3 className="index-group-head">
<span>{title}</span>
<span className="index-group-count">{count}</span>
</h3>
<div className="index-list">{children}</div>
</section>
);
}
function CodingRow({ project }: { project: CodingProject }) {
return (
<a
href={project.url}
target="_blank"
rel="noopener noreferrer"
className="index-row"
style={{ "--index-accent": project.accent } as CSSProperties}
>
<span className="index-dot" aria-hidden="true" />
<span className="index-body">
<span className="index-line1">
<span className="index-title">{project.title}</span>
{project.status && (
<span className={`index-status index-status-${project.status}`}>
{STATUS_LABEL[project.status]}
</span>
)}
</span>
<span className="index-desc">{project.desc}</span>
</span>
<span className="index-meta">
<span>{project.tag}</span>
<span className="index-arrow" aria-hidden="true">
↗
</span>
</span>
</a>
);
}
function HardwareRow({ project }: { project: TechProject }) {
return (
<div className="index-row index-row-plain">
<span className="index-body">
<span className="index-line1">
<span className="index-title">{project.title}</span>
</span>
<span className="index-desc">{project.desc}</span>
</span>
<span className="index-meta">{project.year ?? ""}</span>
</div>
);
}
export function CodingProjectIndex() {
return (
<div className="index-groups">
{PROJECT_GROUPS.map((group) => {
const items = codingProjects.filter((p) => p.group === group.id);
if (!items.length) return null;
return (
<Group key={group.id} title={group.title} count={items.length}>
{items.map((project) => (
<CodingRow key={project.slug} project={project} />
))}
</Group>
);
})}
</div>
);
}
export function HardwareProjectIndex() {
return (
<div className="index-groups">
{HARDWARE_GROUPS.map((group) => {
const items = techProjects.filter((p) => p.group === group.id);
if (!items.length) return null;
return (
<Group key={group.id} title={group.title} count={items.length}>
{items.map((project) => (
<HardwareRow key={project.slug} project={project} />
))}
</Group>
);
})}
</div>
);
}

View file

@ -6,10 +6,11 @@ import JokerLogo from "./JokerLogo";
import ServiceButton from "./ServiceButton";
import ContributionHeatmap from "./ContributionHeatmap";
import FeatureWindow from "./FeatureWindow";
import { CodingProjectIndex, HardwareProjectIndex } from "./ProjectIndex";
import Clock from "./Clock";
import { useTheme } from "./useTheme";
import { useColorScheme } from "./useColorScheme";
import { socials, codingProjects, techProjects, friends } from "../data";
import { socials, friends } from "../data";
const NAV = [
{ id: "overview", label: "Overview" },
@ -234,6 +235,23 @@ export default function WebLayout() {
</article>
</div>
<Section eyebrow="FREEZMO" title="Introducing Freezmo" className="web-feature">
<Image
src="/freezmo-app.webp"
alt="Freezmo quiz screen — a history question about the Schuman plan with AI grading feedback"
width={1941}
height={942}
sizes="(max-width: 900px) 100vw, 1100px"
className="web-feature-shot"
/>
<p className="web-feature-text">
Started working on a new project Freezmo — basically a free version of the
famous Gizmo app, but instead of paying for AI you use your ollama free cloud
API key. Notes in, flashcards and quizzes out. Try it at{" "}
<a href="https://freezmo.dvorakmortonxd.dev">freezmo.dvorakmortonxd.dev</a>.
</p>
</Section>
<div className="web-split">
<Section eyebrow="REVIVAL.INC" title="Revival Inc." className="web-feature">
<FeatureWindow variant="game" />
@ -246,7 +264,7 @@ export default function WebLayout() {
</p>
</Section>
<Section eyebrow="RustyWolf" title="Introducing RustyWolf" className="web-feature">
<Section eyebrow="BROWSER" title="RustyWolf" className="web-feature">
<FeatureWindow variant="browser" />
<p className="web-feature-text">
A lightweight browser made in Rust with WebKitGTK. It&#39;s the successor to my
@ -267,37 +285,11 @@ export default function WebLayout() {
</Section>
<Section id="projects" eyebrow="Portfolio" title="Things I've worked on">
<div className="project-grid">
{codingProjects.map((project, idx) => (
<a
key={idx}
href={project.url}
target="_blank"
rel="noopener noreferrer"
className="project-card"
>
<div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
{project.emoji}
</div>
<h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p>
</a>
))}
</div>
<CodingProjectIndex />
</Section>
<Section id="hardware" eyebrow="My gear" title="Hardware I own">
<div className="project-grid">
{techProjects.map((project, idx) => (
<div key={project.title} className="project-card">
<div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
{project.emoji}
</div>
<h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p>
</div>
))}
</div>
<HardwareProjectIndex />
</Section>
<Section id="friends" eyebrow="Links" title="Friend's websites and projects">

View file

@ -2,15 +2,11 @@
import Image from "next/image";
import { useEffect, useRef, useState, type KeyboardEvent as ReactKeyboardEvent, type ReactNode } from "react";
import {
socials,
codingProjects,
techProjects,
friends,
} from "../data";
import { socials, friends } from "../data";
import ServiceButton from "./ServiceButton";
import ContributionHeatmap from "./ContributionHeatmap";
import FeatureWindow from "./FeatureWindow";
import { CodingProjectIndex, HardwareProjectIndex } from "./ProjectIndex";
import JokerLogo from "./JokerLogo";
import { setTheme } from "./useTheme";
@ -107,10 +103,14 @@ function NowApp() {
<div className="app-text">
<p>
I&#39;m currently hosting an SMP (dm me on discord). Also I&#39;m pretty much
cooking daily, because im bulking through out the non-summer seasons. And me and
my pal <a href="https://skopec.dev">Pepa</a> are working on an education app so we
can study more efficiently, It&#39;s called{" "}
<a href="https://git.dvorakmortonxd.dev/edu/maturita.c">maturita.c</a>!
cooking daily, because im bulking through out the non-summer seasons. I started
working on a new project{" "}
<a href="https://freezmo.dvorakmortonxd.dev">Freezmo</a>, basically a free
version of the famous Gizmo app but instead of paying for AI you use your ollama
free cloud api key. The education app I started with my pal{" "}
<a href="https://skopec.dev">Pepa</a>, maturita.c, now lives on as{" "}
<a href="https://github.com/pepaskopec-blip/Graduately">Graduately</a> under his
wing.
</p>
</div>
</div>
@ -204,23 +204,7 @@ function ServicesApp() {
function ProjectsApp() {
return (
<div className="app-pad">
<div className="project-grid">
{codingProjects.map((project, idx) => (
<a
key={idx}
href={project.url}
target="_blank"
rel="noopener noreferrer"
className="project-card"
>
<div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
{project.emoji}
</div>
<h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p>
</a>
))}
</div>
<CodingProjectIndex />
</div>
);
}
@ -228,17 +212,7 @@ function ProjectsApp() {
function HardwareApp() {
return (
<div className="app-pad">
<div className="project-grid">
{techProjects.map((project, idx) => (
<div key={project.title} className="project-card">
<div className={`project-thumb thumb-${idx % 6}`} aria-hidden="true">
{project.emoji}
</div>
<h4 className="project-title">{project.title}</h4>
<p className="project-desc">{project.desc}</p>
</div>
))}
</div>
<HardwareProjectIndex />
</div>
);
}
@ -480,7 +454,7 @@ function RevivalApp() {
function RustyWolfApp() {
return (
<div className="app-pad app-center">
<h2 className="app-h1">Introducing RustyWolf</h2>
<h2 className="app-h1">RustyWolf</h2>
<FeatureWindow variant="browser" />
<p className="app-h2">What makes it so cool???</p>
<p className="app-text">
@ -513,7 +487,7 @@ const TERMINAL_FILES: Record<string, string> = {
"about_me.md":
"Hey, I'm Martin Dvorak, a 17 year old IT student from the Czech Republic.\nI self-host way too many things and tinker with GNU/Linux.",
"now.txt":
"Currently hosting an SMP and building an education app with Pepa.",
"Currently hosting an SMP. Started working on a new project Freezmo, basically a free version of the famous Gizmo app but instead of paying for AI you use your ollama free cloud api key.",
"resume.txt":
"sssvt.cz - network management & administration (2025-)\nGentoo, SwayFX, RustyWolf, dxdwm, Revival Inc.",
};

View file

@ -4,19 +4,46 @@ export interface Social {
url: string;
}
/** Lifecycle state of a coding project, shown as a chip on its index row. */
export type ProjectStatus =
| "finished"
| "live"
| "wip"
| "unfinished"
| "discontinued"
| "forgotten";
/** Section a coding project is filed under in the Projects index. */
export type CodingProjectGroup = "games" | "linux" | "bots" | "apps";
export interface CodingProject {
title: string;
desc: string;
/** Emoji glyph shown on the card's CSS gradient tile (no image assets). */
emoji: string;
/** Stable key for the index row (also the React key), e.g. `rustywolf`. */
slug: string;
/** Index group the project is listed under. */
group: CodingProjectGroup;
/** Status chip on the row; omitted for grab-bag entries. */
status?: ProjectStatus;
/** Language or host badge on the row. */
tag: string;
/** Hex accent for the row dot + hover tint. */
accent: string;
url: string;
}
/** Section a device is filed under in the Hardware index. */
export type TechProjectGroup = "desktops" | "laptops" | "phones" | "consoles";
export interface TechProject {
title: string;
desc: string;
/** Emoji glyph shown on the card's CSS gradient tile (no image assets). */
emoji: string;
/** Stable key for the index row (also the React key), e.g. `macbook-air-m4`. */
slug: string;
/** Index group the device is listed under. */
group: TechProjectGroup;
/** Model / build year, shown on the right when known. */
year?: string;
}
export interface Friend {
@ -54,190 +81,317 @@ export const socials: Social[] = [
export const codingProjects: CodingProject[] = [
{
title: "Hospital Execution (Unfinished)",
title: "Hospital Execution",
desc: "the first ever real game I made, in pygame. I've abandoned it.",
emoji: "🏥",
slug: "hospital-execution",
group: "games",
status: "unfinished",
tag: "itch.io",
accent: "#fa5c5c",
url: "https://martin-dv.itch.io/hospital-execution-demo",
},
{
title: "Underbaranoia (Finished)",
title: "Underbaranoia",
desc: "basically a undertale like infinite fight simulator that gets progressively harder, made in pygame.",
emoji: "⚔️",
slug: "underbaranoia",
group: "games",
status: "finished",
tag: "itch.io",
accent: "#fa5c5c",
url: "https://martin-dv.itch.io/underbaranoia",
},
{
title: "Offline memer (Finished)",
title: "Offline memer",
desc: "parody of the discord bot \"Dank Memer\" made in batch. (this was technically my first ever game I published).",
emoji: "🤖",
slug: "offline-memer",
group: "games",
status: "finished",
tag: "itch.io",
accent: "#fa5c5c",
url: "https://martin-dv.itch.io/offline-memer",
},
{
title: "4y (Forgotten)",
title: "The Larvae",
desc: "my entry for Mini Jam 212 (theme: Control), made in pygame. you spawn in a well developed city and parasite on people with your manipulative powers until you get so rich you destroy it.",
slug: "the-larvae",
group: "games",
status: "unfinished",
tag: "Python",
accent: "#cf6bd6",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/the-larvae",
},
{
title: "4y",
desc: "just a renamed fork of the ly login manager (based login manager btw I love it), no extra features, just a rename, its actually outdated so dont use it.",
emoji: "🔐",
slug: "4y",
group: "linux",
status: "forgotten",
tag: "C",
accent: "#7d8590",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/4y",
},
{
title: "4rch linux (Forgotten)",
title: "4rch linux",
desc: "same as above, basically just a renamed fork of arch linux with calamares installer, also outdated and not maintained at all so no use please.",
emoji: "🐧",
slug: "4rch-linux",
group: "linux",
status: "forgotten",
tag: "Shell",
accent: "#89e051",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/4rch-linux",
},
{
title: "You_Are_An_Idiot_Virus.py (Finished)",
title: "You_Are_An_Idiot_Virus.py",
desc: "this is just a python script that just spams blinking \"you are an idiot\" windows, made it as a prank I think, not really sure rn.",
emoji: "🪟",
slug: "uranidiot",
group: "apps",
status: "finished",
tag: "Python",
accent: "#3572A5",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/uranidiot",
},
{
title: "Venturesome (Discontinued)",
title: "Venturesome",
desc: "a browser I wrote in python using PyQtWebEngine with PyQt5, actually pretty cool and modular but I couldnt get past the stage of adding a tab system, basically surpassed by my other project: RustyWolf.",
emoji: "🌐",
slug: "venturesome",
group: "linux",
status: "discontinued",
tag: "Python",
accent: "#3572A5",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/Venturesome",
},
{
title: "Mandelbrot set in Pygame (Finished)",
title: "Mandelbrot set in Pygame",
desc: "a python pygame script that renders the mandelbrot set, made it when I was fascinated by how it looks and ngl it still looks cool af.",
emoji: "🌀",
slug: "mandelbrot",
group: "apps",
status: "finished",
tag: "Python",
accent: "#8b5cf6",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/mandelbrot",
},
{
title: "Underpygametale (Unfinished)",
title: "Underpygametale",
desc: "a remake of undertale written in pygame, got it to about the flowey part (at the beginning) and didnt touch it in a long time.",
emoji: "💀",
slug: "underpygametale",
group: "games",
status: "unfinished",
tag: "Python",
accent: "#ef476f",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/underpygametale",
},
{
title: "Many discord bots...",
desc: "so many of them, once I (allegedly, pls discord dont ban me) even wrote a \"self-bot\" which auto-advertised a minecraft server on minecraft discord server in lfg-hosting every two hours, around 800 people joined in total.",
emoji: "💬",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/PYGETEX",
desc: "so many of them, once I (allegedly, pls discord dont ban me) even wrote a \"self-bot\" (lfg-bot) which auto-advertised a minecraft server on minecraft discord server in lfg-hosting every two hours, around 800 people joined in total.",
slug: "lfg-bot",
group: "bots",
tag: "Python",
accent: "#5865F2",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/lfg-bot",
},
{
title: "MMO minecraft plugin (Unfinished)",
title: "MMO minecraft plugin",
desc: "a minceraft plugin with custom bosses, items, and functions. pretty barebones and basic but I did it all manually before vibecoding was even a thing so what.",
emoji: "⛏️",
slug: "mmo-plugin",
group: "bots",
status: "unfinished",
tag: "YouTube",
accent: "#ff3d3d",
url: "https://www.youtube.com/watch?v=cSAX5Ds61ac",
},
{
title: "Many many failed minecraft servers..",
desc: "creative games, softcraft, frostville, eusmp, eurthsmp, softsmp and many other SMPs that died later on. I cant even count how many mc servers I had in total.",
emoji: "🧱",
slug: "smp-graveyard",
group: "bots",
tag: "Discord",
accent: "#62b47a",
url: "https://discord.gg/KtHRm9kRmy",
},
{
title: "Swords and Sandals 2 pygame remake (Unfinished)",
title: "Swords and Sandals 2 pygame remake",
desc: "an unfinished remake of swords and sandals 2 in pygame, I got bored of it and never finished it but ill finish it one day for sure lol.",
emoji: "🗡️",
slug: "sas2py",
group: "games",
status: "unfinished",
tag: "Python",
accent: "#d97706",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/sas2py",
},
{
title: "Llama.cpp server",
desc: "my llama.cpp vm running on my proxmox with a gpu passthroughed RTX 3070, dm me if u wanna test it lol.",
emoji: "🦾",
slug: "llama-cpp",
group: "bots",
status: "live",
tag: "self-hosted",
accent: "#76b900",
url: "https://ai.dvorakmortonxd.dev/",
},
{
title: "Batch MSDOS Simulator (Finished)",
title: "Batch MSDOS Simulator",
desc: "a freshman school project on making games in batch, it was like an OS simulator with a web browser, games, photos, even a video suprise (youll have to try it to figure that one out lol).",
emoji: "💾",
slug: "dvorakmsdos",
group: "linux",
status: "finished",
tag: "Batchfile",
accent: "#b6d94c",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/DvorakMSDOS",
},
{
title: "RustyWolf (WIP, Available now!)",
desc: "a browser I made in rust with webkitgtk, I made it to be lightweight and to be able to be ran on macbook unibody 2010 before I realised I got scammed.. It's the successor to my previous project Venturesome :)",
emoji: "🐺",
title: "RustyWolf",
desc: "my privacy focused browser made in rust with webkitgtk, made lightweight so it can run on a macbook unibody 2010 before I realised I got scammed.. It's the successor to my previous project Venturesome :)",
slug: "rustywolf",
group: "linux",
status: "wip",
tag: "Rust",
accent: "#dea584",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/RustyWolf",
},
{
title: "dxdwm (Discontinued)",
desc: "a small x11 window manager written in rust.",
emoji: "🪟",
title: "dxdwm",
desc: "a small x11 window manager. originally written in rust (dxdwm-rs), now continued in C.",
slug: "dxdwm",
group: "linux",
status: "discontinued",
tag: "C",
accent: "#94a3b8",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/dxdwm",
},
{
title: "Revival Inc. (Finished)",
title: "Revival Inc.",
desc: "My game written in pygame and packaged with pygbag made for the Mini Jame Gam #55! tho I only made it in 6 hours lmao",
emoji: "🧟",
slug: "revival-inc",
group: "games",
status: "finished",
tag: "Python",
accent: "#10b981",
url: "https://dvorakmortonxd.dev/revival-inc",
},
{
title: "freezmo",
desc: "a free version of the famous Gizmo app: notes in, flashcards and quizzes out — instead of paying for AI you use your own ollama free cloud api key.",
slug: "freezmo",
group: "apps",
status: "wip",
tag: "TypeScript",
accent: "#3178c6",
url: "https://freezmo.dvorakmortonxd.dev",
},
{
title: "qol-tweaks",
desc: "my quality of life plugin for the dvorakmortonxd SMP.",
slug: "qol-tweaks",
group: "bots",
status: "live",
tag: "Java",
accent: "#b07219",
url: "https://git.dvorakmortonxd.dev/dvorakmortonxd/qol-tweaks",
},
];
export const techProjects: TechProject[] = [
{
title: "Gentoo Setup",
desc: "my main computer with gentoo and SwayFX. specs: Ryzen 5 3600 (6c/12t), RX 580 8GB ASUS ROG, 16gb of ram and a 240gb sata ssd.",
emoji: "🐧",
slug: "gentoo-swayfx",
group: "desktops",
},
{
title: "Proxmox Server",
desc: "my personal proxmox server (dm me on ig if u need a VPS/hosting), specs: Xeon E5 2680 v4 (14c/28t), RTX 3070 (llama.cpp), 68gb ddr4 rdimm ram (2*16gb, 1*32gb, 1*4gb), 256gb of sata m2 space + 1tb hdd + 750gb hdd.",
emoji: "🖥️",
slug: "proxmox-server",
group: "desktops",
},
{
title: "Xbox 360 S (badupdate)",
desc: "xbox 360 slim (trinity) with badupdate exploit, allowing to boot custom software. (not my photo), the specs are XCGPU (3c/6t), 0.5GB of gddr3 ram and a 500gb hdd.",
emoji: "🎮",
slug: "xbox-360-s",
group: "consoles",
year: "2010",
},
{
title: "Lenovo Legion Go S",
desc: "my gaming handheld which I sometimes use as a computer, it has 32gb of unified lpddr5x memory and 1tb of nvme storage, I dualboot artix linux (openrc) with SwayFX and stock SteamOS on it.",
emoji: "🕹️",
slug: "legion-go-s",
group: "consoles",
year: "2025",
},
{
title: "Macbook Air M4 13\"",
desc: "my main device for basically everything on the go except gaming. specs are Apple M4 (cpu:10c,gpu:8c), 16gb of unified ram (lpddr5x), 256gb Storage.",
emoji: "💻",
slug: "macbook-air-m4",
group: "laptops",
year: "2025",
},
{
title: "Macbook Air 2013 11\"",
desc: "bought this for tinkering lol, specs are intel core i5 (2c/4t), 8gb of ddr3 ram and 250gb ssd, installed arch linux on it with i3wm.",
emoji: "💻",
slug: "macbook-air-2013",
group: "laptops",
year: "2013",
},
{
title: "iPhone 7 128gb",
desc: "somehow found a listing for 4 bucks (refurbed.cz) for it so I got it lol, it has a little dent I marked it on the photo (which isnt mine btw), except cosmetic damage its pretty much fully functional, specs are Apple A10 (4c/4t), 2gb of ram and 128gb of storage",
emoji: "📱",
slug: "iphone-7",
group: "phones",
year: "2016",
},
{
title: "Macbook Unibody 2010",
desc: "found random listing on bazos for 15 bucks but after I bought a power adapter it turned out to be non functioning so I got scammed :(",
emoji: "💻",
slug: "macbook-unibody-2010",
group: "laptops",
year: "2010",
},
{
title: "ThinkPad T400s 14\"",
desc: "got it from a random guy for 20 bucks, its an old laptop from 2009. I put void linux on it and i3wm on it, specs are intel core 2 duo (2c/2t), 8gb ddr3 ram and 800gb hdd.",
emoji: "💻",
slug: "thinkpad-t400s",
group: "laptops",
year: "2009",
},
{
title: "Asus N56JR 15.6\"",
desc: "one of my first laptops my dad got for me when I was like 12, still works but the battery is KO, specs are intel core i7 4700HQ (4c/8t), 8gb ddr3 ram and 240gb ssd, it currently runs ubuntu linux 24.04LTS and my mom uses it for movies and shows.",
emoji: "💻",
slug: "asus-n56jr",
group: "laptops",
year: "2013",
},
{
title: "Small Unknown XP Acer Laptop",
desc: "found that one in ewaste, I think it has like 2gb of ram and an intel atom, not sure what to do with it yet",
emoji: "📟",
slug: "unknown-acer-xp",
group: "laptops",
},
{
title: "Dell Latitude E6320",
desc: "also from ewaste, put mint on it but didnt do anything yet, lowk kinda capable for its time in 2011 so I might put it to use later but the battery is KO.",
emoji: "💻",
slug: "latitude-e6320",
group: "laptops",
year: "2011",
},
{
title: "iPhone 11 128gb",
desc: "my first iphone I got from my parents at 13, rn my dad has it because I switched over to the iphone 16 pro in 2025, specs are Apple A13 (6c/6t), 4gb of ram and 128gb of storage. (NOT MY IMAGE)",
emoji: "📱",
slug: "iphone-11",
group: "phones",
year: "2019",
},
{
title: "iPhone 16 Pro 128gb",
desc: "originally wanted iphone 13 mini (because of the size) but I somehow decided to get 16 pro, which is a bit smaller than my older iphone 11, specs are Apple A18 pro (6c/6t), (btw this chip is the same chip used in the macbook neo so thats cool) and 8gb of ram and 128gb of storage, my main phone.",
emoji: "📱",
slug: "iphone-16-pro",
group: "phones",
year: "2025",
},
{
title: "Xbox one S 2TB",
desc: "my dad bought it for me also in like 2018 or 2019 and I sometimes play the console edition of minecraft because I like to play the gliding minigame on it when someone comes over.",
emoji: "🎮",
slug: "xbox-one-s",
group: "consoles",
year: "2016",
},
];

View file

@ -852,41 +852,6 @@ a.win-icon {
color: #9a9a9a;
}
.app-surface .project-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 12px;
}
.app-surface .project-card {
padding: 10px;
border: 1px solid rgba(0, 0, 0, 0.15);
border-radius: 6px;
background: linear-gradient(to bottom, #ffffff, #f0f4fa);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.14);
}
.app-surface .project-card:hover {
border-color: #7db9e8;
background: #ffffff;
box-shadow: 0 0 0 1px #7db9e8, 0 0 14px rgba(110, 185, 255, 0.5),
0 8px 18px rgba(0, 25, 55, 0.2);
}
.app-surface .project-thumb {
height: 150px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.12);
}
.app-surface .project-title {
color: #0e3355;
}
.app-surface .project-card:hover .project-title {
color: #0d5aa7;
}
.app-surface .project-desc {
color: #43607c;
}
.app-surface .project-card:hover .project-desc {
color: #1d3e5c;
}
.app-surface .gym-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
@ -1151,29 +1116,29 @@ a.win-icon {
.win-body .service-button:hover .service-desc {
color: #d3dae3;
}
.win-body .project-card {
border-color: #1b1e20;
background: linear-gradient(to bottom, #31363b, #2a2e32);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
/* Project/Hardware index inside the dark KDE windows. */
.win-body {
--index-line: rgba(255, 255, 255, 0.07);
--status-finished: #4ade80;
--status-live: #60a5fa;
--status-wip: #fbbf24;
--status-unfinished: #fb923c;
--status-discontinued: #f87171;
--status-forgotten: #94a3b8;
}
.win-body .project-card:hover {
border-color: #3daee9;
background: #353b40;
box-shadow: 0 0 0 1px #3daee9, 0 0 14px rgba(61, 174, 233, 0.45);
.win-body .index-list {
background: rgba(255, 255, 255, 0.03);
}
.win-body .project-thumb {
border-color: #1b1e20;
}
.win-body .project-title {
.win-body .index-title {
color: #eff0f1;
}
.win-body .project-card:hover .project-title {
color: #3daee9;
}
.win-body .project-desc {
.win-body .index-desc {
color: #b0b6bc;
}
.win-body .project-card:hover .project-desc {
.win-body a.index-row:hover {
background: rgba(255, 255, 255, 0.06);
}
.win-body a.index-row:hover .index-desc {
color: #d3dae3;
}
@ -1600,9 +1565,6 @@ html[data-theme="skeuo"] .fw {
.app-pad {
padding: 14px;
}
.app-surface .project-grid {
grid-template-columns: 1fr;
}
.app-surface .social-buttons {
grid-template-columns: 1fr 1fr;
}
@ -1613,8 +1575,14 @@ html[data-theme="skeuo"] .fw {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 6px;
}
.app-surface .project-thumb {
height: 140px;
.app-surface .index-group-head {
font-size: 0.6rem;
}
.app-surface .index-row {
padding: 10px 11px;
}
.app-surface .index-desc {
font-size: 0.75rem;
}
}

View file

@ -44,6 +44,15 @@
--red: #e11d48;
--darkpink: #db2777;
/* Project/Hardware index tokens */
--index-line: rgba(16, 24, 43, 0.1);
--status-finished: #15803d;
--status-live: #1d4ed8;
--status-wip: #b45309;
--status-unfinished: #c2410c;
--status-discontinued: #b91c1c;
--status-forgotten: #64748b;
--web-font: "Inter", ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
--font-custom-mono: ui-monospace, "JetBrains Mono", "SFMono-Regular", Menlo, Consolas,
@ -84,6 +93,15 @@ html[data-theme="web"][data-scheme="dark"] {
--purple: #c084fc;
--red: #fb7185;
--darkpink: #f472b6;
/* Project/Hardware index tokens */
--index-line: rgba(255, 255, 255, 0.09);
--status-finished: #4ade80;
--status-live: #60a5fa;
--status-wip: #fbbf24;
--status-unfinished: #fb923c;
--status-discontinued: #f87171;
--status-forgotten: #94a3b8;
}
html[data-theme="web"] {
@ -430,6 +448,17 @@ html[data-theme="web"] body {
display: flex;
flex-direction: column;
}
/* Real app screenshot inside a feature panel (Freezmo) — framed like the CSS
mock windows so it sits in the same visual language. */
.web-feature-shot {
display: block;
width: 100%;
height: auto;
margin-bottom: 14px;
border-radius: 16px;
border: 1px solid var(--glass-border);
box-shadow: 0 18px 36px -22px rgba(23, 32, 60, 0.5);
}
/* ============================================================
FEATURE WINDOWS
Asset-free decorative mock windows that replace the old
@ -1161,78 +1190,145 @@ html[data-theme="web"] body {
}
/* ============================================================
PROJECTS / HARDWARE
============================================================ */
.project-grid {
/* Masonry-ish gallery: multi-column packs the cards tightly, so a short card
never leaves a half-empty slot under a tall neighbour. */
columns: 3 250px;
column-gap: 14px;
PROJECTS / HARDWARE INDEX
------------------------------------------------------------
Grouped repo-listing layout: per-section group headers, then
rows of accent dot – title (+ status) over description – tag /
year. Replaced the old card gallery. */
.index-groups {
display: flex;
flex-direction: column;
gap: 22px;
}
.project-card {
display: block;
break-inside: avoid;
-webkit-column-break-inside: avoid;
margin-bottom: 14px;
padding: 12px;
border-radius: 16px;
border: 1px solid var(--btn-border, var(--glass-border));
.index-group-head {
display: flex;
align-items: center;
gap: 10px;
margin: 0 0 8px;
font-family: var(--font-custom-mono);
font-size: 0.66rem;
font-weight: 700;
letter-spacing: 0.16em;
text-transform: uppercase;
color: var(--gray);
}
.index-group-count {
font-weight: 600;
color: color-mix(in srgb, var(--gray) 55%, transparent);
}
.index-group-head::after {
content: "";
flex: 1;
height: 1px;
background: var(--index-line);
}
.index-list {
border: 1px solid var(--index-line);
border-radius: 12px;
overflow: hidden;
background: var(--btn-bg, var(--glass-lo));
box-shadow: var(--btn-shadow, none);
}
.index-list > * + * {
border-top: 1px solid var(--index-line);
}
.index-row {
display: grid;
grid-template-columns: 10px minmax(0, 1fr) auto;
gap: 14px;
align-items: start;
padding: 11px 14px;
color: inherit;
text-decoration: none;
transition: transform 0.22s ease, border-color 0.22s ease, box-shadow 0.22s ease,
background-color 0.22s ease;
}
.project-card:hover {
transform: translateY(-4px);
border-color: var(--accent-line);
/* Hardware rows: no accent dot (it carried no meaning there), so the body
takes the full first track. */
.index-row-plain {
grid-template-columns: minmax(0, 1fr) auto;
}
a.index-row {
transition: background-color 0.16s ease;
}
a.index-row:hover {
background: var(--btn-bg-hi, var(--glass-hi));
box-shadow: 0 20px 38px -20px rgba(23, 32, 60, 0.55);
}
.project-thumb {
display: grid;
place-items: center;
height: 150px;
width: 100%;
margin-bottom: 12px;
border-radius: 12px;
border: 1px solid var(--glass-border);
font-size: 2.6rem;
line-height: 1;
user-select: none;
text-shadow: 0 3px 10px rgba(0, 0, 0, 0.35);
.index-dot {
width: 9px;
height: 9px;
margin-top: 6px;
border-radius: 50%;
background: var(--index-accent, var(--accent));
box-shadow: 0 0 9px color-mix(in srgb, var(--index-accent, var(--accent)) 55%, transparent);
}
/* Shared gradient palette for the card tiles (no image assets). */
.thumb-0 {
background: linear-gradient(150deg, #6d5efc, #2f8bff);
.index-line1 {
display: flex;
align-items: baseline;
flex-wrap: wrap;
gap: 8px;
}
.thumb-1 {
background: linear-gradient(150deg, #ec4899, #db2777);
}
.thumb-2 {
background: linear-gradient(150deg, #f59e0b, #d97757);
}
.thumb-3 {
background: linear-gradient(150deg, #10b981, #0d9488);
}
.thumb-4 {
background: linear-gradient(150deg, #8b5cf6, #6366f1);
}
.thumb-5 {
background: linear-gradient(150deg, #06b6d4, #3b82f6);
}
.project-title {
font-size: 0.95rem;
.index-title {
font-size: 0.92rem;
font-weight: 700;
color: var(--fg);
}
.project-card:hover .project-title {
color: var(--accent);
a.index-row:hover .index-title {
color: color-mix(in srgb, var(--index-accent, var(--accent)) 72%, var(--fg));
}
.project-desc {
margin-top: 4px;
font-size: 0.8rem;
color: var(--web-muted);
.index-status {
padding: 1px 7px 2px;
border-radius: 999px;
border: 1px solid color-mix(in srgb, currentColor 32%, transparent);
background: color-mix(in srgb, currentColor 10%, transparent);
font-family: var(--font-custom-mono);
font-size: 0.56rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: var(--status-forgotten);
}
.index-status-finished {
color: var(--status-finished);
}
.index-status-live {
color: var(--status-live);
}
.index-status-wip {
color: var(--status-wip);
}
.index-status-unfinished {
color: var(--status-unfinished);
}
.index-status-discontinued {
color: var(--status-discontinued);
}
.index-status-forgotten {
color: var(--status-forgotten);
}
.index-desc {
display: block;
margin-top: 3px;
font-size: 0.78rem;
line-height: 1.5;
color: var(--gray);
}
.index-meta {
display: inline-flex;
align-items: center;
gap: 10px;
padding-top: 3px;
font-family: var(--font-custom-mono);
font-size: 0.62rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--gray);
white-space: nowrap;
}
.index-arrow {
transition: transform 0.16s ease, color 0.16s ease;
}
a.index-row:hover .index-arrow {
transform: translate(2px, -2px);
color: color-mix(in srgb, var(--index-accent, var(--accent)) 72%, var(--fg));
}
/* ============================================================
@ -1750,27 +1846,20 @@ html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="4"]
font-size: 0.82rem;
font-weight: 600;
}
/* Projects/hardware gallery: 2-up on phones instead of one full-width card
per row, with a shorter image + tighter card so a row is compact. */
.project-grid {
columns: 2;
column-gap: 10px;
/* Projects/hardware index: tighter rows on phones. */
.index-groups {
gap: 18px;
}
.project-card {
margin-bottom: 10px;
padding: 10px;
.index-row {
gap: 10px;
padding: 10px 11px;
}
.project-thumb {
height: 90px;
margin-bottom: 8px;
font-size: 1.8rem;
}
.project-title {
font-size: 0.88rem;
}
.project-desc {
.index-desc {
font-size: 0.75rem;
}
.index-meta {
font-size: 0.58rem;
}
.services-buttons,
.services-buttons.services-main {
grid-template-columns: 1fr;