diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..30cf57e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..3eb4ae5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/skopec.dev-reimagined.iml b/.idea/skopec.dev-reimagined.iml new file mode 100644 index 0000000..c956989 --- /dev/null +++ b/.idea/skopec.dev-reimagined.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/404.html b/404.html new file mode 100644 index 0000000..fd93843 --- /dev/null +++ b/404.html @@ -0,0 +1,62 @@ + + + + + + + + 404 — Page not found · Josef + + + + + + + + + + + + + + + +
+
+ +

404

+

This page doesn’t exist — maybe the link is old, or something moved.

+ +
+
+ + + + + + diff --git a/hardware.html b/hardware.html new file mode 100644 index 0000000..097d9e6 --- /dev/null +++ b/hardware.html @@ -0,0 +1,129 @@ + + + + + + + Hardware — desktop, laptop, server & phone · Josef + + + + + + + + + + + + + + + + + + + + + + +
+
+

Hardware

+

The machines I use every day — desktop build, daily driver laptop, Proxmox server, and phone.

+
+ +
+
+ +

My machines

+

Full component breakdown with key specs from each part.

+
    +
  • + Desktop +

    Custom build · 2022

    +

    Main PC running Artix Linux with SwayFX wm — assembled in 2022.

    +
    +
    CPU
    AMD Ryzen 5 5600 — Zen 3 / Vermeer, 6C/12T, 3.5–4.4 GHz, 32 MB L3, 65 W TDP, AM4, unlocked
    +
    GPU
    XFX Speedster SWFT 309 RX 6700 — RDNA 2, 2304 SP, 10 GB GDDR6 @ 16 Gbps (160-bit, 320 GB/s), boost up to ~2425 MHz, triple-fan, HDMI + 3× DP
    +
    RAM
    Kingston FURY Beast RGB — 16 GB DDR4-3200 · CL16 (16-18-18) · ~1.35 V · Intel XMP 2.0
    +
    Storage
    WD Blue SN570 1 TB — M.2 2280 NVMe, PCIe 3.0 ×4, up to ~3500/3000 MB/s read/write, TLC, ~600 TBW, 5-year warranty
    +
    Board
    ASUS TUF Gaming B550-Plus — AM4 ATX, B550, 8+2 DrMOS VRM, 4× DDR4 (max 128 GB), PCIe 4.0 ×16, dual M.2, 2.5 GbE, USB 3.2 Gen 2
    +
    PSU
    SilentiumPC Vero M3 Bronze 600 W — ATX, 80 PLUS Bronze (~90% @ 50% load), semi-modular, single +12 V rail up to ~588 W
    +
    Cooler
    SilentiumPC Fera 5 — tower air cooler, 4× heatpipes, Fluctus 120 PWM (300–1800 RPM), rated ~220 W TDP
    +
    Case
    SilentiumPC Signum SG1V EVO TG ARGB — ATX mid-tower (~447×216×413 mm), tempered glass, 4× Stella HP ARGB 120 mm included, GPU clearance ~325 mm
    +
    +
  • +
  • + Laptop +

    MacBook Air 13″ · M5 · base

    +

    Portable machine for school and on-the-go work.

    +
    +
    Chip
    Apple M5 — 10-core CPU (4 performance + 6 efficiency), 8-core GPU, 16-core Neural Engine, ~153 GB/s memory bandwidth
    +
    Memory
    16 GB unified memory (base)
    +
    Storage
    512 GB SSD (base; Apple claims ~2× faster vs previous Air gen)
    +
    Display
    13.6″ Liquid Retina IPS — 2560×1664 @ 224 ppi, 500 nits, 60 Hz, P3
    +
    Battery
    Up to ~18 h video streaming / ~15 h wireless web (Apple rating)
    +
    I/O
    2× Thunderbolt 4 · MagSafe · 3.5 mm · Wi-Fi 7 (N1) · Bluetooth 6 · ~1.24 kg
    +
    +
  • +
  • + Server +

    Proxmox server

    +

    Home lab box for VMs and containers — usually runs headless.

    +
    +
    CPU
    Intel Xeon E5-2630L v4 — Broadwell-EP, 14 nm, 10C/20T, 1.8–2.9 GHz, 25 MB L3, 55 W TDP, LGA 2011-3, DDR4 ECC
    +
    GPU
    NVIDIA GeForce GT 440 — Fermi GF108-class entry GPU (~96 CUDA, ~65 W); used only for BIOS, then headless
    +
    RAM
    40 GB ECC registered DDR4 (RDIMM) — dual-channel on this board, CPU supports DDR4-1600/1866/2133
    +
    Storage
    Samsung 970 EVO OEM 256 GB NVMe (PCIe 3.0 ×4, Phoenix controller, ~3400/1500 MB/s class) · WD 2.5″ 512 GB HDD · WD 3.5″ 320 GB HDD
    +
    Board
    Huananzhi X99-P4F — mATX (215×185 mm), LGA 2011-3, 4× DDR4 (max 128 GB), 2× M.2 NVMe PCIe 3.0 ×4, 4× SATA 3 Gb/s, 1× PCIe 3.0 ×16, Realtek GbE, 6-phase VRM
    +
    PSU
    Corsair CX450 — 450 W ATX, 80 PLUS Bronze, non-modular, ~100k h MTBF rating
    +
    Cooler
    Endorfy Fera 5 — same Synergy Cooling design as SilentiumPC Fera 5; 4× heatpipes, 120 mm PWM, ~220 W TDP class
    +
    Case
    mATX case reused from an old office PC
    +
    +
  • +
  • + Mobile +

    Xiaomi Redmi Note 11 Pro 5G · 6/128

    +

    Daily driver phone.

    +
    +
    Chipset
    Qualcomm Snapdragon 695 5G (SM6375, 6 nm) — 2× Kryo 660 @ up to 2.2 GHz + 6× @ 1.7 GHz · Adreno 619
    +
    Display
    6.67″ Super AMOLED DotDisplay — 1080×2400, 120 Hz, ~395 ppi, DCI-P3, HBM ~700 nits / peak ~1200 nits, Gorilla Glass 5
    +
    Memory
    6 GB LPDDR4X + 128 GB UFS 2.2 · microSD expandable (shared SIM slot)
    +
    Camera
    108 MP f/1.9 wide (1/1.52″) + 8 MP ultrawide 118° + 2 MP macro · 16 MP front
    +
    Battery
    5000 mAh · 67 W wired turbo charge (USB-C)
    +
    Body
    ~164.2 × 76.1 × 8.1 mm · ~202 g · dual speakers · 3.5 mm jack · side fingerprint · Bluetooth 5.1 / Wi-Fi 5
    +
    +
  • +
