This commit is contained in:
2026-09-05 20:37:31 -05:00
parent c6fa2c41a7
commit d73ed21719
6 changed files with 246 additions and 10 deletions

View File

@@ -22,7 +22,7 @@ static_file_hashes = {}
for dirpath, _, filenames in os.walk(app.static_folder):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
relative = os.path.relpath(filepath, app.static_folder)
relative = os.path.relpath(filepath, app.static_folder).replace(os.sep, '/')
with open(filepath, 'rb') as f:
static_file_hashes[relative] = hashlib.md5(f.read()).hexdigest()[:8]

View File

@@ -1947,6 +1947,194 @@ tr {
margin-top: 1.5em;
}
/* Deep-dive project panels (collapsed by default) */
.feature-projects {
display: flex;
flex-direction: column;
gap: 2em;
margin-bottom: 3.5em;
}
.feature-project {
position: relative;
padding: 0;
overflow: hidden;
border-top: none;
border-radius: 0.75em;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
}
.feature-project::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--accent) 0%, rgba(var(--accent-rgb), 0.85) 40%, rgba(var(--accent-rgb), 0.15) 100%);
z-index: 2;
}
.feature-project-content {
position: relative;
z-index: 1;
}
.feature-project-media {
position: absolute;
top: 0;
bottom: 0;
right: 0;
width: 620px;
background-size: cover;
background-position: right bottom;
z-index: 0;
}
@media screen and (max-width: 1100px) {
.feature-project-media {
display: none;
}
}
.feature-project-media::after {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(
to right,
rgba(var(--bg-card-rgb), 1) 0%,
rgba(var(--bg-card-rgb), 1) 50%,
rgba(var(--bg-card-rgb), 0.75) 62%,
rgba(var(--bg-card-rgb), 0.15) 82%,
rgba(var(--bg-card-rgb), 0) 96%
);
}
.feature-project-header {
position: relative;
overflow: hidden;
display: flex;
align-items: center;
justify-content: space-between;
gap: 1.5em;
padding: 1.75em 2em;
cursor: pointer;
transition: background 0.2s ease;
}
.feature-project-heading {
position: relative;
z-index: 1;
max-width: 46em;
}
.feature-project-header:hover {
background: rgba(var(--accent-rgb), 0.06);
}
.feature-project-header h3 {
margin: 0 0 0.35em 0;
color: var(--text-heading);
font-size: 1.55rem;
}
.feature-project-tagline {
margin: 0;
color: var(--text-secondary);
font-size: 1rem;
}
.feature-project-toggle {
position: relative;
z-index: 1;
flex-shrink: 0;
width: 2.4em;
height: 2.4em;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid rgba(var(--accent-rgb), 0.4);
border-radius: 50%;
background: rgba(var(--bg-card-rgb), 0.85);
color: var(--text-heading);
font-size: 1.4rem;
line-height: 1;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
transition: transform 0.3s ease, border-color 0.2s ease, background 0.2s ease;
}
.feature-project.expanded .feature-project-toggle {
transform: rotate(45deg);
border-color: var(--accent);
background: rgba(var(--accent-rgb), 0.18);
}
.feature-project-body {
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.3s ease;
}
.feature-project.expanded .feature-project-body {
grid-template-rows: 1fr;
}
.feature-project-body-inner {
overflow: hidden;
}
.feature-project-prose {
max-width: 46em;
padding: 1.5em 2em 1.75em;
border-top: 1px solid var(--border-subtle);
font-size: 1.05rem;
line-height: 1.75;
}
.feature-project-prose p + p {
margin-top: 1em;
}
.feature-project-prose code {
background: rgba(var(--accent-rgb), 0.12);
padding: 0.1em 0.4em;
border-radius: 0.25em;
font-size: 0.9em;
}
.feature-project-stack {
display: flex;
flex-wrap: wrap;
gap: 0.6em;
padding: 0 2em 2em;
}
.feature-project-stack-item {
padding: 0.35em 0.9em;
border: 1px solid rgba(var(--accent-rgb), 0.35);
border-radius: 2em;
background: rgba(var(--accent-rgb), 0.07);
color: var(--text-secondary);
font-family: 'Courier New', Courier, monospace;
font-size: 0.8rem;
letter-spacing: 0.03em;
}
@media screen and (max-width: 900px) {
.feature-project-header {
padding: 1.5em;
}
.feature-project-prose {
padding: 1.25em 1.5em 1.5em;
}
.feature-project-stack {
padding: 0 1.5em 1.5em;
}
}
.skills-certs-grid {
display: grid;
grid-template-columns: 1fr 1fr;

View File

@@ -98,6 +98,12 @@ function backButton() {
goto(path.substring(1), { push: false });
}
function toggleFeatureProject(header) {
const panel = header.closest(".feature-project");
const expanded = panel.classList.toggle("expanded");
header.setAttribute("aria-expanded", expanded ? "true" : "false");
}
function activeSkill(obj) {
let skill = obj.closest(".skill");
if (skill.classList.contains("activeSkill")) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

View File

@@ -0,0 +1,30 @@
{% macro feature_project(id, title, tagline, body, stack, image=none) %}
<div class="feature-project boxed{{ ' has-media' if image else '' }}" id="feature-{{ id }}">
<div class="feature-project-content">
<div class="feature-project-header" onclick="toggleFeatureProject(this)" onKeyDown="if(event.key==='Enter'||event.key===' ')toggleFeatureProject(this)" role="button" tabindex="0" aria-expanded="false" aria-controls="feature-{{ id }}-body">
{% if image %}
<div class="feature-project-media" style="background-image: url('{{ url_for('static', filename=image) }}')"></div>
{% endif %}
<div class="feature-project-heading">
<h3>{{ title }}</h3>
<p class="feature-project-tagline">{{ tagline }}</p>
</div>
<span class="feature-project-toggle" aria-hidden="true">+</span>
</div>
<div class="feature-project-body" id="feature-{{ id }}-body">
<div class="feature-project-body-inner">
<div class="feature-project-prose">
{{ body | safe }}
</div>
{% if stack %}
<div class="feature-project-stack">
{% for item in stack %}
<span class="feature-project-stack-item">{{ item }}</span>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endmacro %}

View File

@@ -10,18 +10,30 @@
<div class="homeSubContent">
<div class="foregroundContent">
<!-- Intro -->
<div style="max-width: 52em; margin-bottom: 3em;">
<p style="color: var(--text-secondary); font-size: 0.9rem;">
Curious who's behind these?
<a href="about">About &amp; certifications</a>
·
<a href="Resume_Simonson_Andrew.pdf" target="_blank">Resume</a>
</p>
<!-- Deep Dives -->
<h2>Highlights</h2>
<div class="feature-projects">
{% from 'partials/feature_project.html' import feature_project %}
{{ feature_project(
'homelab',
'AI Homelab',
'Self-hosted GPU cluster serving MoE models beyond their rated VRAM, with a full LoRA fine-tuning pipeline on top',
'<p>I\'ve refined a method to pool GPUs from different vendors and generations — an RTX 5070 and a Radeon RX 6600 XT — into a single ~20GB inference backend over Vulkan, so neither card\'s limits bottleneck the other. FreeToken, a bleeding-edge MoE inference server, fronts the cluster and handles the actual elastic inference, running Mixture-of-Experts models with far more total parameters than the cluster\'s VRAM would normally allow. My custom scripts resolve KV caching and system prompt compaction that make the setup harness-capable, so agentic tools like Claude Code can hit it over Anthropic- and OpenAI-compatible endpoints exactly like they would a paid API. GPU layer placement alone took generation from ~150s a call to ~88ms/token.</p><p>The same rig trains as well as it serves. I built a complete LoRA fine-tuning pipeline on it — base model, training config, and adapter. Used for fine-tuning Qwen3-1.7B on patent data, and training a Stable Diffusion LoRA set on curated, deduplicated image data.</p>',
['RTX 5070', 'RX 6600 XT', 'Ollama', 'FreeToken', 'ComfyUI', 'PEFT / TRL'],
'photos/deepdives/homelab-dashboard.png'
) }}
{{ feature_project(
'siteinfra',
'Self-Hosted Web Tooling & Maintenance',
'I designed, deployed, and still actively operate this site\'s stack — the app, the edge, and its own health monitoring',
'<p>This portfolio runs on infrastructure I designed, deployed, and still operate myself — the Flask app and its container, the Cloudflare edge and reverse proxy in front of it, DNS and tunneling, and the uptime monitor watching all of it (live at <a href="status">/status</a>). None of that is a managed host\'s job here; it\'s mine.</p><p>What actually proves it\'s production-capable is the maintenance loop, not the launch. I put it through a real adversarial review periodically — most recently a live pass with Claude Code against both the deployed site and the source — and fix what it finds. The latest pass closed two real issues same-day: a database-query pattern on an internal endpoint that could be hammered into needless load, and a self-built redirect that could momentarily hand a browser plaintext HTTP instead of HTTPS. Nothing catastrophic came up — no injection, no auth bypass, no data exposure — which is exactly the point of doing this on a schedule instead of waiting for something to break.</p>',
['Flask', 'Docker', 'PostgreSQL', 'Reverse Proxy', 'Cloudflare']
) }}
</div>
<!-- Projects -->
<h2>Projects</h2>
<h2>All Projects</h2>
<div class="projectList">
{% from 'partials/project.html' import project %} {% for i in
var["projects"] %} {{ project(i, var["projects"][i]["classes"],