diff --git a/Dockerfile b/Dockerfile
index 907b28f..8574c23 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -9,6 +9,7 @@ RUN npm ci --ignore-scripts --no-audit --no-fund
COPY webpack.common.js webpack.config.prod.js ./
COPY index.html 404.html favicon.ico icon.png icon.svg robots.txt site.webmanifest ./
+COPY commissions ./commissions
COPY css ./css
COPY img ./img
COPY js ./js
diff --git a/README.md b/README.md
index 400d8f8..371f743 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# FIW Labs Homepage
-The source for [fiwlabs.dev](https://fiwlabs.dev), the personal portfolio of Alex (`Fi3w0`). It presents systems and desktop projects including TideWM, Scorium, Flux, Moonlit Shell, and the FIW Minecraft tooling collection.
+The source for [fiwlabs.dev](https://fiwlabs.dev), the personal portfolio of Alex (`Fi3w0`). It presents selected systems and desktop projects including TideWM, Scorium, Flux, Moonlit Shell, Prismarine, FiwNode, Nereus, and the FIW Minecraft tooling collection.
The site is intentionally framework-light: semantic HTML, hand-written CSS, and vanilla JavaScript are compiled into a static production bundle. There is no analytics, telemetry, backend, database, or runtime dependency on third-party fonts, icons, or scripts.
@@ -80,6 +80,7 @@ Keep secrets out of the frontend and out of Docker build arguments. Everything d
```text
css/ Site styles
+commissions/ Commission services page
deploy/nginx.conf Static server and security headers
img/ Optimized project artwork
js/app.js Interaction and visual effects
@@ -91,3 +92,11 @@ webpack.*.js Development and production builds
## License
The website source is available under the [MIT License](LICENSE.txt). Product names, screenshots, logos, and third-party marks retain their respective rights.
+
+## Keeping the portfolio current
+
+The homepage is a curated portfolio, not a release feed. Project descriptions focus on what each tool does; versions, download totals, supported Minecraft versions, and release roadmaps belong in the linked repositories or Modrinth pages. Scorium links to its organization so its implementations can grow without adding more homepage cards.
+
+Keep the four illustrated projects as the main showcase. Use the compact project rows for other substantial work, and replace entries rather than continually adding sections. Small utilities belong in the short workbench list; archived prototypes remain discoverable through the all-repositories link.
+
+The GitHub avatar is a local snapshot in `img/avatar.png`, with a cache version on its image URL. Replace it and increment that version when changing the profile picture. The browser needs no GitHub API access, tokens, or scheduled refreshes to display the portfolio. Routine project releases do not require a website update.
diff --git a/commissions/index.html b/commissions/index.html
new file mode 100644
index 0000000..99c53bd
--- /dev/null
+++ b/commissions/index.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+ Commissions - Fi3w0 / Alex
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Skip to content
+
+
+
+
+
+
+
+
+
queue openremote / worldwide
+
alex@fiw:~ $ solve --whatever-this-is
+
Your system is weird . Good.
+
Linux, servers, Minecraft, automation, and small websites. Bring me the setup snag, broken deployment, or idea with a clear finish line. I inspect the problem before quoting the work.
+
+
€10 smallest starting point
6 main rabbit holes
0 need to know the jargon
+
+
+
+
FIW_SCOPE_MONITOR 00:00:00
+
PROBLEM DETECTED
+
01 Minecraft server crashes on startup
TRACE 02 Docker stack works only on Tuesdays
INSPECT 03 Linux laptop Wi-Fi has left the chat
FIX
+
+
+
+
+
+
FIX BUILD DEPLOY MIGRATE EXPLAIN AUTOMATE
+
FIX BUILD DEPLOY MIGRATE EXPLAIN AUTOMATE
+
+
+
+ 01 / WHAT I TOUCH
Choose your chaos. Pick the closest box. The links below show related work I have built and documented.
+
+
01 / daily driver Linux Desktop installs, packages, dev environments, and custom setups. Boot, driver, and performance problems start with a diagnosis.
Related work: Moonlit Shell ↗ Arch Gentoo Ubuntu KDE Wayland
+
02 / lives in a rack Homelab + servers Docker stacks, reverse proxies, HTTPS, DNS, and monitoring for small servers. Migrations and backups include an agreed recovery plan and restore checks.
Related work: Nereus ↗ Compose Traefik Forgejo Portainer
+
03 / block infrastructure Minecraft Server setups, modpack configuration, crash diagnosis, and focused custom mods. Version updates and loader ports are quoted after inspecting the code and dependencies.
Related work: FIW Bosses ↗ Fabric Forge NeoForge Paper
+
04 / do it once Automation Scripts, CLI tools, small bots, APIs, and CI jobs that solve one defined problem. Inputs, integrations, and failure handling are part of the scope.
Related work: AquaTTY ↗ Bash Python Go CI/CD
+
05 / ship the page Small web Personal pages, landing pages, and documentation sites, plus domains, HTTPS, Docker deployment, and build repairs. Content and page count are agreed up front.
Related work: HomePage ↗ Static Nginx Deploy DNS
+
06 / before checkout Hardware PC and homelab parts lists for your budget and workload. I check compatibility, upgrade options, and the limits of advice given remotely.
PC builds Mini PCs Servers
+
+
+
+
+
02 / STARTING PRICES
A number before the email. Quoted in EUR / USD approximate These ranges cover small, defined jobs. You get a fixed quote after I inspect the setup; larger jobs are quoted individually. Hosting, domains, and paid services are separate.
+
All 27 Linux help 5 Servers 8 Automation 2 Minecraft 8 Web + custom 4
+
+
01
Help + Linux 5 jobs General troubleshooting
€10–25 $12–30 Linux help / setup session
€15–30 $18–35 Linux desktop customization
€20–45 $25–55 Developer environment setup
€20–40 $25–45 Hardware consultation / parts list
€10–25 $12–30
+
02
Servers + infrastructure 8 jobs Server / PC performance diagnosis
€15–35 $18–40 Dockerize an existing app
€25–60 $30–70 Deploy a self-hosted service
€25–60 $30–70 Basic homelab / server setup
€40–90 $45–105 Full Docker Compose stack
€50–100 $60–115 Reverse proxy + domain + HTTPS
€25–55 $30–65 Backup setup + restore check
€25–60 $30–70
+
03
Automation + tools 2 jobs Small automation script
€15–50 $18–60 Small Discord / utility bot
€30–80 $35–95
+
04
Minecraft 8 jobs Basic server setup
€20–40 $25–45 Modded server setup
€35–75 $40–90 Server crash diagnosis / targeted fix
€15–45 $18–55 Performance review / targeted tuning
€20–50 $25–60 Modpack setup / configuration
€25–60 $30–70 Simple custom mod
€40–120 $45–140 Small mod port / version update
€25–80 $30–95 Permissions / proxy configuration
€20–50 $25–60
+
05
Web + strange ideas 4 jobs One-page website
€50–90 $60–105 One-page website + deployment / domain
€70–120 $80–140 Website / server deployment only
€20–40 $25–45 Weird / custom project
Tell me the idea quoted individually
+
+
27 starting points visible
+
+
+ 03 / JOB SIZE
How much mess? No mystery package names. Just three useful shapes for the work. FIW-001 QUICK FIX
One sharp problem. One config issue, small script, setup snag, or diagnosis session. We agree on the time or task limit and the result you will receive.
often starts around €10–30 FIW-002 SETUP / BUILD
Make the whole thing work. One agreed server, Docker stack, site, modded setup, or automation. Delivered with checks and notes for running it yourself.
priced from the catalog above FIW-003 CUSTOM
The odd one. Several systems, an unusual idea, or unfamiliar compatibility work. I check feasibility first and quote the parts I can deliver.
quoted after I understand it
+
+ 04 / HOW IT WORKS
Four steps. No ceremony. 01 Show me the thing Send the goal, symptoms, setup, versions, budget, deadline, and useful logs. Tell me what a successful result looks like.
hello 02 I trace the edges I inspect the setup and define the deliverable, access, dependencies, and checks. Troubleshooting may be scoped as diagnosis before a fix is agreed.
scope 03 You get a quote A price and plan before work begins, including revisions and any support period. Extra features or changed requirements get a separate quote first.
agree 04 I make it behave Build, check the agreed result, and hand over the files and setup notes. Follow-up fixes and ongoing maintenance follow the terms in your quote.
ship I use AI tools for parts of development. I own the design, review, testing, and integration, and agree on checks for your setup before delivery.
+
+ 05 / PAYMENT
Pick the boring money pipe. We choose after the quote. Three routes are live; Stripe joins them when its checkout is connected.
+
+
+
+
+
+
+
+
+
diff --git a/css/commissions.css b/css/commissions.css
new file mode 100644
index 0000000..22a0088
--- /dev/null
+++ b/css/commissions.css
@@ -0,0 +1,902 @@
+:root {
+ --commission-pad: clamp(1.25rem, 4vw, 4.5rem);
+ --commission-width: 96rem;
+}
+
+.commissions-page {
+ --mx: 72%;
+ --my: 22%;
+ background: #0b1014;
+}
+
+.commissions-page::before,
+.commissions-page .page-grid {
+ display: none;
+}
+
+.commission-grid {
+ position: fixed;
+ z-index: -2;
+ inset: 0;
+ opacity: 0.34;
+ background-image:
+ linear-gradient(rgba(143, 212, 208, 0.035) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(143, 212, 208, 0.035) 1px, transparent 1px);
+ background-size: 2rem 2rem;
+ pointer-events: none;
+ mask-image: radial-gradient(circle at var(--mx) var(--my), black, transparent 55%);
+}
+
+.commission-shell {
+ width: min(100%, var(--commission-width));
+ margin: 0 auto;
+ padding-right: var(--commission-pad);
+ padding-left: var(--commission-pad);
+}
+
+.commission-nav-header {
+ border-bottom-color: rgba(143, 212, 208, 0.2);
+ background: rgba(11, 16, 20, 0.9);
+ backdrop-filter: blur(16px);
+}
+
+.commission-nav-header .brand > span:last-child {
+ color: var(--cyan);
+}
+
+.commission-nav-header .nav-pill {
+ color: var(--coral);
+}
+
+.commission-hero-new {
+ --hx: 72%;
+ --hy: 24%;
+ position: relative;
+ display: grid;
+ width: min(100%, var(--commission-width));
+ min-height: 100svh;
+ margin: 0 auto;
+ padding: clamp(8.5rem, 13vh, 10rem) var(--commission-pad) clamp(4rem, 8vh, 7rem);
+ grid-template-columns: minmax(0, 7fr) minmax(24rem, 5fr);
+ align-items: center;
+ gap: clamp(2.5rem, 6vw, 7rem);
+}
+
+.commission-glow {
+ position: absolute;
+ z-index: -1;
+ inset: 0;
+ background:
+ radial-gradient(circle at var(--hx) var(--hy), rgba(143, 212, 208, 0.16), transparent 18rem),
+ radial-gradient(circle at 15% 82%, rgba(240, 153, 110, 0.1), transparent 24rem);
+ pointer-events: none;
+}
+
+.commission-hero-copy {
+ min-width: 0;
+}
+
+.commission-availability {
+ display: flex;
+ max-width: 46rem;
+ justify-content: space-between;
+ margin-bottom: 2rem;
+ padding-bottom: 0.65rem;
+ border-bottom: 1px dashed var(--line);
+ color: var(--muted);
+ font-size: 0.62rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.commission-availability span:first-child {
+ color: var(--lime);
+}
+
+.commission-availability i {
+ display: inline-block;
+ width: 0.45rem;
+ height: 0.45rem;
+ margin-right: 0.45rem;
+ border-radius: 50%;
+ background: var(--lime);
+ box-shadow: 0 0 0 0 rgba(200, 238, 128, 0.4);
+ animation: commission-pulse 2s ease-out infinite;
+}
+
+.commission-command {
+ margin-bottom: 1.3rem;
+ color: var(--cyan);
+ font-size: 0.72rem;
+ letter-spacing: 0.05em;
+}
+
+.commission-hero-new h1 {
+ max-width: 9ch;
+ margin-bottom: 2rem;
+ font-size: clamp(4.4rem, 8.5vw, 9rem);
+ letter-spacing: -0.08em;
+ line-height: 0.82;
+}
+
+.commission-word {
+ position: relative;
+ display: inline-block;
+ color: var(--coral);
+}
+
+.commission-word.is-changing {
+ animation: word-swap 260ms steps(2, end);
+}
+
+.commission-lede {
+ max-width: 48rem;
+ margin-bottom: 2.2rem;
+ color: var(--muted);
+ font-size: clamp(0.95rem, 1.35vw, 1.14rem);
+}
+
+.commission-actions {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.8rem;
+ margin-bottom: 3rem;
+}
+
+.commission-actions a {
+ display: inline-flex;
+ min-height: 3rem;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1.5rem;
+ padding: 0.7rem 1rem;
+ border: 1px solid var(--line);
+ color: var(--text);
+ font-size: 0.7rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ transition: background 160ms ease, color 160ms ease, transform 160ms ease;
+}
+
+.commission-actions a:first-child {
+ border-color: var(--coral);
+ background: var(--coral);
+ color: var(--ink);
+}
+
+.commission-actions a:hover {
+ transform: translateY(-0.25rem);
+ background: var(--lime);
+ color: var(--ink);
+}
+
+.commission-facts {
+ display: grid;
+ max-width: 46rem;
+ border-top: 1px solid var(--line);
+ border-bottom: 1px solid var(--line);
+ grid-template-columns: repeat(3, 1fr);
+}
+
+.commission-facts p {
+ display: grid;
+ min-height: 5.5rem;
+ margin: 0;
+ padding: 0.75rem;
+ align-content: center;
+}
+
+.commission-facts p + p {
+ border-left: 1px solid var(--line);
+}
+
+.commission-facts strong,
+.commission-facts span {
+ display: block;
+}
+
+.commission-facts strong {
+ color: var(--coral);
+ font-family: var(--display);
+ font-size: 1.75rem;
+ line-height: 1;
+}
+
+.commission-facts span {
+ margin-top: 0.35rem;
+ color: var(--muted);
+ font-size: 0.54rem;
+ text-transform: uppercase;
+}
+
+.scope-console {
+ position: relative;
+ min-width: 0;
+ border: 2px solid rgba(143, 212, 208, 0.4);
+ background: rgba(13, 22, 28, 0.9);
+ box-shadow: 1rem 1rem 0 rgba(240, 153, 110, 0.75);
+}
+
+.scope-console::before {
+ position: absolute;
+ z-index: -1;
+ inset: -1.4rem 1.4rem 1.4rem -1.4rem;
+ border: 1px dashed rgba(143, 212, 208, 0.28);
+ content: "";
+}
+
+.scope-console-top {
+ display: flex;
+ min-height: 3rem;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 1rem;
+ border-bottom: 1px solid rgba(143, 212, 208, 0.25);
+ color: var(--cyan);
+ font-size: 0.58rem;
+ letter-spacing: 0.08em;
+}
+
+.scope-clock {
+ color: var(--lime);
+}
+
+.scope-radar {
+ position: relative;
+ display: grid;
+ width: min(72%, 19rem);
+ aspect-ratio: 1;
+ margin: 2rem auto;
+ overflow: hidden;
+ place-items: center;
+ border: 1px solid rgba(143, 212, 208, 0.3);
+ border-radius: 50%;
+ background:
+ linear-gradient(rgba(143, 212, 208, 0.16) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(143, 212, 208, 0.16) 1px, transparent 1px),
+ radial-gradient(circle, rgba(143, 212, 208, 0.08), transparent 68%);
+ background-size: 2rem 2rem, 2rem 2rem, auto;
+}
+
+.scope-radar::before,
+.scope-radar::after {
+ position: absolute;
+ background: rgba(143, 212, 208, 0.28);
+ content: "";
+}
+
+.scope-radar::before { width: 100%; height: 1px; }
+.scope-radar::after { width: 1px; height: 100%; }
+
+.radar-ring {
+ position: absolute;
+ border: 1px solid rgba(143, 212, 208, 0.28);
+ border-radius: 50%;
+}
+
+.radar-ring-one { inset: 18%; }
+.radar-ring-two { inset: 36%; }
+
+.radar-sweep {
+ position: absolute;
+ inset: 50% 50% 0 0;
+ transform-origin: 100% 0;
+ background: linear-gradient(35deg, transparent 45%, rgba(200, 238, 128, 0.45));
+ animation: radar-spin 4.5s linear infinite;
+}
+
+.radar-blip {
+ position: absolute;
+ width: 0.55rem;
+ height: 0.55rem;
+ border: 2px solid var(--coral);
+ border-radius: 50%;
+ box-shadow: 0 0 0.9rem var(--coral);
+ animation: radar-blip 1.8s ease-in-out infinite;
+}
+
+.radar-blip-one { top: 23%; left: 62%; }
+.radar-blip-two { top: 67%; left: 27%; animation-delay: 0.55s; }
+.radar-blip-three { top: 72%; left: 74%; animation-delay: 1.1s; }
+
+.scope-radar strong {
+ position: relative;
+ z-index: 2;
+ padding: 0.45rem;
+ background: rgba(11, 16, 20, 0.75);
+ color: var(--lime);
+ font-size: 0.68rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ line-height: 1.3;
+ text-align: center;
+}
+
+.scope-queue {
+ border-top: 1px solid rgba(143, 212, 208, 0.25);
+}
+
+.scope-queue > div {
+ display: grid;
+ min-height: 4.4rem;
+ padding: 0.7rem 1rem;
+ border-bottom: 1px dashed var(--line);
+ grid-template-columns: 2rem minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 0.7rem;
+ transition: background 150ms ease, padding 150ms ease;
+}
+
+.scope-queue > div:hover {
+ padding-left: 1.35rem;
+ background: rgba(143, 212, 208, 0.08);
+}
+
+.scope-queue > div > span,
+.scope-queue em {
+ color: var(--coral);
+ font-size: 0.55rem;
+ font-style: normal;
+}
+
+.scope-queue p { margin: 0; }
+.scope-queue b,
+.scope-queue small { display: block; }
+.scope-queue b { font-size: 0.72rem; font-weight: 500; }
+.scope-queue small { color: var(--muted); font-size: 0.58rem; }
+
+.scope-console-foot {
+ display: flex;
+ min-height: 3rem;
+ align-items: center;
+ justify-content: center;
+ gap: 0.5rem;
+ margin: 0;
+ color: var(--muted);
+ font-size: 0.5rem;
+ text-transform: uppercase;
+}
+
+.scope-console-foot span { color: var(--cyan); }
+.scope-console-foot i { width: 1.2rem; height: 1px; background: var(--line); }
+
+.commission-tape {
+ overflow: hidden;
+ border-top: 3px solid var(--ink);
+ border-bottom: 3px solid var(--ink);
+ background: var(--lime);
+ color: var(--ink);
+}
+
+.commission-tape-track {
+ display: flex;
+ width: max-content;
+ min-height: 3.6rem;
+ align-items: center;
+ animation: commission-tape 24s linear infinite;
+}
+
+.commission-tape-set {
+ display: flex;
+ min-width: 100vw;
+ flex-shrink: 0;
+ align-items: center;
+ justify-content: space-around;
+ gap: 1.3rem;
+ padding: 0 1.3rem;
+}
+
+.commission-tape span {
+ font-family: var(--display);
+ font-size: 1rem;
+ letter-spacing: 0.08em;
+}
+
+.commission-tape i {
+ width: 0.55rem;
+ height: 0.55rem;
+ transform: rotate(45deg);
+ background: var(--ink);
+}
+
+.capability-section,
+.job-section,
+.payment-section-new {
+ padding-top: clamp(5rem, 9vw, 9rem);
+ padding-bottom: clamp(5rem, 9vw, 9rem);
+}
+
+.commission-section-head {
+ display: grid;
+ margin-bottom: clamp(3rem, 6vw, 5rem);
+ padding-top: 1.2rem;
+ border-top: 4px solid var(--text);
+ grid-template-columns: 2fr 7fr 3fr;
+ gap: 1.5rem;
+ align-items: start;
+}
+
+.commission-section-head p,
+.price-head p,
+.process-head p,
+.commission-cta > .commission-shell > p {
+ color: var(--coral);
+ font-size: 0.65rem;
+ font-weight: 600;
+ letter-spacing: 0.09em;
+}
+
+.commission-section-head h2,
+.price-head h2,
+.process-head h2 {
+ margin: 0;
+ font-size: clamp(3.2rem, 6.8vw, 7rem);
+ letter-spacing: -0.07em;
+ line-height: 0.88;
+}
+
+.commission-section-head > span {
+ color: var(--muted);
+ font-size: 0.78rem;
+}
+
+.capability-grid {
+ display: grid;
+ gap: 1rem;
+ grid-template-columns: repeat(12, minmax(0, 1fr));
+}
+
+.capability-card {
+ position: relative;
+ display: flex;
+ min-height: 27rem;
+ flex-direction: column;
+ padding: clamp(1.4rem, 2.5vw, 2.3rem);
+ overflow: hidden;
+ border: 2px solid var(--line);
+ background: rgba(17, 25, 31, 0.86);
+ transition: transform 180ms ease, border-color 180ms ease;
+}
+
+.capability-card:hover {
+ transform: translateY(-0.4rem);
+ border-color: currentColor;
+}
+
+.capability-card > span {
+ font-size: 0.58rem;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+}
+
+.capability-card h3 {
+ margin: auto 0 1.5rem;
+ font-size: clamp(2.2rem, 4.5vw, 5rem);
+ letter-spacing: -0.07em;
+}
+
+.capability-card p {
+ max-width: 42rem;
+ color: var(--muted);
+ font-size: 0.78rem;
+}
+
+.capability-card > div {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.35rem;
+ margin-top: 1rem;
+}
+
+.capability-card b {
+ padding: 0.2rem 0.5rem;
+ border: 1px solid currentColor;
+ font-size: 0.54rem;
+ font-weight: 500;
+}
+
+.capability-linux { grid-column: span 4; color: var(--coral); }
+.capability-server { grid-column: span 8; color: var(--cyan); }
+.capability-minecraft { grid-column: span 7; color: var(--lime); }
+.capability-automation { grid-column: span 5; color: var(--coral); }
+.capability-web { grid-column: span 5; color: var(--cyan); }
+.capability-hardware { grid-column: span 7; color: var(--lime); }
+
+.capability-server,
+.capability-minecraft,
+.capability-hardware {
+ min-height: 31rem;
+}
+
+.capability-card::after {
+ position: absolute;
+ right: -0.08em;
+ bottom: -0.18em;
+ color: currentColor;
+ content: "#";
+ font-family: var(--display);
+ font-size: 13rem;
+ line-height: 0.8;
+ opacity: 0.035;
+ pointer-events: none;
+}
+
+.price-section,
+.process-section-new,
+.commission-cta {
+ box-shadow: 0 0 0 100vmax currentColor;
+ clip-path: inset(0 -100vmax);
+}
+
+.price-section {
+ padding: clamp(5rem, 9vw, 9rem) 0;
+ background: var(--paper);
+ box-shadow: 0 0 0 100vmax var(--paper);
+ color: var(--ink);
+}
+
+.price-head {
+ display: grid;
+ margin-bottom: 3rem;
+ grid-template-columns: minmax(0, 8fr) minmax(18rem, 4fr);
+ gap: 3rem;
+ align-items: end;
+}
+
+.price-head h2 { max-width: 10ch; }
+
+.price-head-note {
+ padding: 1rem;
+ border: 2px solid var(--ink);
+ background: var(--yellow);
+ transform: rotate(1deg);
+}
+
+.price-head-note strong,
+.price-head-note span { display: block; }
+.price-head-note strong { margin-bottom: 0.5rem; font-size: 0.68rem; text-transform: uppercase; }
+.price-head-note span { color: #3f4848; font-size: 0.7rem; }
+
+.price-filter {
+ display: flex;
+ flex-wrap: wrap;
+ margin-bottom: 2.5rem;
+ border-top: 4px solid var(--ink);
+ border-bottom: 2px solid var(--ink);
+}
+
+.price-filter button {
+ flex: 1;
+ min-width: max-content;
+ padding: 0.9rem 0.8rem;
+ border: 0;
+ border-right: 1px solid var(--ink);
+ background: transparent;
+ color: var(--ink);
+ cursor: pointer;
+ font-size: 0.62rem;
+ font-weight: 600;
+ text-transform: uppercase;
+ transition: background 150ms ease, color 150ms ease;
+}
+
+.price-filter button:last-child { border-right: 0; }
+.price-filter button:hover,
+.price-filter button.is-active { background: var(--ink); color: var(--paper); }
+.price-filter button span { margin-left: 0.35rem; color: #a74b2c; }
+.price-filter button.is-active span { color: var(--coral); }
+
+.price-group {
+ display: grid;
+ border-top: 2px solid var(--ink);
+ grid-template-columns: minmax(14rem, 4fr) minmax(0, 8fr);
+}
+
+.price-group[hidden] { display: none; }
+
+.price-group-label {
+ position: sticky;
+ top: 5.5rem;
+ align-self: start;
+ padding: 1.5rem 2rem 1.5rem 0;
+}
+
+.price-group-label span,
+.price-group-label small { color: #a74b2c; font-size: 0.6rem; text-transform: uppercase; }
+.price-group-label h3 { max-width: 9ch; margin: 2rem 0 1rem; font-size: clamp(2rem, 4vw, 4rem); letter-spacing: -0.06em; }
+
+.price-list { border-left: 2px solid var(--ink); }
+
+.price-list > div {
+ display: grid;
+ min-height: 5.4rem;
+ padding: 0.9rem 0 0.9rem 1.5rem;
+ border-bottom: 1px solid rgba(17, 23, 26, 0.28);
+ grid-template-columns: minmax(0, 1fr) 8rem 5rem;
+ align-items: center;
+ gap: 1rem;
+ transition: padding 150ms ease, background 150ms ease;
+}
+
+.price-list > div:hover {
+ padding-right: 0.8rem;
+ padding-left: 2rem;
+ background: var(--lime);
+}
+
+.price-list p { margin: 0; font-size: 0.8rem; }
+.price-list strong { font-family: var(--display); font-size: 1.45rem; letter-spacing: -0.04em; text-align: right; }
+.price-list small { color: #5c6665; font-size: 0.62rem; text-align: right; }
+.price-custom { background: var(--coral); }
+.price-custom strong { font-family: var(--mono); font-size: 0.7rem; text-transform: uppercase; }
+
+.price-count {
+ margin: 1.5rem 0 0;
+ color: #5c6665;
+ font-size: 0.65rem;
+ text-align: right;
+ text-transform: uppercase;
+}
+.price-count span { color: #a74b2c; font-weight: 700; }
+
+.job-tickets {
+ display: grid;
+ gap: 1rem;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+}
+
+.job-ticket {
+ display: flex;
+ min-height: 28rem;
+ flex-direction: column;
+ padding: 1.5rem;
+ border: 2px dashed var(--line);
+ background: rgba(17, 25, 31, 0.72);
+ transition: transform 180ms ease;
+}
+.job-ticket:hover { transform: rotate(-0.6deg) translateY(-0.3rem); }
+.job-ticket-main { border: 0; background: var(--lime); color: var(--ink); transform: rotate(1deg); }
+.job-ticket-main:hover { transform: rotate(0.3deg) translateY(-0.3rem); }
+.job-ticket > div { display: flex; justify-content: space-between; font-size: 0.58rem; color: var(--coral); }
+.job-ticket-main > div { color: #6c4b2f; }
+.job-ticket h3 { margin: auto 0 1.3rem; font-size: clamp(2.2rem, 3.7vw, 4rem); letter-spacing: -0.065em; }
+.job-ticket p { color: var(--muted); font-size: 0.78rem; }
+.job-ticket-main p { color: #3f4848; }
+.job-ticket small { margin-top: 1rem; padding-top: 0.7rem; border-top: 1px dashed currentColor; font-size: 0.58rem; text-transform: uppercase; }
+
+.process-section-new {
+ padding: clamp(5rem, 9vw, 9rem) 0;
+ background: var(--cyan);
+ box-shadow: 0 0 0 100vmax var(--cyan);
+ color: var(--ink);
+}
+
+.process-head {
+ display: grid;
+ margin-bottom: 3.5rem;
+ padding-top: 1.2rem;
+ border-top: 4px solid var(--ink);
+ grid-template-columns: 2fr 10fr;
+ gap: 1.5rem;
+}
+
+.process-head h2 { max-width: 9ch; }
+
+.process-steps {
+ margin: 0;
+ padding: 0;
+ border-top: 2px solid var(--ink);
+ border-bottom: 2px solid var(--ink);
+ list-style: none;
+}
+
+.process-steps li {
+ display: grid;
+ min-height: 8rem;
+ padding: 1.2rem 0;
+ border-bottom: 1px solid rgba(17, 23, 26, 0.28);
+ grid-template-columns: 5rem minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 1.5rem;
+ transition: padding 150ms ease, background 150ms ease;
+}
+.process-steps li:last-child { border-bottom: 0; }
+.process-steps li:hover { padding-right: 1rem; padding-left: 1rem; background: rgba(233, 229, 218, 0.35); }
+.process-steps > li > span { color: #a74b2c; font-size: 0.7rem; }
+.process-steps h3 { margin: 0 0 0.35rem; font-family: var(--mono); font-size: clamp(1.1rem, 2vw, 1.65rem); }
+.process-steps p { margin: 0; color: #3f4848; font-size: 0.72rem; }
+.process-steps b { padding: 0.2rem 0.6rem; border: 1px solid var(--ink); font-size: 0.55rem; font-weight: 500; text-transform: uppercase; }
+
+.payment-rail {
+ display: grid;
+ border-top: 4px solid var(--text);
+ border-bottom: 2px solid var(--text);
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.payment-rail > * {
+ display: block;
+ min-height: 18rem;
+ padding: 1.3rem;
+ border-right: 1px solid var(--line);
+ transition: background 160ms ease, color 160ms ease;
+}
+.payment-rail > *:last-child { border-right: 0; }
+.payment-rail > *:hover { background: var(--coral); color: var(--ink); }
+.payment-rail span { color: var(--coral); font-size: 0.58rem; }
+.payment-rail > *:hover span { color: var(--ink); }
+.payment-rail h3 { margin: 6rem 0 0.8rem; font-size: clamp(1.8rem, 3vw, 3rem); letter-spacing: -0.055em; }
+.payment-rail p { color: var(--muted); font-size: 0.67rem; text-transform: uppercase; }
+.payment-rail > *:hover p { color: var(--ink); }
+
+.commission-cta {
+ padding: clamp(5rem, 9vw, 9rem) 0;
+ background: var(--coral);
+ box-shadow: 0 0 0 100vmax var(--coral);
+ color: var(--ink);
+}
+
+.commission-cta h2 {
+ max-width: 8ch;
+ margin-bottom: 2rem;
+ font-size: clamp(5rem, 14vw, 13rem);
+ letter-spacing: -0.09em;
+ line-height: 0.72;
+}
+
+.commission-cta-copy {
+ max-width: 48rem;
+ margin-bottom: 3rem;
+ font-size: clamp(0.9rem, 1.5vw, 1.1rem);
+}
+
+.commission-contact-grid {
+ display: grid;
+ border-top: 4px solid var(--ink);
+ border-bottom: 2px solid var(--ink);
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.commission-contact-grid > * {
+ display: grid;
+ min-width: 0;
+ min-height: 7.5rem;
+ padding: 1rem;
+ border: 0;
+ border-right: 1px solid var(--ink);
+ background: transparent;
+ color: var(--ink);
+ cursor: pointer;
+ text-align: left;
+ grid-template-columns: 1fr auto;
+ transition: background 150ms ease, padding 150ms ease;
+}
+.commission-contact-grid > *:last-child { border-right: 0; }
+.commission-contact-grid > *:hover { padding-left: 1.4rem; background: var(--yellow); }
+.commission-contact-grid span { font-family: var(--display); font-size: clamp(1.4rem, 2.4vw, 2.3rem); letter-spacing: -0.04em; }
+.commission-contact-grid small { align-self: end; color: #5b4235; font-size: 0.55rem; }
+.commission-contact-grid b { font-size: 1.2rem; }
+
+.commission-footer {
+ display: grid;
+ width: min(100%, var(--commission-width));
+ min-height: 7rem;
+ margin: 0 auto;
+ padding: 1.5rem var(--commission-pad);
+ grid-template-columns: 1fr auto auto;
+ align-items: center;
+ gap: 2rem;
+}
+.commission-footer p { margin: 0; color: var(--muted); font-size: 0.6rem; }
+
+@keyframes radar-spin { to { transform: rotate(360deg); } }
+@keyframes radar-blip { 0%, 100% { opacity: 0.35; transform: scale(0.7); } 45% { opacity: 1; transform: scale(1.3); } }
+@keyframes commission-tape { to { transform: translateX(-50%); } }
+@keyframes commission-pulse { 70%, 100% { box-shadow: 0 0 0 0.5rem rgba(200, 238, 128, 0); } }
+@keyframes word-swap { 0% { transform: translate(0); } 25% { transform: translate(-0.05em, 0.04em); text-shadow: 0.06em 0 var(--cyan); } 50% { transform: translate(0.05em, -0.03em); text-shadow: -0.06em 0 var(--lime); } 100% { transform: translate(0); } }
+
+@media (max-width: 74rem) {
+ .commission-hero-new { min-height: auto; grid-template-columns: 1fr; }
+ .scope-console { width: min(100%, 48rem); margin: 1rem auto 0; }
+ .capability-linux,
+ .capability-automation,
+ .capability-web { grid-column: span 6; }
+ .capability-server,
+ .capability-minecraft,
+ .capability-hardware { grid-column: span 6; min-height: 27rem; }
+ .commission-section-head { grid-template-columns: 2fr 7fr; }
+ .commission-section-head > span { grid-column: 2; }
+ .payment-rail { grid-template-columns: repeat(2, 1fr); }
+ .payment-rail > *:nth-child(2) { border-right: 0; }
+ .payment-rail > *:nth-child(-n + 2) { border-bottom: 1px solid var(--line); }
+ .commission-contact-grid { grid-template-columns: repeat(2, 1fr); }
+ .commission-contact-grid > *:nth-child(2) { border-right: 0; }
+ .commission-contact-grid > *:nth-child(-n + 2) { border-bottom: 1px solid var(--ink); }
+}
+
+@media (max-width: 52rem) {
+ .commission-nav-header .brand > span:last-child { font-size: 0; }
+ .commission-nav-header .brand > span:last-child::before { content: "fiw / work"; font-size: 0.75rem; }
+ .commission-hero-new { padding-top: 7.8rem; }
+ .commission-hero-new h1 { font-size: clamp(3.8rem, 15vw, 6rem); }
+ .commission-section-head,
+ .price-head,
+ .process-head { grid-template-columns: 1fr; }
+ .commission-section-head > span { grid-column: 1; max-width: 36rem; }
+ .capability-grid { grid-template-columns: 1fr; }
+ .capability-linux,
+ .capability-server,
+ .capability-minecraft,
+ .capability-automation,
+ .capability-web,
+ .capability-hardware { min-height: 24rem; grid-column: auto; }
+ .price-group { grid-template-columns: 1fr; }
+ .price-group-label { position: static; padding-right: 0; }
+ .price-list { border-top: 2px solid var(--ink); border-left: 0; }
+ .job-tickets { grid-template-columns: 1fr; }
+ .job-ticket { min-height: 23rem; }
+ .job-ticket-main { transform: none; }
+ .process-head { gap: 0; }
+}
+
+@media (max-width: 36rem) {
+ .commission-availability { display: grid; gap: 0.4rem; }
+ .commission-command { font-size: 0.62rem; }
+ .commission-actions { display: grid; }
+ .commission-actions a { width: 100%; }
+ .commission-facts { grid-template-columns: 1fr; }
+ .commission-facts p { min-height: 4.5rem; grid-template-columns: 4rem 1fr; align-items: center; }
+ .commission-facts p + p { border-top: 1px solid var(--line); border-left: 0; }
+ .commission-facts span { margin-top: 0; }
+ .scope-console { box-shadow: 0.55rem 0.55rem 0 rgba(240, 153, 110, 0.75); }
+ .scope-console::before { display: none; }
+ .scope-console-foot { display: none; }
+ .price-filter { display: grid; grid-template-columns: repeat(2, 1fr); }
+ .price-filter button { min-width: 0; border-right: 1px solid var(--ink); border-bottom: 1px solid var(--ink); }
+ .price-filter button:nth-child(even) { border-right: 0; }
+ .price-filter button:nth-last-child(-n + 2) { border-bottom: 0; }
+ .price-list > div { min-height: 6rem; padding-left: 0; grid-template-columns: minmax(0, 1fr) auto; gap: 0.4rem; }
+ .price-list > div:hover { padding-left: 0.5rem; }
+ .price-list small { grid-column: 2; }
+ .price-list strong { font-size: 1.2rem; }
+ .process-steps li { grid-template-columns: 2.2rem 1fr; gap: 0.7rem; }
+ .process-steps b { display: none; }
+ .payment-rail,
+ .commission-contact-grid { grid-template-columns: 1fr; }
+ .payment-rail > *,
+ .payment-rail > *:nth-child(2) { min-height: 13rem; border-right: 0; border-bottom: 1px solid var(--line); }
+ .payment-rail > *:last-child { border-bottom: 0; }
+ .payment-rail h3 { margin-top: 3rem; }
+ .commission-contact-grid > *,
+ .commission-contact-grid > *:nth-child(2) { border-right: 0; border-bottom: 1px solid var(--ink); }
+ .commission-contact-grid > *:last-child { border-bottom: 0; }
+ .commission-footer { grid-template-columns: 1fr auto; }
+ .commission-footer > p:first-of-type { display: none; }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ .commission-availability i,
+ .radar-sweep,
+ .radar-blip,
+ .commission-tape-track,
+ .commission-word { animation: none !important; }
+}
+
+/* Proof links sit with each service instead of adding another portfolio section. */
+.capability-proof {
+ align-self: flex-start;
+ font-size: 0.68rem;
+ text-underline-offset: 0.25em;
+ text-decoration: underline;
+}
+
+.capability-proof:hover {
+ color: var(--text);
+}
+
+.commission-work-note {
+ max-width: 68ch;
+ margin: 2rem 0 0;
+ color: #3f4848;
+ font-size: 0.78rem;
+}
diff --git a/css/editorial.css b/css/editorial.css
index fff54a6..7a40f85 100644
--- a/css/editorial.css
+++ b/css/editorial.css
@@ -2664,3 +2664,22 @@ article[data-repo="Moonlit-shell"] .project-feature-copy {
display: none;
}
}
+
+/* Keep the selected tools understandable on phones without expanding cards. */
+@media (max-width: 52rem) {
+ .work-section .project-row {
+ row-gap: 0.65rem;
+ align-items: start;
+ }
+
+ .work-section .project-row > p {
+ display: block;
+ grid-column: 2 / 3;
+ grid-row: 2;
+ }
+
+ .work-section .project-row > a {
+ grid-column: 3;
+ grid-row: 1;
+ }
+}
diff --git a/img/avatar.png b/img/avatar.png
index 9f3be0c..b14ae42 100644
Binary files a/img/avatar.png and b/img/avatar.png differ
diff --git a/index.html b/index.html
index 50c7106..0d97662 100644
--- a/index.html
+++ b/index.html
@@ -5,7 +5,7 @@
Fi3w0 / Alex - Systems Builder
-
+
@@ -17,7 +17,7 @@
-
+
@@ -31,7 +31,7 @@