+
+
+
+ + + + + + diff --git a/index.html b/index.html new file mode 100644 index 0000000..180495b --- /dev/null +++ b/index.html @@ -0,0 +1,324 @@ + + + + + + + Josef — portfolio & home lab · skopec.dev + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+

Josef.

+

Design, the web, and a rack that never sleeps.

+

Sixteen, based in the Czech Republic — building interfaces by day and running a Proxmox lab by night.

+ +
+
+ +
+
+ +

Hey, I’m Josef.

+

+ I’m a 16-year-old student from the Czech Republic specializing in graphic design and web development at SSŠVT. + I’m a home lab enthusiast and developer — I run my own Proxmox server where I host things like Jellyfin for movies and TV shows, + Navidrome for music, Ollama for local AI, SearXNG as a search engine, and more. +

+

+ Outside of screens I train five times a week on a UL / PPL split. +

+
+ School SSŠVT + Focus Graphic design & web + Lab Proxmox @ home + Gym 5× / week · UL/PPL +
+
+
+ +
+ +
+ +
+ +
+ +
+
+ +

Quick answers

+

A few things people usually ask.

+
+
+ Who are you? +

Josef — a 16-year-old student at SSŠVT in the Czech Republic, into graphic design, web development, and home labbing.

+
+
+ Can I get access to your services? +

