🐐
This commit is contained in:
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": [
|
||||||
|
"Centvrion",
|
||||||
|
"Gade",
|
||||||
|
"Gitea",
|
||||||
|
"Nørrebro",
|
||||||
|
"PCSX"
|
||||||
|
]
|
||||||
|
}
|
||||||
+17
-17
@@ -144,21 +144,21 @@
|
|||||||
<p class="skills-intro">I have significant experience across a range of languages and pick up new ones quickly.</p>
|
<p class="skills-intro">I have significant experience across a range of languages and pick up new ones quickly.</p>
|
||||||
|
|
||||||
<div class="lang-cloud">
|
<div class="lang-cloud">
|
||||||
<span class="lang-tag lang-primary">Python</span>
|
<span class="lang-tag lang-primary" data-tip="My main language across hundreds of projects throughout more than a decade.">Python</span>
|
||||||
<span class="lang-tag lang-primary">Bash</span>
|
<span class="lang-tag lang-primary" data-tip="Technically I prefer Fish, but who's counting?">Bash</span>
|
||||||
<span class="lang-tag lang-primary">C</span>
|
<span class="lang-tag lang-primary" data-tip="Only academic use so far, but one I genuinely enjoy. Especially when pointers are involved.">C</span>
|
||||||
<span class="lang-tag lang-primary">C#</span>
|
<span class="lang-tag lang-primary" data-tip="A handful of small MonoGame projects, alongside academic work.">C#</span>
|
||||||
<span class="lang-tag lang-primary">SQL</span>
|
<span class="lang-tag lang-primary" data-tip="Used heavily across several of my jobs.">SQL</span>
|
||||||
<span class="lang-tag lang-primary">Go</span>
|
<span class="lang-tag lang-primary" data-tip="Experience from a (now abandoned) web project.">Go</span>
|
||||||
<span class="lang-tag lang-primary">Haskell</span>
|
<span class="lang-tag lang-primary" data-tip="Academic use, including my thesis.">Haskell</span>
|
||||||
<span class="lang-tag lang-primary">JavaScript</span>
|
<span class="lang-tag lang-primary" data-tip="Used across several web projects.">JavaScript</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="skills-cards">
|
<div class="skills-cards">
|
||||||
<div class="skill-card">
|
<div class="skill-card">
|
||||||
<div class="skill-card-icon"><i class="fas fa-terminal"></i></div>
|
<div class="skill-card-icon"><i class="fas fa-terminal"></i></div>
|
||||||
<h3>Environment</h3>
|
<h3>Environment</h3>
|
||||||
<p>I've been running <strong>Arch Linux</strong> (btw) as my daily driver for around five years, so the command line feels like home. I'm comfortable with <strong>Docker</strong> for containerisation and have a solid grasp of Linux system administration.</p>
|
<p>I've been running <strong>Arch Linux</strong> (btw) as my daily driver for around five years, so the command line feels like home. I'm comfortable with <strong>Docker</strong> for containerization and have a solid grasp of Linux system administration.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="skill-card">
|
<div class="skill-card">
|
||||||
<div class="skill-card-icon"><i class="fas fa-comments"></i></div>
|
<div class="skill-card-icon"><i class="fas fa-comments"></i></div>
|
||||||
@@ -199,7 +199,7 @@
|
|||||||
<p>The project has grown into something I actively maintain and support, with a small but dedicated community of around 500 players.</p>
|
<p>The project has grown into something I actively maintain and support, with a small but dedicated community of around 500 players.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-tags">
|
<div class="project-tags">
|
||||||
<span>Python</span><span>PCSX2 / PINE</span><span>MIPS assembly</span>
|
<span data-tip="The client and all the randomizer logic are Python.">Python</span><span data-tip="PCSX2 is the PS2 emulator; PINE is the protocol I use to read and write its memory while the game runs.">PCSX2 / PINE</span><span data-tip="A lot of poking at the game's RAM to work out where it keeps everything.">Reverse engineering</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -221,7 +221,7 @@
|
|||||||
<p>The language includes a tree-walking interpreter and a compiler that targets C. Optional modules extend the standard library with <code>FORS</code> (random numbers), <code>FRACTIO</code> (base-12 fractions for floats), <code>MAGNVM</code> (higher numbers than standard roman numerals), and <code>SVBNVLLA</code> (negative numbers).</p>
|
<p>The language includes a tree-walking interpreter and a compiler that targets C. Optional modules extend the standard library with <code>FORS</code> (random numbers), <code>FRACTIO</code> (base-12 fractions for floats), <code>MAGNVM</code> (higher numbers than standard roman numerals), and <code>SVBNVLLA</code> (negative numbers).</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-tags">
|
<div class="project-tags">
|
||||||
<span>Python</span><span>Language design</span><span>Compiler</span><span>Interpreter</span>
|
<span data-tip="Both the interpreter and the compiler are written in Python.">Python</span><span data-tip="Designed deliberately: grammar, keywords, and semantics planned out with the rigor of my programming-language coursework.">Language design</span><span data-tip="Targets C, which then compiles down to a native binary.">Compiler</span><span data-tip="A tree-walking interpreter for running code directly.">Interpreter</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -251,7 +251,7 @@
|
|||||||
<p>The frontend is built in Svelte and the backend in Python, deployed with Docker Compose.</p>
|
<p>The frontend is built in Svelte and the backend in Python, deployed with Docker Compose.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-tags">
|
<div class="project-tags">
|
||||||
<span>Svelte</span><span>Python</span><span>Docker</span>
|
<span data-tip="The whole frontend: pack opening, trading, and the card game itself.">Svelte</span><span data-tip="Backend, card generation, and game logic.">Python</span><span data-tip="I hardly know 'er!">Docker</span><span data-tip="Every card is generated from a real Wikipedia article, and the data is gathered from calls to the Wikipedia, Wikimedia, and WikiRank APIs.">APIs</span><span data-tip="Frontend, backend, and everything between.">Full-stack</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -271,7 +271,7 @@
|
|||||||
<p>Charizard is turning five this year. In that time I've learned an enormous amount about networking, Linux administration, and the particular satisfaction of fixing something broken at 2 in the morning that nobody asked you to build in the first place.</p>
|
<p>Charizard is turning five this year. In that time I've learned an enormous amount about networking, Linux administration, and the particular satisfaction of fixing something broken at 2 in the morning that nobody asked you to build in the first place.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="project-tags">
|
<div class="project-tags">
|
||||||
<span>Unraid</span><span>Docker</span><span>Reverse Proxy</span><span>Networking</span>
|
<span data-tip="The OS on Charizard, managing the 32 TB disk array.">Unraid</span><span data-tip="Every service runs in its own container.">Docker</span><span data-tip="Nginx Proxy Manager routes traffic to each service, with Let's Encrypt SSL.">Reverse Proxy</span><span data-tip="Subnetting, DNS, and a VPN.">Networking</span><span data-tip="The thing that it is.">Self-hosting</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -299,7 +299,7 @@
|
|||||||
<p class="timeline-company">Copenhagen Capacity · Copenhagen</p>
|
<p class="timeline-company">Copenhagen Capacity · Copenhagen</p>
|
||||||
<p class="timeline-desc">Working on a Python codebase of web crawlers that scrape job postings from across the web, feeding a database that powers a job portal. The work involves maintaining and extending scraping pipelines, handling the messiness of real-world HTML, and keeping the data clean and consistent.</p>
|
<p class="timeline-desc">Working on a Python codebase of web crawlers that scrape job postings from across the web, feeding a database that powers a job portal. The work involves maintaining and extending scraping pipelines, handling the messiness of real-world HTML, and keeping the data clean and consistent.</p>
|
||||||
<div class="timeline-tags">
|
<div class="timeline-tags">
|
||||||
<span>Python</span><span>Web scraping</span><span>Databases</span>
|
<span data-tip="The entire crawler codebase is Python.">Python</span><span data-tip="The codebase has around 200 scrapers. I introduced a scraper class that skips a lot of the boilerplate when building a new one.">Web scraping</span><span data-tip="Direct SQL was rare, but every so often HubSpot would change its ID system and I'd have to go in and swap 1,500 values.">Databases</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -313,9 +313,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<h3 class="timeline-role">IT Project Manager</h3>
|
<h3 class="timeline-role">IT Project Manager</h3>
|
||||||
<p class="timeline-company">360 Law Firm · Copenhagen</p>
|
<p class="timeline-company">360 Law Firm · Copenhagen</p>
|
||||||
<p class="timeline-desc">A broad role spanning data science work, internal tech support, and IT project management. Also served on the firm's AI task force, helping figure out how AI should be used in the organization.</p>
|
<p class="timeline-desc">Brought in to introduce business intelligence to the firm, where I planned and managed the build of a reporting pipeline that ran from API to finished report. The role broadened from there into wider data science work and internal tech support, and I served on the firm's AI task force, helping figure out how AI should be used in the organization.</p>
|
||||||
<div class="timeline-tags">
|
<div class="timeline-tags">
|
||||||
<span>Data science</span><span>Project management</span><span>AI</span>
|
<span data-tip="I was hired to build a pipeline that carried data from API to finished report.">Business intelligence</span><span data-tip="Mostly monthly income calculations broken down across categories and client groups, plus client profiling.">Data science</span><span data-tip="Managed a team of just myself. But I'm pretty difficult to manage, so it's still impressive.">Project management</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -331,7 +331,7 @@
|
|||||||
<p class="timeline-company">Valuer.ai · Copenhagen</p>
|
<p class="timeline-company">Valuer.ai · Copenhagen</p>
|
||||||
<p class="timeline-desc">Database management for a neural network startup, keeping the data infrastructure that fed the core model clean, organised, and running reliably.</p>
|
<p class="timeline-desc">Database management for a neural network startup, keeping the data infrastructure that fed the core model clean, organised, and running reliably.</p>
|
||||||
<div class="timeline-tags">
|
<div class="timeline-tags">
|
||||||
<span>Data science</span><span>Databases</span><span>Neural networks</span>
|
<span data-tip="Keeping the model's data clean and consistent.">Data science</span><span data-tip="Managing the data infrastructure that fed the core model.">Databases</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -353,7 +353,7 @@ main { position: relative; z-index: 1; }
|
|||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
||||||
}
|
}
|
||||||
.skill-card:hover { transform: translateY(-5px); box-shadow: var(--glow-orange); }
|
.skill-card:hover { box-shadow: var(--glow-orange); }
|
||||||
.skill-card-icon { width: 42px; height: 42px; border-radius: 10px; background: var(--grad); display: flex; align-items: center; justify-content: center; color: #fff; font-size: 1.05rem; margin-bottom: 0.85rem; }
|
.skill-card-icon { width: 42px; height: 42px; border-radius: 10px; background: var(--grad); display: flex; align-items: center; justify-content: center; color: #fff; font-size: 1.05rem; margin-bottom: 0.85rem; }
|
||||||
.skill-card h3 { font-size: 1.05rem; font-weight: 800; margin-bottom: 1.25rem; }
|
.skill-card h3 { font-size: 1.05rem; font-weight: 800; margin-bottom: 1.25rem; }
|
||||||
|
|
||||||
@@ -368,7 +368,7 @@ main { position: relative; z-index: 1; }
|
|||||||
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
.project-card:hover { transform: translateY(-4px); box-shadow: var(--glow-orange); }
|
.project-card:hover { box-shadow: var(--glow-orange); }
|
||||||
|
|
||||||
.project-header {
|
.project-header {
|
||||||
display: flex; align-items: flex-start; gap: 1rem;
|
display: flex; align-items: flex-start; gap: 1rem;
|
||||||
@@ -421,6 +421,13 @@ main { position: relative; z-index: 1; }
|
|||||||
padding: 0.25rem 0.7rem; border-radius: 999px;
|
padding: 0.25rem 0.7rem; border-radius: 999px;
|
||||||
background: rgba(26,10,0,0.07); color: var(--ink-soft);
|
background: rgba(26,10,0,0.07); color: var(--ink-soft);
|
||||||
border: 1px solid rgba(244, 114, 43, 0.18);
|
border: 1px solid rgba(244, 114, 43, 0.18);
|
||||||
|
transition: all 0.2s var(--ease);
|
||||||
|
}
|
||||||
|
.project-tags span:hover, .timeline-tags span:hover {
|
||||||
|
background: var(--orange);
|
||||||
|
border-color: var(--orange);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: var(--glow-orange);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ── Timeline / Experience ──────────────────────────────────── */
|
/* ── Timeline / Experience ──────────────────────────────────── */
|
||||||
@@ -444,7 +451,7 @@ main { position: relative; z-index: 1; }
|
|||||||
box-shadow: var(--card-shadow);
|
box-shadow: var(--card-shadow);
|
||||||
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
||||||
}
|
}
|
||||||
.timeline-card:hover { transform: translateX(5px); box-shadow: var(--glow-orange); }
|
.timeline-card:hover { box-shadow: var(--glow-orange); }
|
||||||
.timeline-meta { display: flex; align-items: center; gap: 0.65rem; margin-bottom: 0.6rem; }
|
.timeline-meta { display: flex; align-items: center; gap: 0.65rem; margin-bottom: 0.6rem; }
|
||||||
.timeline-date { font-family: var(--mono); font-size: 0.75rem; color: var(--orange); }
|
.timeline-date { font-family: var(--mono); font-size: 0.75rem; color: var(--orange); }
|
||||||
.timeline-tag { font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; background: rgba(244,114,43,0.12); color: var(--orange); border-radius: 999px; padding: 0.18rem 0.6rem; }
|
.timeline-tag { font-size: 0.65rem; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; background: rgba(244,114,43,0.12); color: var(--orange); border-radius: 999px; padding: 0.18rem 0.6rem; }
|
||||||
@@ -469,7 +476,7 @@ main { position: relative; z-index: 1; }
|
|||||||
display: flex; gap: 1.25rem; align-items: flex-start;
|
display: flex; gap: 1.25rem; align-items: flex-start;
|
||||||
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
transition: transform 0.3s var(--ease), box-shadow 0.3s;
|
||||||
}
|
}
|
||||||
.edu-card:hover { transform: translateY(-4px); box-shadow: var(--glow-orange); }
|
.edu-card:hover { box-shadow: var(--glow-orange); }
|
||||||
.edu-icon { width: 46px; height: 46px; flex-shrink: 0; border-radius: 12px; background: var(--grad); display: flex; align-items: center; justify-content: center; color: #fff; font-size: 1.15rem; }
|
.edu-icon { width: 46px; height: 46px; flex-shrink: 0; border-radius: 12px; background: var(--grad); display: flex; align-items: center; justify-content: center; color: #fff; font-size: 1.15rem; }
|
||||||
.edu-body h3 { font-size: 1.05rem; font-weight: 800; margin-bottom: 0.25rem; }
|
.edu-body h3 { font-size: 1.05rem; font-weight: 800; margin-bottom: 0.25rem; }
|
||||||
.edu-school { font-weight: 700; color: var(--orange); font-size: 0.85rem; margin-bottom: 0.2rem; }
|
.edu-school { font-weight: 700; color: var(--orange); font-size: 0.85rem; margin-bottom: 0.2rem; }
|
||||||
@@ -561,10 +568,59 @@ footer {
|
|||||||
background: var(--orange);
|
background: var(--orange);
|
||||||
border-color: var(--orange);
|
border-color: var(--orange);
|
||||||
color: #fff;
|
color: #fff;
|
||||||
transform: translateY(-2px);
|
|
||||||
box-shadow: var(--glow-orange);
|
box-shadow: var(--glow-orange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Skill pills: shared behavior (no select + tooltip anchor) ─── */
|
||||||
|
.lang-tag, .project-tags span, .timeline-tags span {
|
||||||
|
position: relative;
|
||||||
|
cursor: default;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-tag[data-tip]:not([data-tip=""]):hover::after,
|
||||||
|
.project-tags span[data-tip]:not([data-tip=""]):hover::after,
|
||||||
|
.timeline-tags span[data-tip]:not([data-tip=""]):hover::after {
|
||||||
|
content: attr(data-tip);
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(100% + 10px);
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
width: max-content;
|
||||||
|
max-width: 220px;
|
||||||
|
padding: 0.5rem 0.7rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: var(--ink);
|
||||||
|
color: var(--cream);
|
||||||
|
font-family: var(--font);
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.4;
|
||||||
|
letter-spacing: normal;
|
||||||
|
text-transform: none;
|
||||||
|
text-align: center;
|
||||||
|
white-space: normal;
|
||||||
|
box-shadow: var(--card-shadow);
|
||||||
|
z-index: 10;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip arrow */
|
||||||
|
.lang-tag[data-tip]:not([data-tip=""]):hover::before,
|
||||||
|
.project-tags span[data-tip]:not([data-tip=""]):hover::before,
|
||||||
|
.timeline-tags span[data-tip]:not([data-tip=""]):hover::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(100% - 3px);
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
border: 8px solid transparent;
|
||||||
|
border-top-color: var(--ink);
|
||||||
|
z-index: 10;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.skills-cards {
|
.skills-cards {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
|||||||
Reference in New Issue
Block a user