replaced the hardcoded :first-child/:not(:last-child) rules with :nth-child(Nn+1)/:nth-child(Nn) math scoped per breakpoint (5/3/2 columns), plus a single higher-specificity
This commit is contained in:
@@ -25,7 +25,7 @@ const BOOT_LINES = [
|
||||
|
||||
const GREETING_LINES = [
|
||||
'Welcome back, root.',
|
||||
"Hi, I'm Pietro — systems engineer, developer, homelabber.",
|
||||
"Hi, I'm Pietro, systems engineer, developer, homelabber.",
|
||||
]
|
||||
|
||||
const DURIN_ART = ` ______________ _ _,-----------._ ___
|
||||
@@ -139,10 +139,10 @@ const EGG_ART: Record<Exclude<EggView, 'none'>, string> = {
|
||||
}
|
||||
|
||||
const EGG_CAPTIONS: Record<Exclude<EggView, 'none'>, 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',
|
||||
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
|
||||
@@ -333,7 +333,7 @@ export default function BootSequence({ onComplete, onReboot }: { onComplete: ()
|
||||
disabled={eggBusy}
|
||||
/>
|
||||
</div>
|
||||
<div className="boot-hint">type <strong>start</strong> to enter — speak, friend, and enter…</div>
|
||||
<div className="boot-hint">type <strong>start</strong> to enter, speak, friend, and enter…</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="boot-egg">
|
||||
|
||||
+10
-3
@@ -3,6 +3,7 @@
|
||||
@tailwind utilities;
|
||||
|
||||
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
|
||||
a:focus-visible,button:focus-visible,input:focus-visible{outline:2px solid var(--navy2);outline-offset:2px}
|
||||
:root{
|
||||
--stone:#F7F6F1;
|
||||
--stone2:#FFFFFF;
|
||||
@@ -409,9 +410,10 @@ nav.nav-open .nav-burger span:nth-child(3){transform:translateY(-5.5px) rotate(-
|
||||
.econ-tags{margin-top:22px}
|
||||
|
||||
.langs{display:grid;grid-template-columns:repeat(5,1fr);border-top:1px solid var(--navy);margin-top:28px}
|
||||
.langc{padding:16px 26px;border-bottom:1px solid var(--line)}
|
||||
.langc:first-child{padding-left:0}
|
||||
.langc:not(:last-child){border-right:1px solid var(--line)}
|
||||
.langc{padding:16px 26px;border-bottom:1px solid var(--line);border-right:1px solid var(--line)}
|
||||
.langc:nth-child(5n+1){padding-left:0}
|
||||
.langc:nth-child(5n){border-right:none}
|
||||
.langs .langc:last-child{border-right:none;border-bottom:none}
|
||||
.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)}
|
||||
|
||||
@@ -582,6 +584,9 @@ footer a:hover{color:var(--navy2)}
|
||||
.cert-row{grid-template-columns:36px 1fr}
|
||||
.cert-status{display:none}
|
||||
.langs{grid-template-columns:repeat(3,1fr)}
|
||||
.langc:nth-child(5n+1){padding-left:26px}
|
||||
.langc:nth-child(5n){border-right:1px solid var(--line)}
|
||||
.langc:nth-child(3n+1){padding-left:0}
|
||||
.langc:nth-child(3n){border-right:none}
|
||||
}
|
||||
@media(max-width:700px){
|
||||
@@ -590,6 +595,8 @@ footer a:hover{color:var(--navy2)}
|
||||
.sec-body,.about-split,.contact{padding:40px var(--pad)}
|
||||
.contact-body{padding:40px 24px}
|
||||
.langs{grid-template-columns:repeat(2,1fr)}
|
||||
.langc:nth-child(3n+1){padding-left:26px}
|
||||
.langc:nth-child(3n){border-right:1px solid var(--line)}
|
||||
.langc:nth-child(2n+1){padding-left:0}
|
||||
.langc:nth-child(2n){border-right:none}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div style={{minHeight:'100dvh',display:'flex',flexDirection:'column',justifyContent:'center',maxWidth:'var(--container)',margin:'0 auto',padding:'0 var(--pad)'}}>
|
||||
<div className="status-row"><span className="green-dot"></span>404</div>
|
||||
<div className="hero-name">Page not<br/>found.</div>
|
||||
<p className="hero-lead">The page you’re looking for doesn’t exist or has moved.</p>
|
||||
<div className="btns">
|
||||
<a href="/" className="btn btn-d">Back to home</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+12
-12
@@ -177,8 +177,8 @@ export default function Home() {
|
||||
</div>
|
||||
|
||||
<div className="legend">
|
||||
<span className="l-applied"><span className="mark">▪</span>Applied — used in production or a shipped project</span>
|
||||
<span className="l-exposure"><span className="mark">▫</span>Exposure — coursework or limited use</span>
|
||||
<span className="l-applied"><span className="mark">▪</span>Applied: used in production or a shipped project</span>
|
||||
<span className="l-exposure"><span className="mark">▫</span>Exposure: coursework or limited use</span>
|
||||
</div>
|
||||
|
||||
<div className="category-index">
|
||||
@@ -278,20 +278,20 @@ export default function Home() {
|
||||
<div className="sec-body">
|
||||
<div className="section-label"><div className="num"></div><h2>Homelab Infrastructure</h2><div className="rule"></div></div>
|
||||
<p style={{fontSize:'14px',color:'var(--mid)',marginBottom:'24px',maxWidth:'900px',lineHeight:'1.65',fontWeight:'400'}}>
|
||||
Started with a single Proxmox node — my first real server, and the one that taught me virtualisation and LXC.
|
||||
Started with a single Proxmox node: my first real server, and the one that taught me virtualisation and LXC.
|
||||
When file storage and parity-protected array management outgrew that setup, I built Nargothrond:
|
||||
a dedicated UNRAID box, named after the hidden fortress from <em>The Silmarillion</em>.
|
||||
Every service containerised, every entry-point proxied, every failure logged.
|
||||
</p>
|
||||
|
||||
<div className="section-label" style={{marginBottom:'0'}}><div className="num">—</div><div className="slbl-t">Hardware</div><div className="rule"></div></div>
|
||||
<div className="section-label" style={{marginBottom:'0'}}><div className="num">-</div><div className="slbl-t">Hardware</div><div className="rule"></div></div>
|
||||
<table className="hw-table">
|
||||
<thead><tr><th>Component</th><th>UNRAID Server (Nargothrond)</th><th>Proxmox Node</th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>CPU</td><td>Intel Core Ultra 5 225</td><td>Intel Core i7-8700</td></tr>
|
||||
<tr><td>Motherboard</td><td>ASUS Prime Z890M-Plus</td><td>—</td></tr>
|
||||
<tr><td>Motherboard</td><td>ASUS Prime Z890M-Plus</td><td>-</td></tr>
|
||||
<tr><td>RAM</td><td>48GB DDR5 5600MHz</td><td>32GB DDR4</td></tr>
|
||||
<tr><td>Case</td><td>Jonsbo N6</td><td>—</td></tr>
|
||||
<tr><td>Case</td><td>Jonsbo N6</td><td>-</td></tr>
|
||||
<tr><td>Storage</td><td>14TB + 2×12TB parity array + 2TB NVMe cache + 1TB NVMe boot</td><td>SSD (OS) + SATA pool</td></tr>
|
||||
<tr><td>Network</td><td>10GbE (Realtek RTL8127) + 1Gbps LAN · VLANs</td><td>1Gbps LAN · VLANs</td></tr>
|
||||
<tr><td>OS</td><td>UNRAID 7.3.1</td><td>Proxmox VE 8</td></tr>
|
||||
@@ -299,7 +299,7 @@ export default function Home() {
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div className="section-label" style={{marginBottom:'0',marginTop:'56px'}}><div className="num">—</div><div className="slbl-t">Services & Stack</div><div className="rule"></div></div>
|
||||
<div className="section-label" style={{marginBottom:'0',marginTop:'56px'}}><div className="num">-</div><div className="slbl-t">Services & Stack</div><div className="rule"></div></div>
|
||||
<div className="category-index">
|
||||
<div className="detail-row">
|
||||
<div className="cat-num">01</div>
|
||||
@@ -353,7 +353,7 @@ export default function Home() {
|
||||
<div className="cat-num">07</div>
|
||||
<div className="cat-name">Network Segmentation</div>
|
||||
<div>
|
||||
<div className="desc">Six Docker networks isolate containers by sensitivity — bridge, tunnel, explo_default, a dedicated pangolin network for the security-critical core, macvlan for AdGuard, and a Tailscale overlay for Jellyfin/Immich remote access.</div>
|
||||
<div className="desc">Six Docker networks isolate containers by sensitivity: bridge, tunnel, explo_default, a dedicated pangolin network for the security-critical core, macvlan for AdGuard, and a Tailscale overlay for Jellyfin/Immich remote access.</div>
|
||||
<div className="skill-list"><span className="skill">Docker networking</span><span className="skill">Tailscale</span><span className="skill">VLANs</span><span className="skill">Zero-trust</span></div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -364,7 +364,7 @@ export default function Home() {
|
||||
<div className="note-d">All services reachable remotely through a combination of Tailscale mesh VPN (for trusted devices) and Cloudflare Tunnels (for public-facing services). No port-forwarding. Pangolin’s Newt agent tunnels external traffic into the private LAN.</div>
|
||||
</div>
|
||||
|
||||
<div className="section-label" style={{marginBottom:'0',marginTop:'56px'}}><div className="num">—</div><div className="slbl-t">Traffic Flow</div><div className="rule"></div></div>
|
||||
<div className="section-label" style={{marginBottom:'0',marginTop:'56px'}}><div className="num">-</div><div className="slbl-t">Traffic Flow</div><div className="rule"></div></div>
|
||||
<div className="flow-diagram-wrap">
|
||||
<svg viewBox="0 0 920 540" width="100%" role="img" aria-label="Diagram showing external requests routed through Cloudflare and Pangolin to Traefik, internal LAN requests routed through AdGuard Home to Traefik, and an isolated IoT device with no route to the core.">
|
||||
<defs>
|
||||
@@ -468,7 +468,7 @@ export default function Home() {
|
||||
|
||||
<div className="note" style={{marginTop:'16px'}}>
|
||||
<div className="note-t">Spun up on demand, not left running</div>
|
||||
<div className="note-d">A Satisfactory game server, FileFlows for media re-encoding, and an sftp-server for bulk transfers stay fully configured but stopped until a specific project or game night needs them — keeping idle power draw down.</div>
|
||||
<div className="note-d">A Satisfactory game server, FileFlows for media re-encoding, and an sftp-server for bulk transfers stay fully configured but stopped until a specific project or game night needs them, keeping idle power draw down.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -748,7 +748,7 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="section-label" style={{marginTop:'56px'}}><div className="num">—</div><div className="slbl-t">Education</div><div className="rule"></div></div>
|
||||
<div className="section-label" style={{marginTop:'56px'}}><div className="num">-</div><div className="slbl-t">Education</div><div className="rule"></div></div>
|
||||
<div className="acc">
|
||||
<div className={`acc-item${openAcc['edu-0'] ? ' open' : ''}`}>
|
||||
<button className="acc-btn" onClick={() => toggle('edu-0')}>
|
||||
@@ -815,7 +815,7 @@ export default function Home() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="section-label" style={{marginTop:'48px'}}><div className="num">—</div><div className="slbl-t">Languages</div><div className="rule"></div></div>
|
||||
<div className="section-label" style={{marginTop:'48px'}}><div className="num">-</div><div className="slbl-t">Languages</div><div className="rule"></div></div>
|
||||
<div className="langs">
|
||||
<div className="langc"><div className="lang-n">Italian</div><div className="lang-l">Native · C2</div></div>
|
||||
<div className="langc"><div className="lang-n">English</div><div className="lang-l">Fluent · C2</div></div>
|
||||
|
||||
Reference in New Issue
Block a user