Most *.skopec.dev services are private. If you know me, ask — otherwise they’re not open to the public.

+
+
+ Where’s the source for this site? +

On GitHub: skopec.dev-reimagined. Older version lives in skopec.dev.

+
+
+ What hardware do you run? +

Desktop, MacBook Air M5, a Proxmox Xeon box, and a Redmi Note 11 Pro 5G — full specs on the Hardware page.

+
+
+ How do I contact you? +

Email josef@skopec.dev, or use Discord (ishootn4fun) and the other links in Get in touch.

+
+
+
+
+ +
+
+ +

Get in touch

+

Find me across the web — or just send an email.

+ +
+
+
+ + + + + + diff --git a/llms.txt b/llms.txt new file mode 100644 index 0000000..5c9b2ad --- /dev/null +++ b/llms.txt @@ -0,0 +1,17 @@ +# skopec.dev + +> Personal portfolio of Josef — graphic design, web development, and home lab. + +## Pages +- [Home](https://skopec.dev/): Intro, self-hosted services, friends, FAQ, contact +- [Projects](https://skopec.dev/projects.html): Selected work and lab experiments +- [Hardware](https://skopec.dev/hardware.html): Desktop, laptop, server, and phone specs +- [Privacy](https://skopec.dev/privacy.html): Short privacy note for this personal site + +## Services +- Proxmox: https://proxmox.skopec.dev +- Jellyfin: https://jelly.skopec.dev +- Navidrome: https://navi.skopec.dev +- Ollama: https://ai.skopec.dev +- SearXNG: https://search.skopec.dev +- ConvertX: https://convert.skopec.dev diff --git a/og-image.jpg b/og-image.jpg new file mode 100644 index 0000000..de0239b Binary files /dev/null and b/og-image.jpg differ diff --git a/privacy.html b/privacy.html new file mode 100644 index 0000000..3d8d52f --- /dev/null +++ b/privacy.html @@ -0,0 +1,81 @@ + + + + + + + Privacy — Josef + + + + + + + + + + + + + + + + + + + + +
+
+

Privacy

+

Short and plain — how this personal site handles information.

+
+ +
+
+

What this site is

+

skopec.dev is a personal portfolio. It is not a shop and does not run a contact form that stores messages on this domain.

+ +

What I collect

+

This static site does not use analytics cookies or trackers by default. Your browser may still send standard request data (IP address, user agent) to the host when pages load — that is normal for any website and is handled by the hosting provider’s logs.

+ +

External links

+

Links to social profiles, GitHub, friends’ sites, and self-hosted services leave skopec.dev. Those services have their own privacy policies.

+ +

Email

+

If you write to josef@skopec.dev, I’ll use your message only to reply. I don’t sell or share contact details.

+ +

Questions

+

Reach me at josef@skopec.dev.

+

Last updated: September 2026

+
+
+
+ + + + + + diff --git a/projects.html b/projects.html new file mode 100644 index 0000000..459f983 --- /dev/null +++ b/projects.html @@ -0,0 +1,105 @@ + + + + + + + Projects — websites & games · Josef + + + + + + + + + + + + + + + + + + + + + + +
+
+

Projects

+

Public repositories from GitHub — websites, games, and school projects.

+
+ +
+
+ +

Repositories

+

Open any project on GitHub for the full source.

