From 464da42e38a0de9afaf75b2a94e01b89cc260d67 Mon Sep 17 00:00:00 2001 From: Quinta0 <0pietroquintavalle0@gmail.com> Date: Thu, 23 Jul 2026 17:44:49 +0200 Subject: [PATCH] Revamp and restyle --- README.md | 63 ++- app/components/boot-sequence.tsx | 354 ++++++++++++ app/globals.css | 834 ++++++++++++++++------------- app/layout.tsx | 5 +- app/page.tsx | 887 ++++++++++++++++--------------- 5 files changed, 1319 insertions(+), 824 deletions(-) create mode 100644 app/components/boot-sequence.tsx diff --git a/README.md b/README.md index e215bc4..e19528a 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,49 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). +# whoami -## Getting Started +Personal portfolio site for **Pietro Quintavalle** — systems engineer, developer, and homelabber. Built as a single-page Next.js app with a dark, terminal-inspired aesthetic. -First, run the development server: +**Live:** deployed via GitHub Pages on every push to `main`. + +## What's on it + +- A boot-sequence intro — the page opens on a black screen running a fake BIOS/POST log (hardware checks, homelab services starting up) before greeting the visitor and revealing the site. Skippable at any time with a click or keypress. +- A single-page layout covering skills, homelab infrastructure, desktop specs, personal & client projects, open-source contributions, experience, and education. +- An interactive terminal panel (in the "About" section) styled after a real shell session, complete with a `neofetch`-style system summary. +- Hidden terminal commands — type into the terminal prompt: + - `mellon` — the Doors of Durin + - `you shall not pass` — the Balrog + - `iddqd` / `idkfa` — DOOM cheat codes + - `reboot` — replays the boot sequence + +## Stack + +- [Next.js](https://nextjs.org) 15 (App Router) + React 18 + TypeScript +- Tailwind CSS for utilities, hand-written CSS (`app/globals.css`) for the page's design system +- Statically exported (`output: 'export'`) and deployed to GitHub Pages via GitHub Actions (`.github/workflows/nextjs.yml`) + +## Getting started ```bash +npm install npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev ``` -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. +Open [http://localhost:3000](http://localhost:3000). The page auto-updates as you edit `app/page.tsx`. -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. +Other scripts: -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. +```bash +npm run build # static export to ./out +npm run lint # ESLint +npm run type-check # tsc --noEmit +``` -## Learn More +## Structure -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +``` +app/ + page.tsx # all page content and sections + components/boot-sequence.tsx # the black-screen boot/greeting intro + globals.css # design tokens + component styles + layout.tsx # metadata, fonts +``` diff --git a/app/components/boot-sequence.tsx b/app/components/boot-sequence.tsx new file mode 100644 index 0000000..00019b2 --- /dev/null +++ b/app/components/boot-sequence.tsx @@ -0,0 +1,354 @@ +"use client" + +import { useEffect, useRef, useState } from 'react' + +const BOOT_LINES = [ + 'QUINTA-BIOS (C) 2026, Pietro Quintavalle Systems', + 'CPU: AMD Ryzen 7 9800X3D @ 5.00GHz .................... OK', + 'Memory Test: 32768MB ................................... OK', + 'Detecting storage: Proxmox VE / UNRAID / TrueNAS ....... OK', + '', + 'Booting from /dev/homelab ...', + '', + '[ OK ] Started Proxmox VE 8 hypervisor', + '[ OK ] Mounted UNRAID 7.3.1 parity array', + '[ OK ] Mounted TrueNAS ZFS RAID 10', + '[ OK ] Started Docker daemon (LXC + KVM)', + '[ OK ] Established Cloudflare Tunnel ....... 0 open ports', + '[ OK ] Started Pangolin + Traefik reverse proxy', + '[ OK ] Started Fail2Ban / Crowdsec / Authelia', + '[ OK ] Loaded Grafana + Prometheus', + '[ OK ] Mounted /home/quinta', + '[ OK ] Uptime check ........................ 90%+', + '[ OK ] Loading personal_website.tsx', +] + +const GREETING_LINES = [ + 'Welcome back, root.', + "Hi, I'm Pietro — systems engineer, developer, homelabber.", +] + +const DURIN_ART = ` ______________ _ _,-----------._ ___ +| | (_,.- _,-'_,-----------._\`-._ _)_) +| THE _ _ _ | | ,'_,-' ___________ \`-._\`. +| | / \\|_)| \\ | \`' ,',' _,-'___________\`-._ \`.\`. +| |__\\_/| \\|_/ | ,',' ,'_,-' . \`-._\`. \`.\`. +| | /,' ,',' >|< \`.\`. \`.\\ +| OF THE _ _ | // ,',' >< ,^. >< \`.\`. \\\\ +| |_)||\\|/_(_ | // /,' >< / | \\ >< \`.\\ \\\\ +| | \\|| |\\_|_) | // // >< \\/\\^/\\/ >< \\\\ \\\\ +|______________| ;; ;; \`---' :: :: + || || (____ || || + DOORS OF DURIN _||__||_ ,'----. _||__||_ + (o.____.o)____ \`---' ____(o.____.o) + | | /,--.) (,--.\\ | | + | |(( -\`___ ___\` ))| | + | | \\\\,'', \`. .' .\`\`.// | | + | | // (___,'. .'.___) \\\\ | | + /| | ;;)) ____) . . (____ ((\\\\ | |\\ + \\|.__ | ||/ .'.--.\\/ \`/,--.\`. \\;: | __,|; + |\`-,\`;.| :/ /,' \`)-' \`-(' \`.\\ \\: |.;',-'| + | \`.. ' / \\__.' \`.__/ \\ \` ,.' | + | |,\\ /, ,\\ /,| | + | ||: : ) . ( : :|| | + /| |:; |/ . ./|\\, , \\| :;| |\\ + \\|.__ |/ : ,/- <--:--> ,\\. ; \\| __,|; + |\`-.\`\`: \`'/-. '\\|/\` ,-\\\`; ;'',-'| + | \`.. ,' \`' ' \` \`. ,.' | + | || : : || | + | || | | || | + | || | | || | + | |' | _ | \`| | + | | | '|)) | | | + ;____: \`._ \`' _,' ;____: + {______} \\___________________/ {______} + SSt |______|_______________________________|______|` + +const BALROG_ART = `############################+##++++++########+++++-----. ------------ . - --.--- --.--.------...---------------..--- +###############################+++++++####+++++----- --- - ---- -- -. ..- - - ------------------------------- +##########################+++++++++++++#####+++---. . -- - - - - .--.----------------------------- +########################+++#+++++++++######++++-. ..--++++++------..----- - ------------.- --------- +###########################+##+++++++++++##++- -++++++++--+--------.- .. ...-.. - ----------------.--------- +############################+++++++++++++#++-+ - -++++++++++++------+++---++------------------------- -------------.--- +#############################++++++++++++#++++- --- ++++++++++++++++--+++++++------------------------------- -------------.--- +#############################++++++++++++++++--- .+##+++++++++++++++++++++++++---------------------------.---------------- +############++#+###+#+++#####++++++++++++++-- --- -- -+###+++++++++++++++++++++++++----+--------------------------------------- +###########++++++++++++#++++++++++++++--..---- . .-- .. -+#####++++++++++++++++++++++++-+--++++------------------------------------- +############+++++++++++++++++++++++++++++++++.+ ..--.- +#########+++++++++++++++++++++++++++----+------------------------.----------- +############++++++++++++++++++++++-+++++++++++++.. + .++#####++#++++#++++++++++++++++++++++++++-------+--------------------- --------- +########+#+++++++++++++++++++++---++++-+++++++++.. -+#########-+ .+++++++++++++++++++++++-++++-+++++----+-------------------+-------- ++######+#+++++++++++++++++++++-+++++++-++++++++++##+-.+++++++#+ +++++++++++++++++++++++-----+++++++++++-----+--------+------------- +++##+++++++++++++++++++++++++++++++++++++-+++.++++-+ .+++++-++++++++++++++++++++++----+---------------+++++++-----+---------------- +++++++++++++++++++++++++++##+++++++++++++++. ++# ++ +-.++++++++++++++++#++++---+------------------------+-+++---+++-+---+-----.---- ++++++++++++++++++++++++###++++++++++++++++++-+++- +++ ++++++++++++++#++++-++----+-+----------------------+---++++++++----+--++++---- ++++++++++++++++++++++###++++++++++++++++++++++++ +#+#+#+#+++#######+++#----------------------------------+-++----++-+++--+++++++----. ++++++++++++++++++++++++#++++++++++++-++++++++++##++++#++#+++#+++#+#+++++++++--+----------+------------------+-++--++-+--++++++-++-.--- +++++++++++++++++++++++++++++++++++++++++++#+++++-++++##+#++##++####+++++++++++-+-----------+------------------+---++++++++++++++-++--- ++++++++++++++++++++++++++++++++++++++++--++++++#++++###+#++#+#++++++++++++++++++++---+---------------------------+++++++++++++++++++-- ++++++++++++++++++++++++++++++-------++--+++++#++++#+#######+#++++#+#-++-+++++++++++------------------------------+--++++++++++++++++-- ++++++++++++++++++++++++++++-------+-----+#+++#+++# #.# #+####+###++--+++++++++++++--------------.-------------+-++--++++++++++++++-- ++++++++++++++++++++++-----------------+++#+--# . - - .+####+##++-----++++++++--+----+-+--------.--------------+-----+++++++++++++-- ++++++++++++++++++++++-------++----------#++++# - #. ####+-+-.+--+++++++---+++++++----------------------------.--++++++++++++-- ++++++++++++++--+++++-+-------+----------+++##. . + +#+- -++---+ ++++----+++++++++---------------------------..+++++++++++++-- ++++++++++++++-++++++---------------------#++ ---. +#- +++-. -++----++-+-+-+++++--------------------------..-+++++++++++++-- +++++++++++++++-----+--------------++++++++#+ +...-- + . - ----+-+++--++-+++-----------------------.---++++++++++++++-- +++++++++----++--+++----------+++++++++++-++#++. . . ----+----++---+--++-------------------------+ +++++++++++++--- ++++++++++---+--------------++++++++++++++++...-. . -----------+---++++------------------------++++++++++++++++--- ++++++++---+-++-+++-+-----+++++++++++++++++------ .. ---------+-----+++++-+-----------------++++++++++++++++++---- ++++++++-+++-+----++------+++++++++++++++++++++--- ....----+--+------++++-++------------.+.+++++++++++##+++++++---- +++++++++++++++++------+++++++++++++++++++++++++--. . ... ++---+--+----++++---++-------.+-.+++++++++++++#######+------ +++++---++++------+---++++++++++++++++++++++++++---. ...-.--+--+---++--+------+-+--.-.++.+#+++#+++++###++++++#--------- +-++++.--+++++---+----+++++++++++++++++++++++++++-+-... ......---+++-++----++-----+--.---++####+++++###--++####++-----.---- +++++-----+----------++++++++++++++++++++++++++++++--.... ..------++++--------------.+- ############+++##+###+--------------- ++++-+++------------++++++++++++++++++++++++++++++----..... ..----+. -+---------- ---..+###+####+###-#####++-+--.--------------- ++++++++++--.------+++++++++++++++++++++++++++++. -----.... . . ..-++-+--+----------- .#++++#++##+--++++---+-----------.----------- +-----+-+++--------++++++++++++++++++++++++++++++---.--.... . .. ---++-.+------------ .+-++##--#+--++--.----------------------------- +-------+++--------++++#++++++++++++++++++++++-+-------..-. .---+--+++---------. +#-#+#+--+------.---.---.----- ----------------.. +-------++++++-+------.+++++++++++++++-++++++++-- ----..... ..----+--+.++-------- +--#++-------------------------....-.---------..-- +------++++++++++++---+--##++++++++++++++++++++---------......-.--+++-.-++---.---. -##++----------------------- ---.....---------...... +-++---++++++#+++++##++...+#++++++++ +++++++++---------...-.---.++++...--.-+-----..+#+-----------.---------------.... ......-......... +-------+++-+++#+# ++#+....+##+#++++#+++++++++----++++---...----++++-..+.--++---.+#+-------------------------..... . ....... . ... +++----+++++++-+---###+-+..-##+##+##+++++++++-.--------.--------+-+--------++---.#-----------..----------...... .. . .. +++++-+++++++++++++++##++-..##++++##+++++++++-+----..----+---+--++---------++-------------------------...-. . . .... +++++++++++++++++++++++##-..+++++++ ++++++ +- - --------------+-.---------------- ----------.----------.... . . +-+++++++-+++++++++++++#+--++.++-----------------------. .--- --------------------------------------...--..... ...... . +-++++--++++++++++++++++-++ -++.-------.------------ ------.---------.....--------------------- .--- ....... ...... +----------++++--------+++.----------..--------.------------ ...-.-.-.----------------------..---.--. .. ........ +-----.-...-------------------------------.------ -----..----...-.........-----------------.. .-.... ....... ... . +........ .--------------------------------------- --... ---......----.. . - ----------.--...-... ......... .. + ... ..---------------.--.-------------...-.---------.. ....-...... --- --- ---.-----...... . +...... .-----.-.----------.---------------..--------.--...... . ..----------------.-.. .. + . ...--------------------------...----.--------.-..---.-. .---------------------. . ` + +const DOOM_ART = `######## ###### ###### ## ## +## ## ## ## ## ## #### #### +## ## ## ## ## ## ## ## ## +## ## ## ## ## ## ## ## ## +## ## ## ## ## ## ## ## +## ## ## ## ## ## ## ## +######## ###### ###### ## ##` + +type Phase = 'boot' | 'greet' | 'prompt' | 'exit' +type EggView = 'none' | 'moria' | 'balrog' | 'iddqd' | 'idkfa' + +const EGG_ART: Record, string> = { + moria: DURIN_ART, + balrog: BALROG_ART, + iddqd: DOOM_ART, + idkfa: DOOM_ART, +} + +const EGG_CAPTIONS: Record, string> = { + moria: 'the doors of durin, lord of moria — speak, friend, and enter', + balrog: 'a demon of the ancient world — YOU SHALL NOT PASS', + iddqd: 'IDDQD — GOD MODE ON — degreelessness achieved', + idkfa: 'IDKFA — armed to the teeth — all keys, all ammo', +} + +const EGG_DURATION = 4200 +const IDLE_TIMEOUT = 15000 + +export default function BootSequence({ onComplete, onReboot }: { onComplete: () => void; onReboot: () => void }) { + const [visibleBoot, setVisibleBoot] = useState(0) + const [visibleGreet, setVisibleGreet] = useState(0) + const [phase, setPhase] = useState('boot') + const [cmd, setCmd] = useState('') + const [eggView, setEggView] = useState('none') + const [eggBusy, setEggBusy] = useState(false) + + const doneRef = useRef(false) + const phaseRef = useRef('boot') + const eggViewRef = useRef('none') + const timers = useRef[]>([]) + const idleTimer = useRef | null>(null) + const overlayRef = useRef(null) + const inputRef = useRef(null) + + useEffect(() => { phaseRef.current = phase }, [phase]) + useEffect(() => { eggViewRef.current = eggView }, [eggView]) + + const finish = () => { + if (doneRef.current) return + doneRef.current = true + timers.current.forEach(clearTimeout) + timers.current = [] + if (idleTimer.current) clearTimeout(idleTimer.current) + setPhase('exit') + const t = setTimeout(onComplete, 380) + timers.current.push(t) + } + + const fastForward = () => { + if (phaseRef.current === 'boot') setVisibleBoot(BOOT_LINES.length) + else if (phaseRef.current === 'greet') setVisibleGreet(GREETING_LINES.length) + } + + const resetIdleTimer = () => { + if (idleTimer.current) clearTimeout(idleTimer.current) + idleTimer.current = setTimeout(() => { + if (phaseRef.current === 'prompt' && eggViewRef.current === 'none') finish() + }, IDLE_TIMEOUT) + } + + const triggerEgg = (view: Exclude) => { + if (eggBusy) return + setEggBusy(true) + setEggView(view) + const t = setTimeout(() => { + setEggView('none') + setCmd('') + setEggBusy(false) + resetIdleTimer() + inputRef.current?.focus() + }, EGG_DURATION) + timers.current.push(t) + } + + useEffect(() => { + document.body.style.overflow = 'hidden' + + const reduced = typeof window !== 'undefined' && + window.matchMedia('(prefers-reduced-motion: reduce)').matches + + if (reduced) { + setVisibleBoot(BOOT_LINES.length) + setVisibleGreet(GREETING_LINES.length) + setPhase('prompt') + return () => { document.body.style.overflow = '' } + } + + let i = 0 + const stepBoot = () => { + i += 1 + setVisibleBoot(i) + if (i < BOOT_LINES.length) { + const line = BOOT_LINES[i] + const delay = line === '' ? 90 : 60 + Math.random() * 90 + timers.current.push(setTimeout(stepBoot, delay)) + } else { + timers.current.push(setTimeout(() => setPhase('greet'), 450)) + } + } + timers.current.push(setTimeout(stepBoot, 240)) + + overlayRef.current?.focus() + + return () => { + document.body.style.overflow = '' + timers.current.forEach(clearTimeout) + timers.current = [] + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + useEffect(() => { + if (phase !== 'greet') return + let i = 0 + const stepGreet = () => { + i += 1 + setVisibleGreet(i) + if (i < GREETING_LINES.length) { + timers.current.push(setTimeout(stepGreet, 950)) + } else { + timers.current.push(setTimeout(() => setPhase('prompt'), 1900)) + } + } + timers.current.push(setTimeout(stepGreet, 300)) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [phase]) + + useEffect(() => { + if (phase !== 'prompt') return + inputRef.current?.focus() + resetIdleTimer() + return () => { + if (idleTimer.current) clearTimeout(idleTimer.current) + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [phase]) + + const handleOverlayClick = () => fastForward() + const handleOverlayKeyDown = () => fastForward() + + const handleInputKeyDown = (e: React.KeyboardEvent) => { + if (e.key !== 'Enter' || eggBusy) return + const value = cmd.trim().toLowerCase() + if (value === 'start') { + finish() + return + } else if (value === 'mellon') { + triggerEgg('moria') + } else if (value === 'you shall not pass') { + triggerEgg('balrog') + } else if (value === 'iddqd') { + triggerEgg('iddqd') + } else if (value === 'idkfa') { + triggerEgg('idkfa') + } else if (value === 'reboot') { + onReboot() + return + } + setCmd('') + } + + return ( +
+
+
+ {BOOT_LINES.slice(0, visibleBoot).map((line, idx) => ( +
{line || ' '}
+ ))} +
+ {phase !== 'boot' && ( +
+ {GREETING_LINES.slice(0, visibleGreet).map((line, idx) => ( +
{line}
+ ))} +
+ )} + {phase === 'prompt' && ( +
+ {eggView === 'none' ? ( +
+
+ ➤ ~ + { setCmd(e.target.value); resetIdleTimer() }} + onKeyDown={handleInputKeyDown} + autoComplete="off" + spellCheck={false} + aria-label="boot command input" + disabled={eggBusy} + /> +
+
type start to enter — speak, friend, and enter…
+
+ ) : ( +
+
+
{EGG_ART[eggView]}
+
+
{EGG_CAPTIONS[eggView]}
+
+ )} +
+ )} +
+
+ {phase === 'prompt' ? 'auto-launching soon if idle' : 'press any key to skip animation'} +
+
+ ) +} diff --git a/app/globals.css b/app/globals.css index ba839b1..9b1ed9f 100644 --- a/app/globals.css +++ b/app/globals.css @@ -4,461 +4,575 @@ *,*::before,*::after{box-sizing:border-box;margin:0;padding:0} :root{ - --stone:#E8E5DF; - --stone2:#DDD9D2; - --stone3:#D4D0C9; - --navy:#1C2033; - --navy2:#252A42; - --mid:#626880; - --muted:#959BAF; - --line:rgba(28,32,51,0.11); - --line2:rgba(28,32,51,0.18); - --line3:rgba(28,32,51,0.26); - --g1:240px; - --g2:calc(100% - 480px); + --stone:#F7F6F1; + --stone2:#FFFFFF; + --stone3:rgba(193,18,31,.03); + --deep:#FFFFFF; + --navy:#111110; + --navy2:#C1121F; + --accent2:#C1121F; + --mid:#726F66; + --muted:#8F8C82; + --line:#DAD7CC; + --line2:rgba(17,17,16,.32); + --line3:rgba(17,17,16,.5); + --container:1180px; + --pad:52px; + --r:0px; + --r-sm:0px; + --shadow:none; + --f-head:'Archivo',sans-serif; + --f-body:'Inter',sans-serif; + --f-mono:'IBM Plex Mono',monospace; } html{scroll-behavior:smooth} body{ background:var(--stone); color:var(--navy); - font-family:'DM Sans',system-ui,sans-serif; + font-family:var(--f-body); font-size:15px; line-height:1.6; -webkit-font-smoothing:antialiased; overflow-x:hidden; } -.col-rule-l,.col-rule-r{ - position:fixed;top:0;bottom:0;width:1px; - background:var(--line2);z-index:200;pointer-events:none; -} -.col-rule-l{left:var(--g1)} -.col-rule-r{right:var(--g1)} -.col-rule-c1{ - position:fixed;top:0;bottom:0;width:1px; - background:var(--line);z-index:200;pointer-events:none; - left:calc(var(--g1) + var(--g2)/3);display:none; -} -.col-rule-c2{ - position:fixed;top:0;bottom:0;width:1px; - background:var(--line);z-index:200;pointer-events:none; - left:calc(var(--g1) + var(--g2)*2/3);display:none; -} +.wrap{max-width:var(--container);margin:0 auto;padding-left:var(--pad);padding-right:var(--pad)} +/* ---------- SECTION NUMBERING (Swiss index) ---------- */ +.site-shell{counter-reset:sec} +.about-split,.sec{counter-increment:sec} + +/* ---------- NAV ---------- */ nav{ position:sticky;top:0;z-index:300; - background:var(--stone); - border-bottom:1px solid var(--line3); - display:flex;align-items:stretch; - height:52px; + background:rgba(247,246,241,.9); + backdrop-filter:blur(10px) saturate(140%); + -webkit-backdrop-filter:blur(10px) saturate(140%); + border-bottom:1px solid var(--line); + transition:background .25s ease,border-color .25s ease; +} +nav.nav-scrolled{ + background:rgba(247,246,241,.98); + border-bottom-color:var(--navy); +} +.nav-bar{ + max-width:var(--container);margin:0 auto; + padding:0 var(--pad); + height:64px; + display:flex;align-items:center;justify-content:space-between; } .nav-logo-wrap{ - width:var(--g1);flex-shrink:0; - display:flex;align-items:center; - padding:0 28px; - border-right:1px solid var(--line3); - gap:9px; + display:flex;align-items:center;gap:9px; text-decoration:none;color:var(--navy); } -.nav-logosvg{width:22px;height:22px} +.nav-logosvg{width:24px;height:24px} .nav-name{ - font-family:'Syne',sans-serif; - font-size:14px;font-weight:700; + font-family:var(--f-head); + font-size:15px;font-weight:700; letter-spacing:-.01em; } .nav-name sup{ font-size:8px;font-weight:500; - opacity:.5;vertical-align:super; - letter-spacing:.06em;font-family:'DM Sans',sans-serif; + opacity:.55;vertical-align:super; + letter-spacing:.06em;font-family:var(--f-mono); } -.nav-links{display:flex;align-items:stretch;flex:1} +.nav-links{display:flex;align-items:center;gap:4px} .nav-links a{ display:flex;align-items:center; - padding:0 22px; - font-size:13px;color:var(--mid); + padding:8px 14px; + font-size:12.5px;color:var(--mid); text-decoration:none; - border-right:1px solid var(--line); - transition:color .15s; - font-weight:400; - letter-spacing:.01em; -} -.nav-links a:hover{color:var(--navy)} -.nav-socials{ - display:flex;align-items:stretch; - width:var(--g1); - border-left:1px solid var(--line3); + transition:color .15s,background .15s; + font-weight:500;letter-spacing:.03em; + font-family:var(--f-mono);text-transform:uppercase; } +.nav-links a:hover{color:var(--navy2);background:var(--stone3)} +.nav-socials{display:flex;align-items:center;gap:4px;margin-left:8px;padding-left:14px;border-left:1px solid var(--line)} .nav-socials a{ display:flex;align-items:center;justify-content:center; - padding:0 22px; + width:32px;height:32px; color:var(--mid);text-decoration:none; - border-left:1px solid var(--line); - transition:color .15s; + transition:color .15s,background .15s; } -.nav-socials a:hover{color:var(--navy)} -.nav-socials svg{width:16px;height:16px;fill:currentColor} +.nav-socials a:hover{color:var(--navy2);background:var(--stone3)} +.nav-socials svg{width:15px;height:15px;fill:currentColor} +.nav-burger{ + display:none;flex-direction:column;justify-content:center;gap:4px; + width:36px;height:36px;background:transparent;border:1px solid var(--line3); + cursor:pointer;flex-shrink:0; +} +.nav-burger span{display:block;width:15px;height:1.5px;background:var(--navy);margin:0 auto;transition:transform .2s,opacity .2s} +nav.nav-open .nav-burger span:nth-child(1){transform:translateY(5.5px) rotate(45deg)} +nav.nav-open .nav-burger span:nth-child(2){opacity:0} +nav.nav-open .nav-burger span:nth-child(3){transform:translateY(-5.5px) rotate(-45deg)} +/* ---------- STRIP ---------- */ .strip{ - background:#D0CCC5; - border-bottom:1px solid var(--line3); + background:var(--navy); + border-bottom:1px solid var(--navy); display:flex;align-items:center;justify-content:center;gap:12px; - padding:9px 24px;font-size:12.5px; - color:var(--mid); + padding:9px 24px;font-size:12px; + color:rgba(247,246,241,.7); + font-family:var(--f-mono); } .strip-pill{ - background:var(--navy);color:var(--stone); - border-radius:20px;padding:2px 10px; - font-size:10.5px;font-weight:600;letter-spacing:.08em;text-transform:uppercase; + background:var(--navy2); + color:#F7F6F1; + padding:2px 10px; + font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase; } -.strip a{color:var(--navy);text-decoration:none;font-weight:500;display:flex;align-items:center;gap:5px} -.strip-arr{opacity:.45;font-size:14px} - -.row{ - display:grid; - grid-template-columns:var(--g1) 1fr var(--g1); - border-bottom:1px solid var(--line3); -} -.row-inner{padding:0} -.row-gutter-l{border-right:1px solid var(--line3)} -.row-gutter-r{border-left:1px solid var(--line3)} +.strip a{color:#F7F6F1;text-decoration:none;font-weight:500;display:flex;align-items:center;gap:5px} +.strip-arr{opacity:.6;font-size:13px} +/* ---------- HERO ---------- */ .hero-row{ - display:grid; - grid-template-columns:var(--g1) 1fr 300px var(--g1); - border-bottom:1px solid var(--line3); - min-height:340px; + position:relative; + display:flex;flex-wrap:wrap;gap:48px; + max-width:var(--container);margin:0 auto; + padding:80px var(--pad) 76px; + overflow:hidden; } -.hero-gutter{border-right:1px solid var(--line3)} -.hero-gutter-r{border-left:1px solid var(--line3)} -.hero-main{ - padding:60px 52px 52px; - border-right:1px solid var(--line3); +.hero-glow{ + position:absolute;top:-220px;left:50%; + width:900px;height:600px;transform:translateX(-50%); + background:radial-gradient(closest-side,rgba(193,18,31,.06),transparent 70%); + filter:blur(20px);pointer-events:none;z-index:0; } +.hero-main{flex:1 1 480px;position:relative;z-index:1} .status-row{ display:inline-flex;align-items:center;gap:8px; - font-size:12px;color:var(--mid);font-weight:500; - letter-spacing:.03em;margin-bottom:22px; + font-size:11.5px;color:var(--mid);font-weight:500; + letter-spacing:.04em;margin-bottom:26px; + font-family:var(--f-mono);text-transform:uppercase; } -.green-dot{width:7px;height:7px;border-radius:50%;background:#4CAF7D;position:relative;flex-shrink:0} -.green-dot::after{content:'';position:absolute;inset:-3px;border-radius:50%;background:rgba(76,175,125,.2);animation:gpulse 2.2s ease-in-out infinite} +.green-dot{width:6px;height:6px;background:var(--navy2);position:relative;flex-shrink:0} +.green-dot::after{content:'';position:absolute;inset:-3px;background:rgba(193,18,31,.18);animation:gpulse 2.2s ease-in-out infinite} @keyframes gpulse{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.7);opacity:0}} .hero-name{ - font-family:'Syne',sans-serif; - font-size:clamp(36px,4vw,56px); + font-family:var(--f-head); + font-size:clamp(42px,6vw,72px); font-weight:700; - line-height:1.03; - letter-spacing:-.035em; + line-height:1.0; + letter-spacing:-.03em; color:var(--navy); - margin-bottom:6px; + margin-bottom:10px; } +.hero-name::after{content:'';display:block;width:64px;height:3px;background:var(--navy2);margin-top:18px} .hero-role{ - font-family:'DM Sans',sans-serif; - font-size:clamp(14px,1.5vw,16px); + font-family:var(--f-mono); + font-size:clamp(12px,1.5vw,13.5px); color:var(--mid); - font-weight:300; - font-style:italic; - letter-spacing:.01em; - margin-bottom:22px; + font-weight:400; + letter-spacing:.04em;text-transform:uppercase; + margin-top:18px; + margin-bottom:26px; } .hero-lead{ - font-size:14.5px;color:var(--mid);line-height:1.68; - max-width:500px;margin-bottom:36px;font-weight:300; + font-size:14.5px;color:var(--mid);line-height:1.7; + max-width:520px;margin-bottom:16px;font-weight:400; } .hero-thesis{ - font-size:13.5px;color:var(--navy);line-height:1.55; - max-width:480px;margin-bottom:16px;font-weight:500; - font-style:italic; + font-size:14.5px;color:var(--navy);line-height:1.6; + max-width:520px;margin-bottom:18px;font-weight:500; + padding-left:14px;border-left:2px solid var(--navy2); } -.now-line{ - display:inline-flex;align-items:flex-start;gap:7px; - font-size:12px;color:var(--mid);line-height:1.5; - max-width:480px;margin-bottom:20px; - padding:8px 12px;background:var(--stone2); - border:1px solid var(--line3);border-radius:4px; -} -.now-line strong{color:var(--navy);font-weight:600} -.now-dot{color:#4CAF7D;font-size:8px;flex-shrink:0;margin-top:4px} -.status-badge{ - display:inline-flex;align-items:center;gap:7px; - margin-bottom:22px;text-decoration:none; - font-size:12px;color:var(--mid);font-family:'DM Mono',monospace; -} -.status-badge img{height:20px;display:block;border-radius:2px} -.status-badge span{border-bottom:1px solid var(--line3)} -.status-badge:hover span{color:var(--navy);border-color:var(--line2)} -.btns{display:flex;gap:9px;flex-wrap:wrap} +.btns{display:flex;gap:10px;flex-wrap:wrap;margin-top:8px} .btn{ display:inline-flex;align-items:center;gap:7px; - border-radius:3px;padding:8px 16px; - font-size:13px;font-weight:500;font-family:'DM Sans',sans-serif; - text-decoration:none;cursor:pointer;border:none; - transition:all .14s;letter-spacing:.01em; + padding:11px 22px; + font-size:12.5px;font-weight:600;font-family:var(--f-mono); + text-decoration:none;cursor:pointer;border:1px solid transparent; + transition:all .15s ease;letter-spacing:.05em;text-transform:uppercase; } -.btn-d{background:var(--navy);color:var(--stone)} -.btn-d:hover{background:var(--navy2)} -.btn-o{background:transparent;color:var(--navy);border:1px solid var(--line3)} -.btn-o:hover{background:var(--stone2)} -.hero-stats{padding:32px 28px;display:flex;flex-direction:column} -.stat{padding:15px 0;border-bottom:1px solid var(--line);display:flex;gap:11px;align-items:flex-start} -.stat:first-child{border-top:1px solid var(--line)} -.stat-ico{font-size:15px;opacity:.35;flex-shrink:0;margin-top:2px} -.stat-val{font-size:13.5px;font-weight:600;color:var(--navy);letter-spacing:-.01em} -.stat-lbl{font-size:11px;color:var(--muted);margin-top:1px} +.btn-d{background:var(--navy2);color:#F7F6F1} +.btn-d:hover{background:#96090F;transform:translateY(-1px)} +.btn-o{background:transparent;color:var(--navy);border:1px solid var(--navy)} +.btn-o:hover{background:var(--navy);color:#F7F6F1} +.hero-stats{ + flex:0 1 300px;position:relative;z-index:1; + display:flex;flex-direction:column; + align-self:flex-start; + border-top:1px solid var(--navy); +} +.stat{ + padding:13px 2px; + border-bottom:1px solid var(--line); + display:flex;align-items:baseline;justify-content:space-between;gap:12px; +} +.stat-val{font-size:13.5px;font-weight:600;color:var(--navy);letter-spacing:-.01em;font-family:var(--f-head)} +.stat-lbl{font-size:10px;color:var(--muted);font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.05em;text-align:right} -.sec{ - display:grid; - grid-template-columns:var(--g1) 1fr var(--g1); - border-bottom:1px solid var(--line3); -} -.sec-g{border-right:1px solid var(--line3)} -.sec-gr{border-left:1px solid var(--line3)} -.sec-body{padding:48px 52px} -.slbl{ - font-size:10.5px;font-weight:600;letter-spacing:.12em; - text-transform:uppercase;color:var(--muted); - margin-bottom:30px;font-family:'DM Mono',monospace; -} -.sec-h{ - font-family:'Syne',sans-serif;font-weight:700; - font-size:clamp(22px,2.4vw,32px); - letter-spacing:-.025em;color:var(--navy); - margin-bottom:12px;line-height:1.1; -} +/* ---------- SECTIONS ---------- */ +.sec{border-bottom:1px solid var(--line)} +.sec-body{padding:64px var(--pad);max-width:var(--container);margin:0 auto} +.section-label{display:flex;align-items:baseline;gap:14px;margin:0 0 32px} +.section-label .num{font-family:var(--f-mono);font-size:13px;color:var(--navy2);flex-shrink:0} +.about-text>.section-label:first-child .num::before, +.sec-body>.section-label:first-child .num::before{content:counter(sec,decimal-leading-zero)} +.section-label h2,.section-label .slbl-t{ + font-family:var(--f-head);font-weight:600;font-size:13px; + text-transform:uppercase;letter-spacing:.12em;color:var(--navy); +} +.section-label .rule{flex:1;height:1px;background:var(--line)} +.section-sub{font-family:var(--f-mono);font-size:11px;color:var(--navy2);text-transform:uppercase;letter-spacing:.1em;margin:0 0 32px;display:flex;align-items:baseline;gap:14px} +.section-sub .rule{flex:1;height:1px;background:var(--line)} + +/* ---------- ABOUT ---------- */ .about-split{ + border-bottom:1px solid var(--line); + max-width:var(--container);margin:0 auto; + padding:64px var(--pad); + display:grid;grid-template-columns:1.3fr 1fr;gap:56px; +} +.about-text p{font-size:14.5px;color:var(--mid);line-height:1.75;font-weight:400;margin-bottom:16px} +.about-text strong{color:var(--navy);font-weight:600} +.about-card{ + align-self:start; + border-top:1px solid var(--navy); +} +.about-card-row{ + padding:16px 2px; + border-bottom:1px solid var(--line); +} +.about-card-t{font-size:13.5px;font-weight:600;color:var(--navy);font-family:var(--f-head)} +.about-card-d{font-size:11px;color:var(--mid);margin-top:4px;font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.04em} + +/* ---------- LEGEND ---------- */ +.legend{ + display:flex;gap:28px;margin:0 0 32px;flex-wrap:wrap; + font-family:var(--f-mono);font-size:10.5px;color:var(--mid); + text-transform:uppercase;letter-spacing:.04em; +} +.legend span{display:flex;align-items:center;gap:7px} +.legend .mark{font-size:9px} +.legend .l-applied .mark{color:var(--navy2)} +.legend .l-exposure .mark{color:var(--mid)} + +/* ---------- CORE STACK BAND ---------- */ +.core-band{ + border-top:1px solid var(--navy);border-bottom:1px solid var(--navy); + padding:22px 0;margin-bottom:48px; + display:grid;grid-template-columns:220px 1fr;gap:32px;align-items:baseline; +} +.core-band .label{ + font-family:var(--f-mono);font-size:11px;color:var(--navy2); + text-transform:uppercase;letter-spacing:.1em; +} +.core-band .label span{display:block;color:var(--mid);font-size:10.5px;margin-top:6px;letter-spacing:.03em;text-transform:none} +.core-band .stack{ + font-family:var(--f-head);font-weight:600;font-size:19px;letter-spacing:-.01em; + display:flex;flex-wrap:wrap;row-gap:6px; +} +.core-band .stack .item{white-space:nowrap;padding-right:14px} +.core-band .stack .item:not(:last-child)::after{ + content:'/';color:var(--line);padding-left:14px;font-weight:400; +} + +/* ---------- CATEGORY INDEX (skills, homelab services, desktop parts, projects) ---------- */ +.category-index{border-top:1px solid var(--navy)} +.category-row{ display:grid; - grid-template-columns:var(--g1) 1fr 1fr var(--g1); - border-bottom:1px solid var(--line3); + grid-template-columns:48px 220px 1fr; + gap:28px; + padding:24px 2px; + border-bottom:1px solid var(--line); + align-items:baseline; + transition:background .15s; } -.about-g{border-right:1px solid var(--line3)} -.about-gr{border-left:1px solid var(--line3)} -.about-left{padding:44px 48px;border-right:1px solid var(--line3)} -.about-right{padding:44px 48px} -.terminal{background:var(--navy);border-radius:6px;overflow:hidden} -.t-bar{background:rgba(255,255,255,.05);padding:10px 15px;display:flex;align-items:center;gap:7px;border-bottom:1px solid rgba(255,255,255,.06)} -.td{width:10px;height:10px;border-radius:50%} -.td.r{background:#FF5F57}.td.y{background:#FEBC2E}.td.g{background:#28C840} -.t-title{flex:1;text-align:center;font-size:10.5px;color:rgba(255,255,255,.2);font-family:'DM Mono',monospace} -.t-body{ - padding:18px 20px; - font-family:'DM Mono',monospace; - font-size:13px;line-height:1.85; - color:rgba(232,229,223,.75); -} -.t-p{color:#5FCE8A}.t-c{color:rgba(232,229,223,.35)} -.t-k{color:#82B4D0}.t-v{color:rgba(232,229,223,.8)} -.cur{display:inline-block;width:7px;height:12px;background:rgba(232,229,223,.5);vertical-align:middle;animation:bl 1s step-end infinite} -@keyframes bl{0%,100%{opacity:1}50%{opacity:0}} - -.t-stage{position:relative;min-height:400px} -.t-pane{position:absolute;inset:0;opacity:1;transition:opacity .55s ease} -.t-pane.t-hidden{opacity:0;pointer-events:none} -.t-cmd-row{display:flex;align-items:center;gap:6px;margin-top:2px} -.t-input{background:transparent;border:none;outline:none;color:rgba(232,229,223,.85);font:inherit;flex:1;caret-color:#5FCE8A} -.t-hint{color:rgba(232,229,223,.28);font-size:11px;margin-top:10px} -.t-pane.t-egg-pane{ - display:flex;flex-direction:column;align-items:center;justify-content:center; - text-align:center;gap:12px;padding:18px 20px; - opacity:0;pointer-events:none; -} -.t-pane.t-egg-pane.t-egg-show{opacity:1;pointer-events:auto} -.t-ascii-scroll{max-width:100%;overflow-x:auto} -.t-ascii{white-space:pre;margin:0;line-height:1.05;font-family:'DM Mono',monospace} -.t-ascii-moria{ - font-size:clamp(5px,2.1vw,8.5px); - color:#7ee7ff; - text-shadow:0 0 4px #7ee7ff,0 0 14px rgba(126,231,255,.55),0 0 28px rgba(126,231,255,.3); - animation:t-pulse-cyan 2.2s ease-in-out infinite; -} -.t-ascii-balrog{ - font-size:clamp(3px,1vw,4.8px); - color:#ff7a3d; - text-shadow:0 0 4px #ff7a3d,0 0 14px rgba(255,122,61,.6),0 0 30px rgba(255,80,20,.35); - animation:t-pulse-fire 1.4s ease-in-out infinite; -} -@keyframes t-pulse-cyan{0%,100%{filter:brightness(1)}50%{filter:brightness(1.35)}} -@keyframes t-pulse-fire{0%,100%{filter:brightness(1) hue-rotate(0deg)}50%{filter:brightness(1.4) hue-rotate(-6deg)}} -.t-egg-cap{font-size:11px;letter-spacing:.02em;font-family:'DM Mono',monospace;color:rgba(232,229,223,.45)} -.t-egg-cap-moria{color:#a9e9f7} -.t-egg-cap-balrog{color:#ffb08a} -.about-right p{font-size:14px;color:var(--mid);line-height:1.72;font-weight:300;margin-bottom:14px} -.about-right strong{color:var(--navy);font-weight:500} - -.sk4{ - display:grid;grid-template-columns:repeat(4,1fr); - border:1px solid var(--line3);border-radius:5px; - overflow:hidden;background:var(--line); -} -.sk{ - background:var(--stone);padding:22px 20px; - border-right:1px solid var(--line);border-bottom:1px solid var(--line); - transition:background .14s;overflow:hidden; -} -.sk:hover{background:var(--stone2)} -.sk-ico{font-size:17px;margin-bottom:10px;opacity:.38} -.sk-t{font-size:11.5px;font-weight:600;color:var(--navy);letter-spacing:.01em;margin-bottom:10px;font-family:'DM Mono',monospace} -.tags{display:flex;flex-wrap:wrap;gap:3px} -.tag{ - background:rgba(28,32,51,.06);border:1px solid var(--line); - border-radius:2px;padding:2px 6px; - font-size:10.5px;color:var(--mid); - font-family:'DM Mono',monospace; +.category-row:hover{background:var(--stone3)} +.category-row .cat-num{font-family:var(--f-mono);font-size:12.5px;color:var(--navy2)} +.category-row .cat-name{font-family:var(--f-head);font-weight:600;font-size:16px;letter-spacing:-.01em} +.category-row .cat-name span{ + display:block;font-family:var(--f-mono);font-weight:400;font-size:10.5px; + color:var(--mid);letter-spacing:.06em;margin-top:5px;text-transform:uppercase; } -.hl3{ - display:grid;grid-template-columns:repeat(3,1fr); - border:1px solid var(--line3);border-radius:5px; - overflow:hidden;background:var(--line); - margin-bottom:24px; +/* row variant with a description line (homelab services, desktop parts, projects) */ +.detail-row{ + display:grid;grid-template-columns:48px 220px 1fr;gap:28px; + padding:26px 2px;border-bottom:1px solid var(--line);align-items:start; + transition:background .15s; } -.hl{ - background:var(--stone);padding:24px 20px; - border-right:1px solid var(--line);border-bottom:1px solid var(--line); - transition:background .14s;overflow:hidden; -} -.hl:hover{background:var(--stone2)} -.hl-hd{display:flex;align-items:center;gap:9px;margin-bottom:9px} -.hl-ico{font-size:18px;opacity:.4} -.hl-t{font-size:14px;font-weight:600;color:var(--navy);letter-spacing:-.01em} -.hl-d{font-size:12.5px;color:var(--mid);line-height:1.62;margin-bottom:11px;font-weight:300} -.specs{display:flex;flex-wrap:wrap;gap:3px} -.spec{background:rgba(28,32,51,.05);border:1px solid var(--line);border-radius:2px;padding:2px 6px;font-size:10.5px;color:var(--mid);font-family:'DM Mono',monospace} +.detail-row:hover{background:var(--stone3)} +.detail-row .cat-num{font-family:var(--f-mono);font-size:12.5px;color:var(--navy2);padding-top:2px} +.detail-row .row-meta{display:flex;flex-direction:column;gap:8px} +.detail-row .cat-name{font-family:var(--f-head);font-weight:600;font-size:15.5px;letter-spacing:-.01em} +.detail-row .status-tag{font-family:var(--f-mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;width:fit-content} +.status-tag.live{color:var(--navy2)} +.status-tag.oss{color:var(--mid)} +.status-tag.dev{color:#8A5A1E} +.detail-row .desc{font-family:var(--f-body);font-size:13px;color:var(--mid);line-height:1.65;margin-bottom:12px;max-width:560px} +.detail-row .pr-type{font-family:var(--f-mono);font-size:10px;letter-spacing:.1em;text-transform:uppercase;color:var(--muted)} +.detail-row .pr-t{font-family:var(--f-head);font-weight:600;font-size:16px;letter-spacing:-.01em;margin-bottom:8px} -.hw-table{width:100%;border-collapse:collapse;margin-bottom:24px;font-size:12.5px} -.hw-table th{text-align:left;font-weight:600;font-family:'DM Mono',monospace;font-size:10.5px;letter-spacing:.08em;text-transform:uppercase;color:var(--muted);padding:8px 12px;border-bottom:1px solid var(--line3);background:var(--stone2)} -.hw-table td{padding:9px 12px;border-bottom:1px solid var(--line);color:var(--mid);vertical-align:top} -.hw-table td:first-child{color:var(--navy);font-weight:500;width:140px;min-width:100px;font-family:'DM Mono',monospace;font-size:11.5px} -.hw-table tr:last-child td{border-bottom:none} -.hw-table tr:hover td{background:var(--stone2)} +/* skill / spec / tag lists — slash-separated mono lists */ +.skill-list{ + font-family:var(--f-mono);font-size:12.5px;color:var(--navy); + line-height:2.1; + display:flex;flex-wrap:wrap; +} +.skill-list .skill{white-space:nowrap;padding-right:10px} +.skill-list .skill:not(:last-child)::after{ + content:'/';color:var(--line);padding-left:10px; +} +.skill-list .skill.applied::before{content:'\25AA';color:var(--navy2);font-size:9px;padding-right:7px} +.skill-list .skill.exposure{color:var(--mid)} +.skill-list .skill.exposure::before{content:'\25AB';color:var(--mid);font-size:9px;padding-right:7px} + +/* ---------- QUANT GRID (leaner two-col variant) ---------- */ +.quant-grid{ + display:grid;grid-template-columns:1fr 1fr;gap:0; + border-top:1px solid var(--navy);margin-bottom:48px; +} +.quant-cell{padding:24px 28px 24px 2px;border-bottom:1px solid var(--line)} +.quant-cell:nth-child(odd){border-right:1px solid var(--line);padding-right:28px} +.quant-cell .cat-head{display:flex;align-items:baseline;gap:10px;margin-bottom:14px} +.quant-cell .cat-head .cat-num{font-family:var(--f-mono);font-size:12px;color:var(--navy2)} +.quant-cell .cat-head h3{font-family:var(--f-head);font-weight:600;font-size:14.5px} +.quant-cell .skill-list{font-size:12px;line-height:2} + +.hw-table{ + width:100%;border-collapse:separate;border-spacing:0;margin-bottom:32px;font-size:12.5px; + border-top:1px solid var(--navy); +} +.hw-table th{text-align:left;font-weight:500;font-family:var(--f-mono);font-size:10px;letter-spacing:.08em;text-transform:uppercase;color:var(--mid);padding:12px 16px 12px 2px;border-bottom:1px solid var(--navy)} +.hw-table td{padding:12px 16px 12px 2px;border-bottom:1px solid var(--line);color:var(--mid);vertical-align:top;font-family:var(--f-body)} +.hw-table td:first-child{color:var(--navy);font-weight:600;width:140px;min-width:100px;font-family:var(--f-head);font-size:12.5px} +.hw-table tr:last-child td{border-bottom:1px solid var(--navy)} +.hw-table tr:hover td{background:var(--stone3)} .note{ - padding:16px 18px; - background:var(--stone2);border:1px solid var(--line3);border-radius:5px; - display:flex;gap:12px;align-items:flex-start; + padding:16px 0 16px 20px; + border-left:2px solid var(--navy2); } -.note-ico{font-size:16px;opacity:.38;flex-shrink:0;margin-top:2px} -.note-t{font-size:13px;font-weight:500;color:var(--navy);margin-bottom:2px} -.note-d{font-size:12.5px;color:var(--mid);line-height:1.6;font-weight:300} +.note-t{font-size:13px;font-weight:600;color:var(--navy);margin-bottom:4px;font-family:var(--f-head)} +.note-d{font-size:12.5px;color:var(--mid);line-height:1.6;font-weight:400} -.pr3{ - display:grid;grid-template-columns:repeat(3,1fr); - border:1px solid var(--line3);border-radius:5px; - overflow:hidden;background:var(--line); -} -.pr{ - background:var(--stone);padding:24px 20px; - border-right:1px solid var(--line);border-bottom:1px solid var(--line); - display:flex;flex-direction:column; - transition:background .14s; -} -.pr:hover{background:var(--stone2)} -.pr-type{font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--muted);margin-bottom:7px;font-family:'DM Mono',monospace} -.pr-t{font-size:14.5px;font-weight:600;color:var(--navy);letter-spacing:-.01em;margin-bottom:7px;line-height:1.3} -.pr-d{font-size:12.5px;color:var(--mid);line-height:1.62;flex:1;margin-bottom:14px;font-weight:300} -.pr-tags{display:flex;flex-wrap:wrap;gap:3px} -.pr-tag{background:rgba(28,32,51,.05);border:1px solid var(--line);border-radius:2px;padding:2px 6px;font-size:10.5px;color:var(--muted);font-family:'DM Mono',monospace} -.badge{display:inline-flex;align-items:center;gap:4px;border-radius:2px;padding:2px 7px;font-size:10px;font-weight:600;letter-spacing:.06em;text-transform:uppercase;margin-bottom:8px;font-family:'DM Mono',monospace} -.b-live{background:rgba(76,175,125,.1);color:#2A7A4F} -.b-dev{background:rgba(210,140,0,.1);color:#8A5E00} -.b-oss{background:rgba(28,32,51,.07);color:var(--mid)} - -.gh-list{border:1px solid var(--line3);border-radius:5px;overflow:hidden} +.gh-list{border-top:1px solid var(--navy)} .gh-item{ display:flex;align-items:center;justify-content:space-between; - padding:14px 18px;background:var(--stone); + padding:18px 2px; border-bottom:1px solid var(--line); - transition:background .14s;gap:16px; + transition:background .15s;gap:16px; } -.gh-item:last-child{border-bottom:none} -.gh-item:hover{background:var(--stone2)} -.gh-n{font-size:13.5px;font-weight:500;color:var(--navy);margin-bottom:2px;font-family:'DM Mono',monospace} -.gh-d{font-size:12px;color:var(--mid);font-weight:300} -.gh-r{display:flex;align-items:center;gap:8px;flex-shrink:0} -.gh-lang{font-size:10.5px;font-family:'DM Mono',monospace;color:var(--muted);padding:2px 7px;border:1px solid var(--line);border-radius:2px} -.gh-stars{font-size:10.5px;font-family:'DM Mono',monospace;color:#B08A2E;padding:2px 7px;border:1px solid var(--line);border-radius:2px} -.gh-lnk{color:var(--mid);text-decoration:none;font-size:12px;font-family:'DM Mono',monospace;transition:color .14s} -.gh-lnk:hover{color:var(--navy)} +.gh-item:hover{background:var(--stone3)} +.gh-n{font-size:13px;font-weight:500;color:var(--navy);margin-bottom:3px;font-family:var(--f-mono)} +.gh-d{font-size:12px;color:var(--mid);font-weight:400;font-family:var(--f-body)} +.gh-r{display:flex;align-items:center;gap:14px;flex-shrink:0} +.gh-lang,.gh-stars{font-size:10px;font-family:var(--f-mono);color:var(--mid);text-transform:uppercase;letter-spacing:.04em} +.gh-stars{color:#8A6416} +.gh-lnk{color:var(--navy2);text-decoration:none;font-size:11px;font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.04em;transition:opacity .14s} +.gh-lnk:hover{opacity:.6} -.acc{border-top:1px solid var(--line3)} -.acc-item{border-bottom:1px solid var(--line3)} +/* ---------- ACCORDION ---------- */ +.acc{display:flex;flex-direction:column} +.acc-item{border-bottom:1px solid var(--line);transition:background .18s} +.acc .acc-item:first-child{border-top:1px solid var(--navy)} +.acc-item.open{background:var(--stone3)} .acc-btn{ width:100%;background:none;border:none; - padding:20px 0;display:flex;align-items:flex-start; + padding:20px 2px;display:flex;align-items:flex-start; justify-content:space-between;cursor:pointer; text-align:left;font-family:inherit;gap:20px; - transition:background .15s; } .acc-btn:hover .acc-t{color:var(--navy2)} .acc-l{flex:1} -.acc-t{font-size:16.5px;font-weight:500;color:var(--navy);letter-spacing:-.015em;line-height:1.25;margin-bottom:3px;transition:color .14s;font-family:'Syne',sans-serif} -.acc-s{font-size:12px;color:var(--muted);font-family:'DM Mono',monospace} +.acc-t{font-size:16px;font-weight:600;color:var(--navy);letter-spacing:-.01em;line-height:1.25;margin-bottom:4px;transition:color .14s;font-family:var(--f-head)} +.acc-s{font-size:11px;color:var(--mid);font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.04em} .acc-r2{display:flex;align-items:center;gap:12px;flex-shrink:0} -.acc-per{font-size:11.5px;color:var(--muted);font-family:'DM Mono',monospace} -.acc-chev{width:17px;height:17px;color:var(--muted);transition:transform .2s} +.acc-per{font-size:11px;color:var(--mid);font-family:var(--f-mono);text-transform:uppercase;letter-spacing:.03em} +.acc-chev{width:15px;height:15px;color:var(--mid);transition:transform .2s} .acc-item.open .acc-chev{transform:rotate(180deg)} -.acc-body{display:none;padding:0 0 22px;color:var(--mid);font-size:13.5px;line-height:1.7;font-weight:300} -.acc-item.open .acc-body{display:block} -.acc-body ul{list-style:none;margin-top:10px;display:flex;flex-direction:column;gap:6px} +.acc-body-wrap{ + display:grid;grid-template-rows:0fr; + transition:grid-template-rows .32s cubic-bezier(.16,1,.3,1); +} +.acc-item.open .acc-body-wrap{grid-template-rows:1fr} +.acc-body{min-height:0;overflow:hidden;color:var(--mid);font-size:13.5px;line-height:1.7;font-weight:400} +.acc-body > *{padding-left:2px;padding-right:2px} +.acc-body > p:first-child{padding-top:0} +.acc-body > *:last-child{padding-bottom:24px} +.acc-body ul{list-style:none;margin-top:10px;display:flex;flex-direction:column;gap:8px} .acc-body li{display:flex;gap:11px;align-items:flex-start} -.acc-body li::before{content:'--';color:var(--muted);flex-shrink:0;font-family:'DM Mono',monospace;font-size:10px;margin-top:4px} -.econ-tags{display:flex;flex-wrap:wrap;gap:4px;margin-top:12px} -.econ-tag{background:rgba(28,32,51,.06);border:1px solid var(--line3);border-radius:2px;padding:3px 8px;font-size:11.5px;color:var(--navy);font-weight:500} +.acc-body li::before{content:'\2192';color:var(--navy2);flex-shrink:0;font-size:11px;margin-top:2px} +.econ-tags{margin-top:14px} -.langs{ - display:grid;grid-template-columns:repeat(5,1fr); - border:1px solid var(--line3);border-radius:5px;overflow:hidden; - background:var(--line);margin-top:28px; +.langs{display:flex;flex-wrap:wrap;border-top:1px solid var(--navy);margin-top:28px} +.langc{padding:16px 26px 16px 0;margin-right:26px;border-bottom:1px solid var(--line)} +.langc:not(:last-child){border-right:1px solid var(--line)} +.lang-n{font-size:13px;font-weight:600;color:var(--navy);margin-bottom:3px;font-family:var(--f-head)} +.lang-l{font-size:10px;color:var(--mid);text-transform:uppercase;letter-spacing:.05em;font-family:var(--f-mono)} + +.certs{border-top:1px solid var(--navy)} +.cert-row{ + display:grid;grid-template-columns:44px 1fr 130px;gap:24px; + padding:18px 2px;border-bottom:1px solid var(--line);align-items:center; } -.langc{background:var(--stone);padding:16px 12px;text-align:center;border-right:1px solid var(--line)} -.langc:last-child{border-right:none} -.lang-n{font-size:13px;font-weight:600;color:var(--navy);margin-bottom:3px;font-family:'Syne',sans-serif} -.lang-l{font-size:10px;color:var(--muted);text-transform:uppercase;letter-spacing:.07em;font-family:'DM Mono',monospace} +.cert-mark{ + font-family:var(--f-mono);font-size:11px;color:var(--navy2);border:1px solid var(--navy2); + width:36px;height:36px;display:flex;align-items:center;justify-content:center; +} +.cert-name{font-family:var(--f-body);font-weight:500;font-size:14px;color:var(--navy)} +.cert-issuer{font-family:var(--f-mono);font-size:10.5px;color:var(--mid);margin-top:3px;text-transform:uppercase;letter-spacing:.04em} +.cert-status{ + font-family:var(--f-mono);font-size:10.5px;color:var(--mid);text-align:right; + text-transform:uppercase;letter-spacing:.05em; +} -.certs{display:flex;gap:9px;flex-wrap:wrap;margin-top:24px} -.cert{display:flex;align-items:center;gap:8px;border:1px solid var(--line3);border-radius:4px;padding:10px 14px;font-size:12.5px;color:var(--navy)} - -.contact{ - display:grid; - grid-template-columns:var(--g1) 1fr var(--g1); - background:var(--navy);border-top:1px solid var(--line3); +/* ---------- CONTACT ---------- */ +.contact{border-bottom:1px solid var(--line);padding:64px var(--pad)} +.contact-body{ + max-width:var(--container);margin:0 auto; + background:var(--navy); + padding:64px 56px; + position:relative;overflow:hidden; } -.contact-g{border-right:1px solid rgba(255,255,255,.07)} -.contact-gr{border-left:1px solid rgba(255,255,255,.07)} -.contact-body{padding:64px 52px} .contact-body h2{ - font-family:'Syne',sans-serif;font-weight:700; - font-size:clamp(28px,3.2vw,46px);letter-spacing:-.03em; - margin-bottom:12px;line-height:1.06;color:var(--stone); + font-family:var(--f-head);font-weight:700; + font-size:clamp(30px,3.6vw,50px);letter-spacing:-.02em; + margin-bottom:14px;line-height:1.05;color:var(--stone); } -.contact-body p{font-size:14px;color:rgba(232,229,223,.45);margin-bottom:32px;line-height:1.65;max-width:440px;font-weight:300} -.contact-ctas{display:flex;gap:9px;flex-wrap:wrap} -.btn-li{background:var(--stone);color:var(--navy);border-radius:3px;padding:8px 16px;font-size:13px;font-weight:500;font-family:inherit;text-decoration:none;transition:all .14s;word-break:break-all} -.btn-li:hover{background:#F0EDE7} -.btn-gh{background:transparent;color:rgba(232,229,223,.55);border:1px solid rgba(255,255,255,.14);border-radius:3px;padding:8px 16px;font-size:13px;font-weight:500;font-family:inherit;text-decoration:none;transition:all .14s} -.btn-gh:hover{border-color:rgba(255,255,255,.32);color:var(--stone)} +.contact-body p{font-size:14.5px;color:rgba(247,246,241,.68);margin-bottom:32px;line-height:1.65;max-width:460px;font-weight:400} +.contact-ctas{display:flex;gap:10px;flex-wrap:wrap} +.btn-li{background:var(--navy2);color:#F7F6F1;padding:11px 22px;font-size:12.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;font-family:var(--f-mono);text-decoration:none;transition:all .15s;word-break:break-all} +.btn-li:hover{background:#96090F;transform:translateY(-1px)} +.btn-gh{background:transparent;color:var(--stone);border:1px solid rgba(247,246,241,.3);padding:11px 22px;font-size:12.5px;font-weight:600;letter-spacing:.05em;text-transform:uppercase;font-family:var(--f-mono);text-decoration:none;transition:all .15s} +.btn-gh:hover{background:rgba(247,246,241,.08);border-color:rgba(247,246,241,.6)} -footer{ - background:var(--navy);border-top:1px solid rgba(255,255,255,.06); - display:grid;grid-template-columns:var(--g1) 1fr var(--g1); +/* ---------- FOOTER ---------- */ +footer{padding:24px var(--pad)} +.foot{max-width:var(--container);margin:0 auto;display:flex;align-items:center;justify-content:space-between} +footer p,footer a{font-size:11.5px;color:var(--muted);text-decoration:none;font-family:var(--f-mono)} +footer a:hover{color:var(--navy2)} + +.site-shell{transition:opacity .3s ease} +.site-hidden{opacity:0;visibility:hidden;height:0;overflow:hidden} + +/* ---------- BOOT SEQUENCE ---------- */ +.boot-overlay{ + position:fixed;inset:0;z-index:1000; + background:#000;color:#8fe3a8; + font-family:'DM Mono',monospace; + cursor:pointer; + display:flex;flex-direction:column;justify-content:center; + padding:8vh 6vw; + opacity:1;transition:opacity .38s ease; } -.foot{grid-column:2/3;display:flex;align-items:center;justify-content:space-between;padding:18px 52px} -footer p,footer a{font-size:12px;color:rgba(232,229,223,.24);text-decoration:none;font-family:'DM Mono',monospace} - -@media(max-width:1100px){ - :root{--g1:20px} - .col-rule-l,.col-rule-r,.col-rule-c1,.col-rule-c2{display:none} - .nav-socials{width:auto} - .hero-row{grid-template-columns:20px 1fr 260px 20px} - .sk4,.hl3,.pr3{grid-template-columns:repeat(2,1fr)} - .langs{grid-template-columns:repeat(3,1fr)} +.boot-overlay.boot-exit{opacity:0} +.boot-inner{max-width:640px} +.boot-lines{display:flex;flex-direction:column} +.boot-line{ + font-size:clamp(11px,1.6vw,13.5px); + line-height:1.7;white-space:pre; + color:rgba(143,227,168,.82); +} +.boot-greet{margin-top:22px;display:flex;flex-direction:column;gap:6px} +.boot-greet-line{ + font-size:clamp(13px,2vw,17px); + color:#EAF7EE;font-weight:500;letter-spacing:-.005em; +} +.boot-prompt-stage{margin-top:22px} +.boot-cmd-row{display:flex;align-items:center;gap:6px;font-size:14px} +.boot-p{color:#5FCE8A} +.boot-c{color:rgba(143,227,168,.4)} +.boot-input{ + background:transparent;border:none;outline:none; + color:#EAF7EE;font:inherit;flex:1;caret-color:#8fe3a8; +} +.boot-input:disabled{opacity:.5} +.boot-hint{color:rgba(143,227,168,.35);font-size:11.5px;margin-top:12px} +.boot-hint strong{color:rgba(143,227,168,.7)} +.boot-skip{ + position:absolute;bottom:28px;right:32px; + font-size:11px;letter-spacing:.04em;color:rgba(143,227,168,.32); } @media(max-width:700px){ - nav .nav-links{display:none} - .hero-row{grid-template-columns:16px 1fr 16px} - .hero-stats{display:none} - .about-split{grid-template-columns:16px 1fr 16px} - .about-left{display:none} - .sk4,.hl3,.pr3{grid-template-columns:1fr} - .langs{grid-template-columns:repeat(2,1fr)} - .sec-body,.about-right,.contact-body,.hero-main{padding:32px 20px} -} \ No newline at end of file + .boot-overlay{padding:10vh 6vw} + .boot-skip{right:20px;bottom:20px} +} + +.boot-egg{display:flex;flex-direction:column;gap:12px;max-width:100%} +.boot-ascii-scroll{max-width:100%;overflow-x:auto} +.boot-ascii{white-space:pre;margin:0;line-height:1.05;font-family:'DM Mono',monospace} +.boot-ascii-moria{ + font-size:clamp(5px,2.1vw,8.5px); + color:#7ee7ff; + text-shadow:0 0 4px #7ee7ff,0 0 14px rgba(126,231,255,.55),0 0 28px rgba(126,231,255,.3); + animation:boot-pulse-cyan 2.2s ease-in-out infinite; +} +.boot-ascii-balrog{ + font-size:clamp(3px,1vw,4.8px); + color:#ff7a3d; + text-shadow:0 0 4px #ff7a3d,0 0 14px rgba(255,122,61,.6),0 0 30px rgba(255,80,20,.35); + animation:boot-pulse-fire 1.4s ease-in-out infinite; +} +.boot-ascii-iddqd{ + font-size:clamp(7px,2.6vw,13px); + color:#ff2e4d; + text-shadow:0 0 4px #ff2e4d,0 0 16px rgba(255,46,77,.65),0 0 32px rgba(255,0,30,.4); + animation:boot-pulse-fire 1.1s ease-in-out infinite; +} +.boot-ascii-idkfa{ + font-size:clamp(7px,2.6vw,13px); + color:#ffb020; + text-shadow:0 0 4px #ffb020,0 0 16px rgba(255,176,32,.6),0 0 32px rgba(255,140,0,.35); + animation:boot-pulse-fire 1.1s ease-in-out infinite; +} +@keyframes boot-pulse-cyan{0%,100%{filter:brightness(1)}50%{filter:brightness(1.35)}} +@keyframes boot-pulse-fire{0%,100%{filter:brightness(1) hue-rotate(0deg)}50%{filter:brightness(1.4) hue-rotate(-6deg)}} +.boot-egg-cap{font-size:11px;letter-spacing:.02em;font-family:'DM Mono',monospace;color:rgba(232,229,223,.45)} +.boot-egg-cap-moria{color:#a9e9f7} +.boot-egg-cap-balrog{color:#ffb08a} +.boot-egg-cap-iddqd{color:#ff8a97} +.boot-egg-cap-idkfa{color:#ffce85} + +/* ---------- SCROLL REVEAL ---------- */ +.sec-body,.hero-main,.hero-stats,.about-text,.about-card,.contact-body{ + opacity:0;transform:translateY(26px); + transition:opacity .8s cubic-bezier(.16,1,.3,1),transform .8s cubic-bezier(.16,1,.3,1); +} +.sec-body.in-view,.hero-main.in-view,.hero-stats.in-view,.about-text.in-view,.about-card.in-view,.contact-body.in-view{ + opacity:1;transform:translateY(0); +} +.hero-stats{transition-delay:.12s} +.about-card{transition-delay:.08s} +@media(prefers-reduced-motion:reduce){ + .sec-body,.hero-main,.hero-stats,.about-text,.about-card,.contact-body{ + opacity:1!important;transform:none!important;transition:none!important; + } + .acc-body-wrap{transition:none!important} +} + +/* ---------- RESPONSIVE ---------- */ +@media(max-width:1100px){ + :root{--pad:28px} + .about-split{grid-template-columns:1fr;gap:36px} + .about-card{max-width:440px} +} +@media(max-width:820px){ + .nav-links{ + position:fixed;top:64px;left:0;right:0;bottom:0; + background:rgba(247,246,241,.98);backdrop-filter:blur(16px); + flex-direction:column;align-items:stretch;gap:2px; + padding:16px; + transform:translateY(-8px);opacity:0;pointer-events:none; + transition:opacity .2s ease,transform .2s ease; + } + nav.nav-open .nav-links{opacity:1;transform:translateY(0);pointer-events:auto} + .nav-links a{padding:14px 16px;font-size:14px} + .nav-socials{margin:10px 0 0;padding:14px 16px 0;border-left:none;border-top:1px solid var(--line);justify-content:flex-start} + .nav-burger{display:flex} + .category-row,.detail-row{grid-template-columns:1fr;gap:8px} + .core-band{grid-template-columns:1fr;gap:10px} + .quant-grid{grid-template-columns:1fr} + .quant-cell:nth-child(odd){border-right:none} + .cert-row{grid-template-columns:36px 1fr} + .cert-status{display:none} +} +@media(max-width:700px){ + :root{--pad:18px} + .hero-row{padding:56px var(--pad) 48px;flex-direction:column} + .sec-body,.about-split,.contact{padding:40px var(--pad)} + .contact-body{padding:40px 24px} +} diff --git a/app/layout.tsx b/app/layout.tsx index 9a11a0a..9ae29b0 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -16,11 +16,12 @@ export const metadata: Metadata = { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + + - + {children} diff --git a/app/page.tsx b/app/page.tsx index 88ddd94..9c4d24d 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,98 +1,7 @@ "use client" -import { useState } from 'react' - -const DURIN_ART = ` ______________ _ _,-----------._ ___ -| | (_,.- _,-'_,-----------._\`-._ _)_) -| THE _ _ _ | | ,'_,-' ___________ \`-._\`. -| | / \\|_)| \\ | \`' ,',' _,-'___________\`-._ \`.\`. -| |__\\_/| \\|_/ | ,',' ,'_,-' . \`-._\`. \`.\`. -| | /,' ,',' >|< \`.\`. \`.\\ -| OF THE _ _ | // ,',' >< ,^. >< \`.\`. \\\\ -| |_)||\\|/_(_ | // /,' >< / | \\ >< \`.\\ \\\\ -| | \\|| |\\_|_) | // // >< \\/\\^/\\/ >< \\\\ \\\\ -|______________| ;; ;; \`---' :: :: - || || (____ || || - DOORS OF DURIN _||__||_ ,'----. _||__||_ - (o.____.o)____ \`---' ____(o.____.o) - | | /,--.) (,--.\\ | | - | |(( -\`___ ___\` ))| | - | | \\\\,'', \`. .' .\`\`.// | | - | | // (___,'. .'.___) \\\\ | | - /| | ;;)) ____) . . (____ ((\\\\ | |\\ - \\|.__ | ||/ .'.--.\\/ \`/,--.\`. \\;: | __,|; - |\`-,\`;.| :/ /,' \`)-' \`-(' \`.\\ \\: |.;',-'| - | \`.. ' / \\__.' \`.__/ \\ \` ,.' | - | |,\\ /, ,\\ /,| | - | ||: : ) . ( : :|| | - /| |:; |/ . ./|\\, , \\| :;| |\\ - \\|.__ |/ : ,/- <--:--> ,\\. ; \\| __,|; - |\`-.\`\`: \`'/-. '\\|/\` ,-\\\`; ;'',-'| - | \`.. ,' \`' ' \` \`. ,.' | - | || : : || | - | || | | || | - | || | | || | - | |' | _ | \`| | - | | | '|)) | | | - ;____: \`._ \`' _,' ;____: - {______} \\___________________/ {______} - SSt |______|_______________________________|______|` - -const BALROG_ART = `############################+##++++++########+++++-----. ------------ . - --.--- --.--.------...---------------..--- -###############################+++++++####+++++----- --- - ---- -- -. ..- - - ------------------------------- -##########################+++++++++++++#####+++---. . -- - - - - .--.----------------------------- -########################+++#+++++++++######++++-. ..--++++++------..----- - ------------.- --------- -###########################+##+++++++++++##++- -++++++++--+--------.- .. ...-.. - ----------------.--------- -############################+++++++++++++#++-+ - -++++++++++++------+++---++-------------. -.----------.-------- -#############################++++++++++++#++++- --- ++++++++++++++++--+++++++------------------------- -------------.--- -#############################++++++++++++++++--- .+##+++++++++++++++++++++++++---------------------------.---------------- -############++#+###+#+++#####++++++++++++++-- --- -- -+###+++++++++++++++++++++++++----+--------------------------------------- -###########++++++++++++#++++++++++++++--..---- . .-- .. -+#####++++++++++++++++++++++++-+--++++------------------------------------- -############+++++++++++++++++++++++++++++++++.+ ..--.- +#########+++++++++++++++++++++++++++----+------------------------.----------- -############++++++++++++++++++++++-+++++++++++++.. + .++#####++#++++#++++++++++++++++++++++++++-------+--------------------- --------- -########+#+++++++++++++++++++++---++++-+++++++++.. -+#########-+ .+++++++++++++++++++++++-++++-+++++----+-------------------+-------- -+######+#+++++++++++++++++++++-+++++++-++++++++++##+-.+++++++#+ +++++++++++++++++++++++-----+++++++++++-----+--------+------------- -++##+++++++++++++++++++++++++++++++++++++-+++.++++-+ .+++++-++++++++++++++++++++++----+---------------+++++++-----+---------------- -++++++++++++++++++++++++++##+++++++++++++++. ++# ++ +-.++++++++++++++++#++++---+------------------------+-+++---+++-+---+-----.---- -+++++++++++++++++++++++###++++++++++++++++++-+++- +++ ++++++++++++++#++++-++----+-+----------------------+---++++++++----+--++++---- -+++++++++++++++++++++###++++++++++++++++++++++++ +#+#+#+#+++#######+++#----------------------------------+-++----++-+++--+++++++----. -+++++++++++++++++++++++#++++++++++++-++++++++++##++++#++#+++#+++#+#+++++++++--+----------+------------------+-++--++-+--++++++-++-.--- -++++++++++++++++++++++++++++++++++++++++++#+++++-++++##+#++##++####+++++++++++-+-----------+------------------+---++++++++++++++-++--- -+++++++++++++++++++++++++++++++++++++++--++++++#++++###+#++#+#++++++++++++++++++++---+---------------------------+++++++++++++++++++-- -+++++++++++++++++++++++++++++-------++--+++++#++++#+#######+#++++#+#-++-+++++++++++------------------------------+--++++++++++++++++-- -+++++++++++++++++++++++++++-------+-----+#+++#+++# #.# #+####+###++--+++++++++++++--------------.-------------+-++--++++++++++++++-- -+++++++++++++++++++++++-----------------+++#+--# . - - .+####+##++-----++++++++--+----+-+--------.--------------+-----+++++++++++++-- -+++++++++++++++++++++-------++----------#++++# - #. ####+-+-.+--+++++++---+++++++----------------------------.--++++++++++++-- -+++++++++++++--+++++-+-------+----------+++##. . + +#+- -++---+ ++++----+++++++++---------------------------..+++++++++++++-- -+++++++++++++-++++++---------------------#++ ---. +#- +++-. -++----++-+-+-+++++--------------------------..-+++++++++++++-- -++++++++++++++-----+--------------++++++++#+ +...-- + . - ----+-+++--++-+++-----------------------.---++++++++++++++-- -++++++++----++--+++----------+++++++++++-++#++. . . ----+----++---+--++-------------------------+ +++++++++++++--- -+++++++++---+--------------++++++++++++++++...-. . -----------+---++++------------------------++++++++++++++++--- -+++++++---+-++-+++-+-----+++++++++++++++++------ .. ---------+-----+++++-+-----------------++++++++++++++++++---- -+++++++-+++-+----++------+++++++++++++++++++++--- ....----+--+------++++-++------------.+.+++++++++++##+++++++---- -++++++++++++++++------+++++++++++++++++++++++++--. . ... ++---+--+----++++---++-------.+-.+++++++++++++#######+------ -++++---++++------+---++++++++++++++++++++++++++---. ...-.--+--+---++--+------+-+--.-.++.+#+++#+++++###++++++#--------- --++++.--+++++---+----+++++++++++++++++++++++++++-+-... ......---+++-++----++-----+--.---++####+++++###--++####++-----.---- -++++-----+----------++++++++++++++++++++++++++++++--.... ..------++++--------------.+- ############+++##+###+--------------- -+++-+++------------++++++++++++++++++++++++++++++----..... ..----+. -+---------- ---..+###+####+###-#####++-+--.--------------- -+++++++++--.------+++++++++++++++++++++++++++++. -----.... . . ..-++-+--+----------- .#++++#++##+--++++---+-----------.----------- ------+-+++--------++++++++++++++++++++++++++++++---.--.... . .. ---++-.+------------ .+-++##--#+--++--.----------------------------- --------+++--------++++#++++++++++++++++++++++-+-------..-. .---+--+++---------. +#-#+#+--+------.---.---.----- ----------------.. --------++++++-+------.+++++++++++++++++-++++++++-- ----..... ..----+--+.++-------- +--#++-------------------------....-.---------..-- -------++++++++++++---+--##++++++++++++++++++++---------......-.--+++-.-++---.---. -##++----------------------- ---.....---------...... --++---++++++#+++++##++...+#++++++++ +++++++++---------...-.---.++++...--.-+-----..+#+-----------.---------------.... ......-......... --------+++-+++#+# ++#+....+##+#++++#+++++++++----++++---...----++++-..+.--++---.+#+-------------------------..... . ....... . ... -++----+++++++-+---###+-+..-##+##+##+++++++++-.--------.--------+-+--------++---.#-----------..----------...... .. . .. -++++-+++++++++++++++##++-..##++++##+++++++++-+----..----+---+--++---------++-------------------------...-. . . .... -++++++++++++++++++++++##-..+++++++ ++++++ +- - --------------+-.---------------- ----------.----------.... . . --+++++++-+++++++++++++#+--++.++-----------------------. .--- --------------------------------------...--..... ...... . --++++--++++++++++++++++-++ -++.-------.------------ ------.---------.....--------------------- .--- ....... ...... -----------++++--------+++.----------..--------.------------ ...-.-.-.----------------------..---.--. .. ........ ------.-...-------------------------------.------ -----..----...-.........-----------------.. .-.... ....... ... . -........ .--------------------------------------- --... ---......----.. . - ----------.--...-... ......... .. - ... ..---------------.--.-------------...-.---------.. ....-...... --- --- ---.-----...... . -...... .-----.-.----------.---------------..--------.--...... . ..----------------.-.. .. - . ...--------------------------...----.--------.-..---.-. .---------------------. . ` +import { useEffect, useState } from 'react' +import BootSequence from './components/boot-sequence' const ChevronDown = () => ( @@ -110,67 +19,91 @@ export default function Home() { setOpenAcc(prev => ({ ...prev, [id]: !prev[id] })) } - const [cmd, setCmd] = useState('') - const [eggView, setEggView] = useState<'main' | 'moria' | 'balrog'>('main') - const [eggBusy, setEggBusy] = useState(false) + const [mobileOpen, setMobileOpen] = useState(false) + const closeMobile = () => setMobileOpen(false) - const triggerEgg = (view: 'moria' | 'balrog') => { - if (eggBusy) return - setEggBusy(true) - setEggView(view) - setTimeout(() => { - setEggView('main') - setCmd('') - setEggBusy(false) - }, 4200) - } + const [booting, setBooting] = useState(false) + const [bootKey, setBootKey] = useState(0) - const handleTermKey = (e: React.KeyboardEvent) => { - if (e.key !== 'Enter' || eggBusy) return - const value = cmd.trim().toLowerCase() - if (value === 'mellon') { - triggerEgg('moria') - } else if (value === 'you shall not pass') { - triggerEgg('balrog') - } else { - setCmd('') + useEffect(() => { + setBooting(true) + }, []) + + useEffect(() => { + if (booting) return + + const nav = document.querySelector('nav') + const onScroll = () => { + if (!nav) return + nav.classList.toggle('nav-scrolled', window.scrollY > 8) } + onScroll() + window.addEventListener('scroll', onScroll, { passive: true }) + + const revealEls = document.querySelectorAll( + '.sec-body, .hero-main, .hero-stats, .about-text, .about-card, .contact-body' + ) + const observer = new IntersectionObserver( + entries => { + entries.forEach(entry => { + if (entry.isIntersecting) { + entry.target.classList.add('in-view') + observer.unobserve(entry.target) + } + }) + }, + { threshold: 0.12, rootMargin: '0px 0px -40px 0px' } + ) + revealEls.forEach(el => observer.observe(el)) + + return () => { + window.removeEventListener('scroll', onScroll) + observer.disconnect() + } + }, [booting]) + + const reboot = () => { + setBootKey(k => k + 1) + setBooting(true) } return ( <> -
-
-
-
+ {booting && setBooting(false)} onReboot={reboot} />} +
{/* NAV */} -