+ +
+
+
+ + + + + + diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..17731a4 --- /dev/null +++ b/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://skopec.dev/sitemap.xml diff --git a/script.js b/script.js new file mode 100644 index 0000000..37df83f --- /dev/null +++ b/script.js @@ -0,0 +1,51 @@ +(() => { + const yearNodes = document.querySelectorAll("[data-year]"); + const year = String(new Date().getFullYear()); + yearNodes.forEach((node) => { + node.textContent = year; + }); + + const toggle = document.querySelector(".nav-toggle"); + const nav = document.querySelector("#site-nav"); + + if (toggle && nav) { + const setOpen = (open) => { + nav.classList.toggle("is-open", open); + toggle.setAttribute("aria-expanded", open ? "true" : "false"); + toggle.setAttribute("aria-label", open ? "Close menu" : "Open menu"); + }; + + toggle.addEventListener("click", () => { + setOpen(!nav.classList.contains("is-open")); + }); + + nav.querySelectorAll("a").forEach((link) => { + link.addEventListener("click", () => setOpen(false)); + }); + + document.addEventListener("keydown", (event) => { + if (event.key === "Escape") setOpen(false); + }); + } + + const revealItems = document.querySelectorAll(".reveal"); + if (!revealItems.length) return; + + if (!("IntersectionObserver" in window)) { + revealItems.forEach((el) => el.classList.add("is-visible")); + return; + } + + const observer = new IntersectionObserver( + (entries, obs) => { + entries.forEach((entry) => { + if (!entry.isIntersecting) return; + entry.target.classList.add("is-visible"); + obs.unobserve(entry.target); + }); + }, + { threshold: 0.12, rootMargin: "0px 0px -8% 0px" } + ); + + revealItems.forEach((el) => observer.observe(el)); +})(); diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..a6cccfa --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,23 @@ + + + + https://skopec.dev/ + monthly + 1.0 + + + https://skopec.dev/projects.html + monthly + 0.8 + + + https://skopec.dev/hardware.html + monthly + 0.8 + + + https://skopec.dev/privacy.html + yearly + 0.3 + + diff --git a/style.css b/style.css new file mode 100644 index 0000000..b7771db --- /dev/null +++ b/style.css @@ -0,0 +1,898 @@ +/* skopec.dev — cool workshop / signal aesthetic (dark) */ +:root { + --bg: #07090d; + --bg-deep: #0c1018; + --ink: #eef2f6; + --ink-soft: #c5ced8; + --muted: #8492a3; + --line: rgba(238, 242, 246, 0.08); + --line-strong: rgba(238, 242, 246, 0.16); + --surface: rgba(255, 255, 255, 0.04); + --surface-solid: #121820; + --accent: #ff4a1c; + --accent-ink: #ff7a52; + --glow: rgba(255, 74, 28, 0.22); + --radius: 2px; + --nav-h: 4.25rem; + --font-display: "Syne", sans-serif; + --font-body: "Instrument Sans", sans-serif; + --ease: cubic-bezier(0.22, 1, 0.36, 1); + --max: 72rem; +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +html { + scroll-behavior: smooth; +} + +body { + margin: 0; + min-height: 100vh; + color: var(--ink); + font-family: var(--font-body); + font-size: 1.0625rem; + line-height: 1.65; + background-color: var(--bg); + background-image: + radial-gradient(ellipse 90% 55% at 8% -10%, var(--glow), transparent 55%), + radial-gradient(ellipse 70% 45% at 100% 0%, rgba(60, 100, 160, 0.12), transparent 50%), + linear-gradient(180deg, var(--bg) 0%, var(--bg-deep) 100%); + background-attachment: fixed; + color-scheme: dark; +} + +body::before { + content: ""; + position: fixed; + inset: 0; + pointer-events: none; + z-index: 0; + opacity: 0.22; + background-image: + linear-gradient(var(--line) 1px, transparent 1px), + linear-gradient(90deg, var(--line) 1px, transparent 1px); + background-size: 48px 48px; + mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.7), transparent 85%); +} + +img { + max-width: 100%; + display: block; +} + +a { + color: inherit; + text-decoration-thickness: 1px; + text-underline-offset: 0.2em; +} + +a:hover { + color: var(--accent-ink); +} + +:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 3px; +} + +.skip-link { + position: absolute; + left: 1rem; + top: -100%; + z-index: 100; + padding: 0.6rem 1rem; + background: var(--accent); + color: #fff; + text-decoration: none; + font-weight: 600; +} + +.skip-link:focus { + top: 1rem; +} + +/* —— Nav —— */ +.site-header { + position: sticky; + top: 0; + z-index: 50; + height: var(--nav-h); + display: flex; + align-items: center; + backdrop-filter: blur(14px) saturate(1.2); + background: rgba(7, 9, 13, 0.82); + border-bottom: 1px solid var(--line); +} + +.nav-inner { + width: min(100% - 2.5rem, var(--max)); + margin-inline: auto; + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; +} + +.brand { + font-family: var(--font-display); + font-weight: 800; + font-size: 1.35rem; + letter-spacing: -0.03em; + text-decoration: none; + color: var(--ink); + line-height: 1; +} + +.brand:hover { + color: var(--accent); +} + +.brand span { + color: var(--accent); +} + +.nav-links { + display: flex; + align-items: center; + gap: 0.35rem; + list-style: none; + margin: 0; + padding: 0; +} + +.nav-links a { + display: inline-flex; + align-items: center; + padding: 0.45rem 0.85rem; + font-size: 0.9rem; + font-weight: 560; + letter-spacing: 0.01em; + text-decoration: none; + color: var(--ink-soft); + border: 1px solid transparent; + border-radius: var(--radius); + transition: color 0.2s var(--ease), border-color 0.2s var(--ease), background 0.2s var(--ease); +} + +.nav-links a:hover { + color: var(--ink); + background: var(--surface); + border-color: var(--line); +} + +.nav-links a[aria-current="page"] { + color: var(--ink); + background: var(--surface-solid); + border-color: var(--line-strong); +} + +.nav-toggle { + display: none; + width: 2.5rem; + height: 2.5rem; + padding: 0; + border: 1px solid var(--line-strong); + border-radius: var(--radius); + background: var(--surface-solid); + cursor: pointer; +} + +.nav-toggle span { + display: block; + width: 1.1rem; + height: 2px; + margin: 5px auto; + background: var(--ink); +} + +/* —— Layout —— */ +.page { + position: relative; + z-index: 1; +} + +.wrap { + width: min(100% - 2.5rem, var(--max)); + margin-inline: auto; +} + +.section { + padding: 4.5rem 0; +} + +.section + .section { + border-top: 1px solid var(--line); +} + +.section-label { + display: inline-block; + margin: 0 0 0.75rem; + font-size: 0.75rem; + font-weight: 650; + letter-spacing: 0.14em; + text-transform: uppercase; + color: var(--accent-ink); +} + +.section h2 { + margin: 0 0 0.75rem; + font-family: var(--font-display); + font-size: clamp(1.75rem, 3vw, 2.35rem); + font-weight: 750; + letter-spacing: -0.03em; + line-height: 1.15; +} + +.section-lead { + margin: 0 0 2rem; + max-width: 38rem; + color: var(--muted); +} + +/* —— Hero —— */ +.hero { + position: relative; + min-height: calc(100vh - var(--nav-h)); + display: grid; + align-items: end; + padding: 3rem 0 4.5rem; + overflow: hidden; +} + +.hero::after { + content: ""; + position: absolute; + right: -8%; + top: 8%; + width: min(52vw, 34rem); + height: min(52vw, 34rem); + border-radius: 50%; + background: + radial-gradient(circle at 35% 35%, rgba(255, 255, 255, 0.06), transparent 55%), + conic-gradient(from 210deg at 50% 50%, transparent 0 55%, var(--glow) 70%, transparent 100%); + border: 1px solid var(--line); + opacity: 0.85; + animation: drift 18s var(--ease) infinite alternate; + pointer-events: none; +} + +@keyframes drift { + from { transform: translate(0, 0) rotate(0deg); } + to { transform: translate(-4%, 6%) rotate(8deg); } +} + +.hero-content { + position: relative; + z-index: 1; + max-width: 40rem; +} + +.hero-brand { + margin: 0 0 1.25rem; + font-family: var(--font-display); + font-weight: 800; + font-size: clamp(4.5rem, 14vw, 8.5rem); + letter-spacing: -0.06em; + line-height: 0.9; + color: var(--ink); +} + +.hero-brand em { + font-style: normal; + color: var(--accent); +} + +.hero h1 { + margin: 0 0 0.9rem; + font-family: var(--font-display); + font-size: clamp(1.35rem, 2.6vw, 1.85rem); + font-weight: 650; + letter-spacing: -0.02em; + line-height: 1.25; + color: var(--ink-soft); +} + +.hero p { + margin: 0 0 1.75rem; + max-width: 32rem; + color: var(--muted); + font-size: 1.05rem; +} + +.hero-actions { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 0.45rem; + padding: 0.7rem 1.15rem; + font-family: var(--font-body); + font-size: 0.95rem; + font-weight: 600; + text-decoration: none; + border-radius: var(--radius); + border: 1px solid transparent; + cursor: pointer; + transition: transform 0.2s var(--ease), background 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease); +} + +.btn:hover { + transform: translateY(-1px); + color: inherit; +} + +.btn-primary { + background: var(--ink); + color: var(--bg); + border-color: var(--ink); +} + +.btn-primary:hover { + background: var(--accent); + border-color: var(--accent); + color: #fff; +} + +.btn-ghost { + background: var(--surface); + border-color: var(--line-strong); + color: var(--ink); +} + +.btn-ghost:hover { + background: var(--surface-solid); + border-color: var(--ink); +} + +/* —— About —— */ +.about-text { + max-width: 42rem; + margin: 0; + color: var(--ink-soft); + font-size: 1.1rem; +} + +.about-text + .about-text { + margin-top: 1rem; +} + +.meta-row { + display: flex; + flex-wrap: wrap; + gap: 0.5rem 1.25rem; + margin-top: 1.75rem; + padding-top: 1.25rem; + border-top: 1px solid var(--line); + color: var(--muted); + font-size: 0.92rem; +} + +.meta-row strong { + color: var(--ink); + font-weight: 650; +} + +/* —— Services —— */ +.service-list { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 0; + border-top: 1px solid var(--line-strong); +} + +.service-list a { + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + grid-template-rows: auto auto; + align-items: center; + column-gap: 1rem; + row-gap: 0.15rem; + padding: 1.1rem 0.15rem; + text-decoration: none; + border-bottom: 1px solid var(--line); + transition: padding-left 0.25s var(--ease), color 0.2s var(--ease); +} + +.service-list a:hover { + padding-left: 0.65rem; + color: var(--accent-ink); +} + +.service-name { + grid-column: 1; + grid-row: 1; + font-family: var(--font-display); + font-weight: 700; + font-size: 1.2rem; + letter-spacing: -0.02em; +} + +.service-host { + grid-column: 1; + grid-row: 2; + font-size: 0.9rem; + color: var(--muted); + font-variant-numeric: tabular-nums; +} + +.service-list a:hover .service-host { + color: var(--accent-ink); +} + +.service-arrow { + grid-column: 2; + grid-row: 1 / span 2; + font-size: 1.25rem; + opacity: 0.35; + transition: opacity 0.2s var(--ease), transform 0.2s var(--ease); +} + +.service-list a:hover .service-arrow { + opacity: 1; + transform: translateX(3px); + color: var(--accent); +} + +@media (min-width: 640px) { + .service-list a { + grid-template-columns: 11rem minmax(0, 1fr) auto; + grid-template-rows: auto; + } + + .service-name { + grid-column: 1; + grid-row: 1; + } + + .service-host { + grid-column: 2; + grid-row: 1; + } + + .service-arrow { + grid-column: 3; + grid-row: 1; + } +} + +/* —— Friends —— */ +.friend-list { + list-style: none; + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + gap: 0.65rem; +} + +.friend-list a { + display: flex; + flex-wrap: wrap; + align-items: baseline; + justify-content: space-between; + gap: 0.35rem 1.5rem; + padding: 0.95rem 1.1rem; + text-decoration: none; + background: var(--surface); + border: 1px solid var(--line); + border-radius: var(--radius); + transition: border-color 0.2s var(--ease), background 0.2s var(--ease), transform 0.2s var(--ease); +} + +.friend-list a:hover { + border-color: var(--ink); + background: var(--surface-solid); + transform: translateX(4px); + color: var(--ink); +} + +.friend-name { + font-weight: 650; +} + +.friend-url { + color: var(--muted); + font-size: 0.9rem; +} + +/* —— Connect —— */ +.social-row { + display: flex; + flex-wrap: wrap; + gap: 0.55rem; +} + +.social-link { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.75rem; + height: 2.75rem; + border: 1px solid var(--line-strong); + border-radius: var(--radius); + background: var(--surface-solid); + color: var(--ink-soft); + text-decoration: none; + transition: background 0.2s var(--ease), border-color 0.2s var(--ease), color 0.2s var(--ease), transform 0.2s var(--ease); +} + +.social-link:hover { + color: var(--bg); + background: var(--ink); + border-color: var(--ink); + transform: translateY(-2px); +} + +.social-link svg { + width: 1.25rem; + height: 1.25rem; + fill: currentColor; +} + +.social-link.is-placeholder { + opacity: 0.55; + cursor: default; + pointer-events: none; +} + +.connect-note { + margin: 1rem 0 0; + font-size: 0.9rem; + color: var(--muted); +} + +/* —— Inner pages —— */ +.page-hero { + padding: 3.5rem 0 2.5rem; +} + +.page-hero h1 { + margin: 0 0 0.75rem; + font-family: var(--font-display); + font-size: clamp(2.4rem, 6vw, 4rem); + font-weight: 800; + letter-spacing: -0.04em; + line-height: 1; +} + +.page-hero p { + margin: 0; + max-width: 36rem; + color: var(--muted); +} + +.stack-list, +.project-list { + list-style: none; + margin: 0; + padding: 0; + display: grid; + gap: 1.5rem; +} + +.stack-item, +.project-item { + padding-bottom: 1.5rem; + border-bottom: 1px solid var(--line); +} + +.stack-item:last-child, +.project-item:last-child { + border-bottom: 0; + padding-bottom: 0; +} + +.stack-item h3, +.project-item h3 { + margin: 0 0 0.4rem; + font-family: var(--font-display); + font-size: 1.35rem; + font-weight: 700; + letter-spacing: -0.02em; +} + +.project-item h3 a { + text-decoration: none; +} + +.project-item h3 a:hover { + color: var(--accent-ink); +} + +.stack-item p, +.project-item p { + margin: 0; + color: var(--muted); + max-width: 40rem; +} + +.project-link { + display: inline-block; + margin-top: 0.75rem; + font-size: 0.92rem; + font-weight: 600; + text-decoration: none; + color: var(--accent-ink); +} + +.project-link:hover { + color: var(--accent); +} + +.tag { + display: inline-block; + margin-bottom: 0.5rem; + font-size: 0.72rem; + font-weight: 650; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--accent-ink); +} + +.spec-list { + display: grid; + gap: 0.55rem; + margin: 1.15rem 0 0; + padding: 0; + max-width: 36rem; +} + +.spec-list > div { + display: grid; + grid-template-columns: 5.5rem minmax(0, 1fr); + gap: 0.75rem 1rem; + align-items: baseline; + padding-bottom: 0.55rem; + border-bottom: 1px solid var(--line); +} + +@media (min-width: 640px) { + .spec-list > div { + grid-template-columns: 6.75rem minmax(0, 1fr); + } +} + +.spec-list > div:last-child { + padding-bottom: 0; + border-bottom: 0; +} + +.spec-list dt { + margin: 0; + font-size: 0.78rem; + font-weight: 650; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--muted); +} + +.spec-list dd { + margin: 0; + color: var(--ink-soft); + font-variant-numeric: tabular-nums; +} + +/* —— Footer —— */ +.site-footer { + position: relative; + z-index: 1; + margin-top: 2rem; + padding: 1.75rem 0 2.25rem; + border-top: 1px solid var(--line-strong); + color: var(--muted); + font-size: 0.88rem; +} + +.footer-inner { + width: min(100% - 2.5rem, var(--max)); + margin-inline: auto; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 0.75rem 1.5rem; +} + +.footer-links { + display: flex; + flex-wrap: wrap; + gap: 0.65rem 1.1rem; +} + +.footer-inner a { + text-decoration: none; + font-weight: 560; +} + +.footer-inner a:hover { + color: var(--accent-ink); +} + +/* —— FAQ —— */ +.faq-list { + display: grid; + gap: 0.65rem; + max-width: 42rem; +} + +.faq-item { + border: 1px solid var(--line); + border-radius: var(--radius); + background: var(--surface); + padding: 0.15rem 1rem; +} + +.faq-item summary { + cursor: pointer; + list-style: none; + padding: 0.9rem 0; + font-weight: 650; + font-family: var(--font-display); + letter-spacing: -0.02em; +} + +.faq-item summary::-webkit-details-marker { + display: none; +} + +.faq-item summary::after { + content: "+"; + float: right; + color: var(--accent); + font-weight: 700; +} + +.faq-item[open] summary::after { + content: "–"; +} + +.faq-item p { + margin: 0 0 1rem; + color: var(--muted); +} + +.faq-item a { + color: var(--accent-ink); +} + +/* —— Prose (privacy etc.) —— */ +.prose { + max-width: 40rem; +} + +.prose h2 { + margin: 2rem 0 0.65rem; + font-family: var(--font-display); + font-size: 1.35rem; + font-weight: 700; + letter-spacing: -0.02em; +} + +.prose h2:first-child { + margin-top: 0; +} + +.prose p { + margin: 0 0 0.85rem; + color: var(--ink-soft); +} + +.prose a { + color: var(--accent-ink); +} + +.muted-note { + margin-top: 2rem !important; + font-size: 0.88rem; + color: var(--muted) !important; +} + +/* —— Motion / reveal —— */ +.reveal { + opacity: 0; + transform: translateY(18px); + transition: opacity 0.7s var(--ease), transform 0.7s var(--ease); +} + +.reveal.is-visible { + opacity: 1; + transform: none; +} + +.hero-content > * { + animation: rise 0.85s var(--ease) both; +} + +.hero-brand { animation-delay: 0.05s; } +.hero h1 { animation-delay: 0.16s; } +.hero p { animation-delay: 0.28s; } +.hero-actions { animation-delay: 0.4s; } + +@keyframes rise { + from { + opacity: 0; + transform: translateY(22px); + } + to { + opacity: 1; + transform: none; + } +} + +@media (prefers-reduced-motion: reduce) { + html { scroll-behavior: auto; } + *, + *::before, + *::after { + animation: none !important; + transition: none !important; + } + .reveal { + opacity: 1; + transform: none; + } + body::before, + .hero::after { + display: none; + } +} + +/* —— Responsive —— */ +@media (max-width: 720px) { + .nav-toggle { + display: inline-block; + } + + .nav-links { + position: absolute; + top: var(--nav-h); + left: 0; + right: 0; + display: none; + flex-direction: column; + align-items: stretch; + gap: 0.25rem; + padding: 0.75rem 1.25rem 1rem; + background: rgba(12, 16, 24, 0.97); + border-bottom: 1px solid var(--line); + backdrop-filter: blur(12px); + } + + .nav-links.is-open { + display: flex; + } + + .nav-links a { + padding: 0.75rem 0.85rem; + } + + .hero { + min-height: auto; + padding-top: 2.5rem; + padding-bottom: 3.25rem; + } + + .hero::after { + width: 16rem; + height: 16rem; + top: 0; + right: -20%; + opacity: 0.55; + } + + .section { + padding: 3.25rem 0; + } +}